gitbay/Views/Admin/AdminView.swift
83 lines · 3661 bytes
1 symbol in this file
1import SwiftUI
2
3/// The web's /admin page: the server build, a way to the accounts, and
4/// every worker queue's backlog in the web's order and words.
5struct AdminView: View {
6
7 @State private var model: AdminOverviewViewModel
8
9 init(client: GitbayClient) {
10 _model = State(initialValue: AdminOverviewViewModel(client: client))
11 }
12
13 var body: some View {
14 List {
15 if let overview = model.state.value {
16 Section {
17 NavigationLink(value: RepoRoute.adminUsers) {
18 Label("Accounts", systemImage: "person.2")
19 }
20 .accessibilityIdentifier("admin-accounts")
21 NavigationLink(value: RepoRoute.adminRepos) {
22 Label("Repositories", systemImage: "books.vertical")
23 }
24 .accessibilityIdentifier("admin-repos")
25 NavigationLink(value: RepoRoute.adminOrgLimits) {
26 Label("Organization limits", systemImage: "building.2")
27 }
28 .accessibilityIdentifier("admin-org-limits")
29 NavigationLink(value: RepoRoute.adminStats) {
30 Label("Statistics", systemImage: "chart.bar")
31 }
32 .accessibilityIdentifier("admin-stats")
33 NavigationLink(value: RepoRoute.adminAudit) {
34 Label("Audit log", systemImage: "list.bullet.rectangle")
35 }
36 .accessibilityIdentifier("admin-audit")
37 NavigationLink(value: RepoRoute.adminRunners) {
38 Label("Runners", systemImage: "hammer")
39 }
40 .accessibilityIdentifier("admin-runners")
41 NavigationLink(value: RepoRoute.adminInbound) {
42 Label("Inbound mail", systemImage: "tray.and.arrow.down")
43 }
44 .accessibilityIdentifier("admin-inbound")
45 } footer: {
46 if let commit = overview.commit {
47 Text("Server build \(commit).")
48 }
49 }
50 ForEach(overview.sections) { section in
51 Section {
52 if section.rows.isEmpty {
53 Text(section.empty)
54 .font(.gbSans(.subheadline))
55 .foregroundStyle(.secondary)
56 } else {
57 ForEach(section.rows) { line in
58 VStack(alignment: .leading, spacing: 2) {
59 Text(line.title)
60 .font(.gbSans(.subheadline).weight(.medium))
61 Text(line.detail)
62 .font(.gbSans(.caption))
63 .foregroundStyle(.secondary)
64 }
65 }
66 }
67 } header: {
68 Text("\(section.heading) \(section.count)")
69 } footer: {
70 if let summary = section.summary {
71 Text(summary)
72 }
73 }
74 }
75 }
76 }
77 .overlay { LoadStateOverlay(state: model.state) }
78 .navigationTitle("Admin")
79 .navigationBarTitleDisplayMode(.inline)
80 .task { await model.load() }
81 .refreshable { await model.load() }
82 }
83}