diff options
| author | Christian Cleberg <[email protected]> | 2026-08-07 04:19:04 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-08-07 04:19:04 -0500 |
| commit | 3ae35daa9495b9b1e01f1063bd05c636b97fd927 (patch) | |
| tree | 25cf810fa0e9a0ab343b60b59c26d8cc096c9a98 /Hutch/Views/Projects/ProjectDetailView.swift | |
| parent | db40d346c28800a1eec384a5e4f3d7d090198e4d (diff) | |
| parent | 1f8f651a9c2ac2d6be231ff9f71ee3bcce9936d8 (diff) | |
| download | hutch-3ae35daa9495b9b1e01f1063bd05c636b97fd927.tar.gz hutch-3ae35daa9495b9b1e01f1063bd05c636b97fd927.tar.bz2 hutch-3ae35daa9495b9b1e01f1063bd05c636b97fd927.zip | |
Merge pull request #33 from krazywarez/projects-crud
Projects: discovery, create, edit, manage linked resources (#12, #13, #14, #15)
Diffstat (limited to 'Hutch/Views/Projects/ProjectDetailView.swift')
| -rw-r--r-- | Hutch/Views/Projects/ProjectDetailView.swift | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/Hutch/Views/Projects/ProjectDetailView.swift b/Hutch/Views/Projects/ProjectDetailView.swift index c30ec88..0685a83 100644 --- a/Hutch/Views/Projects/ProjectDetailView.swift +++ b/Hutch/Views/Projects/ProjectDetailView.swift @@ -2,6 +2,7 @@ import SwiftUI struct ProjectDetailView: View { let project: Project + var canManage: Bool = false @Environment(AppState.self) private var appState @Environment(\.dismiss) private var dismiss @@ -10,6 +11,14 @@ struct ProjectDetailView: View { @State private var isLoading = false @State private var error: String? @State private var pinChangeCount = 0 + @State private var isPresentingEdit = false + @State private var isPresentingManage = false + @State private var isSavingEdit = false + @State private var editError: String? + + private var projectService: ProjectService { + ProjectService(client: appState.client) + } private var displayedProject: Project { detailProject ?? project @@ -59,6 +68,46 @@ struct ProjectDetailView: View { .accessibilityLabel(isPinnedToHome ? "Unpin from Home" : "Pin to Home") } } + if canManage { + ToolbarItem(placement: .topBarTrailing) { + Menu { + Button { + editError = nil + isPresentingEdit = true + } label: { + Label("Edit Project", systemImage: "pencil") + } + Button { + isPresentingManage = true + } label: { + Label("Manage Resources", systemImage: "link") + } + } label: { + Image(systemName: "ellipsis.circle") + } + .accessibilityLabel("Manage project") + } + } + } + .sheet(isPresented: $isPresentingEdit) { + ProjectFormSheet( + title: "Edit Project", + confirmationTitle: "Save", + isSaving: isSavingEdit, + error: editError, + includeWebsite: true, + initialName: displayedProject.name, + initialDescription: displayedProject.description ?? "", + initialWebsite: displayedProject.website ?? "", + initialTags: displayedProject.tags, + initialVisibility: displayedProject.visibility, + onSave: { await saveEdits($0) } + ) + } + .sheet(isPresented: $isPresentingManage) { + ManageProjectResourcesView(project: displayedProject) { + await loadProjectIfNeeded(forceRefresh: true) + } } .task { await loadProjectIfNeeded() @@ -69,6 +118,47 @@ struct ProjectDetailView: View { .srhtErrorBanner(error: $error) } + private func saveEdits(_ values: ProjectFormValues) async -> Bool { + guard !isSavingEdit else { return false } + isSavingEdit = true + editError = nil + defer { isSavingEdit = false } + + do { + let updated = try await projectService.updateProject( + rid: displayedProject.id, + name: values.name, + description: values.description, + website: values.website, + visibility: values.visibility, + tags: values.tags + ) + // Preserve already-loaded linked resources; the mutation returns metadata only. + detailProject = Project( + metadata: .init( + id: updated.id, + name: updated.name, + description: updated.description, + website: updated.website, + visibility: updated.visibility, + tags: updated.tags, + updated: updated.updated + ), + resources: .init( + mailingLists: displayedProject.mailingLists, + sources: displayedProject.sources, + trackers: displayedProject.trackers, + isFullyLoaded: displayedProject.isFullyLoaded + ) + ) + await loadProjectIfNeeded(forceRefresh: true) + return true + } catch { + editError = error.userFacingMessage + return false + } + } + @ViewBuilder private var headerSection: some View { Section { |
