Compare commits
26 Commits
c4a8026276
...
d679d0c804
Author | SHA1 | Date |
---|---|---|
Jose Olarte III | d679d0c804 | 3 months ago |
Trent Larson | 7cba232e44 | 3 months ago |
Trent Larson | c95b2178ef | 3 months ago |
Trent Larson | 511be5f9a2 | 3 months ago |
Trent Larson | 8b4f46d07b | 3 months ago |
Trent Larson | 4064eb75a9 | 3 months ago |
Trent Larson | d96aa01107 | 3 months ago |
Trent Larson | 6e89271616 | 3 months ago |
Trent Larson | ee9c14942c | 3 months ago |
Trent Larson | a8bb1b46c2 | 3 months ago |
Trent Larson | a8b82037b9 | 3 months ago |
trentlarson | 5811dacb84 | 3 months ago |
Trent Larson | 1a4052d1a0 | 3 months ago |
Trent Larson | a9b12f4d7c | 3 months ago |
Trent Larson | 269d00a096 | 3 months ago |
Trent Larson | 05f898d462 | 3 months ago |
Trent Larson | 2c2c95a824 | 3 months ago |
Trent Larson | 4244e6b279 | 3 months ago |
Trent Larson | 56e3440875 | 3 months ago |
Trent Larson | 1fe540d5a8 | 3 months ago |
Trent Larson | 089d4f0733 | 3 months ago |
Trent Larson | da79d581b7 | 3 months ago |
Trent Larson | cefa384ff1 | 3 months ago |
Trent Larson | 5849ae2de4 | 3 months ago |
Trent Larson | 60ed21c0d9 | 3 months ago |
Trent Larson | 3f77f9b3ff | 3 months ago |
34 changed files with 1794 additions and 557 deletions
@ -0,0 +1,633 @@ |
|||||
|
<template> |
||||
|
<QuickNav /> |
||||
|
<TopMessage /> |
||||
|
|
||||
|
<!-- CONTENT --> |
||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> |
||||
|
<!-- Back --> |
||||
|
<div |
||||
|
v-if="!hideBackButton" |
||||
|
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="cancelBack()" |
||||
|
> |
||||
|
<fa icon="chevron-left" class="fa-fw"></fa> |
||||
|
</h1> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Heading --> |
||||
|
<h1 class="text-4xl text-center font-light px-4 mb-4">What Is Offered</h1> |
||||
|
|
||||
|
<h1 class="text-xl font-bold text-center mb-4"> |
||||
|
<span> |
||||
|
Offer to |
||||
|
{{ |
||||
|
offeredToProject |
||||
|
? projectName |
||||
|
: offeredToRecipient |
||||
|
? recipientName |
||||
|
: "someone unidentified" |
||||
|
}}</span |
||||
|
> |
||||
|
</h1> |
||||
|
<textarea |
||||
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2" |
||||
|
placeholder="What is offered" |
||||
|
v-model="itemDescription" |
||||
|
data-testId="itemDescription" |
||||
|
/> |
||||
|
<div class="flex flex-row justify-center"> |
||||
|
<span |
||||
|
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center text-blue-500 px-2 py-2 w-20" |
||||
|
@click="changeUnitCode()" |
||||
|
> |
||||
|
{{ libsUtil.UNIT_SHORT[unitCode] || unitCode }} |
||||
|
</span> |
||||
|
<div |
||||
|
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2" |
||||
|
@click="amountInput === '0' ? null : decrement()" |
||||
|
> |
||||
|
<fa icon="chevron-left" /> |
||||
|
</div> |
||||
|
<input |
||||
|
type="number" |
||||
|
class="border border-r-0 border-slate-400 px-2 py-2 text-center w-20" |
||||
|
v-model="amountInput" |
||||
|
data-testId="inputOfferAmount" |
||||
|
/> |
||||
|
<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 flex-row mt-2"> |
||||
|
<span |
||||
|
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center px-2 py-2" |
||||
|
> |
||||
|
Conditions |
||||
|
</span> |
||||
|
<textarea |
||||
|
class="w-full border border-slate-400 px-3 py-2 rounded-r" |
||||
|
placeholder="Prerequisites, other people to include, etc." |
||||
|
v-model="conditionDescription" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="flex flex-row mt-2"> |
||||
|
<span |
||||
|
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center px-2 py-2" |
||||
|
> |
||||
|
{{ validThroughDateInput ? "" : "No" }} Expiration |
||||
|
</span> |
||||
|
<input |
||||
|
v-model="validThroughDateInput" |
||||
|
type="date" |
||||
|
class="w-full rounded border border-slate-400 px-3 py-2 rounded-r" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="h-7 mt-4 flex"> |
||||
|
<input |
||||
|
v-if="projectId && !offeredToRecipient" |
||||
|
type="checkbox" |
||||
|
class="h-6 w-6 mr-2" |
||||
|
v-model="offeredToProject" |
||||
|
/> |
||||
|
<fa |
||||
|
v-else |
||||
|
icon="square" |
||||
|
class="bg-slate-500 text-slate-500 h-5 w-5 px-0.5 py-0.5 mr-2 rounded" |
||||
|
@click="notifyUserOfProject()" |
||||
|
/> |
||||
|
<label class="text-sm mt-1"> |
||||
|
{{ |
||||
|
projectId |
||||
|
? "This is offered to " + projectName |
||||
|
: "No project was chosen" |
||||
|
}} |
||||
|
</label> |
||||
|
</div> |
||||
|
|
||||
|
<div class="h-7 mt-4 flex"> |
||||
|
<input |
||||
|
v-if="recipientDid && !offeredToProject" |
||||
|
type="checkbox" |
||||
|
class="h-6 w-6 mr-2" |
||||
|
v-model="offeredToRecipient" |
||||
|
/> |
||||
|
<fa |
||||
|
v-else |
||||
|
icon="square" |
||||
|
class="bg-slate-500 text-slate-500 h-5 w-5 px-0.5 py-0.5 mr-2 rounded" |
||||
|
@click="notifyUserOfRecipient()" |
||||
|
/> |
||||
|
<label class="text-sm mt-1"> |
||||
|
{{ |
||||
|
recipientDid |
||||
|
? "This is offered to " + recipientName |
||||
|
: "No recipient was chosen." |
||||
|
}} |
||||
|
</label> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mt-4 flex"> |
||||
|
<router-link |
||||
|
:to="{ |
||||
|
name: 'claim-add-raw', |
||||
|
query: { |
||||
|
claim: constructOfferParam(), |
||||
|
}, |
||||
|
}" |
||||
|
class="text-blue-500" |
||||
|
> |
||||
|
Edit & Submit Raw |
||||
|
</router-link> |
||||
|
</div> |
||||
|
|
||||
|
<p class="text-center mb-2 mt-6 italic"> |
||||
|
Sign & Send to publish to the world |
||||
|
<fa |
||||
|
icon="circle-info" |
||||
|
class="pl-2 text-blue-500 cursor-pointer" |
||||
|
@click="explainData()" |
||||
|
/> |
||||
|
</p> |
||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> |
||||
|
<button |
||||
|
class="block w-full text-center text-lg font-bold uppercase 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-3 rounded-md" |
||||
|
@click="confirm" |
||||
|
> |
||||
|
Sign & Send |
||||
|
</button> |
||||
|
<button |
||||
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" |
||||
|
@click="cancel" |
||||
|
> |
||||
|
Cancel |
||||
|
</button> |
||||
|
</div> |
||||
|
</section> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Vue } from "vue-facing-decorator"; |
||||
|
import { Router } from "vue-router"; |
||||
|
|
||||
|
import QuickNav from "@/components/QuickNav.vue"; |
||||
|
import TopMessage from "@/components/TopMessage.vue"; |
||||
|
import { NotificationIface } from "@/constants/app"; |
||||
|
import { accountsDB, db } from "@/db/index"; |
||||
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; |
||||
|
import { |
||||
|
createAndSubmitOffer, |
||||
|
didInfo, |
||||
|
editAndSubmitOffer, |
||||
|
GenericCredWrapper, |
||||
|
getPlanFromCache, |
||||
|
hydrateOffer, |
||||
|
OfferVerifiableCredential, |
||||
|
} from "@/libs/endorserServer"; |
||||
|
import * as libsUtil from "@/libs/util"; |
||||
|
import { Contact } from "@/db/tables/contacts"; |
||||
|
|
||||
|
@Component({ |
||||
|
components: { |
||||
|
QuickNav, |
||||
|
TopMessage, |
||||
|
}, |
||||
|
}) |
||||
|
export default class OfferDetailsView extends Vue { |
||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
||||
|
|
||||
|
activeDid = ""; |
||||
|
apiServer = ""; |
||||
|
|
||||
|
amountInput = "0"; |
||||
|
conditionDescription = ""; |
||||
|
itemDescription = ""; |
||||
|
destinationPathAfter = ""; |
||||
|
offeredToProject = false; |
||||
|
offeredToRecipient = false; |
||||
|
offererDid: string | undefined; |
||||
|
hideBackButton = false; |
||||
|
message = ""; |
||||
|
offerId = ""; |
||||
|
prevCredToEdit?: GenericCredWrapper<OfferVerifiableCredential>; |
||||
|
projectId = ""; |
||||
|
projectName = "a project"; |
||||
|
recipientDid = ""; |
||||
|
recipientName = ""; |
||||
|
unitCode = "HUR"; |
||||
|
validThroughDateInput = ""; |
||||
|
|
||||
|
libsUtil = libsUtil; |
||||
|
|
||||
|
async mounted() { |
||||
|
try { |
||||
|
this.prevCredToEdit = (this.$route as Router).query["prevCredToEdit"] |
||||
|
? (JSON.parse( |
||||
|
(this.$route as Router).query["prevCredToEdit"], |
||||
|
) as GenericCredWrapper<OfferVerifiableCredential>) |
||||
|
: undefined; |
||||
|
} catch (error) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Retrieval Error", |
||||
|
text: "The previous record isn't available for editing. If you submit, you'll create a new record.", |
||||
|
}, |
||||
|
6000, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const prevAmount = |
||||
|
this.prevCredToEdit?.claim?.includesObject?.amountOfThisGood; |
||||
|
this.amountInput = |
||||
|
(this.$route as Router).query["amountInput"] || |
||||
|
(prevAmount ? String(prevAmount) : "") || |
||||
|
this.amountInput; |
||||
|
this.unitCode = ((this.$route as Router).query["unitCode"] || |
||||
|
this.prevCredToEdit?.claim?.includesObject?.unitCode || |
||||
|
this.unitCode) as string; |
||||
|
|
||||
|
this.conditionDescription = |
||||
|
this.prevCredToEdit?.claim?.description || this.conditionDescription; |
||||
|
this.itemDescription = |
||||
|
(this.$route as Router).query["description"] || |
||||
|
this.prevCredToEdit?.claim?.itemOffered?.description || |
||||
|
this.itemDescription; |
||||
|
this.destinationPathAfter = (this.$route as Router).query[ |
||||
|
"destinationPathAfter" |
||||
|
]; |
||||
|
this.offererDid = ((this.$route as Router).query["offererDid"] || |
||||
|
this.prevCredToEdit?.claim?.agent?.identifier || |
||||
|
this.offererDid) as string; |
||||
|
this.hideBackButton = |
||||
|
(this.$route as Router).query["hideBackButton"] === "true"; |
||||
|
this.message = ((this.$route as Router).query["message"] as string) || ""; |
||||
|
|
||||
|
// find any project ID |
||||
|
let project; |
||||
|
if ( |
||||
|
this.prevCredToEdit?.claim?.itemOffered?.isPartOf?.["@type"] === |
||||
|
"PlanAction" |
||||
|
) { |
||||
|
project = this.prevCredToEdit?.claim?.itemOffered?.isPartOf; |
||||
|
} |
||||
|
this.projectId = ((this.$route as Router).query["projectId"] || |
||||
|
project?.identifier || |
||||
|
this.projectId) as string; |
||||
|
this.projectName = ((this.$route as Router).query["projectName"] || |
||||
|
project?.name || |
||||
|
this.projectName) as string; |
||||
|
|
||||
|
this.recipientDid = ((this.$route as Router).query["recipientDid"] || |
||||
|
this.prevCredToEdit?.claim?.recipient?.identifier) as string; |
||||
|
this.recipientName = |
||||
|
((this.$route as Router).query["recipientName"] as string) || ""; |
||||
|
|
||||
|
this.validThroughDateInput = |
||||
|
this.prevCredToEdit?.claim?.validThrough || this.validThroughDateInput; |
||||
|
|
||||
|
try { |
||||
|
await db.open(); |
||||
|
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings; |
||||
|
this.apiServer = settings?.apiServer || ""; |
||||
|
this.activeDid = settings?.activeDid || ""; |
||||
|
|
||||
|
let allContacts: Contact[] = []; |
||||
|
let allMyDids: string[] = []; |
||||
|
if (this.recipientDid && !this.recipientName) { |
||||
|
allContacts = await db.contacts.toArray(); |
||||
|
|
||||
|
await accountsDB.open(); |
||||
|
const allAccounts = await accountsDB.accounts.toArray(); |
||||
|
allMyDids = allAccounts.map((acc) => acc.did); |
||||
|
this.recipientName = didInfo( |
||||
|
this.recipientDid, |
||||
|
this.activeDid, |
||||
|
allMyDids, |
||||
|
allContacts, |
||||
|
); |
||||
|
} |
||||
|
// these should be functions but something's wrong with the syntax in the <> conditional |
||||
|
this.offeredToProject = !!this.projectId; |
||||
|
this.offeredToRecipient = !this.offeredToProject && !!this.recipientDid; |
||||
|
|
||||
|
// 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, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (this.projectId && !this.projectName) { |
||||
|
// console.log("Getting project name from cache", this.projectId); |
||||
|
const project = await getPlanFromCache( |
||||
|
this.projectId, |
||||
|
this.axios, |
||||
|
this.apiServer, |
||||
|
this.activeDid, |
||||
|
); |
||||
|
this.projectName = project?.name |
||||
|
? "the project: " + project.name |
||||
|
: "a project"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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() { |
||||
|
if (this.destinationPathAfter) { |
||||
|
(this.$router as Router).push({ path: this.destinationPathAfter }); |
||||
|
} else { |
||||
|
(this.$router as Router).back(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
cancelBack() { |
||||
|
(this.$router as Router).back(); |
||||
|
} |
||||
|
|
||||
|
async confirm() { |
||||
|
if (!this.activeDid) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: "You must select an identifier before you can record a offer.", |
||||
|
}, |
||||
|
2000, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
if (parseFloat(this.amountInput) < 0) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
text: "You may not send a negative number.", |
||||
|
title: "", |
||||
|
}, |
||||
|
2000, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
if (!this.itemDescription && !parseFloat(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] |
||||
|
}.`, |
||||
|
}, |
||||
|
2000, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "toast", |
||||
|
text: "Recording the offer...", |
||||
|
title: "", |
||||
|
}, |
||||
|
1000, |
||||
|
); |
||||
|
|
||||
|
// this is asynchronous, but we don't need to wait for it to complete |
||||
|
await this.recordOffer(); |
||||
|
} |
||||
|
|
||||
|
notifyUserOfProject() { |
||||
|
if (!this.projectId) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "warning", |
||||
|
title: "Error", |
||||
|
text: "To assign to a project, you must open this page through a project.", |
||||
|
}, |
||||
|
3000, |
||||
|
); |
||||
|
} else { |
||||
|
// must be because offeredToRecipient is true |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "warning", |
||||
|
title: "Error", |
||||
|
text: "You cannot assign both to a project and to a recipient.", |
||||
|
}, |
||||
|
3000, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
notifyUserOfRecipient() { |
||||
|
if (!this.recipientDid) { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "warning", |
||||
|
title: "Error", |
||||
|
text: "To assign to a recipient, you must open this page from a contact.", |
||||
|
}, |
||||
|
3000, |
||||
|
); |
||||
|
} else { |
||||
|
// must be because offeredToProject is true |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "warning", |
||||
|
title: "Error", |
||||
|
text: "You cannot assign both to a recipient and to a project.", |
||||
|
}, |
||||
|
3000, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param offererDid 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 recordOffer() { |
||||
|
try { |
||||
|
const recipientDid = this.offeredToRecipient |
||||
|
? this.recipientDid |
||||
|
: undefined; |
||||
|
const projectId = this.offeredToProject ? this.projectId : undefined; |
||||
|
let result; |
||||
|
if (this.prevCredToEdit) { |
||||
|
// don't create from a blank one in case some properties were set from a different interface |
||||
|
result = await editAndSubmitOffer( |
||||
|
this.axios, |
||||
|
this.apiServer, |
||||
|
this.prevCredToEdit, |
||||
|
this.activeDid, |
||||
|
this.itemDescription, |
||||
|
parseFloat(this.amountInput), |
||||
|
this.unitCode, |
||||
|
this.conditionDescription, |
||||
|
this.validThroughDateInput, |
||||
|
recipientDid, |
||||
|
projectId, |
||||
|
); |
||||
|
} else { |
||||
|
result = await createAndSubmitOffer( |
||||
|
this.axios, |
||||
|
this.apiServer, |
||||
|
this.activeDid, |
||||
|
this.itemDescription, |
||||
|
parseFloat(this.amountInput), |
||||
|
this.unitCode, |
||||
|
this.conditionDescription, |
||||
|
this.validThroughDateInput, |
||||
|
recipientDid, |
||||
|
projectId, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (result.type === "error" || this.isCreationError(result.response)) { |
||||
|
const errorMessage = this.getCreationErrorMessage(result); |
||||
|
console.error("Error with offer creation result:", result); |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: errorMessage || "There was an error creating the offer.", |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} else { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "success", |
||||
|
title: "Success", |
||||
|
text: `That offer was recorded.`, |
||||
|
}, |
||||
|
5000, |
||||
|
); |
||||
|
localStorage.removeItem("imageUrl"); |
||||
|
if (this.destinationPathAfter) { |
||||
|
(this.$router as Router).push({ path: this.destinationPathAfter }); |
||||
|
} else { |
||||
|
(this.$router as Router).back(); |
||||
|
} |
||||
|
} |
||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any |
||||
|
} catch (error: any) { |
||||
|
console.error("Error with offer recordation caught:", error); |
||||
|
const errorMessage = |
||||
|
error.userMessage || |
||||
|
error.response?.data?.error?.message || |
||||
|
"There was an error recording the offer."; |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: errorMessage, |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
constructOfferParam() { |
||||
|
const recipientDid = this.offeredToRecipient |
||||
|
? this.recipientDid |
||||
|
: undefined; |
||||
|
const projectId = this.offeredToProject ? this.projectId : undefined; |
||||
|
const offerClaim = hydrateOffer( |
||||
|
this.prevCredToEdit?.claim as OfferVerifiableCredential, |
||||
|
this.activeDid, |
||||
|
recipientDid, |
||||
|
this.itemDescription, |
||||
|
parseFloat(this.amountInput), |
||||
|
this.unitCode, |
||||
|
this.conditionDescription, |
||||
|
projectId, |
||||
|
this.validThroughDateInput, |
||||
|
this.prevCredToEdit?.id as string, |
||||
|
); |
||||
|
const claimStr = JSON.stringify(offerClaim); |
||||
|
return claimStr; |
||||
|
} |
||||
|
|
||||
|
// 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 |
||||
|
isCreationError(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 |
||||
|
getCreationErrorMessage(result: any) { |
||||
|
return ( |
||||
|
result.error?.userMessage || |
||||
|
result.error?.error || |
||||
|
result.response?.data?.error?.message |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
explainData() { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "success", |
||||
|
title: "Data Sharing", |
||||
|
text: libsUtil.PRIVACY_MESSAGE, |
||||
|
}, |
||||
|
-1, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,63 @@ |
|||||
|
import { test, expect } from '@playwright/test'; |
||||
|
import { importUser } from './testUtils'; |
||||
|
|
||||
|
test('Record an offer', async ({ page }) => { |
||||
|
// Generate a random string of 3 characters, skipping the "0." at the beginning
|
||||
|
const randomString = Math.random().toString(36).substring(2, 5); |
||||
|
// Standard title prefix
|
||||
|
const description = `Offering of ${randomString}`; |
||||
|
const updatedDescription = `Updated ${description}`; |
||||
|
const randomNonZeroNumber = Math.floor(Math.random() * 998) + 1; |
||||
|
|
||||
|
// Create new ID for default user
|
||||
|
await importUser(page); |
||||
|
|
||||
|
// Select a project
|
||||
|
await page.goto('./discover'); |
||||
|
await page.locator('ul#listDiscoverResults li:nth-child(1)').click(); |
||||
|
|
||||
|
// Record an offer
|
||||
|
await page.getByTestId('offerButton').click(); |
||||
|
await page.getByTestId('inputDescription').fill(description); |
||||
|
await page.getByTestId('inputOfferAmount').fill(randomNonZeroNumber.toString()); |
||||
|
await page.getByRole('button', { name: 'Sign & Send' }).click(); |
||||
|
await expect(page.getByText('That offer was recorded.')).toBeVisible(); |
||||
|
|
||||
|
// go to the offer and check the values
|
||||
|
await page.goto('./projects'); |
||||
|
await page.locator('li').filter({ hasText: description }).locator('a').first().click(); |
||||
|
await expect(page.getByRole('heading', { name: 'Verifiable Claim Details' })).toBeVisible(); |
||||
|
await expect(page.getByText(description, { exact: true })).toBeVisible(); |
||||
|
const serverPagePromise = page.waitForEvent('popup'); |
||||
|
await page.getByRole('link', { name: 'View on the Public Server' }).click(); |
||||
|
const serverPage = await serverPagePromise; |
||||
|
await serverPage.getByText(description); |
||||
|
await serverPage.getByText('did:none:HIDDEN'); |
||||
|
|
||||
|
// Now update that offer
|
||||
|
|
||||
|
// find the edit page and check the old values again
|
||||
|
await page.goto('./projects'); |
||||
|
await page.locator('li').filter({ hasText: description }).locator('a').first().click(); |
||||
|
await page.getByTestId('editClaimButton').click(); |
||||
|
await page.locator('heading', { hasText: 'What is offered' }).isVisible(); |
||||
|
const itemDesc = await page.getByTestId('itemDescription'); |
||||
|
await expect(itemDesc).toHaveValue(description); |
||||
|
const amount = await page.getByTestId('inputOfferAmount'); |
||||
|
await expect(amount).toHaveValue(randomNonZeroNumber.toString()); |
||||
|
// update the values
|
||||
|
await itemDesc.fill(updatedDescription); |
||||
|
await amount.fill(String(randomNonZeroNumber + 1)); |
||||
|
await page.getByRole('button', { name: 'Sign & Send' }).click(); |
||||
|
|
||||
|
// go to the offer claim again and check the updated values
|
||||
|
await page.goto('./projects'); |
||||
|
await page.locator('li').filter({ hasText: description }).locator('a').first().click(); |
||||
|
const newItemDesc = await page.getByTestId('description'); |
||||
|
await expect(newItemDesc).toHaveText(updatedDescription); |
||||
|
|
||||
|
// go to edit page
|
||||
|
await page.getByTestId('editClaimButton').click(); |
||||
|
const newAmount = await page.getByTestId('inputOfferAmount'); |
||||
|
await expect(newAmount).toHaveValue((randomNonZeroNumber + 1).toString()); |
||||
|
}); |
Loading…
Reference in new issue