Browse Source

show totals of gives to a project

master
Trent Larson 3 weeks ago
parent
commit
61afba3bca
  1. 7
      CHANGELOG.md
  2. 102
      src/views/ProjectViewView.vue

7
CHANGELOG.md

@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.4.5] - 2025.02.23
### Added
- Total amounts of gives on project page
### Changed in DB or environment
- Requires Endorser.ch version 4.2.6+
## [0.4.4] - 2025.02.17 ## [0.4.4] - 2025.02.17
### Fixed ### Fixed
- On production (due to data?) the search results would disappear after scrolling down. Now we don't show any results when going to the people map with a shortcut. - On production (due to data?) the search results would disappear after scrolling down. Now we don't show any results when going to the people map with a shortcut.

102
src/views/ProjectViewView.vue

@ -325,16 +325,55 @@
</div> </div>
</div> </div>
<h3 class="text-lg font-bold mb-3 mt-4">Given To This Idea</h3> <h3 class="text-lg font-bold mt-4">Given To This Idea</h3>
<div v-if="givesToThis.length === 0"> <div v-if="givesToThis.length === 0" class="text-sm">
(None yet. If you've seen something, say something by clicking a (None yet. If you've seen something, say something by clicking a
contact above.) contact above.)
</div> </div>
<ul v-else class="text-sm border-t border-slate-300"> <div v-else class="mt-1 text-sm">
<li <!-- Totals section -->
v-for="give in givesToThis" <div class="mt-1 flex items-center min-h-[1.5rem]">
<div v-if="loadingTotals" class="flex-1">
<fa icon="spinner" class="fa-spin-pulse text-blue-500" />
</div>
<div v-else-if="givesTotalsByUnit.length > 0" class="flex-1">
<span class="font-semibold mr-2 shrink-0">Totals</span>
<span class="whitespace-nowrap overflow-hidden text-ellipsis">
<a @click="totalsExpanded = !totalsExpanded" class="cursor-pointer text-blue-500">
<!-- just show the hours, or alternatively whatever is first -->
<span v-if="givenTotalHours() > 0">
{{ givenTotalHours() }} {{ libsUtil.UNIT_SHORT["HUR"] }}
</span>
<span v-else>
{{ givesTotalsByUnit[0].amount }} {{ libsUtil.UNIT_SHORT[givesTotalsByUnit[0].unit] }}
</span>
<span v-if="givesTotalsByUnit.length > 1">...</span>
<span>
<fa :icon="totalsExpanded ? 'chevron-up' : 'chevron-right'" class="fa-fw text-xs ml-1" />
</span>
</a>
<!-- show the full list when expanded -->
<div v-if="totalsExpanded">
<div v-for="(total, index) in givesTotalsByUnit" :key="total.unit" class="ml-2">
<fa :icon="libsUtil.iconForUnitCode(total.unit)" class="fa-fw text-slate-400 mr-1" />
{{ total.amount }} {{ libsUtil.UNIT_LONG[total.unit] }}
</div>
</div>
</span>
</div>
<div v-else>
<span class="font-semibold mr-2 shrink-0">
{{ givesToThis.length }}{{ givesHitLimit ? "+" : "" }} record{{ givesToThis.length === 1 ? "" : "s" }}
</span>
</div>
</div>
<!-- List of gives -->
<ul class="mt-2 text-sm border-t border-slate-300">
<li
v-for="give in givesToThis"
:key="give.id" :key="give.id"
class="py-1.5 border-b border-slate-300" class="py-1.5 border-b border-slate-300"
> >
@ -391,8 +430,9 @@
<img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" /> <img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" />
</a> </a>
</div> </div>
</li> </li>
</ul> </ul>
</div>
<div v-if="givesHitLimit" class="text-center text-blue-500"> <div v-if="givesHitLimit" class="text-center text-blue-500">
<button @click="loadGives()">Load More</button> <button @click="loadGives()">Load More</button>
</div> </div>
@ -554,6 +594,7 @@ export default class ProjectViewView extends Vue {
givesHitLimit = false; givesHitLimit = false;
givesProvidedByThis: Array<GiveSummaryRecord> = []; givesProvidedByThis: Array<GiveSummaryRecord> = [];
givesProvidedByHitLimit = false; givesProvidedByHitLimit = false;
givesTotalsByUnit: Array<{unit: string, amount: number}> = [];
imageUrl = ""; imageUrl = "";
isRegistered = false; isRegistered = false;
issuer = ""; issuer = "";
@ -564,6 +605,7 @@ export default class ProjectViewView extends Vue {
} | null = null; } | null = null;
issuerVisibleToDids: Array<string> = []; issuerVisibleToDids: Array<string> = [];
latitude = 0; latitude = 0;
loadingTotals = false;
longitude = 0; longitude = 0;
name = ""; name = "";
offersToThis: Array<OfferSummaryRecord> = []; offersToThis: Array<OfferSummaryRecord> = [];
@ -571,6 +613,7 @@ export default class ProjectViewView extends Vue {
projectId = ""; // handle ID projectId = ""; // handle ID
recentlyCheckedAndUnconfirmableJwts: string[] = []; recentlyCheckedAndUnconfirmableJwts: string[] = [];
startTime = ""; startTime = "";
totalsExpanded = false;
truncatedDesc = ""; truncatedDesc = "";
truncateLength = 40; truncateLength = 40;
url = ""; url = "";
@ -609,6 +652,7 @@ export default class ProjectViewView extends Vue {
this.projectId = decodeURIComponent(pathParam); this.projectId = decodeURIComponent(pathParam);
} }
this.loadProject(this.projectId, this.activeDid); this.loadProject(this.projectId, this.activeDid);
this.loadTotals();
} }
onEditClick() { onEditClick() {
@ -1207,5 +1251,49 @@ export default class ProjectViewView extends Vue {
this.allMyDids, this.allMyDids,
); );
} }
async loadTotals() {
this.loadingTotals = true;
const url = this.apiServer + "/api/v2/report/givesToPlans?planIds=" + encodeURIComponent(JSON.stringify([this.projectId]));
const headers = await serverUtil.getHeaders(this.activeDid);
try {
const resp = await this.axios.get(url, { headers });
if (resp.status === 200 && resp.data.data) {
// Calculate totals by unit
const totals: {[key: string]: number} = {};
resp.data.data.forEach((give: GiveSummaryRecord) => {
const amount = give.fullClaim.object?.amountOfThisGood;
const unit = give.fullClaim.object?.unitCode;
if (amount && unit) {
totals[unit] = (totals[unit] || 0) + amount;
}
});
// Convert totals object to array format
this.givesTotalsByUnit = Object.entries(totals).map(([unit, amount]) => ({
unit,
amount
}));
}
} catch (error) {
console.error("Error loading totals:", error);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to load totals for this project.",
},
5000,
);
} finally {
this.loadingTotals = false;
}
}
givenTotalHours(): number {
return this.givesTotalsByUnit.find(total => total.unit === "HUR")?.amount || 0;
}
} }
</script> </script>

Loading…
Cancel
Save