Trent Larson
9 months ago
3 changed files with 338 additions and 13 deletions
@ -0,0 +1,313 @@ |
|||||
|
<template> |
||||
|
<QuickNav /> |
||||
|
<TopMessage /> |
||||
|
|
||||
|
<!-- CONTENT --> |
||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> |
||||
|
<!-- Back --> |
||||
|
<div class="text-lg text-center font-light relative px-7"> |
||||
|
<h1 |
||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" |
||||
|
@click="$router.back()" |
||||
|
> |
||||
|
<fa icon="chevron-left" class="fa-fw"></fa> |
||||
|
</h1> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Heading --> |
||||
|
<h1 class="text-4xl text-center font-light px-4 mb-4">What Was Given</h1> |
||||
|
|
||||
|
<h1 class="text-xl font-bold text-center mb-4"> |
||||
|
{{ message }} {{ giverName || "somebody not named" }} |
||||
|
</h1> |
||||
|
<textarea |
||||
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2" |
||||
|
placeholder="What was received" |
||||
|
v-model="description" |
||||
|
/> |
||||
|
<div class="flex flex-row"> |
||||
|
<span |
||||
|
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 w-1/3 text-center text-blue-500 px-2 py-2" |
||||
|
@click="changeUnitCode()" |
||||
|
> |
||||
|
{{ libsUtil.UNIT_SHORT[unitCode] }} |
||||
|
</span> |
||||
|
<div |
||||
|
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2" |
||||
|
@click="decrement()" |
||||
|
v-if="amountInput !== '0'" |
||||
|
> |
||||
|
<fa icon="chevron-left" /> |
||||
|
</div> |
||||
|
<input |
||||
|
type="number" |
||||
|
class="w-full border border-r-0 border-slate-400 px-2 py-2 text-center" |
||||
|
v-model="amountInput" |
||||
|
/> |
||||
|
<div |
||||
|
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2" |
||||
|
@click="increment()" |
||||
|
> |
||||
|
<fa icon="chevron-right" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="flex justify-center mb-4 mt-4"> |
||||
|
<router-link |
||||
|
:to="{ name: 'gifted-photo' }" |
||||
|
class="bg-blue-500 text-white px-1.5 py-1 rounded-md" |
||||
|
> |
||||
|
<fa icon="camera" class="fa-fw" /> |
||||
|
</router-link> |
||||
|
</div> |
||||
|
<div class="mt-4"> |
||||
|
<input type="checkbox" class="h-6 w-6 mr-2" v-model="givenToUser" /> |
||||
|
<label class="text-sm">Given to you</label> |
||||
|
</div> |
||||
|
<div class="mt-4"> |
||||
|
<input type="checkbox" class="h-6 w-6 mr-2" v-model="isTrade" /> |
||||
|
<label class="text-sm">Trade (not a gift)</label> |
||||
|
</div> |
||||
|
<p class="text-center mb-2 mt-6 italic"> |
||||
|
Sign & Send to publish to the world |
||||
|
</p> |
||||
|
<button |
||||
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2" |
||||
|
@click="confirm" |
||||
|
> |
||||
|
Sign & Send |
||||
|
</button> |
||||
|
<button |
||||
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md" |
||||
|
@click="cancel" |
||||
|
> |
||||
|
Cancel |
||||
|
</button> |
||||
|
</section> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Vue } from "vue-facing-decorator"; |
||||
|
|
||||
|
import { NotificationIface } from "@/constants/app"; |
||||
|
import QuickNav from "@/components/QuickNav.vue"; |
||||
|
import TopMessage from "@/components/TopMessage.vue"; |
||||
|
import { db } from "@/db/index"; |
||||
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; |
||||
|
import { createAndSubmitGive } from "@/libs/endorserServer"; |
||||
|
import * as libsUtil from "@/libs/util"; |
||||
|
|
||||
|
@Component({ |
||||
|
components: { |
||||
|
QuickNav, |
||||
|
TopMessage, |
||||
|
}, |
||||
|
}) |
||||
|
export default class GiftedDetails extends Vue { |
||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
||||
|
|
||||
|
activeDid = ""; |
||||
|
apiServer = ""; |
||||
|
|
||||
|
amountInput = "0"; |
||||
|
description = ""; |
||||
|
givenToUser = false; |
||||
|
giverDid: string | undefined; |
||||
|
giverName = ""; |
||||
|
isTrade = false; |
||||
|
message = ""; |
||||
|
offerId = ""; |
||||
|
projectId = ""; |
||||
|
unitCode = "HUR"; |
||||
|
|
||||
|
libsUtil = libsUtil; |
||||
|
|
||||
|
async mounted() { |
||||
|
this.amountInput = this.$route.query.amountInput as string; |
||||
|
this.description = this.$route.query.description as string; |
||||
|
this.giverDid = this.$route.query.giverDid as string; |
||||
|
this.giverName = this.$route.query.giverName as string; |
||||
|
this.message = this.$route.query.message as string; |
||||
|
this.offerId = this.$route.query.offerId as string; |
||||
|
this.projectId = this.$route.query.projectId as string; |
||||
|
this.unitCode = this.$route.query.unitCode as string; |
||||
|
|
||||
|
try { |
||||
|
await db.open(); |
||||
|
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings; |
||||
|
this.apiServer = settings?.apiServer || ""; |
||||
|
this.activeDid = settings?.activeDid || ""; |
||||
|
|
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
||||
|
} catch (err: any) { |
||||
|
console.error("Error retrieving settings from database:", err); |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: err.message || "There was an error retrieving your settings.", |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
changeUnitCode() { |
||||
|
const units = Object.keys(this.libsUtil.UNIT_SHORT); |
||||
|
const index = units.indexOf(this.unitCode); |
||||
|
this.unitCode = units[(index + 1) % units.length]; |
||||
|
} |
||||
|
|
||||
|
increment() { |
||||
|
this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`; |
||||
|
} |
||||
|
|
||||
|
decrement() { |
||||
|
this.amountInput = `${Math.max( |
||||
|
0, |
||||
|
(parseFloat(this.amountInput) || 1) - 1, |
||||
|
)}`; |
||||
|
} |
||||
|
|
||||
|
cancel() { |
||||
|
this.$router.back(); |
||||
|
} |
||||
|
|
||||
|
async confirm() { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "toast", |
||||
|
text: "Recording the give...", |
||||
|
title: "", |
||||
|
}, |
||||
|
1000, |
||||
|
); |
||||
|
// this is asynchronous, but we don't need to wait for it to complete |
||||
|
await this.recordGive(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param giverDid may be null |
||||
|
* @param description may be an empty string |
||||
|
* @param amountInput may be 0 |
||||
|
* @param unitCode may be omitted, defaults to "HUR" |
||||
|
*/ |
||||
|
public async recordGive() { |
||||
|
if (!this.activeDid) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: "You must select an identifier before you can record a give.", |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!this.description && !this.amountInput) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: `You must enter a description or some number of ${ |
||||
|
this.libsUtil.UNIT_LONG[this.unitCode] |
||||
|
}.`, |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const identity = await libsUtil.getIdentity(this.activeDid); |
||||
|
const result = await createAndSubmitGive( |
||||
|
this.axios, |
||||
|
this.apiServer, |
||||
|
identity, |
||||
|
this.giverDid, |
||||
|
this.givenToUser ? this.activeDid : undefined, |
||||
|
this.description, |
||||
|
parseFloat(this.amountInput), |
||||
|
this.unitCode, |
||||
|
this.projectId, |
||||
|
this.offerId, |
||||
|
this.isTrade, |
||||
|
); |
||||
|
|
||||
|
if ( |
||||
|
result.type === "error" || |
||||
|
this.isGiveCreationError(result.response) |
||||
|
) { |
||||
|
const errorMessage = this.getGiveCreationErrorMessage(result); |
||||
|
console.error("Error with give creation result:", result); |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: errorMessage || "There was an error creating the give.", |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} else { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "success", |
||||
|
title: "Success", |
||||
|
text: `That ${this.isTrade ? "trade" : "gift"} was recorded.`, |
||||
|
}, |
||||
|
7000, |
||||
|
); |
||||
|
this.$router.back(); |
||||
|
} |
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
||||
|
} catch (error: any) { |
||||
|
console.error("Error with give recordation caught:", error); |
||||
|
const message = |
||||
|
error.userMessage || |
||||
|
error.response?.data?.error?.message || |
||||
|
"There was an error recording the give."; |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: message, |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Helper functions for readability |
||||
|
|
||||
|
/** |
||||
|
* @param result response "data" from the server |
||||
|
* @returns true if the result indicates an error |
||||
|
*/ |
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
||||
|
isGiveCreationError(result: any) { |
||||
|
return result.status !== 201 || result.data?.error; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data") |
||||
|
* @returns best guess at an error message |
||||
|
*/ |
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
||||
|
getGiveCreationErrorMessage(result: any) { |
||||
|
return ( |
||||
|
result.error?.userMessage || |
||||
|
result.error?.error || |
||||
|
result.response?.data?.error?.message |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
</script> |
Loading…
Reference in new issue