refactor project screen: add action to record a give from it, and add checks to give confirmation buttons

This commit is contained in:
2024-11-28 11:26:51 -07:00
parent 60eb4a9dc7
commit 8db7ac3f6f
8 changed files with 429 additions and 181 deletions

View File

@@ -15,7 +15,17 @@
<fa icon="chevron-left" class="fa-fw"></fa>
</button>
Idea
<h2 class="text-xl font-semibold">{{ name }}</h2>
<h2 class="text-xl font-semibold">
{{ name }}
<button
v-if="activeDid === issuer || activeDid === agentDid"
@click="onEditClick()"
title="Edit"
data-testId="editClaimButton"
>
<fa icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
</button>
</h2>
</h1>
</div>
@@ -104,15 +114,6 @@
<fa icon="file-lines" class="pl-2 pt-1 text-blue-500" />
</a>
</div>
<button
v-if="activeDid === issuer || activeDid === agentDid"
type="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="onEditClick()"
>
Edit
</button>
</div>
<div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
@@ -159,31 +160,14 @@
</div>
</div>
<div v-if="activeDid && isRegistered" class="mt-4">
<div class="text-center">
<button
data-testId="offerButton"
@click="openOfferDialog()"
class="block w-full text-lg font-bold 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"
>
Offer (maybe with conditions)...
</button>
</div>
</div>
<OfferDialog
ref="customOfferDialog"
:projectId="this.projectId"
:projectName="this.name"
/>
<div v-if="activeDid && isRegistered">
<div class="text-center">
<p class="mt-2 mt-4 text-center">Record a contribution from:</p>
</div>
<ul
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5 mt-2"
>
<li @click="openGiftDialog({ name: 'you', did: activeDid })">
<li @click="openGiftDialogToProject({ name: 'you', did: activeDid })">
<fa icon="hand" class="fa-fw text-blue-500 text-5xl cursor-pointer" />
<h3
class="mt-5 text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
@@ -191,7 +175,7 @@
You
</h3>
</li>
<li @click="openGiftDialog()">
<li @click="openGiftDialogToProject()">
<img
src="../assets/blank-square.svg"
class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
@@ -203,9 +187,9 @@
</h3>
</li>
<li
v-for="contact in allContacts.slice(0, 6)"
v-for="contact in allContacts.slice(0, 5)"
:key="contact.did"
@click="openGiftDialog(contact)"
@click="openGiftDialogToProject(contact)"
>
<EntityIcon
:contact="contact"
@@ -218,27 +202,41 @@
{{ contact.name || "(no name)" }}
</h3>
</li>
<li>
<span
v-if="allContacts.length >= 5"
@click="onClickAllContactsGifting()"
class="flex align-bottom text-xs text-blue-500 mt-12 cursor-pointer"
>
... or someone else...
</span>
</li>
</ul>
<!--
Ideally, this button should only be visible when the active account has more than 7 or 11 contacts in their list
(we want to limit the grid count above to 8 or 12 accounts to keep it compact)
-->
<a
v-if="allContacts.length >= 7"
@click="onClickAllContactsGifting()"
class="block text-center text-md font-bold 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-2 py-3 rounded-md"
>
Show More Contacts&hellip;
</a>
<GiftedDialog ref="customGiveDialog" :projectId="this.projectId" />
<GiftedDialog ref="giveDialogToThis" :toProjectId="this.projectId" />
</div>
<!-- Offers & Gifts to & from this -->
<div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
<!-- First, offers on the left-->
<div class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm font-semibold mb-3">Offered To This Idea</h3>
<div v-if="activeDid && isRegistered">
<div class="text-center">
<button
data-testId="offerButton"
@click="openOfferDialog()"
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
>
Offer (maybe with conditions)...
</button>
</div>
</div>
<OfferDialog
ref="customOfferDialog"
:projectId="this.projectId"
:projectName="this.name"
/>
<h3 class="text-lg font-bold mb-3 mt-4">Offered To This Idea</h3>
<div v-if="offersToThis.length === 0">
(None yet. Wanna
@@ -300,15 +298,27 @@
</div>
</div>
<!-- Now, gives TO this project in the middle -->
<!-- (similar to "FROM" gift display below) -->
<div class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm font-semibold mb-3">Given To This Idea</h3>
<div v-if="activeDid && isRegistered">
<div class="text-center">
<button
@click="openGiftDialogToProject()"
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
>
Given To This...
</button>
</div>
</div>
<h3 class="text-lg font-bold mb-3 mt-4">Given To This Idea</h3>
<div v-if="givesToThis.length === 0">
(None yet. If you've seen something, say something by clicking a
contact above.)
</div>
<!-- similar to gift display below -->
<ul v-else class="text-sm border-t border-slate-300">
<li
v-for="give in givesToThis"
@@ -346,12 +356,22 @@
<a @click="onClickLoadClaim(give.jwtId)">
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
</a>
<a
v-if="checkIsConfirmable(give)"
@click="confirmConfirmClaim(give)"
v-if="
checkIsConfirmable(give) &&
!recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
"
@click="deepCheckConfirmable(give)"
>
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
</a>
<a v-else-if="checkingConfirmationForJwtId === give.jwtId">
<fa icon="spinner" class="fa-spin-pulse" />
</a>
<a v-else @click="shallowNotifyWhyCannotConfirm(give)">
<fa icon="circle-check" class="text-slate-500 cursor-pointer" />
</a>
</div>
<div v-if="give.fullClaim.image" class="flex justify-center">
<a :href="give.fullClaim.image" target="_blank">
@@ -365,58 +385,91 @@
</div>
</div>
<div class="grid items-start grid-cols-1 gap-4">
<div
v-if="givesProvidedByThis.length > 0"
class="bg-slate-100 px-4 py-3 rounded-md"
>
<div>
<h3 class="text-sm font-semibold border-b">
Individuals Getting Contributions From This
</h3>
<!-- similar to gift display above -->
<ul class="text-sm border-t border-slate-300">
<li
v-for="give in givesProvidedByThis"
:key="give.id"
class="py-1.5 border-b border-slate-300"
>
<div class="flex justify-between gap-4">
<span>
{{
serverUtil.didInfo(
give.recipientDid,
activeDid,
allMyDids,
allContacts,
)
}}
</span>
<span v-if="give.amount" class="whitespace-nowrap">
<fa
:icon="libsUtil.iconForUnitCode(give.unit)"
class="fa-fw text-slate-400"
/>{{ give.amount }}
</span>
</div>
<div class="text-slate-500">
<fa icon="calendar" class="fa-fw text-slate-400" />
{{ give.issuedAt?.substring(0, 10) }}
</div>
<div v-if="give.description" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400" />
{{ give.description }}
</div>
<a @click="onClickLoadClaim(give.jwtId)">
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
</a>
</li>
</ul>
<div v-if="givesProvidedByHitLimit" class="text-center">
<button @click="loadGivesProvidedBy()">Load More</button>
</div>
<!-- Finally, gives FROM this project on the right -->
<!-- (similar to "TO" gift display above) -->
<div class="bg-slate-100 px-4 py-3 rounded-md">
<div v-if="activeDid && isRegistered">
<div class="text-center">
<button
@click="openGiftDialogFromProject()"
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
>
Given By This...
</button>
</div>
</div>
<GiftedDialog
ref="giveDialogFromThis"
:fromProjectId="this.projectId"
/>
<h3 class="text-lg font-bold mb-3 mt-4">Benefitted By This</h3>
<div v-if="givesProvidedByThis.length === 0">(None yet.)</div>
<ul v-else class="text-sm border-t border-slate-300">
<li
v-for="give in givesProvidedByThis"
:key="give.id"
class="py-1.5 border-b border-slate-300"
>
<div class="flex justify-between gap-4">
<span>
{{
serverUtil.didInfo(
give.recipientDid,
activeDid,
allMyDids,
allContacts,
)
}}
</span>
<span v-if="give.amount" class="whitespace-nowrap">
<fa
:icon="libsUtil.iconForUnitCode(give.unit)"
class="fa-fw text-slate-400"
/>{{ give.amount }}
</span>
</div>
<div class="text-slate-500">
<fa icon="calendar" class="fa-fw text-slate-400" />
{{ give.issuedAt?.substring(0, 10) }}
</div>
<div v-if="give.description" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400" />
{{ give.description }}
</div>
<div class="flex justify-between">
<a @click="onClickLoadClaim(give.jwtId)">
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
</a>
<a
v-if="
checkIsConfirmable(give) &&
!recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
"
@click="deepCheckConfirmable(give)"
>
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
</a>
<a v-else-if="checkingConfirmationForJwtId === give.jwtId">
<fa icon="spinner" class="fa-spin-pulse" />
</a>
<a v-else @click="shallowNotifyWhyCannotConfirm(give)">
<fa icon="circle-check" class="text-slate-500 cursor-pointer" />
</a>
</div>
<div v-if="give.fullClaim.image" class="flex justify-center">
<a :href="give.fullClaim.image" target="_blank">
<img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" />
</a>
</div>
</li>
</ul>
<div v-if="givesProvidedByHitLimit" class="text-center">
<button @click="loadGivesProvidedBy()">Load More</button>
</div>
</div>
</div>
</section>
@@ -468,6 +521,7 @@ export default class ProjectViewView extends Vue {
allMyDids: Array<string> = [];
allContacts: Array<Contact> = [];
apiServer = "";
checkingConfirmationForJwtId = "";
description = "";
expanded = false;
fulfilledByThis: PlanSummaryRecord | null = null;
@@ -486,6 +540,7 @@ export default class ProjectViewView extends Vue {
offersToThis: Array<OfferSummaryRecord> = [];
offersHitLimit = false;
projectId = ""; // handle ID
recentlyCheckedAndUnconfirmableJwts = [];
showDidCopy = false;
startTime = "";
truncatedDesc = "";
@@ -847,12 +902,21 @@ export default class ProjectViewView extends Vue {
);
}
openGiftDialog(contact?: libsUtil.GiverReceiverInputInfo) {
(this.$refs.customGiveDialog as GiftedDialog).open(
openGiftDialogToProject(contact?: libsUtil.GiverReceiverInputInfo) {
(this.$refs.giveDialogToThis as GiftedDialog).open(
contact,
undefined,
undefined,
"Given by " + (contact?.name || "someone not named"),
(contact?.name || "Someone not named") + ` gave to this project`,
);
}
openGiftDialogFromProject() {
(this.$refs.giveDialogFromThis as GiftedDialog).open(
undefined,
{ did: this.activeDid, name: "You" },
undefined,
`This project gave to you`,
);
}
@@ -896,7 +960,7 @@ export default class ProjectViewView extends Vue {
const giver: libsUtil.GiverReceiverInputInfo = {
did: libsUtil.offerGiverDid(offerRecord),
};
(this.$refs.customGiveDialog as GiftedDialog).open(
(this.$refs.giveDialogToThis as GiftedDialog).open(
giver,
undefined,
offer.handleId,
@@ -932,20 +996,70 @@ export default class ProjectViewView extends Vue {
}
}
checkIsConfirmable(give: GiveSummaryRecord) {
/**
* @param confirmerIdList optional list of DIDs who confirmed; if missing, doesn't do a full server check
*/
checkIsConfirmable(give: GiveSummaryRecord, confirmerIdList?: string[]) {
const giveDetails: GenericCredWrapper<GiveVerifiableCredential> = {
...BLANK_GENERIC_SERVER_RECORD,
claim: give.fullClaim,
claimType: "GiveAction",
issuer: give.agentDid,
issuer: give.issuerDid,
};
return libsUtil.isGiveRecordTheUserCanConfirm(
this.isRegistered,
giveDetails,
this.activeDid,
confirmerIdList,
);
}
shallowNotifyWhyCannotConfirm(give: GiveSummaryRecord) {
const confirmerIds = this.recentlyCheckedAndUnconfirmableJwts.includes(
give.jwtId,
)
? [this.activeDid]
: [];
libsUtil.notifyWhyCannotConfirm(
this.$notify,
this.isRegistered,
"GiveAction",
give,
this.activeDid,
confirmerIds,
);
}
async deepCheckConfirmable(give: GiveSummaryRecord) {
this.checkingConfirmationForJwtId = give.jwtId;
const confirmerInfo: libsUtil.ConfirmerData | undefined =
await libsUtil.retrieveConfirmerIdList(
this.apiServer,
give.jwtId,
give.issuerDid,
this.activeDid,
);
if (
this.checkIsConfirmable(give, confirmerInfo?.confirmerIdList as string[])
) {
this.confirmConfirmClaim(give);
} else {
this.recentlyCheckedAndUnconfirmableJwts = [
...this.recentlyCheckedAndUnconfirmableJwts,
give.jwtId,
];
libsUtil.notifyWhyCannotConfirm(
this.$notify,
this.isRegistered,
"GiveAction",
give,
this.activeDid,
confirmerInfo?.confirmerIdList as string[],
);
}
this.checkingConfirmationForJwtId = "";
}
confirmConfirmClaim(give: GiveSummaryRecord) {
this.$notify(
{
@@ -994,6 +1108,10 @@ export default class ProjectViewView extends Vue {
},
5000,
);
this.recentlyCheckedAndUnconfirmableJwts = [
...this.recentlyCheckedAndUnconfirmableJwts,
give.jwtId,
];
} else {
console.error("Got error submitting the confirmation:", result);
const message =