|  |  | @ -29,10 +29,31 @@ | 
			
		
	
		
			
				
					|  |  |  |       v-model="fullClaim.name" | 
			
		
	
		
			
				
					|  |  |  |     /> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     <div class="flex justify-center mt-4"> | 
			
		
	
		
			
				
					|  |  |  |       <span v-if="imageUrl" class="flex justify-between"> | 
			
		
	
		
			
				
					|  |  |  |         <a :href="imageUrl" target="_blank" class="text-blue-500 ml-4"> | 
			
		
	
		
			
				
					|  |  |  |           <img :src="imageUrl" class="h-24 rounded-xl" /> | 
			
		
	
		
			
				
					|  |  |  |         </a> | 
			
		
	
		
			
				
					|  |  |  |         <fa | 
			
		
	
		
			
				
					|  |  |  |           icon="trash-can" | 
			
		
	
		
			
				
					|  |  |  |           @click="confirmDeleteImage" | 
			
		
	
		
			
				
					|  |  |  |           class="text-red-500 fa-fw ml-8 mt-10" | 
			
		
	
		
			
				
					|  |  |  |         /> | 
			
		
	
		
			
				
					|  |  |  |       </span> | 
			
		
	
		
			
				
					|  |  |  |       <span v-else> | 
			
		
	
		
			
				
					|  |  |  |         <fa | 
			
		
	
		
			
				
					|  |  |  |           icon="camera" | 
			
		
	
		
			
				
					|  |  |  |           class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md" | 
			
		
	
		
			
				
					|  |  |  |           @click="openImageDialog" | 
			
		
	
		
			
				
					|  |  |  |         /> | 
			
		
	
		
			
				
					|  |  |  |       </span> | 
			
		
	
		
			
				
					|  |  |  |     </div> | 
			
		
	
		
			
				
					|  |  |  |     <ImageMethodDialog ref="imageDialog" /> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     <input | 
			
		
	
		
			
				
					|  |  |  |       type="text" | 
			
		
	
		
			
				
					|  |  |  |       placeholder="Other Authorized Representative" | 
			
		
	
		
			
				
					|  |  |  |       class="block w-full rounded border border-slate-400 px-3 py-2" | 
			
		
	
		
			
				
					|  |  |  |       class="mt-4 block w-full rounded border border-slate-400 px-3 py-2" | 
			
		
	
		
			
				
					|  |  |  |       v-model="agentDid" | 
			
		
	
		
			
				
					|  |  |  |     /> | 
			
		
	
		
			
				
					|  |  |  |     <div class="mb-4"> | 
			
		
	
	
		
			
				
					|  |  | @ -155,23 +176,31 @@ import "leaflet/dist/leaflet.css"; | 
			
		
	
		
			
				
					|  |  |  | import { AxiosError } from "axios"; | 
			
		
	
		
			
				
					|  |  |  | import * as didJwt from "did-jwt"; | 
			
		
	
		
			
				
					|  |  |  | import { DateTime } from "luxon"; | 
			
		
	
		
			
				
					|  |  |  | import { IIdentifier } from "@veramo/core"; | 
			
		
	
		
			
				
					|  |  |  | import { Component, Vue } from "vue-facing-decorator"; | 
			
		
	
		
			
				
					|  |  |  | import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | import QuickNav from "@/components/QuickNav.vue"; | 
			
		
	
		
			
				
					|  |  |  | import { NotificationIface } from "@/constants/app"; | 
			
		
	
		
			
				
					|  |  |  | import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app"; | 
			
		
	
		
			
				
					|  |  |  | import { accountsDB, db } from "@/db/index"; | 
			
		
	
		
			
				
					|  |  |  | import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; | 
			
		
	
		
			
				
					|  |  |  | import { accessToken, SimpleSigner } from "@/libs/crypto"; | 
			
		
	
		
			
				
					|  |  |  | import * as libsUtil from "@/libs/util"; | 
			
		
	
		
			
				
					|  |  |  | import { useAppStore } from "@/store/app"; | 
			
		
	
		
			
				
					|  |  |  | import { IIdentifier } from "@veramo/core"; | 
			
		
	
		
			
				
					|  |  |  | import { PlanVerifiableCredential } from "@/libs/endorserServer"; | 
			
		
	
		
			
				
					|  |  |  | import ImageMethodDialog from "@/components/ImageMethodDialog.vue"; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | @Component({ | 
			
		
	
		
			
				
					|  |  |  |   components: { LMap, LMarker, LTileLayer, QuickNav }, | 
			
		
	
		
			
				
					|  |  |  |   components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav }, | 
			
		
	
		
			
				
					|  |  |  | }) | 
			
		
	
		
			
				
					|  |  |  | export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |   $notify!: (notification: NotificationIface, timeout?: number) => void; | 
			
		
	
		
			
				
					|  |  |  |   errNote(message) { | 
			
		
	
		
			
				
					|  |  |  |     this.$notify( | 
			
		
	
		
			
				
					|  |  |  |       { group: "alert", type: "danger", title: "Error", text: message }, | 
			
		
	
		
			
				
					|  |  |  |       5000, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   activeDid = ""; | 
			
		
	
		
			
				
					|  |  |  |   agentDid = ""; | 
			
		
	
	
		
			
				
					|  |  | @ -183,6 +212,7 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |     name: "", | 
			
		
	
		
			
				
					|  |  |  |     description: "", | 
			
		
	
		
			
				
					|  |  |  |   }; // this default is only to avoid errors before plan is loaded | 
			
		
	
		
			
				
					|  |  |  |   imageUrl = ""; | 
			
		
	
		
			
				
					|  |  |  |   includeLocation = false; | 
			
		
	
		
			
				
					|  |  |  |   isHiddenSave = false; | 
			
		
	
		
			
				
					|  |  |  |   isHiddenSpinner = true; | 
			
		
	
	
		
			
				
					|  |  | @ -197,10 +227,7 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |   zoneName = DateTime.local().zoneName; | 
			
		
	
		
			
				
					|  |  |  |   zoom = 2; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async beforeCreate() { | 
			
		
	
		
			
				
					|  |  |  |     await accountsDB.open(); | 
			
		
	
		
			
				
					|  |  |  |     this.numAccounts = await accountsDB.accounts.count(); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |   libsUtil = libsUtil; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   public async getIdentity(activeDid: string) { | 
			
		
	
		
			
				
					|  |  |  |     await accountsDB.open(); | 
			
		
	
	
		
			
				
					|  |  | @ -228,6 +255,9 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async mounted() { | 
			
		
	
		
			
				
					|  |  |  |     await accountsDB.open(); | 
			
		
	
		
			
				
					|  |  |  |     this.numAccounts = await accountsDB.accounts.count(); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     await db.open(); | 
			
		
	
		
			
				
					|  |  |  |     const settings = await db.settings.get(MASTER_SETTINGS_KEY); | 
			
		
	
		
			
				
					|  |  |  |     this.activeDid = (settings?.activeDid as string) || ""; | 
			
		
	
	
		
			
				
					|  |  | @ -235,7 +265,7 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     if (this.projectId) { | 
			
		
	
		
			
				
					|  |  |  |       if (this.numAccounts === 0) { | 
			
		
	
		
			
				
					|  |  |  |         console.error("Error: no account was found."); | 
			
		
	
		
			
				
					|  |  |  |         this.errNote("There was a problem loading your account info."); | 
			
		
	
		
			
				
					|  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |         const identity = await this.getIdentity(this.activeDid); | 
			
		
	
		
			
				
					|  |  |  |         if (!identity) { | 
			
		
	
	
		
			
				
					|  |  | @ -264,6 +294,7 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |       if (resp.status === 200) { | 
			
		
	
		
			
				
					|  |  |  |         this.projectIssuerDid = resp.data.issuer; | 
			
		
	
		
			
				
					|  |  |  |         this.fullClaim = resp.data.claim; | 
			
		
	
		
			
				
					|  |  |  |         this.imageUrl = resp.data.claim.image || ""; | 
			
		
	
		
			
				
					|  |  |  |         this.lastClaimJwtId = resp.data.id; | 
			
		
	
		
			
				
					|  |  |  |         if (this.fullClaim?.location) { | 
			
		
	
		
			
				
					|  |  |  |           this.includeLocation = true; | 
			
		
	
	
		
			
				
					|  |  | @ -283,6 +314,84 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } catch (error) { | 
			
		
	
		
			
				
					|  |  |  |       console.error("Got error retrieving that project", error); | 
			
		
	
		
			
				
					|  |  |  |       this.errNote("There was an error retrieving that project."); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   openImageDialog() { | 
			
		
	
		
			
				
					|  |  |  |     (this.$refs.imageDialog as ImageMethodDialog).open((imgUrl) => { | 
			
		
	
		
			
				
					|  |  |  |       this.imageUrl = imgUrl; | 
			
		
	
		
			
				
					|  |  |  |     }, "PlanAction"); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   confirmDeleteImage() { | 
			
		
	
		
			
				
					|  |  |  |     this.$notify( | 
			
		
	
		
			
				
					|  |  |  |       { | 
			
		
	
		
			
				
					|  |  |  |         group: "modal", | 
			
		
	
		
			
				
					|  |  |  |         type: "confirm", | 
			
		
	
		
			
				
					|  |  |  |         title: "Are you sure you want to delete the image?", | 
			
		
	
		
			
				
					|  |  |  |         text: "", | 
			
		
	
		
			
				
					|  |  |  |         onYes: this.deleteImage, | 
			
		
	
		
			
				
					|  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |       -1, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   async deleteImage() { | 
			
		
	
		
			
				
					|  |  |  |     if (!this.imageUrl) { | 
			
		
	
		
			
				
					|  |  |  |       return; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     try { | 
			
		
	
		
			
				
					|  |  |  |       const identity = await libsUtil.getIdentity(this.activeDid); | 
			
		
	
		
			
				
					|  |  |  |       const token = await accessToken(identity); | 
			
		
	
		
			
				
					|  |  |  |       const response = await this.axios.delete( | 
			
		
	
		
			
				
					|  |  |  |         DEFAULT_IMAGE_API_SERVER + | 
			
		
	
		
			
				
					|  |  |  |           "/image/" + | 
			
		
	
		
			
				
					|  |  |  |           encodeURIComponent(this.imageUrl), | 
			
		
	
		
			
				
					|  |  |  |         { | 
			
		
	
		
			
				
					|  |  |  |           headers: { | 
			
		
	
		
			
				
					|  |  |  |             Authorization: `Bearer ${token}`, | 
			
		
	
		
			
				
					|  |  |  |           }, | 
			
		
	
		
			
				
					|  |  |  |         }, | 
			
		
	
		
			
				
					|  |  |  |       ); | 
			
		
	
		
			
				
					|  |  |  |       if (response.status === 204) { | 
			
		
	
		
			
				
					|  |  |  |         // don't bother with a notification | 
			
		
	
		
			
				
					|  |  |  |         // (either they'll simply continue or they're canceling and going back) | 
			
		
	
		
			
				
					|  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |         console.error("Problem deleting image:", response); | 
			
		
	
		
			
				
					|  |  |  |         this.$notify( | 
			
		
	
		
			
				
					|  |  |  |           { | 
			
		
	
		
			
				
					|  |  |  |             group: "alert", | 
			
		
	
		
			
				
					|  |  |  |             type: "danger", | 
			
		
	
		
			
				
					|  |  |  |             title: "Error", | 
			
		
	
		
			
				
					|  |  |  |             text: "There was a problem deleting the image.", | 
			
		
	
		
			
				
					|  |  |  |           }, | 
			
		
	
		
			
				
					|  |  |  |           5000, | 
			
		
	
		
			
				
					|  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |         return; | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |       this.imageUrl = ""; | 
			
		
	
		
			
				
					|  |  |  |     } catch (error) { | 
			
		
	
		
			
				
					|  |  |  |       console.error("Error deleting image:", error); | 
			
		
	
		
			
				
					|  |  |  |       // eslint-disable-next-line @typescript-eslint/no-explicit-any | 
			
		
	
		
			
				
					|  |  |  |       if ((error as any).response.status === 404) { | 
			
		
	
		
			
				
					|  |  |  |         console.log("The image was already deleted:", error); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         this.imageUrl = ""; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         // it already doesn't exist so we won't say anything to the user | 
			
		
	
		
			
				
					|  |  |  |       } else { | 
			
		
	
		
			
				
					|  |  |  |         this.$notify( | 
			
		
	
		
			
				
					|  |  |  |           { | 
			
		
	
		
			
				
					|  |  |  |             group: "alert", | 
			
		
	
		
			
				
					|  |  |  |             type: "danger", | 
			
		
	
		
			
				
					|  |  |  |             title: "Error", | 
			
		
	
		
			
				
					|  |  |  |             text: "There was an error deleting the image.", | 
			
		
	
		
			
				
					|  |  |  |           }, | 
			
		
	
		
			
				
					|  |  |  |           5000, | 
			
		
	
		
			
				
					|  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -299,6 +408,11 @@ export default class NewEditProjectView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       delete vcClaim.agent; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     if (this.imageUrl) { | 
			
		
	
		
			
				
					|  |  |  |       vcClaim.image = this.imageUrl; | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       delete vcClaim.image; | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |     if (this.includeLocation) { | 
			
		
	
		
			
				
					|  |  |  |       vcClaim.location = { | 
			
		
	
		
			
				
					|  |  |  |         geo: { | 
			
		
	
	
		
			
				
					|  |  | 
 |