Files
crowd-funder-from-jason/src/components/ActivityListItem.vue
Matthew Raymer e5518cd47c fix: update Vue template syntax and improve Vite config
- Fix Vue template syntax in App.vue by using proper event handler format
- Update Vite config to properly handle ESM imports and crypto modules
- Add manual chunks for better code splitting
- Improve environment variable handling in vite-env.d.ts
- Fix TypeScript linting errors in App.vue
2025-04-18 09:59:33 +00:00

266 lines
8.4 KiB
Vue

<template>
<li>
<!-- Last viewed separator -->
<div
v-if="record.jwtId == lastViewedClaimId"
class="border-b border-dashed border-slate-300 text-orange-400 mt-4 mb-6 font-bold text-sm"
>
<span class="block w-fit mx-auto -mb-2.5 bg-white px-2">
You've already seen all the following
</span>
</div>
<div
class="flex items-center justify-between gap-2 text-lg bg-slate-200 border border-slate-300 border-b-0 rounded-t-md px-3 sm:px-4 py-1 sm:py-2"
>
<div class="flex items-center gap-2">
<div v-if="record.issuerDid">
<EntityIcon
:entity-id="record.issuerDid"
class="rounded-full bg-white overflow-hidden !size-[2rem] object-cover"
/>
</div>
<div v-else>
<font-awesome
icon="person-circle-question"
class="text-slate-300 text-[2rem]"
/>
</div>
<div>
<h3 class="font-semibold">
{{ record.issuer.known ? record.issuer.displayName : '' }}
</h3>
<p class="ms-auto text-xs text-slate-500 italic">
{{ friendlyDate }}
</p>
</div>
</div>
<a class="cursor-pointer" @click="$emit('loadClaim', record.jwtId)">
<font-awesome icon="circle-info" class="fa-fw text-slate-500" />
</a>
</div>
<div class="bg-slate-100 rounded-b-md border border-slate-300 p-3 sm:p-4">
<!-- Record Image -->
<div
v-if="record.image"
class="bg-cover mb-6 -mt-3 sm:-mt-4 -mx-3 sm:-mx-4"
:style="`background-image: url(${record.image});`"
>
<a
class="block bg-slate-100/50 backdrop-blur-md px-6 py-4 cursor-pointer"
@click="$emit('viewImage', record.image)"
>
<img
class="w-full h-auto max-w-lg max-h-96 object-contain mx-auto drop-shadow-md"
:src="record.image"
alt="Activity image"
@load="$emit('cacheImage', record.image)"
/>
</a>
</div>
<div
class="relative flex justify-between gap-4 max-w-[40rem] mx-auto mb-5"
>
<!-- Source -->
<div
class="w-[8rem] sm:w-[12rem] text-center bg-white border border-slate-200 rounded p-2 sm:p-3"
>
<div class="relative w-fit mx-auto">
<div>
<!-- Project Icon -->
<div v-if="record.providerPlanName">
<ProjectIcon
:entity-id="record.providerPlanName"
:icon-size="48"
class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full"
/>
</div>
<!-- Identicon for DIDs -->
<div v-else-if="record.agentDid">
<EntityIcon
:entity-id="record.agentDid"
:profile-image-url="record.issuer.profileImageUrl"
class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]"
/>
</div>
<!-- Unknown Person -->
<div v-else>
<font-awesome
icon="person-circle-question"
class="text-slate-300 text-[3rem] sm:text-[4rem]"
/>
</div>
</div>
</div>
<div
v-if="record.providerPlanName || record.giver.known"
class="text-xs mt-2 truncate"
>
<font-awesome
:icon="record.providerPlanName ? 'users' : 'user'"
class="fa-fw text-slate-400"
/>
{{ record.providerPlanName || record.giver.displayName }}
</div>
</div>
<!-- Arrow -->
<div
class="absolute inset-x-[8rem] sm:inset-x-[12rem] mx-2 top-1/2 -translate-y-1/2"
>
<div class="text-sm text-center leading-none font-semibold pe-[15px]">
{{ fetchAmount }}
</div>
<div class="flex items-center">
<hr
class="grow border-t-[18px] sm:border-t-[24px] border-slate-300"
/>
<div
class="shrink-0 w-0 h-0 border border-slate-300 border-t-[20px] sm:border-t-[25px] border-t-transparent border-b-[20px] sm:border-b-[25px] border-b-transparent border-s-[27px] sm:border-s-[34px] border-e-0"
/>
</div>
</div>
<!-- Destination -->
<div
class="w-[8rem] sm:w-[12rem] text-center bg-white border border-slate-200 rounded p-2 sm:p-3"
>
<div class="relative w-fit mx-auto">
<div>
<!-- Project Icon -->
<div v-if="record.recipientProjectName">
<ProjectIcon
:entity-id="record.recipientProjectName"
:icon-size="48"
class="rounded size-[3rem] sm:size-[4rem] *:w-full *:h-full"
/>
</div>
<!-- Identicon for DIDs -->
<div v-else-if="record.recipientDid">
<EntityIcon
:entity-id="record.recipientDid"
:profile-image-url="record.receiver.profileImageUrl"
class="rounded-full bg-slate-100 overflow-hidden !size-[3rem] sm:!size-[4rem]"
/>
</div>
<!-- Unknown Person -->
<div v-else>
<font-awesome
icon="person-circle-question"
class="text-slate-300 text-[3rem] sm:text-[4rem]"
/>
</div>
</div>
</div>
<div
v-if="record.recipientProjectName || record.receiver.known"
class="text-xs mt-2 truncate"
>
<font-awesome
:icon="record.recipientProjectName ? 'users' : 'user'"
class="fa-fw text-slate-400"
/>
{{ record.recipientProjectName || record.receiver.displayName }}
</div>
</div>
</div>
<!-- Description -->
<p class="font-medium">
<a class="cursor-pointer" @click="$emit('loadClaim', record.jwtId)">
{{ description }}
</a>
</p>
</div>
</li>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-facing-decorator'
import { GiveRecordWithContactInfo } from '../types'
import EntityIcon from './EntityIcon.vue'
import { isGiveClaimType, notifyWhyCannotConfirm } from '../libs/util'
import { containsHiddenDid } from '../libs/endorserServer'
import ProjectIcon from './ProjectIcon.vue'
@Component({
components: {
EntityIcon,
ProjectIcon
}
})
export default class ActivityListItem extends Vue {
@Prop() record!: GiveRecordWithContactInfo
@Prop() lastViewedClaimId?: string
@Prop() isRegistered!: boolean
@Prop() activeDid!: string
@Prop() confirmerIdList?: string[]
get fetchAmount(): string {
const claim =
(this.record.fullClaim as unknown).claim || this.record.fullClaim
const amount = claim.object?.amountOfThisGood
? this.displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
: ''
return amount
}
get description(): string {
const claim =
(this.record.fullClaim as unknown).claim || this.record.fullClaim
return `${claim.description}`
}
private displayAmount(code: string, amt: number) {
return `${amt} ${this.currencyShortWordForCode(code, amt === 1)}`
}
private currencyShortWordForCode(unitCode: string, single: boolean) {
return unitCode === 'HUR' ? (single ? 'hour' : 'hours') : unitCode
}
get canConfirm(): boolean {
if (!this.isRegistered) return false
if (!isGiveClaimType(this.record.fullClaim?.['@type'])) return false
if (this.confirmerIdList?.includes(this.activeDid)) return false
if (this.record.issuerDid === this.activeDid) return false
if (containsHiddenDid(this.record.fullClaim)) return false
return true
}
handleConfirmClick() {
if (!this.canConfirm) {
notifyWhyCannotConfirm(
this.$notify,
this.isRegistered,
this.record.fullClaim?.['@type'],
this.record,
this.activeDid,
this.confirmerIdList
)
return
}
this.$emit('confirmClaim', this.record)
}
get friendlyDate(): string {
const date = new Date(this.record.issuedAt)
return date.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric'
})
}
}
</script>