|
|
@ -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: { |
|
|
|