From 61afba3bca11003283dbda6ac2eec2ff64eedd9a Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sat, 22 Feb 2025 20:34:23 -0700 Subject: [PATCH 1/5] show totals of gives to a project --- CHANGELOG.md | 7 +++ src/views/ProjectViewView.vue | 102 +++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dafcf0d..dfacfb8 100644 --- a/CHANGELOG.md +++ b/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 ### 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. diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index 5fc0acd..70be78d 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -325,16 +325,55 @@ -

Given To This Idea

+

Given To This Idea

-
+
(None yet. If you've seen something, say something by clicking a contact above.)
-
- - + + +
@@ -554,6 +594,7 @@ export default class ProjectViewView extends Vue { givesHitLimit = false; givesProvidedByThis: Array = []; givesProvidedByHitLimit = false; + givesTotalsByUnit: Array<{unit: string, amount: number}> = []; imageUrl = ""; isRegistered = false; issuer = ""; @@ -564,6 +605,7 @@ export default class ProjectViewView extends Vue { } | null = null; issuerVisibleToDids: Array = []; latitude = 0; + loadingTotals = false; longitude = 0; name = ""; offersToThis: Array = []; @@ -571,6 +613,7 @@ export default class ProjectViewView extends Vue { projectId = ""; // handle ID recentlyCheckedAndUnconfirmableJwts: string[] = []; startTime = ""; + totalsExpanded = false; truncatedDesc = ""; truncateLength = 40; url = ""; @@ -609,6 +652,7 @@ export default class ProjectViewView extends Vue { this.projectId = decodeURIComponent(pathParam); } this.loadProject(this.projectId, this.activeDid); + this.loadTotals(); } onEditClick() { @@ -1207,5 +1251,49 @@ export default class ProjectViewView extends Vue { 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; + } } From f6871e139dd0fddee790bc20fa54e8331f1c25ca Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Thu, 27 Feb 2025 17:51:57 -0700 Subject: [PATCH 2/5] fix linting --- src/views/ProjectViewView.vue | 172 ++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 70 deletions(-) diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index 70be78d..885fcb2 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -341,23 +341,37 @@
Totals - + {{ givenTotalHours() }} {{ libsUtil.UNIT_SHORT["HUR"] }} - {{ givesTotalsByUnit[0].amount }} {{ libsUtil.UNIT_SHORT[givesTotalsByUnit[0].unit] }} + {{ givesTotalsByUnit[0].amount }} + {{ libsUtil.UNIT_SHORT[givesTotalsByUnit[0].unit] }} ... - +
-
- +
+ {{ total.amount }} {{ libsUtil.UNIT_LONG[total.unit] }}
@@ -365,7 +379,9 @@
- {{ givesToThis.length }}{{ givesHitLimit ? "+" : "" }} record{{ givesToThis.length === 1 ? "" : "s" }} + {{ givesToThis.length }}{{ givesHitLimit ? "+" : "" }} record{{ + givesToThis.length === 1 ? "" : "s" + }}
@@ -374,62 +390,71 @@
  • -
    - - - {{ - serverUtil.didInfo( - give.agentDid, - activeDid, - allMyDids, - allContacts, - ) - }} - - - {{ give.amount }} - -
    -
    - - {{ give.issuedAt?.substring(0, 10) }} -
    -
    - - {{ give.description }} -
    -
    - - - + :key="give.id" + class="py-1.5 border-b border-slate-300" + > +
    + + + {{ + serverUtil.didInfo( + give.agentDid, + activeDid, + allMyDids, + allContacts, + ) + }} + + + {{ give.amount }} + +
    +
    + + {{ give.issuedAt?.substring(0, 10) }} +
    +
    + + {{ give.description }} +
    + -
    - - - -
    + + + + + + + + + +
    +
    + + + +
@@ -594,7 +619,7 @@ export default class ProjectViewView extends Vue { givesHitLimit = false; givesProvidedByThis: Array = []; givesProvidedByHitLimit = false; - givesTotalsByUnit: Array<{unit: string, amount: number}> = []; + givesTotalsByUnit: Array<{ unit: string; amount: number }> = []; imageUrl = ""; isRegistered = false; issuer = ""; @@ -1254,14 +1279,17 @@ export default class ProjectViewView extends Vue { async loadTotals() { this.loadingTotals = true; - const url = this.apiServer + "/api/v2/report/givesToPlans?planIds=" + encodeURIComponent(JSON.stringify([this.projectId])); + 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} = {}; + const totals: { [key: string]: number } = {}; resp.data.data.forEach((give: GiveSummaryRecord) => { const amount = give.fullClaim.object?.amountOfThisGood; const unit = give.fullClaim.object?.unitCode; @@ -1269,12 +1297,14 @@ export default class ProjectViewView extends Vue { totals[unit] = (totals[unit] || 0) + amount; } }); - + // Convert totals object to array format - this.givesTotalsByUnit = Object.entries(totals).map(([unit, amount]) => ({ - unit, - amount - })); + this.givesTotalsByUnit = Object.entries(totals).map( + ([unit, amount]) => ({ + unit, + amount, + }), + ); } } catch (error) { console.error("Error loading totals:", error); @@ -1293,7 +1323,9 @@ export default class ProjectViewView extends Vue { } givenTotalHours(): number { - return this.givesTotalsByUnit.find(total => total.unit === "HUR")?.amount || 0; + return ( + this.givesTotalsByUnit.find((total) => total.unit === "HUR")?.amount || 0 + ); } } From b91f2a5df74086d5266ebedc6a6da3534aabecd0 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Thu, 27 Feb 2025 18:02:05 -0700 Subject: [PATCH 3/5] fix one more styling error --- src/views/ProjectViewView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index 885fcb2..d572628 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -364,7 +364,7 @@
From e96617ca0fa1f7b2fab8c50282ca0f7e43da8276 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Fri, 28 Feb 2025 12:17:22 -0700 Subject: [PATCH 4/5] tweak tests for clarity --- .../35-record-gift-from-image-share.spec.ts | 10 ++++------ test-playwright/40-add-contact.spec.ts | 1 - test-playwright/testUtils.ts | 7 +++++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test-playwright/35-record-gift-from-image-share.spec.ts b/test-playwright/35-record-gift-from-image-share.spec.ts index bdece66..f1de7fb 100644 --- a/test-playwright/35-record-gift-from-image-share.spec.ts +++ b/test-playwright/35-record-gift-from-image-share.spec.ts @@ -48,7 +48,7 @@ */ import path from 'path'; import { test, expect } from '@playwright/test'; -import { importUser } from './testUtils'; +import { importUserAndCloseOnboarding } from './testUtils'; /** * Note: by default, this test uses the test image API server. @@ -65,7 +65,7 @@ test('Record item given from image-share', async ({ page }) => { // Combine title prefix with the random string const finalTitle = `Gift ${randomString} from image-share`; - await importUser(page, '00'); + await importUserAndCloseOnboarding(page, '00'); // Record something given await page.goto('./test'); @@ -84,10 +84,8 @@ test('Record item given from image-share', async ({ page }) => { await page.getByRole('spinbutton').fill('2'); await page.getByRole('button', { name: 'Sign & Send' }).click(); - // we end up on a page with the onboarding info - await page.getByTestId('closeOnboardingAndFinish').click(); - - await expect(page.getByText('That gift was recorded.')).toBeVisible(); + // const recorded = await page.getByText('That gift was recorded.'); + await expect(await page.getByText('That gift was recorded.')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert // Refresh home view and check gift diff --git a/test-playwright/40-add-contact.spec.ts b/test-playwright/40-add-contact.spec.ts index 8d5416a..5f2fe1e 100644 --- a/test-playwright/40-add-contact.spec.ts +++ b/test-playwright/40-add-contact.spec.ts @@ -207,7 +207,6 @@ test('Add contact, copy details, delete, and import from paste & from file', asy // Add another new contact await page.getByPlaceholder('URL or DID, Name, Public Key').fill('did:ethr:0x222BB77E6Ff3774d34c751f3c1260866357B677b, User #222, asdf1234'); await page.locator('button > svg.fa-plus').click(); - await expect(page.locator('div[role="alert"]')).toBeVisible(); await expect(page.locator('div[role="alert"] span:has-text("No")')).toBeVisible(); await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register await expect(page.locator('div[role="alert"] span:has-text("Contact Added")')).toBeVisible(); diff --git a/test-playwright/testUtils.ts b/test-playwright/testUtils.ts index 654f0c8..13cced1 100644 --- a/test-playwright/testUtils.ts +++ b/test-playwright/testUtils.ts @@ -33,6 +33,13 @@ export async function importUser(page: Page, id?: string): Promise { return did; } +export async function importUserAndCloseOnboarding(page: Page, id?: string): Promise { + const did = await importUser(page, id); + await page.goto('./'); + await page.getByTestId('closeOnboardingAndFinish').click(); + return did; +} + // This is to switch to someone already in the identity table. It doesn't include registration. export async function switchToUser(page: Page, did: string): Promise { // This is the direct approach but users have to tap on things so we'll do that instead. From 562e82f176f13b0c6ede6c8e935d0c4320f96f36 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 10 Mar 2025 20:06:00 -0600 Subject: [PATCH 5/5] clean up build & test instructions --- BUILDING.md | 5 +---- README.md | 21 ++++----------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index cb6c3db..1639bcd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -227,10 +227,7 @@ Run local tests: npm run test-local ``` -Run all tests (includes building): -```bash -npm run test-all -``` +See [TESTING.md](test-playwright/TESTING.md) for more details. ## Linting diff --git a/README.md b/README.md index 0607f5c..99df379 100644 --- a/README.md +++ b/README.md @@ -19,21 +19,15 @@ npm install npm run dev ``` -See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or use http://localhost:3000 for local endorser.ch +See [BUILDING.md](BUILDING.md) for more details. -### Build the test & production app -``` -npm run serve -``` +See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or use http://localhost:3000 for local endorser.ch -### Lint and fix files -``` -npm run lint -``` ### Run all UI tests -Look below for the "test-all" instructions. +Look at [BUILDING.md](BUILDING.md) for the "test-all" instructions and [TESTING.md](test-playwright/TESTING.md) for more details. + ### Compile and minify for test & production @@ -80,13 +74,6 @@ TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari. -## Tests - -See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions. - - - - ## Icons To add an icon, add to main.ts and reference with `fa` element and `icon` attribute with the hyphenated name.