Beware!
@@ -232,9 +280,12 @@ import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import { LeafletMouseEvent } from "leaflet";
+import EntityIcon from "../components/EntityIcon.vue";
import ImageMethodDialog from "../components/ImageMethodDialog.vue";
+import ProjectRepresentativeDialog from "../components/ProjectRepresentativeDialog.vue";
import QuickNav from "../components/QuickNav.vue";
import {
+ AppString,
DEFAULT_IMAGE_API_SERVER,
DEFAULT_PARTNER_API_SERVER,
NotificationIface,
@@ -268,6 +319,7 @@ import {
retrieveAccountCount,
retrieveFullyDecryptedAccount,
} from "../libs/util";
+import { Contact } from "../db/tables/contacts";
import {
EventTemplate,
@@ -323,7 +375,15 @@ import { logger } from "../utils/logger";
*/
@Component({
- components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav },
+ components: {
+ EntityIcon,
+ ImageMethodDialog,
+ ProjectRepresentativeDialog,
+ LMap,
+ LMarker,
+ LTileLayer,
+ QuickNav,
+ },
mixins: [PlatformServiceMixin],
})
export default class NewEditProjectView extends Vue {
@@ -334,6 +394,9 @@ export default class NewEditProjectView extends Vue {
// Notification helpers
private notify!: ReturnType;
+ // Constants
+ AppString = AppString;
+
/**
* Display error notification to user
* Provides consistent error messaging with 5-second timeout
@@ -346,6 +409,8 @@ export default class NewEditProjectView extends Vue {
// Component state properties
activeDid = "";
agentDid = "";
+ allContacts: Array = [];
+ allMyDids: string[] = [];
apiServer = "";
endDateInput?: string;
endTimeInput?: string;
@@ -392,16 +457,24 @@ export default class NewEditProjectView extends Vue {
const activeIdentity = await (this as any).$getActiveIdentity();
this.activeDid = activeIdentity.activeDid || "";
+ // Get all user's DIDs
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ this.allMyDids = await (this as any).$getAllAccountDids();
+
+ // Load contacts sorted by date added (newest first) for consistent "Recently Added" display
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ this.allContacts = await (this as any).$contactsByDateAdded();
+
this.apiServer = settings.apiServer || "";
this.showGeneralAdvanced = !!settings.showGeneralAdvanced;
this.projectId = (this.$route.query["projectId"] as string) || "";
- if (this.projectId) {
+ if (this.isSavedProject()) {
if (this.numAccounts === 0) {
this.notify.error(NOTIFY_PROJECT_ACCOUNT_LOADING_ERROR.message);
} else {
- this.loadProject(this.activeDid);
+ this.loadProject(this.activeDid, this.projectId);
}
}
}
@@ -411,11 +484,9 @@ export default class NewEditProjectView extends Vue {
* Retrieves project information from the API and populates form fields
* @param userDid - User's decentralized identifier
*/
- async loadProject(userDid: string) {
+ async loadProject(userDid: string, projectId: string) {
const url =
- this.apiServer +
- "/api/claim/byHandle/" +
- encodeURIComponent(this.projectId);
+ this.apiServer + "/api/claim/byHandle/" + encodeURIComponent(projectId);
const headers = await getHeaders(userDid);
try {
@@ -432,6 +503,12 @@ export default class NewEditProjectView extends Vue {
}
if (this.fullClaim?.agent?.identifier) {
this.agentDid = this.fullClaim.agent.identifier;
+ if (this.activeDid !== this.projectIssuerDid) {
+ this.agentDid = this.projectIssuerDid;
+ this.notify.warning(
+ "You were previously the agent, so the agent has been set to the previous owner. You can change it.",
+ );
+ }
}
if (this.fullClaim.startTime) {
const localDateTime = DateTime.fromISO(
@@ -536,7 +613,7 @@ export default class NewEditProjectView extends Vue {
private async saveProject() {
// Make a claim
const vcClaim: PlanActionClaim = this.fullClaim;
- if (this.projectId) {
+ if (this.isSavedProject()) {
vcClaim.lastClaimId = this.lastClaimJwtId;
}
if (this.agentDid) {
@@ -870,6 +947,10 @@ export default class NewEditProjectView extends Vue {
this.longitude = event.latlng.lng;
}
+ private isSavedProject(): boolean {
+ return !!this.projectId;
+ }
+
/**
* Computed property for character count display
* Shows current description length and maximum character limit
@@ -885,6 +966,7 @@ export default class NewEditProjectView extends Vue {
*/
get shouldShowOwnershipWarning(): boolean {
return (
+ this.isSavedProject() &&
this.activeDid !== this.projectIssuerDid &&
this.agentDid !== this.projectIssuerDid
);
@@ -961,5 +1043,37 @@ export default class NewEditProjectView extends Vue {
get shouldShowSpinner(): boolean {
return !this.isHiddenSpinner;
}
+
+ /**
+ * Computed property for selected representative contact
+ * Derives the contact from agentDid by finding it in allContacts
+ */
+ get selectedRepresentative(): Contact | null {
+ if (!this.agentDid) {
+ return null;
+ }
+ return this.allContacts.find((c) => c.did === this.agentDid) || null;
+ }
+
+ /**
+ * Open the representative selection dialog
+ */
+ openRepresentativeDialog(): void {
+ (this.$refs.representativeDialog as ProjectRepresentativeDialog).open();
+ }
+
+ /**
+ * Handle representative assignment from dialog
+ */
+ handleRepresentativeAssigned(contact: Contact): void {
+ this.agentDid = contact.did;
+ }
+
+ /**
+ * Unset the representative and revert to initial state
+ */
+ unsetRepresentative(): void {
+ this.agentDid = "";
+ }
}