Browse Source

Merge branch 'gifting-periphery-improvements' into build-improvement

pull/142/head
Matthew Raymer 2 days ago
parent
commit
b388dc3d8e
  1. 62
      src/components/GiftedDialog.vue
  2. 2
      src/libs/fontawesome.ts
  3. 2
      src/libs/util.ts
  4. 199
      src/views/ContactGiftingView.vue
  5. 169
      src/views/GiftedDetailsView.vue
  6. 104
      src/views/HomeView.vue
  7. 118
      src/views/ProjectViewView.vue

62
src/components/GiftedDialog.vue

@ -3,7 +3,7 @@
<div class="dialog"> <div class="dialog">
<!-- Step 1: Entity Selection --> <!-- Step 1: Entity Selection -->
<EntitySelectionStep <EntitySelectionStep
v-show="currentStep === 1" v-show="firstStep"
:step-type="stepType" :step-type="stepType"
:giver-entity-type="giverEntityType" :giver-entity-type="giverEntityType"
:recipient-entity-type="recipientEntityType" :recipient-entity-type="recipientEntityType"
@ -24,7 +24,7 @@
<!-- Step 2: Gift Details --> <!-- Step 2: Gift Details -->
<GiftDetailsStep <GiftDetailsStep
v-show="currentStep === 2" v-show="!firstStep"
:giver="giver" :giver="giver"
:receiver="receiver" :receiver="receiver"
:giver-entity-type="giverEntityType" :giver-entity-type="giverEntityType"
@ -52,6 +52,7 @@
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-facing-decorator"; import { Vue, Component, Prop, Watch } from "vue-facing-decorator";
import { Vue, Component, Prop, Watch } from "vue-facing-decorator";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import { import {
@ -59,6 +60,7 @@ import {
didInfo, didInfo,
serverMessageForUser, serverMessageForUser,
getHeaders, getHeaders,
getHeaders,
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import * as libsUtil from "../libs/util"; import * as libsUtil from "../libs/util";
import { db, retrieveSettingsForActiveAccount } from "../db/index"; import { db, retrieveSettingsForActiveAccount } from "../db/index";
@ -99,6 +101,23 @@ export default class GiftedDialog extends Vue {
this.updateEntityTypes(); this.updateEntityTypes();
} }
@Watch("toProjectId")
onToProjectIdChange() {
this.updateEntityTypes();
}
@Prop({ default: false }) showProjects = false;
@Prop() isFromProjectView = false;
@Watch("showProjects")
onShowProjectsChange() {
this.updateEntityTypes();
}
@Watch("fromProjectId")
onFromProjectIdChange() {
this.updateEntityTypes();
}
@Watch("toProjectId") @Watch("toProjectId")
onToProjectIdChange() { onToProjectIdChange() {
this.updateEntityTypes(); this.updateEntityTypes();
@ -113,14 +132,14 @@ export default class GiftedDialog extends Vue {
callbackOnSuccess?: (amount: number) => void = () => {}; callbackOnSuccess?: (amount: number) => void = () => {};
customTitle?: string; customTitle?: string;
description = ""; description = "";
firstStep = true; // true = Step 1 (giver/recipient selection), false = Step 2 (amount/description)
giver?: libsUtil.GiverReceiverInputInfo; // undefined means no identified giver agent giver?: libsUtil.GiverReceiverInputInfo; // undefined means no identified giver agent
offerId = ""; offerId = "";
prompt = ""; prompt = "";
receiver?: libsUtil.GiverReceiverInputInfo; receiver?: libsUtil.GiverReceiverInputInfo;
unitCode = "HUR"; unitCode = "HUR";
visible = false; visible = false;
currentStep = 1;
libsUtil = libsUtil; libsUtil = libsUtil;
projects: PlanData[] = []; projects: PlanData[] = [];
@ -222,12 +241,15 @@ export default class GiftedDialog extends Vue {
this.amountInput = "0"; this.amountInput = "0";
this.callbackOnSuccess = callbackOnSuccess; this.callbackOnSuccess = callbackOnSuccess;
this.offerId = offerId || ""; this.offerId = offerId || "";
this.currentStep = giver ? 2 : 1; this.firstStep = !giver;
this.stepType = "giver"; this.stepType = "giver";
// Update entity types based on current props // Update entity types based on current props
this.updateEntityTypes(); this.updateEntityTypes();
// Update entity types based on current props
this.updateEntityTypes();
try { try {
let settings = await databaseUtil.retrieveSettingsForActiveAccount(); let settings = await databaseUtil.retrieveSettingsForActiveAccount();
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
@ -313,7 +335,7 @@ export default class GiftedDialog extends Vue {
this.amountInput = "0"; this.amountInput = "0";
this.prompt = ""; this.prompt = "";
this.unitCode = "HUR"; this.unitCode = "HUR";
this.currentStep = 1; this.firstStep = true;
} }
async confirm() { async confirm() {
@ -355,6 +377,20 @@ export default class GiftedDialog extends Vue {
); );
return; return;
} }
// Check for person conflict
if (this.hasPersonConflict) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "You cannot select the same person as both giver and recipient.",
},
3000,
);
return;
}
// Check for person conflict // Check for person conflict
if (this.hasPersonConflict) { if (this.hasPersonConflict) {
@ -446,14 +482,18 @@ export default class GiftedDialog extends Vue {
this.activeDid, this.activeDid,
fromDid, fromDid,
toDid, toDid,
fromDid,
toDid,
description, description,
amount, amount,
unitCode, unitCode,
fulfillsProjectHandleId, fulfillsProjectHandleId,
fulfillsProjectHandleId,
this.offerId, this.offerId,
false, false,
undefined, undefined,
providerPlanHandleId, providerPlanHandleId,
providerPlanHandleId,
); );
if (!result.success) { if (!result.success) {
@ -540,12 +580,12 @@ export default class GiftedDialog extends Vue {
name: "Unnamed", name: "Unnamed",
}; };
} }
this.currentStep = 2; this.firstStep = false;
} }
goBackToStep1(step: string) { goBackToStep1(step: string) {
this.stepType = step; this.stepType = step;
this.currentStep = 1; this.firstStep = true;
} }
async loadProjects() { async loadProjects() {
@ -588,7 +628,7 @@ export default class GiftedDialog extends Vue {
did: this.activeDid, did: this.activeDid,
name: "You", name: "You",
}; };
this.currentStep = 2; this.firstStep = false;
} }
selectRecipient(contact?: Contact) { selectRecipient(contact?: Contact) {
@ -603,7 +643,7 @@ export default class GiftedDialog extends Vue {
name: "Unnamed", name: "Unnamed",
}; };
} }
this.currentStep = 2; this.firstStep = false;
} }
selectRecipientProject(project: PlanData) { selectRecipientProject(project: PlanData) {
@ -613,7 +653,7 @@ export default class GiftedDialog extends Vue {
image: project.image, image: project.image,
handleId: project.handleId, handleId: project.handleId,
}; };
this.currentStep = 2; this.firstStep = false;
} }
// Computed property for the query parameters // Computed property for the query parameters

