You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
8.4 KiB
314 lines
8.4 KiB
9 months ago
|
<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>
|