From 05d346edce2025cd9afaac506483e434d35de091 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sun, 22 Mar 2026 17:58:46 -0600 Subject: [PATCH] add project selection for one that this 'fulfills' --- ...tDialog.vue => ProjectSelectionDialog.vue} | 4 +- src/interfaces/claims.ts | 1 + src/views/NewEditProjectView.vue | 88 +++++++++++++++++++ src/views/OnboardMeetingSetupView.vue | 8 +- src/views/ProjectViewView.vue | 4 +- 5 files changed, 97 insertions(+), 8 deletions(-) rename src/components/{MeetingProjectDialog.vue => ProjectSelectionDialog.vue} (95%) diff --git a/src/components/MeetingProjectDialog.vue b/src/components/ProjectSelectionDialog.vue similarity index 95% rename from src/components/MeetingProjectDialog.vue rename to src/components/ProjectSelectionDialog.vue index 4262334c..a7605066 100644 --- a/src/components/MeetingProjectDialog.vue +++ b/src/components/ProjectSelectionDialog.vue @@ -39,7 +39,7 @@ import { PlanData } from "../interfaces/records"; import { NotificationIface } from "../constants/app"; /** - * MeetingProjectDialog - Dialog for selecting a project link for a meeting + * ProjectSelectionDialog - Dialog for selecting a project * * Features: * - EntityGrid integration for project selection @@ -52,7 +52,7 @@ import { NotificationIface } from "../constants/app"; EntityGrid, }, }) -export default class MeetingProjectDialog extends Vue { +export default class ProjectSelectionDialog extends Vue { /** Whether the dialog is visible */ visible = false; diff --git a/src/interfaces/claims.ts b/src/interfaces/claims.ts index 49e2b4a8..76572fd8 100644 --- a/src/interfaces/claims.ts +++ b/src/interfaces/claims.ts @@ -80,6 +80,7 @@ export interface PlanActionClaim extends ClaimObject { agent?: { identifier: string }; description?: string; endTime?: string; + fulfills?: { "@type": string; identifier?: string; lastClaimId?: string }; identifier?: string; image?: string; lastClaimId?: string; diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index 7f0964bf..dae7d3fd 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -114,6 +114,49 @@ @assign="handleRepresentativeAssigned" /> + +
+
+
+ +
+
+
+ {{ + parentProjectHandleId + ? parentProjectName || "Parent Project" + : "Select Parent Project\u2026" + }} +
+
+
+ +
+ + +

Beware! @@ -283,6 +326,7 @@ import { LeafletMouseEvent } from "leaflet"; import EntityIcon from "../components/EntityIcon.vue"; import ImageMethodDialog from "../components/ImageMethodDialog.vue"; import ProjectRepresentativeDialog from "../components/ProjectRepresentativeDialog.vue"; +import ProjectSelectionDialog from "../components/ProjectSelectionDialog.vue"; import QuickNav from "../components/QuickNav.vue"; import { AppString, @@ -311,6 +355,7 @@ import { PROJECT_TIMEOUT_VERY_LONG, } from "../constants/notifications"; import { PlanActionClaim } from "../interfaces/claims"; +import { PlanData } from "../interfaces/records"; import { createEndorserJwtVcFromClaim, getHeaders, @@ -378,6 +423,7 @@ import { logger } from "../utils/logger"; components: { EntityIcon, ImageMethodDialog, + ProjectSelectionDialog, ProjectRepresentativeDialog, LMap, LMarker, @@ -429,6 +475,8 @@ export default class NewEditProjectView extends Vue { latitude = 0; longitude = 0; numAccounts = 0; + parentProjectHandleId = ""; + parentProjectName = ""; projectId = ""; projectIssuerDid = ""; sendToTrustroots = false; @@ -510,6 +558,10 @@ export default class NewEditProjectView extends Vue { ); } } + if (this.fullClaim?.fulfills?.identifier) { + this.parentProjectHandleId = this.fullClaim.fulfills.identifier; + this.loadParentProjectName(this.parentProjectHandleId); + } if (this.fullClaim.startTime) { const localDateTime = DateTime.fromISO( this.fullClaim.startTime as string, @@ -623,6 +675,14 @@ export default class NewEditProjectView extends Vue { } else { delete vcClaim.agent; } + if (this.parentProjectHandleId) { + vcClaim.fulfills = { + "@type": "PlanAction", + identifier: this.parentProjectHandleId, + }; + } else { + delete vcClaim.fulfills; + } if (this.imageUrl) { vcClaim.image = this.imageUrl; } else { @@ -1075,5 +1135,33 @@ export default class NewEditProjectView extends Vue { unsetRepresentative(): void { this.agentDid = ""; } + + openParentProjectDialog(): void { + (this.$refs.parentProjectDialog as ProjectSelectionDialog).open(); + } + + handleParentProjectSelected(project: PlanData): void { + this.parentProjectHandleId = project.handleId; + this.parentProjectName = project.name; + } + + unsetParentProject(): void { + this.parentProjectHandleId = ""; + this.parentProjectName = ""; + } + + private async loadParentProjectName(handleId: string): Promise { + try { + const url = + this.apiServer + "/api/claim/byHandle/" + encodeURIComponent(handleId); + const headers = await getHeaders(this.activeDid); + const resp = await this.axios.get(url, { headers }); + if (resp.status === 200 && resp.data?.claim?.name) { + this.parentProjectName = resp.data.claim.name; + } + } catch { + // Parent project name will remain empty + } + } } diff --git a/src/views/OnboardMeetingSetupView.vue b/src/views/OnboardMeetingSetupView.vue index 1d7c91b3..143a1eb5 100644 --- a/src/views/OnboardMeetingSetupView.vue +++ b/src/views/OnboardMeetingSetupView.vue @@ -267,7 +267,7 @@

-

- Projects That Contribute To This + These Projects Are Part Of This