Compare commits
16 Commits
split_buil
...
ui-fixes-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dbd66e51b | ||
|
|
312b4aaaa3 | ||
|
|
3a6a24d923 | ||
|
|
d7afb80a07 | ||
|
|
751df09fe5 | ||
|
|
8858495f73 | ||
|
|
ecb088bee2 | ||
| e96617ca0f | |||
| b91f2a5df7 | |||
| f6871e139d | |||
| 61afba3bca | |||
| eabe2b9448 | |||
| 5eaaf32043 | |||
| 1e9c3f3101 | |||
| 2e60e2bba9 | |||
| 78d7f38aa3 |
27
BUILDING.md
27
BUILDING.md
@@ -259,13 +259,23 @@ See `.env.*` files for configuration.
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
### Version Management
|
||||||
|
1. Update CHANGELOG.md with new changes
|
||||||
|
2. Update version in package.json
|
||||||
|
3. Commit changes and tag release:
|
||||||
|
```bash
|
||||||
|
git tag <VERSION_TAG>
|
||||||
|
git push origin <VERSION_TAG>
|
||||||
|
```
|
||||||
|
4. After deployment, update package.json with next version + "-beta"
|
||||||
|
|
||||||
### Test Server
|
### Test Server
|
||||||
```bash
|
```bash
|
||||||
# Build using staging environment
|
# Build using staging environment
|
||||||
npm run build -- --mode staging
|
npm run build -- --mode staging
|
||||||
|
|
||||||
# Deploy to test server
|
# Deploy to test server
|
||||||
rsync -azvu -e "ssh -i ~/.ssh/your_key" dist ubuntutest@test.timesafari.app:time-safari/
|
rsync -azvu -e "ssh -i ~/.ssh/<YOUR_KEY>" dist ubuntutest@test.timesafari.app:time-safari/
|
||||||
```
|
```
|
||||||
|
|
||||||
### Production Server
|
### Production Server
|
||||||
@@ -274,26 +284,15 @@ rsync -azvu -e "ssh -i ~/.ssh/your_key" dist ubuntutest@test.timesafari.app:time
|
|||||||
pkgx +npm sh
|
pkgx +npm sh
|
||||||
cd crowd-funder-for-time-pwa
|
cd crowd-funder-for-time-pwa
|
||||||
git checkout master && git pull
|
git checkout master && git pull
|
||||||
git checkout <version_tag>
|
git checkout <VERSION_TAG>
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
# Backup and deploy
|
# Backup and deploy
|
||||||
mv time-safari/dist time-safari-dist-prev.0
|
mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/
|
||||||
mv crowd-funder-for-time-pwa/dist time-safari/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Version Management
|
|
||||||
1. Update CHANGELOG.md with new changes
|
|
||||||
2. Update version in package.json
|
|
||||||
3. Commit changes and tag release:
|
|
||||||
```bash
|
|
||||||
git tag <version>
|
|
||||||
git push origin <version>
|
|
||||||
```
|
|
||||||
4. After deployment, update package.json with next version + "-beta"
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Common Build Issues
|
### Common Build Issues
|
||||||
|
|||||||
31
CHANGELOG.md
31
CHANGELOG.md
@@ -6,12 +6,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
== [0.4.2] - 2025.02.17
|
|
||||||
### Merged
|
## [0.4.5] - 2025.02.23
|
||||||
- Master to split_process_build
|
### Added
|
||||||
- fixed path issues
|
- Total amounts of gives on project page
|
||||||
- all tests passing
|
### Changed in DB or environment
|
||||||
- capacitor build to Android working
|
- 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.
|
||||||
|
|
||||||
|
|
||||||
|
## [0.4.3] - 2025.02.17
|
||||||
|
### Added
|
||||||
|
- Discover query parameter searchPeople to go directly to the people map
|
||||||
|
|
||||||
|
|
||||||
|
## [0.4.2] - 2025.02.17
|
||||||
|
### Added
|
||||||
|
- Capacitor build to Android
|
||||||
|
### Fixed
|
||||||
|
- Path issues
|
||||||
|
|
||||||
|
|
||||||
## [0.4.1] - 2025.02.16
|
## [0.4.1] - 2025.02.16
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -23,7 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
- Images in the home feed now take up the full width of the card.
|
- Images in the home feed now take up the full width of the card.
|
||||||
- Clicking the image previously, would open the image in a new tab. Now, clicking the image opens the image in a lightbox view.
|
- Clicking the image previously, would open the image in a new tab. Now, clicking the image opens the image in a lightbox view.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Clicking an image also now displays an in-app lightbox view of the image.
|
- Clicking an image also now displays an in-app lightbox view of the image.
|
||||||
- The lightbox view includes a download button for the image in mobile view.
|
- The lightbox view includes a download button for the image in mobile view.
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "TimeSafari",
|
"name": "timesafari",
|
||||||
"version": "0.4.1",
|
"version": "0.4.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "TimeSafari",
|
"name": "timesafari",
|
||||||
"version": "0.4.1",
|
"version": "0.4.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^6.2.0",
|
"@capacitor/android": "^6.2.0",
|
||||||
"@capacitor/cli": "^6.2.0",
|
"@capacitor/cli": "^6.2.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "timesafari",
|
"name": "timesafari",
|
||||||
"version": "0.4.2",
|
"version": "0.4.4",
|
||||||
"description": "TimeSafari Desktop Application",
|
"description": "TimeSafari Desktop Application",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "TimeSafari Team"
|
"name": "TimeSafari Team"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- QUICK NAV -->
|
<!-- QUICK NAV -->
|
||||||
<nav id="QuickNav" class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50">
|
<nav id="QuickNav" class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50">
|
||||||
<ul class="flex text-2xl p-2 gap-2 max-w-3xl mx-auto">
|
<ul class="flex text-2xl px-6 py-2 gap-1 max-w-3xl mx-auto">
|
||||||
<!-- Home Feed -->
|
<!-- Home Feed -->
|
||||||
<li
|
<li
|
||||||
:class="{
|
:class="{
|
||||||
|
|||||||
@@ -256,7 +256,7 @@
|
|||||||
<span class="mb-2 font-bold">Location for Searches</span>
|
<span class="mb-2 font-bold">Location for Searches</span>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'search-area' }"
|
:to="{ name: 'search-area' }"
|
||||||
class="text-m 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-1.5 py-2 rounded-md mb-2"
|
class="text-m 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-4 py-2 rounded-md mb-2"
|
||||||
>
|
>
|
||||||
{{ isSearchAreasSet ? "Change" : "Set" }} Search Area…
|
{{ isSearchAreasSet ? "Change" : "Set" }} Search Area…
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|||||||
@@ -204,7 +204,7 @@
|
|||||||
<div v-if="libsUtil.isGiveAction(veriClaim)">
|
<div v-if="libsUtil.isGiveAction(veriClaim)">
|
||||||
<div class="flex columns-3">
|
<div class="flex columns-3">
|
||||||
<button
|
<button
|
||||||
class="col-span-1 bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-4 py-2 rounded-md"
|
class="col-span-1 bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-4 py-2 rounded-md"
|
||||||
v-if="
|
v-if="
|
||||||
libsUtil.isGiveRecordTheUserCanConfirm(
|
libsUtil.isGiveRecordTheUserCanConfirm(
|
||||||
isRegistered,
|
isRegistered,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
href=""
|
href=""
|
||||||
class="text-md bg-gradient-to-b shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-2 px-1 py-1 rounded-md"
|
class="text-md bg-gradient-to-b shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md"
|
||||||
:style="
|
:style="
|
||||||
contactsSelected.length > 0
|
contactsSelected.length > 0
|
||||||
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
|
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
<div class="w-full text-right">
|
<div class="w-full text-right">
|
||||||
<button
|
<button
|
||||||
href=""
|
href=""
|
||||||
class="text-md 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 py-1 rounded-md"
|
class="text-md 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-3 py-1.5 rounded-md"
|
||||||
@click="toggleShowContactAmounts()"
|
@click="toggleShowContactAmounts()"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
@@ -177,14 +177,7 @@
|
|||||||
data-testId="contactListItem"
|
data-testId="contactListItem"
|
||||||
>
|
>
|
||||||
<div class="grow overflow-hidden">
|
<div class="grow overflow-hidden">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center gap-3">
|
||||||
<EntityIcon
|
|
||||||
:contact="contact"
|
|
||||||
:iconSize="24"
|
|
||||||
class="inline-block align-text-bottom border border-slate-300 rounded cursor-pointer"
|
|
||||||
@click="showLargeIdenticon = contact"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
v-if="!showGiveNumbers"
|
v-if="!showGiveNumbers"
|
||||||
@@ -201,94 +194,97 @@
|
|||||||
data-testId="contactCheckOne"
|
data-testId="contactCheckOne"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2
|
<EntityIcon
|
||||||
class="text-base font-semibold ml-2 w-1/3 truncate flex-shrink-0"
|
:contact="contact"
|
||||||
>
|
:iconSize="48"
|
||||||
|
class="inline-block align-text-bottom border border-slate-300 rounded cursor-pointer overflow-hidden"
|
||||||
|
@click="showLargeIdenticon = contact"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2 class="text-base font-semibold w-1/3 truncate flex-shrink-0">
|
||||||
{{ contactNameNonBreakingSpace(contact.name) }}
|
{{ contactNameNonBreakingSpace(contact.name) }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<div class="flex items-center">
|
<div class="flex gap-2 items-center">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
path: '/did/' + encodeURIComponent(contact.did),
|
path: '/did/' + encodeURIComponent(contact.did),
|
||||||
}"
|
}"
|
||||||
title="See more about this person"
|
title="See more about this person"
|
||||||
>
|
>
|
||||||
<fa icon="circle-info" class="text-xl text-blue-500 ml-4" />
|
<fa icon="circle-info" class="text-xl text-blue-500" />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<span class="ml-4 text-sm overflow-hidden">{{
|
<span class="text-sm overflow-hidden">{{
|
||||||
libsUtil.shortDid(contact.did)
|
libsUtil.shortDid(contact.did)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4 text-sm">
|
<div class="text-sm">
|
||||||
{{ contact.notes }}
|
{{ contact.notes }}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="ContactActions" class="flex gap-1.5 mt-2">
|
<div
|
||||||
<div
|
v-if="showGiveNumbers && contact.did != activeDid"
|
||||||
v-if="showGiveNumbers && contact.did != activeDid"
|
class="ml-auto flex gap-1.5 mt-2"
|
||||||
class="ml-auto flex gap-1.5"
|
>
|
||||||
|
<button
|
||||||
|
class="text-sm 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-1.5 rounded-l-md"
|
||||||
|
@click="confirmShowGiftedDialog(contact.did, activeDid)"
|
||||||
|
:title="givenToMeDescriptions[contact.did] || ''"
|
||||||
>
|
>
|
||||||
<button
|
From:
|
||||||
class="text-sm 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-1.5 rounded-l-md"
|
<br />
|
||||||
@click="confirmShowGiftedDialog(contact.did, activeDid)"
|
{{
|
||||||
:title="givenToMeDescriptions[contact.did] || ''"
|
/* eslint-disable prettier/prettier */
|
||||||
>
|
this.showGiveTotals
|
||||||
From:
|
? ((givenToMeConfirmed[contact.did] || 0)
|
||||||
<br />
|
+ (givenToMeUnconfirmed[contact.did] || 0))
|
||||||
{{
|
: this.showGiveConfirmed
|
||||||
/* eslint-disable prettier/prettier */
|
? (givenToMeConfirmed[contact.did] || 0)
|
||||||
this.showGiveTotals
|
: (givenToMeUnconfirmed[contact.did] || 0)
|
||||||
? ((givenToMeConfirmed[contact.did] || 0)
|
/* eslint-enable prettier/prettier */
|
||||||
+ (givenToMeUnconfirmed[contact.did] || 0))
|
}}
|
||||||
: this.showGiveConfirmed
|
</button>
|
||||||
? (givenToMeConfirmed[contact.did] || 0)
|
|
||||||
: (givenToMeUnconfirmed[contact.did] || 0)
|
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
}}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="text-sm bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white -ml-1.5 px-2 py-1.5 rounded-r-md border-l"
|
class="text-sm bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white -ml-1.5 px-2 py-1.5 rounded-r-md border-l"
|
||||||
@click="confirmShowGiftedDialog(activeDid, contact.did)"
|
@click="confirmShowGiftedDialog(activeDid, contact.did)"
|
||||||
:title="givenByMeDescriptions[contact.did] || ''"
|
:title="givenByMeDescriptions[contact.did] || ''"
|
||||||
>
|
>
|
||||||
To:
|
To:
|
||||||
<br />
|
<br />
|
||||||
{{
|
{{
|
||||||
/* eslint-disable prettier/prettier */
|
/* eslint-disable prettier/prettier */
|
||||||
this.showGiveTotals
|
this.showGiveTotals
|
||||||
? ((givenByMeConfirmed[contact.did] || 0)
|
? ((givenByMeConfirmed[contact.did] || 0)
|
||||||
+ (givenByMeUnconfirmed[contact.did] || 0))
|
+ (givenByMeUnconfirmed[contact.did] || 0))
|
||||||
: this.showGiveConfirmed
|
: this.showGiveConfirmed
|
||||||
? (givenByMeConfirmed[contact.did] || 0)
|
? (givenByMeConfirmed[contact.did] || 0)
|
||||||
: (givenByMeUnconfirmed[contact.did] || 0)
|
: (givenByMeUnconfirmed[contact.did] || 0)
|
||||||
/* eslint-enable prettier/prettier */
|
/* eslint-enable prettier/prettier */
|
||||||
}}
|
}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="text-sm 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-1.5 rounded-md border border-blue-400"
|
class="text-sm 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-1.5 rounded-md border border-blue-400"
|
||||||
@click="openOfferDialog(contact.did, contact.name)"
|
@click="openOfferDialog(contact.did, contact.name)"
|
||||||
data-testId="offerButton"
|
data-testId="offerButton"
|
||||||
>
|
>
|
||||||
Offer
|
Offer
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name: 'contact-amounts',
|
name: 'contact-amounts',
|
||||||
query: { contactDid: contact.did },
|
query: { contactDid: contact.did },
|
||||||
}"
|
}"
|
||||||
class="text-sm 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-1.5 rounded-md border border-slate-400"
|
class="text-sm 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-1.5 rounded-md border border-slate-400"
|
||||||
title="See more given activity"
|
title="See more given activity"
|
||||||
>
|
>
|
||||||
<fa icon="file-lines" class="fa-fw" />
|
<fa icon="file-lines" class="fa-fw" />
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -310,7 +306,7 @@
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
href=""
|
href=""
|
||||||
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-2 px-1 py-1 rounded-md"
|
class="text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-3 px-3 py-1.5 rounded-md"
|
||||||
:style="
|
:style="
|
||||||
contactsSelected.length > 0
|
contactsSelected.length > 0
|
||||||
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
|
? 'background-image: linear-gradient(to bottom, #3b82f6, #1e40af);'
|
||||||
|
|||||||
@@ -352,9 +352,9 @@ export default class DiscoverView extends Vue {
|
|||||||
allMyDids: Array<string> = [];
|
allMyDids: Array<string> = [];
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
isLocalActive = true;
|
isLocalActive = false;
|
||||||
isMappedActive = false;
|
isMappedActive = false;
|
||||||
isAnywhereActive = false;
|
isAnywhereActive = true;
|
||||||
isProjectsActive = true;
|
isProjectsActive = true;
|
||||||
isPeopleActive = false;
|
isPeopleActive = false;
|
||||||
isSearchVisible = true;
|
isSearchVisible = true;
|
||||||
@@ -375,6 +375,11 @@ export default class DiscoverView extends Vue {
|
|||||||
didInfo = didInfo;
|
didInfo = didInfo;
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
||||||
|
this.searchTerms = this.$route.query["searchText"]?.toString() || "";
|
||||||
|
|
||||||
|
const searchPeople = !!this.$route.query["searchPeople"];
|
||||||
|
|
||||||
const settings = await retrieveSettingsForActiveAccount();
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
this.activeDid = (settings.activeDid as string) || "";
|
this.activeDid = (settings.activeDid as string) || "";
|
||||||
this.apiServer = (settings.apiServer as string) || "";
|
this.apiServer = (settings.apiServer as string) || "";
|
||||||
@@ -386,25 +391,35 @@ export default class DiscoverView extends Vue {
|
|||||||
|
|
||||||
this.allMyDids = await retrieveAccountDids();
|
this.allMyDids = await retrieveAccountDids();
|
||||||
|
|
||||||
this.searchTerms = this.$route.query["searchText"]?.toString() || "";
|
|
||||||
|
|
||||||
if (!settings.finishedOnboarding) {
|
if (!settings.finishedOnboarding) {
|
||||||
(this.$refs.onboardingDialog as OnboardingDialog).open(
|
(this.$refs.onboardingDialog as OnboardingDialog).open(
|
||||||
OnboardPage.Discover,
|
OnboardPage.Discover,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.searchBox) {
|
// Someday we'll have enough people that we can default to their local area.
|
||||||
await this.searchLocal();
|
// if (this.searchBox) {
|
||||||
|
// this.isLocalActive = true;
|
||||||
|
// this.isAnywhereActive = false;
|
||||||
|
// await this.searchLocal();
|
||||||
|
//
|
||||||
|
// const bbox = this.searchBox.bbox;
|
||||||
|
// this.localCenterLat = (bbox.maxLat + bbox.minLat) / 2;
|
||||||
|
// this.localCenterLong = (bbox.eastLong + bbox.westLong) / 2;
|
||||||
|
// } else {
|
||||||
|
|
||||||
const bbox = this.searchBox.bbox;
|
if (searchPeople) {
|
||||||
this.localCenterLat = (bbox.maxLat + bbox.minLat) / 2;
|
this.isPeopleActive = true;
|
||||||
this.localCenterLong = (bbox.eastLong + bbox.westLong) / 2;
|
this.isProjectsActive = false;
|
||||||
|
this.isMappedActive = true;
|
||||||
|
this.isAnywhereActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMappedActive) {
|
||||||
|
// The map will be loaded when it's ready
|
||||||
|
// and if we try to do it here before the map is ready then we get errors.
|
||||||
} else {
|
} else {
|
||||||
this.isLocalActive = false;
|
await this.searchSelected();
|
||||||
this.isMappedActive = false;
|
|
||||||
this.isAnywhereActive = true;
|
|
||||||
await this.searchAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +481,7 @@ export default class DiscoverView extends Vue {
|
|||||||
} else {
|
} else {
|
||||||
throw JSON.stringify(results);
|
throw JSON.stringify(results);
|
||||||
}
|
}
|
||||||
} else {
|
} else { // people search must be active
|
||||||
this.projects = [];
|
this.projects = [];
|
||||||
const profiles: UserProfile[] = results.data;
|
const profiles: UserProfile[] = results.data;
|
||||||
if (profiles) {
|
if (profiles) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TopMessage />
|
<TopMessage />
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto">
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||||
<h1 id="ViewHeading" class="text-4xl text-center font-light mb-8">
|
<h1 id="ViewHeading" class="text-4xl text-center font-light mb-8">
|
||||||
{{ AppString.APP_NAME }}
|
{{ AppString.APP_NAME }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
<button
|
<button
|
||||||
data-testId="offerButton"
|
data-testId="offerButton"
|
||||||
@click="openOfferDialog()"
|
@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 px-1 rounded-md"
|
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 px-3 py-1.5 text-sm leading-tight rounded-md"
|
||||||
>
|
>
|
||||||
Offer to this (maybe with conditions)...
|
Offer to this (maybe with conditions)...
|
||||||
</button>
|
</button>
|
||||||
@@ -318,81 +318,146 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button
|
<button
|
||||||
@click="openGiftDialogToProject()"
|
@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 px-1rounded-md"
|
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 px-3 py-1.5 text-sm leading-tight rounded-md"
|
||||||
>
|
>
|
||||||
Given To This...
|
Given To This...
|
||||||
</button>
|
</button>
|
||||||
</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]">
|
||||||
:key="give.id"
|
<div v-if="loadingTotals" class="flex-1">
|
||||||
class="py-1.5 border-b border-slate-300"
|
<fa icon="spinner" class="fa-spin-pulse text-blue-500" />
|
||||||
>
|
</div>
|
||||||
<div class="flex justify-between gap-4">
|
<div v-else-if="givesTotalsByUnit.length > 0" class="flex-1">
|
||||||
<span>
|
<span class="font-semibold mr-2 shrink-0">Totals</span>
|
||||||
<fa icon="user" class="fa-fw text-slate-400" />
|
<span class="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
{{
|
<a
|
||||||
serverUtil.didInfo(
|
@click="totalsExpanded = !totalsExpanded"
|
||||||
give.agentDid,
|
class="cursor-pointer text-blue-500"
|
||||||
activeDid,
|
>
|
||||||
allMyDids,
|
<!-- just show the hours, or alternatively whatever is first -->
|
||||||
allContacts,
|
<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 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>
|
</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>
|
||||||
<div class="text-slate-500">
|
</div>
|
||||||
<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
|
<!-- List of gives -->
|
||||||
v-if="
|
<ul class="mt-2 text-sm border-t border-slate-300">
|
||||||
checkIsConfirmable(give) &&
|
<li
|
||||||
!recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
|
v-for="give in givesToThis"
|
||||||
"
|
:key="give.id"
|
||||||
@click="deepCheckConfirmable(give)"
|
class="py-1.5 border-b border-slate-300"
|
||||||
>
|
>
|
||||||
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
|
<div class="flex justify-between gap-4">
|
||||||
</a>
|
<span>
|
||||||
<a v-else-if="checkingConfirmationForJwtId === give.jwtId">
|
<fa icon="user" class="fa-fw text-slate-400" />
|
||||||
<fa icon="spinner" class="fa-spin-pulse" />
|
{{
|
||||||
</a>
|
serverUtil.didInfo(
|
||||||
<a v-else @click="shallowNotifyWhyCannotConfirm(give)">
|
give.agentDid,
|
||||||
<fa icon="circle-check" class="text-slate-500 cursor-pointer" />
|
activeDid,
|
||||||
</a>
|
allMyDids,
|
||||||
</div>
|
allContacts,
|
||||||
<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" />
|
</span>
|
||||||
</a>
|
<span v-if="give.amount" class="whitespace-nowrap">
|
||||||
</div>
|
<fa
|
||||||
</li>
|
:icon="libsUtil.iconForUnitCode(give.unit)"
|
||||||
</ul>
|
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>
|
||||||
<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>
|
||||||
@@ -405,7 +470,7 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button
|
<button
|
||||||
@click="openGiftDialogFromProject()"
|
@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 px-1 rounded-md"
|
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 px-3 py-1.5 text-sm leading-tight rounded-md"
|
||||||
>
|
>
|
||||||
Given By This...
|
Given By This...
|
||||||
</button>
|
</button>
|
||||||
@@ -554,6 +619,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 +630,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 +638,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 +677,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 +1276,56 @@ 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>
|
||||||
|
|||||||
@@ -233,7 +233,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
@click="onClickLoadProject(project.handleId)"
|
@click="onClickLoadProject(project.handleId)"
|
||||||
class="block py-4 flex gap-4"
|
class="block py-4 flex gap-4 cursor-pointer"
|
||||||
>
|
>
|
||||||
<div class="flex-none">
|
<div class="flex-none">
|
||||||
<ProjectIcon
|
<ProjectIcon
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
*/
|
*/
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { test, expect } from '@playwright/test';
|
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.
|
* 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
|
// Combine title prefix with the random string
|
||||||
const finalTitle = `Gift ${randomString} from image-share`;
|
const finalTitle = `Gift ${randomString} from image-share`;
|
||||||
|
|
||||||
await importUser(page, '00');
|
await importUserAndCloseOnboarding(page, '00');
|
||||||
|
|
||||||
// Record something given
|
// Record something given
|
||||||
await page.goto('./test');
|
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('spinbutton').fill('2');
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
|
||||||
// we end up on a page with the onboarding info
|
// const recorded = await page.getByText('That gift was recorded.');
|
||||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
await expect(await page.getByText('That gift was recorded.')).toBeVisible();
|
||||||
|
|
||||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
|
||||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||||
|
|
||||||
// Refresh home view and check gift
|
// Refresh home view and check gift
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ test('Add contact, copy details, delete, and import from paste & from file', asy
|
|||||||
// Add another new contact
|
// Add another new contact
|
||||||
await page.getByPlaceholder('URL or DID, Name, Public Key').fill('did:ethr:0x222BB77E6Ff3774d34c751f3c1260866357B677b, User #222, asdf1234');
|
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 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 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 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();
|
await expect(page.locator('div[role="alert"] span:has-text("Contact Added")')).toBeVisible();
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ export async function importUser(page: Page, id?: string): Promise<string> {
|
|||||||
return did;
|
return did;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function importUserAndCloseOnboarding(page: Page, id?: string): Promise<string> {
|
||||||
|
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.
|
// 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<void> {
|
export async function switchToUser(page: Page, did: string): Promise<void> {
|
||||||
// This is the direct approach but users have to tap on things so we'll do that instead.
|
// This is the direct approach but users have to tap on things so we'll do that instead.
|
||||||
|
|||||||
Reference in New Issue
Block a user