2
src/libs/fontawesome.ts

@ -61,6 +61,7 @@ import {
faLightbulb, faLightbulb,
faLink, faLink,
faLocationDot, faLocationDot,
faLock,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,
faMagnifyingGlass, faMagnifyingGlass,
@ -145,6 +146,7 @@ library.add(
faLightbulb, faLightbulb,
faLink, faLink,
faLocationDot, faLocationDot,
faLock,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,
faMagnifyingGlass, faMagnifyingGlass,

2
src/libs/util.ts

@ -39,6 +39,8 @@ import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
export interface GiverReceiverInputInfo { export interface GiverReceiverInputInfo {
did?: string; did?: string;
name?: string; name?: string;
image?: string;
handleId?: string;
} }
export enum OnboardPage { export enum OnboardPage {

199
src/views/ContactGiftingView.vue

@ -4,14 +4,14 @@
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb --> <!-- Breadcrumb -->
<div id="ViewBreadcrumb" class="mb-8"> <div id="ViewBreadcrumb" class="mb-8">
<h1 class="text-lg text-center font-light relative px-7"> <h1 class="text-2xl text-center font-semibold relative px-7">
<!-- Back --> <!-- Back -->
<router-link <router-link
:to="{ name: 'home' }" :to="{ name: 'home' }"
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
><font-awesome icon="chevron-left" class="fa-fw"></font-awesome> ><font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
</router-link> </router-link>
Given by... {{ stepType === "giver" ? "Given by..." : "Given to..." }}
</h1> </h1>
</div> </div>
@ -19,19 +19,18 @@
<ul class="border-t border-slate-300"> <ul class="border-t border-slate-300">
<li class="border-b border-slate-300 py-3"> <li class="border-b border-slate-300 py-3">
<h2 class="text-base flex gap-4 items-center"> <h2 class="text-base flex gap-4 items-center">
<span class="grow"> <span class="grow flex gap-2 items-center font-medium">
<img <font-awesome
src="../assets/blank-square.svg" icon="circle-question"
width="32" class="text-slate-400 text-4xl"
class="inline-block align-middle border border-slate-300 rounded-md mr-1"
/> />
Unnamed/Unknown <span class="italic text-slate-400">(Unnamed/Unknown)</span>
</span> </span>
<span class="text-right"> <span class="text-right">
<button <button
type="button" type="button"
class="block w-full text-center text-sm 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-3 py-1.5 rounded-md" class="block w-full text-center text-sm 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-3 py-1.5 rounded-md"
@click="openDialog()" @click="openDialog('Unnamed')"
> >
<font-awesome icon="gift" class="fa-fw"></font-awesome> <font-awesome icon="gift" class="fa-fw"></font-awesome>
</button> </button>
@ -44,13 +43,14 @@
class="border-b border-slate-300 py-3" class="border-b border-slate-300 py-3"
> >
<h2 class="text-base flex gap-4 items-center"> <h2 class="text-base flex gap-4 items-center">
<span class="grow font-semibold"> <span class="grow flex gap-2 items-center font-medium">
<EntityIcon <EntityIcon
:contact="contact" :contact="contact"
:icon-size="32" :icon-size="34"
class="inline-block align-middle border border-slate-300 rounded-md mr-1" class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden"
/> />
{{ contact.name || "(no name)" }} <span v-if="contact.name">{{ contact.name }}</span>
<span v-else class="italic text-slate-400">(No name)</span>
</span> </span>
<span class="text-right"> <span class="text-right">
<button <button
@ -65,7 +65,13 @@
</li> </li>
</ul> </ul>
<GiftedDialog ref="customDialog" :to-project-id="projectId" /> <GiftedDialog
ref="customDialog"
:from-project-id="fromProjectId"
:to-project-id="toProjectId"
:show-projects="showProjects"
:is-from-project-view="isFromProjectView"
/>
</section> </section>
</template> </template>
@ -96,6 +102,24 @@ export default class ContactGiftingView extends Vue {
description = ""; description = "";
projectId = ""; projectId = "";
prompt = ""; prompt = "";
recipientProjectName = "";
recipientProjectImage = "";
recipientProjectHandleId = "";
// New context parameters
stepType = "giver";
giverEntityType = "person" as "person" | "project";
recipientEntityType = "person" as "person" | "project";
giverProjectId = "";
giverProjectName = "";
giverProjectImage = "";
giverProjectHandleId = "";
giverDid = "";
recipientDid = "";
fromProjectId = "";
toProjectId = "";
showProjects = false;
isFromProjectView = false;
async created() { async created() {
try { try {
@ -111,9 +135,41 @@ export default class ContactGiftingView extends Vue {
dbAllContacts, dbAllContacts,
) as unknown as Contact[]; ) as unknown as Contact[];
this.projectId = (this.$route.query["projectId"] as string) || ""; this.projectId =
(this.$route.query["recipientProjectId"] as string) || "";
this.recipientProjectName =
(this.$route.query["recipientProjectName"] as string) || "";
this.recipientProjectImage =
(this.$route.query["recipientProjectImage"] as string) || "";
this.recipientProjectHandleId =
(this.$route.query["recipientProjectHandleId"] as string) || "";
this.prompt = (this.$route.query["prompt"] as string) ?? this.prompt; this.prompt = (this.$route.query["prompt"] as string) ?? this.prompt;
// Read new context parameters
this.stepType = (this.$route.query["stepType"] as string) || "giver";
this.giverEntityType =
(this.$route.query["giverEntityType"] as "person" | "project") ||
"person";
this.recipientEntityType =
(this.$route.query["recipientEntityType"] as "person" | "project") ||
"person";
this.giverProjectId =
(this.$route.query["giverProjectId"] as string) || "";
this.giverProjectName =
(this.$route.query["giverProjectName"] as string) || "";
this.giverProjectImage =
(this.$route.query["giverProjectImage"] as string) || "";
this.giverProjectHandleId =
(this.$route.query["giverProjectHandleId"] as string) || "";
this.giverDid = (this.$route.query["giverDid"] as string) || "";
this.recipientDid = (this.$route.query["recipientDid"] as string) || "";
this.fromProjectId = (this.$route.query["fromProjectId"] as string) || "";
this.toProjectId = (this.$route.query["toProjectId"] as string) || "";
this.showProjects =
(this.$route.query["showProjects"] as string) === "true";
this.isFromProjectView =
(this.$route.query["isFromProjectView"] as string) === "true";
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);
@ -131,17 +187,108 @@ export default class ContactGiftingView extends Vue {
} }
} }
openDialog(giver?: GiverReceiverInputInfo) { openDialog(contact?: GiverReceiverInputInfo | "Unnamed") {
const recipient = this.projectId if (contact === "Unnamed") {
? undefined // Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
: { did: this.activeDid, name: "you" }; let recipient: GiverReceiverInputInfo;
(this.$refs.customDialog as GiftedDialog).open( let giver: GiverReceiverInputInfo | undefined;
giver,
recipient, if (this.stepType === "giver") {
undefined, // We're selecting a giver, so recipient is either a project or the current user
"Given by " + (giver?.name || "someone not named"), if (this.recipientEntityType === "project") {
this.prompt, recipient = {
); did: this.recipientProjectHandleId,
name: this.recipientProjectName,
image: this.recipientProjectImage,
handleId: this.recipientProjectHandleId,
};
} else {
recipient = { did: this.activeDid, name: "You" };
}
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
} else {
// We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context
recipient = { did: "", name: "Unnamed" };
// Preserve the existing giver from the context
if (this.giverEntityType === "project") {
giver = {
// no did, because it's a project
name: this.giverProjectName,
image: this.giverProjectImage,
handleId: this.giverProjectHandleId,
};
} else if (this.giverDid) {
giver = {
did: this.giverDid,
name: this.giverProjectName || "Someone",
};
} else {
giver = { did: this.activeDid, name: "You" };
}
}
(this.$refs.customDialog as GiftedDialog).open(
giver,
recipient,
undefined,
this.stepType === "giver" ? "Given by Unnamed" : "Given to Unnamed",
this.prompt,
);
// Immediately select "Unnamed" and move to Step 2
(this.$refs.customDialog as GiftedDialog).selectGiver();
} else {
// Regular case: contact is a GiverReceiverInputInfo
let giver: GiverReceiverInputInfo;
let recipient: GiverReceiverInputInfo;
if (this.stepType === "giver") {
// We're selecting a giver, so the contact becomes the giver
giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
// Recipient is either a project or the current user
if (this.recipientEntityType === "project") {
recipient = {
did: this.recipientProjectHandleId,
name: this.recipientProjectName,
image: this.recipientProjectImage,
handleId: this.recipientProjectHandleId,
};
} else {
recipient = { did: this.activeDid, name: "You" };
}
} else {
// We're selecting a recipient, so the contact becomes the recipient
recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
// Preserve the existing giver from the context
if (this.giverEntityType === "project") {
giver = {
did: this.giverProjectHandleId,
name: this.giverProjectName,
image: this.giverProjectImage,
handleId: this.giverProjectHandleId,
};
} else if (this.giverDid) {
giver = {
did: this.giverDid,
name: this.giverProjectName || "Someone",
};
} else {
giver = { did: this.activeDid, name: "You" };
}
}
(this.$refs.customDialog as GiftedDialog).open(
giver,
recipient,
undefined,
this.stepType === "giver"
? "Given by " + (contact?.name || "someone not named")
: "Given to " + (contact?.name || "someone not named"),
this.prompt,
);
}
} }
} }
</script> </script>

169
src/views/GiftedDetailsView.vue

@ -4,87 +4,91 @@
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Back --> <!-- Breadcrumb -->
<div <div id="ViewBreadcrumb" class="mb-8">
v-if="!hideBackButton" <h1 class="text-2xl text-center font-semibold relative px-7 mb-2">
class="text-lg text-center font-light relative px-7" <!-- Back -->
> <div
<h1 v-if="!hideBackButton"
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
@click="cancelBack()" @click="cancelBack()"
> >
<font-awesome icon="chevron-left" class="fa-fw"></font-awesome> <font-awesome icon="chevron-left" class="fa-fw" />
</div>
What Was Given
</h1> </h1>
<h2 class="text-lg font-normal text-center overflow-hidden">
<div class="truncate">
From
{{
providedByProject
? providerProjectName
: providedByGiver
? giverName
: "someone not named"
}}
</div>
<div class="truncate">
to
{{
givenToProject
? fulfillsProjectName
: givenToRecipient
? recipientName
: "someone not named"
}}
</div>
</h2>
</div> </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">
<span>
From
{{
providedByProject
? providerProjectName
: providedByGiver
? giverName
: "someone not named"
}}
</span>
<br />
<span>
to
{{
givenToProject
? fulfillsProjectName
: givenToRecipient
? recipientName
: "someone not named"
}}</span
>
</h1>
<textarea <textarea
v-model="description" v-model="description"
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2" class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
placeholder="What was received" placeholder="What was received"
/> />
<div class="flex flex-row justify-center"> <div class="flex mb-4">
<span <button
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 text-center text-blue-500 px-2 py-2 w-20" class="rounded-s border border-e-0 border-slate-400 bg-slate-200 px-4 py-2"
@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()" @click="amountInput === '0' ? null : decrement()"
> >
<font-awesome icon="chevron-left" /> <font-awesome icon="chevron-left" />
</div> </button>
<input <input
id="inputGivenAmount"
v-model="amountInput" v-model="amountInput"
type="number" type="number"
class="border border-r-0 border-slate-400 px-2 py-2 text-center w-20" class="flex-1 border border-e-0 border-slate-400 px-2 py-2 text-center w-[1px]"
/> />
<div <button
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2" class="rounded-e border border-slate-400 bg-slate-200 px-4 py-2"
@click="increment()" @click="increment()"
> >
<font-awesome icon="chevron-right" /> <font-awesome icon="chevron-right" />
</div> </button>
<select
v-model="unitCode"
class="flex-1 rounded border border-slate-400 ms-2 px-3 py-2"
>
<option
v-for="(displayName, code) in unitOptions"
:key="code"
:value="code"
>
{{ displayName }}
</option>
</select>
</div> </div>
<div class="flex justify-center mt-4" data-testId="imagery"> <div class="flex justify-center mt-4" data-testId="imagery">
<span v-if="imageUrl" class="flex justify-between"> <span v-if="imageUrl" class="flex items-end gap-3">
<a :href="imageUrl" target="_blank"> <a :href="imageUrl" target="_blank">
<img <img :src="imageUrl" class="h-36 rounded-lg" />
:src="libsUtil.transformImageUrlForCors(imageUrl)"
class="h-24 rounded-xl"
/>
</a> </a>
<font-awesome <font-awesome
icon="trash-can" icon="trash-can"
class="text-red-500 fa-fw ml-8 mt-10" class="text-red-500 fa-fw cursor-pointer"
@click="confirmDeleteImage" @click="confirmDeleteImage"
/> />
</span> </span>
@ -98,22 +102,22 @@
</div> </div>
<ImageMethodDialog ref="imageDialog" default-camera-mode="environment" /> <ImageMethodDialog ref="imageDialog" default-camera-mode="environment" />
<div class="mt-4 flex justify-between gap-2"> <div class="mt-4 sm:flex justify-between gap-2">
<!-- First Column for Giver --> <!-- First Column for Giver -->
<div class="flex-grow border border-slate-400 p-2 rounded-md"> <div class="sm:flex-grow sm:w-1/2 border border-slate-400 p-2 rounded-md overflow-hidden">
<div class="flex"> <div class="flex items-center">
<input <input
v-if="giverDid && !providedByProject" v-if="giverDid && !providedByProject"
v-model="providedByGiver" v-model="providedByGiver"
type="checkbox" type="checkbox"
class="h-6 w-6 mr-2" class="flex-shrink-0 h-6 w-6 mr-2"
/> />
<font-awesome <font-awesome
v-else v-else
icon="square" icon="square"
class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm" class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm"
/> />
<label class="text-sm mt-1"> <label class="text-sm truncate">
{{ {{
giverDid giverDid
? "This was provided by " + giverName + "." ? "This was provided by " + giverName + "."
@ -123,24 +127,24 @@
<font-awesome <font-awesome
v-if="!giverDid || providedByProject" v-if="!giverDid || providedByProject"
icon="info-circle" icon="info-circle"
class="-mt-1 bg-white text-slate-500 h-5 w-5 px-0.5 py-0.5 rounded-sm" class="text-base cursor-pointer bg-white text-slate-500 ms-1"
@click="notifyUserOfGiver()" @click="notifyUserOfGiver()"
/> />
</div> </div>
<div class="flex"> <div class="flex items-center">
<input <input
v-if="providerProjectId && !providedByGiver" v-if="providerProjectId && !providedByGiver"
v-model="providedByProject" v-model="providedByProject"
type="checkbox" type="checkbox"
class="h-6 w-6 mr-2" class="flex-shrink-0 h-6 w-6 mr-2"
/> />
<font-awesome <font-awesome
v-else v-else
icon="square" icon="square"
class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm" class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm"
/> />
<label class="text-sm mt-1"> <label class="text-sm truncate">
{{ {{
providerProjectId providerProjectId
? "This was provided by " + providerProjectName + "." ? "This was provided by " + providerProjectName + "."
@ -150,31 +154,31 @@
<font-awesome <font-awesome
v-if="!providerProjectId || providedByGiver" v-if="!providerProjectId || providedByGiver"
icon="info-circle" icon="info-circle"
class="-mt-1 bg-white text-slate-500 h-5 w-5 px-0.5 py-0.5 rounded-sm" class="text-base cursor-pointer bg-white text-slate-500 ms-1"
@click="notifyUserOfProvidingProject()" @click="notifyUserOfProvidingProject()"
/> />
</div> </div>
</div> </div>
<div class="flex-shrink flex justify-center items-center"> <div class="sm:flex-shrink flex justify-center items-center my-1 sm:my-0">
<font-awesome icon="arrow-right" class="fa-fw h-7" /> <font-awesome icon="arrow-right" class="fa-fw h-7 rotate-90 sm:rotate-0" />
</div> </div>
<!-- Third Column for Recipient --> <!-- Third Column for Recipient -->
<div class="flex-grow border border-slate-400 p-2 rounded-md"> <div class="sm:flex-grow sm:w-1/2 border border-slate-400 p-2 rounded-md overflow-hidden">
<div class="flex"> <div class="flex items-center">
<input <input
v-if="recipientDid && !givenToProject" v-if="recipientDid && !givenToProject"
v-model="givenToRecipient" v-model="givenToRecipient"
type="checkbox" type="checkbox"
class="h-6 w-6 mr-2" class="flex-shrink-0 h-6 w-6 mr-2"
/> />
<font-awesome <font-awesome
v-else v-else
icon="square" icon="square"
class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm" class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm"
/> />
<label class="text-sm mt-1"> <label class="text-sm truncate">
{{ {{
recipientDid recipientDid
? "This was given to " + recipientName + "." ? "This was given to " + recipientName + "."
@ -184,24 +188,24 @@
<font-awesome <font-awesome
v-if="!recipientDid || givenToProject" v-if="!recipientDid || givenToProject"
icon="info-circle" icon="info-circle"
class="-mt-1 bg-white text-slate-500 h-5 w-5 px-0.5 py-0.5 rounded-sm" class="text-base cursor-pointer bg-white text-slate-500 ms-1"
@click="notifyUserOfRecipient()" @click="notifyUserOfRecipient()"
/> />
</div> </div>
<div class="flex"> <div class="flex items-center">
<input <input
v-if="fulfillsProjectId && !givenToRecipient" v-if="fulfillsProjectId && !givenToRecipient"
v-model="givenToProject" v-model="givenToProject"
type="checkbox" type="checkbox"
class="h-6 w-6 mr-2" class="flex-shrink-0 h-6 w-6 mr-2"
/> />
<font-awesome <font-awesome
v-else v-else
icon="square" icon="square"
class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm" class="mr-2 bg-white text-white h-5 w-5 px-0.5 py-0.5 rounded-sm"
/> />
<label class="text-sm mt-1"> <label class="text-sm truncate">
{{ {{
fulfillsProjectId fulfillsProjectId
? "This was given to " + fulfillsProjectName + ". " ? "This was given to " + fulfillsProjectName + ". "
@ -211,7 +215,7 @@
<font-awesome <font-awesome
v-if="!fulfillsProjectId || givenToRecipient" v-if="!fulfillsProjectId || givenToRecipient"
icon="info-circle" icon="info-circle"
class="-mt-1 bg-white text-slate-500 h-5 w-5 px-0.5 py-0.5 rounded-sm" class="text-base cursor-pointer bg-white text-slate-500 ms-1"
@click="notifyUserFulfillsProject()" @click="notifyUserFulfillsProject()"
/> />
</div> </div>
@ -232,11 +236,11 @@
</router-link> </router-link>
</div> </div>
<p class="text-center mb-2 mt-6 italic"> <p class="text-center text-sm my-4">
Sign & Send to publish to the world <b class="font-medium">Sign &amp; Send</b> to publish to the world
<font-awesome <font-awesome
icon="circle-info" icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer" class="fa-fw text-blue-500 text-base cursor-pointer"
@click="explainData()" @click="explainData()"
/> />
</p> </p>
@ -902,5 +906,10 @@ export default class GiftedDetails extends Vue {
7000, 7000,
); );
} }
// Computed property to get unit options
get unitOptions() {
return this.libsUtil.UNIT_SHORT;
}
} }
</script> </script>

104
src/views/HomeView.vue

@ -157,40 +157,34 @@ Raymer * @version 1.0.0 */
</div> </div>
</div> </div>
<GiftedDialog ref="customDialog" /> <GiftedDialog ref="customDialog" :show-projects="showProjectsDialog" />
<GiftedPrompts ref="giftedPrompts" /> <GiftedPrompts ref="giftedPrompts" />
<FeedFilters ref="feedFilters" /> <FeedFilters ref="feedFilters" />
<div class="relative">
<button
v-if="isRegistered"
class="absolute right-6 bottom-0 transform translate-y-1/2 text-center text-4xl leading-none bg-green-600 text-white w-14 py-2.5 rounded-full"
@click="openDialog()"
>
<font-awesome icon="plus" class="fa-fw" />
</button>
</div>
<!-- Results List --> <!-- Results List -->
<div class="mt-4 mb-4"> <div class="mt-4 mb-4">
<div class="flex items-center mb-4"> <div class="flex gap-2 items-center mb-3">
<h2 class="text-xl font-bold flex items-center gap-4"> <h2 class="text-xl font-bold">Latest Activity</h2>
Latest Activity <button
<button v-if="resultsAreFiltered()"
v-if="resultsAreFiltered()" class="block ms-auto text-center text-white bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-2 rounded-full"
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] px-3 py-1.5 rounded-md text-xs text-white" @click="openFeedFilters()"
@click="openFeedFilters()" >
> <font-awesome
<font-awesome icon="filter" class="fa-fw" /> icon="filter"
</button> class="block text-center w-[1em] translate-y-[0.05em]"
<button />
v-else </button>
class="bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] px-3 py-1.5 rounded-md text-xs text-white" <button
@click="openFeedFilters()" v-else
> class="block ms-auto text-center text-white bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-2 rounded-full"
<font-awesome icon="filter" class="fa-fw" /> @click="openFeedFilters()"
</button> >
</h2> <font-awesome
icon="filter"
class="block text-center w-[1em] translate-y-[0.05em]"
/>
</button>
</div> </div>
<div <div
@ -445,6 +439,7 @@ export default class HomeView extends Vue {
selectedImageData: Blob | null = null; selectedImageData: Blob | null = null;
isImageViewerOpen = false; isImageViewerOpen = false;
imageCache: Map<string, Blob | null> = new Map(); imageCache: Map<string, Blob | null> = new Map();
showProjectsDialog = false;
/** /**
* Initializes the component on mount * Initializes the component on mount
@ -1604,17 +1599,33 @@ export default class HomeView extends Vue {
* @param giver Optional contact info for giver * @param giver Optional contact info for giver
* @param description Optional gift description * @param description Optional gift description
*/ */
openDialog(giver?: GiverReceiverInputInfo, description?: string) { openDialog(giver?: GiverReceiverInputInfo | "Unnamed", description?: string) {
(this.$refs.customDialog as GiftedDialog).open( if (giver === "Unnamed") {
giver, // Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
{ (this.$refs.customDialog as GiftedDialog).open(
did: this.activeDid, undefined,
name: "you", {
} as GiverReceiverInputInfo, did: this.activeDid,
undefined, name: "You",
"Given by " + (giver?.name || "someone not named"), } as GiverReceiverInputInfo,
description, undefined,
); "Given by Unnamed",
description,
);
// Immediately select "Unnamed" and move to Step 2
(this.$refs.customDialog as GiftedDialog).selectGiver();
} else {
(this.$refs.customDialog as GiftedDialog).open(
giver,
{
did: this.activeDid,
name: "You",
} as GiverReceiverInputInfo,
undefined,
"Given by " + (giver?.name || "someone not named"),
description,
);
}
} }
/** /**
@ -1848,5 +1859,18 @@ export default class HomeView extends Vue {
this.$router.push({ name: "contact-qr" }); this.$router.push({ name: "contact-qr" });
} }
} }
openDialogPerson(
giver?: GiverReceiverInputInfo | "Unnamed",
description?: string,
) {
this.showProjectsDialog = false;
this.openDialog(giver, description);
}
openProjectDialog() {
this.showProjectsDialog = true;
(this.$refs.customDialog as any).open();
}
} }
</script> </script>

118
src/views/ProjectViewView.vue

@ -214,63 +214,11 @@
</div> </div>
</div> </div>
<div v-if="activeDid && isRegistered"> <GiftedDialog
<div class="text-center"> ref="giveDialogToThis"
<p class="mt-2 mt-4 text-center">Record a contribution from:</p> :to-project-id="projectId"
</div> :is-from-project-view="true"
<ul />
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="openGiftDialogToProject({ name: 'you', did: activeDid })">
<font-awesome
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"
>
You
</h3>
</li>
<li @click="openGiftDialogToProject()">
<img
src="../assets/blank-square.svg"
class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
/>
<h3
class="text-xs text-blue-500 italic font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
>
Unnamed/Unknown
</h3>
</li>
<li
v-for="contact in allContacts.slice(0, 5)"
:key="contact.did"
@click="openGiftDialogToProject(contact)"
>
<EntityIcon
:contact="contact"
:icon-size="64"
class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
/>
<h3
class="text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
>
{{ contact.name || "(no name)" }}
</h3>
</li>
<li>
<span
v-if="allContacts.length >= 5"
class="flex align-bottom text-xs text-blue-500 mt-12 cursor-pointer"
@click="onClickAllContactsGifting()"
>
... or someone else...
</span>
</li>
</ul>
</div>
<GiftedDialog ref="giveDialogToThis" :to-project-id="projectId" />
<!-- Offers & Gifts to & from this --> <!-- Offers & Gifts to & from this -->
<div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4"> <div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
@ -536,7 +484,12 @@
</button> </button>
</div> </div>
</div> </div>
<GiftedDialog ref="giveDialogFromThis" :from-project-id="projectId" /> <GiftedDialog
ref="giveDialogFromThis"
:from-project-id="projectId"
:show-projects="true"
:is-from-project-view="true"
/>
<h3 class="text-lg font-bold mb-3 mt-4"> <h3 class="text-lg font-bold mb-3 mt-4">
Benefitted From This Project Benefitted From This Project
@ -1263,21 +1216,52 @@ export default class ProjectViewView extends Vue {
); );
} }
openGiftDialogToProject(contact?: libsUtil.GiverReceiverInputInfo) { openGiftDialogToProject(
(this.$refs.giveDialogToThis as GiftedDialog).open( contact?: libsUtil.GiverReceiverInputInfo | "Unnamed",
contact, ) {
undefined, if (contact === "Unnamed") {
undefined, // Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
(contact?.name || "Someone not named") + ` gave to this project`, (this.$refs.giveDialogToThis as GiftedDialog).open(
); undefined,
undefined,
undefined,
"Given by Unnamed to this project",
);
// Immediately select "Unnamed" and move to Step 2
(this.$refs.giveDialogToThis as GiftedDialog).selectGiver();
} else {
// Open straight to Step 2 with current user as giver and current project as recipient
(this.$refs.giveDialogToThis as GiftedDialog).open(
{
did: this.activeDid,
name: "You",
},
{
did: this.issuer,
name: this.name,
handleId: this.projectId,
image: this.imageUrl,
},
undefined,
`Given to ${this.name}`,
);
}
} }
openGiftDialogFromProject() { openGiftDialogFromProject() {
// Set the project as giver and the current user as recipient
(this.$refs.giveDialogFromThis as GiftedDialog).open( (this.$refs.giveDialogFromThis as GiftedDialog).open(
undefined, {
did: undefined,
name: this.name,
handleId: this.projectId,
image: this.imageUrl,
},
{ did: this.activeDid, name: "You" }, { did: this.activeDid, name: "You" },
undefined, undefined,
`This project gave to you`, `${this.name} gave to you`,
undefined,
undefined,
); );
} }

Loading…
Cancel
Save