Browse Source

Merge pull request 'DiscoverView searches almost done.' (#38) from discover-view-etc into master

Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/kick-starter-for-time-pwa/pulls/38
kb/add-usage-guide
anomalist 1 year ago
parent
commit
3eda246e85
  1. 8
      project.task.yaml
  2. 146
      src/components/HelloWorld.vue
  3. 1
      src/db/index.ts
  4. 50
      src/libs/endorserServer.ts
  5. 246
      src/views/AccountViewView.vue
  6. 61
      src/views/ContactAmountsView.vue
  7. 6
      src/views/ContactQRScanShowView.vue
  8. 14
      src/views/ContactsView.vue
  9. 142
      src/views/DiscoverView.vue
  10. 14
      src/views/NewEditProjectView.vue
  11. 41
      src/views/ProjectViewView.vue
  12. 22
      src/views/ProjectsView.vue
  13. 6
      src/views/StatisticsView.vue

8
project.task.yaml

@ -1,13 +1,11 @@
tasks: tasks:
- .5 audit all console.log calls
- 01 design ideas for simple gives on the Home page - 01 design ideas for simple gives on the Home page
- 01 add list of 'give' records for a project on ProjectView UI - 01 add list of 'give' records for a project on ProjectView UI
- 02 Discover page - display results (currently in console.log) & link, spin when searching - 02 Discover page - add infinite search
- 08 search by location, endpoint, etc assignee:trent - 08 search by location, endpoint, etc assignee:trent
- 01 add a location for a project via map pin (see API by clicking on "Nearby") - 01 add a location for a project via map pin (see API by clicking on "Nearby")
- 01 remove all the "form" fields (or at least investigate to see if that page refresh is desired) - 01 remove all the "form" fields (or at least investigate to see if that page refresh is desired)
- .2 change "errorMessage" to "alertMessage" on ProjectViewView.vue
- 08 Scan QR code to import into contacts. - 08 Scan QR code to import into contacts.
@ -22,8 +20,6 @@ tasks:
- refactor UI : - refactor UI :
- .5 Alerts show at the top and can be missed, eg. account data download - .5 Alerts show at the top and can be missed, eg. account data download
- .5 Fix how icons show on top of bottom bar on ContactAmounts page
- .2 Hide "Advanced" section in Account page by default
- show pop-up confirming that settings & contacts have been downloaded - show pop-up confirming that settings & contacts have been downloaded
@ -44,7 +40,7 @@ tasks:
- Discuss whether the remaining tasks are worthwhile before MVP release. - Discuss whether the remaining tasks are worthwhile before MVP release.
- 01 fix images on project page, on discovery page - 01 fix images on project page, on discovery page
- .2 fix "Rotary" and static icon to the right on project page - .2 fix static icon to the right on project page (Matthew: I've made "Rotary" into issuer?)
- stats v1 : - stats v1 :
- 01 show numeric stats - 01 show numeric stats

146
src/components/HelloWorld.vue

@ -1,146 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br />
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
>vue-cli documentation</a
>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa"
target="_blank"
rel="noopener"
>pwa</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router"
target="_blank"
rel="noopener"
>router</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex"
target="_blank"
rel="noopener"
>vuex</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript"
target="_blank"
rel="noopener"
>typescript</a
>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
</li>
<li>
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
>Forum</a
>
</li>
<li>
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
>Community Chat</a
>
</li>
<li>
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
>Twitter</a
>
</li>
<li>
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
>vue-router</a
>
</li>
<li>
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a
>
</li>
<li>
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
>vue-loader</a
>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a
>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
@Component
export default class HelloWorld extends Vue {
@Prop msg!: string;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

1
src/db/index.ts

@ -54,7 +54,6 @@ if (localStorage.getItem("secret") == null) {
localStorage.setItem("secret", secret); localStorage.setItem("secret", secret);
} }
//console.log("IndexedDB Encryption Secret:", secret);
encrypted(accountsDB, { secretKey: secret }); encrypted(accountsDB, { secretKey: secret });
accountsDB.version(1).stores(SensitiveSchemas); accountsDB.version(1).stores(SensitiveSchemas);

50
src/libs/endorserServer.ts

@ -197,3 +197,53 @@ export function isNumeric(str: string): boolean {
export function numberOrZero(str: string): number { export function numberOrZero(str: string): number {
return isNumeric(str) ? +str : 0; return isNumeric(str) ? +str : 0;
} }
export interface ErrorResponse {
error?: {
message?: string;
};
}
export interface RateLimits {
doneClaimsThisWeek: string;
doneRegistrationsThisMonth: string;
maxClaimsPerWeek: string;
maxRegistrationsPerMonth: string;
nextMonthBeginDateTime: string;
nextWeekBeginDateTime: string;
}
/**
* Represents data about a project
**/
export interface ProjectData {
/**
* Name of the project
**/
name: string;
/**
* Description of the project
**/
description: string;
/**
* URL referencing information about the project
**/
handleId: string;
/**
* The Identier of the project
**/
rowid: string;
}
export interface VerifiableCredential {
"@context": string;
"@type": string;
name: string;
description: string;
identifier?: string;
}
export interface WorldProperties {
startTime?: string;
endTime?: string;
}

246
src/views/AccountViewView.vue

@ -158,117 +158,126 @@
</form> </form>
</dialog> </dialog>
<h3 class="text-sm uppercase font-semibold mb-3">Advanced</h3> <h3
class="text-sm uppercase font-semibold mb-3"
<label @click="showAdvanced = !showAdvanced"
for="toggleShowAmounts"
class="flex items-center cursor-pointer mb-6"
@click="handleChange"
> >
<!-- toggle --> Advanced
<div class="relative"> </h3>
<!-- input --> <div v-if="showAdvanced">
<label
for="toggleShowAmounts"
class="flex items-center cursor-pointer mb-6"
@click="handleChange"
>
<!-- toggle -->
<div class="relative">
<!-- input -->
<input
type="checkbox"
v-model="showContactGives"
name="showContactGives"
class="sr-only"
/>
<!-- line -->
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
<!-- dot -->
<div
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
></div>
</div>
<!-- label -->
<div class="ml-2">Show amounts given with contacts</div>
</label>
<div class="flex py-2">
<button
class="text-center text-md text-blue-500"
@click="checkLimits()"
>
Check Limits
</button>
<!-- show spinner if loading limits -->
<div v-if="loadingLimits" class="ml-2">
Checking... <fa icon="spinner" class="fa-spin"></fa>
</div>
<div class="ml-2">
{{ limitsMessage }}
</div>
<div v-if="!!limits?.nextWeekBeginDateTime" class="px-9">
<span class="font-bold">Rate Limits</span>
<p>
You have done {{ limits.doneClaimsThisWeek }} claims out of
{{ limits.maxClaimsPerWeek }} for this week. Your claims counter
resets at {{ readableTime(limits.nextWeekBeginDateTime) }}
</p>
<p>
You have done {{ limits.doneRegistrationsThisMonth }} registrations
out of {{ limits.maxRegistrationsPerMonth }} for this month. Your
registrations counter resets at
{{ readableTime(limits.nextMonthBeginDateTime) }}
</p>
</div>
</div>
<div class="flex py-2">
Claim Server
<input <input
type="checkbox" type="text"
v-model="showContactGives" class="block w-full rounded border border-slate-400 px-3 py-2"
name="showContactGives" v-model="apiServerInput"
class="sr-only"
/> />
<!-- line --> <button
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div> v-if="apiServerInput != apiServer"
<!-- dot --> class="px-4 rounded bg-red-500 border border-slate-400"
<div @click="onClickSaveApiServer()"
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition" >
></div> <fa icon="floppy-disk" class="fa-fw" color="white"></fa>
</div> </button>
<!-- label --> <button
<div class="ml-2">Show amounts given with contacts</div> class="px-4 rounded bg-slate-200 border border-slate-400"
</label> @click="setApiServerInput(Constants.PROD_ENDORSER_API_SERVER)"
>
<div class="flex py-2"> Use Prod
<button class="text-center text-md text-blue-500" @click="checkLimits()"> </button>
Check Limits <button
</button> class="px-4 rounded bg-slate-200 border border-slate-400"
<!-- show spinner if loading limits --> @click="setApiServerInput(Constants.TEST_ENDORSER_API_SERVER)"
<div v-if="loadingLimits" class="ml-2"> >
Checking... <fa icon="spinner" class="fa-spin"></fa> Use Test
</div> </button>
<div class="ml-2"> <button
{{ limitsMessage }} class="px-4 rounded bg-slate-200 border border-slate-400"
</div> @click="setApiServerInput(Constants.LOCAL_ENDORSER_API_SERVER)"
<div v-if="!!limits?.nextWeekBeginDateTime" class="px-9"> >
<span class="font-bold">Rate Limits</span> Use Local
<p> </button>
You have done {{ limits.doneClaimsThisWeek }} claims out of
{{ limits.maxClaimsPerWeek }} for this week. Your claims counter
resets at {{ readableTime(limits.nextWeekBeginDateTime) }}
</p>
<p>
You have done {{ limits.doneRegistrationsThisMonth }} registrations
out of {{ limits.maxRegistrationsPerMonth }} for this month. Your
registrations counter resets at
{{ readableTime(limits.nextMonthBeginDateTime) }}
</p>
</div> </div>
</div>
<div class="flex py-2"> <div v-if="numAccounts > 0" class="flex py-2">
Claim Server Switch Identifier
<input <span>
type="text" <button class="text-blue-500 px-2" @click="switchAccount(0)">
class="block w-full rounded border border-slate-400 px-3 py-2" None
v-model="apiServerInput" </button>
/> </span>
<button <span v-for="accountNum in numAccounts" :key="accountNum">
v-if="apiServerInput != apiServer" <button class="text-blue-500 px-2" @click="switchAccount(accountNum)">
class="px-4 rounded bg-red-500 border border-slate-400" #{{ accountNum }}
@click="onClickSaveApiServer()" </button>
> </span>
<fa icon="floppy-disk" class="fa-fw" color="white"></fa> </div>
</button>
<button
class="px-4 rounded bg-slate-200 border border-slate-400"
@click="setApiServerInput(Constants.PROD_ENDORSER_API_SERVER)"
>
Use Prod
</button>
<button
class="px-4 rounded bg-slate-200 border border-slate-400"
@click="setApiServerInput(Constants.TEST_ENDORSER_API_SERVER)"
>
Use Test
</button>
<button
class="px-4 rounded bg-slate-200 border border-slate-400"
@click="setApiServerInput(Constants.LOCAL_ENDORSER_API_SERVER)"
>
Use Local
</button>
</div>
<div v-if="numAccounts > 0" class="flex py-2"> <div>
Switch Identifier <button class="text-blue-500">
<span> <router-link
<button class="text-blue-500 px-2" @click="switchAccount(0)"> :to="{ name: 'statistics' }"
None class="block text-center py-3"
</button> >
</span> See Achievements & Statistics
<span v-for="accountNum in numAccounts" :key="accountNum"> </router-link>
<button class="text-blue-500 px-2" @click="switchAccount(accountNum)">
#{{ accountNum }}
</button> </button>
</span> </div>
</div>
<div>
<button class="text-blue-500">
<router-link
:to="{ name: 'statistics' }"
class="block text-center py-3"
>
See Achievements & Statistics
</router-link>
</button>
</div> </div>
<AlertMessage <AlertMessage
:alertTitle="alertTitle" :alertTitle="alertTitle"
@ -294,15 +303,6 @@ import QuickNav from "@/components/QuickNav";
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer; const Buffer = require("buffer/").Buffer;
interface RateLimits {
doneClaimsThisWeek: string;
doneRegistrationsThisMonth: string;
maxClaimsPerWeek: string;
maxRegistrationsPerMonth: string;
nextMonthBeginDateTime: string;
nextWeekBeginDateTime: string;
}
@Component({ components: { AlertMessage, QuickNav } }) @Component({ components: { AlertMessage, QuickNav } })
export default class AccountViewView extends Vue { export default class AccountViewView extends Vue {
Constants = AppString; Constants = AppString;
@ -327,6 +327,10 @@ export default class AccountViewView extends Vue {
showB64Copy = false; showB64Copy = false;
showPubCopy = false; showPubCopy = false;
showAdvanced = false;
alertMessage = "";
alertTitle = "";
public async getIdentity(activeDid) { public async getIdentity(activeDid) {
await accountsDB.open(); await accountsDB.open();
const account = await accountsDB.accounts const account = await accountsDB.accounts
@ -483,10 +487,14 @@ export default class AccountViewView extends Vue {
} else { } else {
const serverError = error as AxiosError; const serverError = error as AxiosError;
console.error("Bad response retrieving limits: ", serverError); console.error("Bad response retrieving limits: ", serverError);
// Anybody know how to access items inside "response.data" without this?
// eslint-disable-next-line @typescript-eslint/no-explicit-any const data: ErrorResponse | undefined =
const data: any = serverError.response?.data; serverError.response && serverError.response.data;
this.limitsMessage = data?.error?.message || "Bad server response."; if (data && data.error && data.error.message) {
this.limitsMessage = data.error.message;
} else {
this.limitsMessage = "Bad server response.";
}
} }
} }
@ -539,9 +547,5 @@ export default class AccountViewView extends Vue {
setApiServerInput(value) { setApiServerInput(value) {
this.apiServerInput = value; this.apiServerInput = value;
} }
// This same popup code is in many files.
alertMessage = "";
alertTitle = "";
} }
</script> </script>

61
src/views/ContactAmountsView.vue

@ -1,48 +1,5 @@
<template> <template>
<!-- QUICK NAV --> <QuickNav selected="Contacts"></QuickNav>
<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">
<!-- Home Feed -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link :to="{ name: 'home' }" class="block text-center py-3 px-1"
><fa icon="house-chimney" class="fa-fw"></fa
></router-link>
</li>
<!-- Search -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'discover' }"
class="block text-center py-3 px-1"
><fa icon="magnifying-glass" class="fa-fw"></fa
></router-link>
</li>
<!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'projects' }"
class="block text-center py-3 px-1"
><fa icon="folder-open" class="fa-fw"></fa
></router-link>
</li>
<!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'contacts' }"
class="block text-center py-3 px-1"
><fa icon="users" class="fa-fw"></fa
></router-link>
</li>
<!-- Profile -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'account' }"
class="block text-center py-3 px-1"
><fa icon="circle-user" class="fa-fw"></fa
></router-link>
</li>
</ul>
</nav>
<section id="Content" class="p-6 pb-24"> <section id="Content" class="p-6 pb-24">
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
Given with {{ contact?.name }} Given with {{ contact?.name }}
@ -134,8 +91,9 @@ import {
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
@Component({ components: { AlertMessage } }) @Component({ components: { AlertMessage, QuickNav } })
export default class ContactsView extends Vue { export default class ContactsView extends Vue {
activeDid = ""; activeDid = "";
apiServer = ""; apiServer = "";
@ -208,11 +166,7 @@ export default class ContactsView extends Vue {
encodeURIComponent(identity.did) + encodeURIComponent(identity.did) +
"&recipientDid=" + "&recipientDid=" +
encodeURIComponent(contact.did); encodeURIComponent(contact.did);
const token = await accessToken(identity); const headers = this.getHeaders(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
if (resp.status === 200) { if (resp.status === 200) {
result = resp.data.data; result = resp.data.data;
@ -233,11 +187,7 @@ export default class ContactsView extends Vue {
encodeURIComponent(contact.did) + encodeURIComponent(contact.did) +
"&recipientDid=" + "&recipientDid=" +
encodeURIComponent(identity.did); encodeURIComponent(identity.did);
const token2 = await accessToken(identity); const headers2 = await this.getHeaders(identity);
const headers2 = {
"Content-Type": "application/json",
Authorization: "Bearer " + token2,
};
const resp2 = await this.axios.get(url2, { headers: headers2 }); const resp2 = await this.axios.get(url2, { headers: headers2 });
if (resp2.status === 200) { if (resp2.status === 200) {
result = R.concat(result, resp2.data.data); result = R.concat(result, resp2.data.data);
@ -313,7 +263,6 @@ export default class ContactsView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
//console.log("Got resp data:", resp.data);
if (resp.data?.success) { if (resp.data?.success) {
record.amountConfirmed = origClaim.object?.amountOfThisGood || 1; record.amountConfirmed = origClaim.object?.amountOfThisGood || 1;
} }

6
src/views/ContactQRScanShowView.vue

@ -37,6 +37,8 @@ import QuickNav from "@/components/QuickNav";
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer; const Buffer = require("buffer/").Buffer;
alertTitle = "";
alertMessage = "";
@Component({ @Component({
components: { components: {
@ -110,9 +112,5 @@ export default class ContactQRScanShow extends Vue {
this.qrValue = viewPrefix + vcJwt; this.qrValue = viewPrefix + vcJwt;
} }
} }
// This same popup code is in many files.
alertTitle = "";
alertMessage = "";
} }
</script> </script>

14
src/views/ContactsView.vue

@ -457,15 +457,10 @@ export default class ContactsView extends Vue {
// Make the xhr request payload // Make the xhr request payload
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = this.apiServer + "/api/v2/claim"; const url = this.apiServer + "/api/v2/claim";
const token = await accessToken(identity); const headers = await this.getHeaders(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
//console.log("Got resp data:", resp.data);
if (resp.data?.success?.embeddedRecordError) { if (resp.data?.success?.embeddedRecordError) {
this.alertTitle = "Registration Still Unknown"; this.alertTitle = "Registration Still Unknown";
let message = "There was some problem with the registration."; let message = "There was some problem with the registration.";
@ -684,15 +679,10 @@ export default class ContactsView extends Vue {
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = this.apiServer + "/api/v2/claim"; const url = this.apiServer + "/api/v2/claim";
const token = await accessToken(identity); const headers = await this.getHeaders(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
//console.log("Got resp data:", resp.data);
if (resp.data?.success?.handleId) { if (resp.data?.success?.handleId) {
this.alertTitle = "Done"; this.alertTitle = "Done";
this.alertMessage = "Successfully logged time to the server."; this.alertMessage = "Successfully logged time to the server.";

142
src/views/DiscoverView.vue

@ -31,39 +31,48 @@
<a <a
href="#" href="#"
@click="searchLocal()" @click="searchLocal()"
class="inline-block py-3 rounded-t-lg border-b-2 active text-blue-600 border-blue-600 font-semibold" v-bind:class="computedLocalTabClassNames()"
> >
Nearby Nearby
<span <span
class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md" class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md"
>20+</span >{{ localCount }}</span
> >
</a> </a>
</li> </li>
<li> <li>
<a <a
href="#" href="#"
class="inline-block py-3 rounded-t-lg border-b-2 border-transparent hover:text-slate-600 hover:border-slate-300" v-bind:class="computedRemoteTabClassNames()"
@click="search()"
> >
Remote Remote
<span <span
class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md" class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md"
>13</span >{{ remoteCount }}</span
> >
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<!-- Loading Animation -->
<div
class="fixed left-6 bottom-24 text-center text-4xl leading-none bg-slate-400 text-white w-14 py-2.5 rounded-full"
v-if="isLoading"
>
<fa icon="spinner" class="fa-spin-pulse"></fa>
</div>
<!-- Results List --> <!-- Results List -->
<ul class=""> <ul v-if="projects.length > 0">
<li class="border-b border-slate-300"> <li
class="border-b border-slate-300"
v-for="project in projects"
:key="project.handleId"
>
<a <a
@click=" @click="onClickLoadProject(project.handleId)"
onClickLoadProject(
'https://endorser.ch/entity/01H3HER4DTNCR6ZC4VXA3VPWVQ',
)
"
class="block py-4 flex gap-4" class="block py-4 flex gap-4"
> >
<div class="w-12"> <div class="w-12">
@ -76,63 +85,14 @@
<div class="grow"> <div class="grow">
<h2 class="text-base font-semibold">Canyon cleanup</h2> <h2 class="text-base font-semibold">Canyon cleanup</h2>
<div class="text-sm"> <div class="text-sm">
<fa icon="user" class="fa-fw text-slate-400"></fa> Rotary <fa icon="user" class="fa-fw text-slate-400"></fa>
</div> {{ project.name }}
</div>
</a>
</li>
<li class="border-b border-slate-300">
<a
@click="
onClickLoadProject(
'https://endorser.ch/entity/01H3HER4DTNCR6ZC4VXA3VPWVQ',
)
"
class="block py-4 flex gap-4"
>
<div class="w-12">
<img
src="https://picsum.photos/200/200?random=2"
class="w-full rounded"
/>
</div>
<div class="grow">
<h2 class="text-base font-semibold">Potluck with neighbors</h2>
<div class="text-sm">
<fa icon="user" class="fa-fw text-slate-400"></fa> Andrew A.
</div>
</div>
</a>
</li>
<li class="border-b border-slate-300">
<a
@click="
onClickLoadProject(
'https://endorser.ch/entity/01H3HER4DTNCR6ZC4VXA3VPWVQ',
)
"
class="block py-4 flex gap-4"
>
<div class="w-12">
<img
src="https://picsum.photos/200/200?random=3"
class="w-full rounded"
/>
</div>
<div class="grow">
<h2 class="text-base font-semibold">Historical site</h2>
<div class="text-sm">
<fa icon="user" class="fa-fw text-slate-400 mr-1"></fa>
<em>Unknown</em>
</div> </div>
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
<p v-else>No projects found yet.</p>
<AlertMessage <AlertMessage
:alertTitle="alertTitle" :alertTitle="alertTitle"
:alertMessage="alertMessage" :alertMessage="alertMessage"
@ -158,12 +118,19 @@ export default class DiscoverView extends Vue {
searchTerms = ""; searchTerms = "";
alertMessage = ""; alertMessage = "";
alertTitle = ""; alertTitle = "";
projects: ProjectData[] = [];
isLocalActive = true;
isRemoteActive = false;
localCount = 0;
remoteCount = 0;
isLoading = false;
async mounted() { async mounted() {
await db.open(); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
this.activeDid = settings?.activeDid || ""; this.activeDid = settings?.activeDid || "";
this.apiServer = settings?.apiServer || ""; this.apiServer = settings?.apiServer || "";
this.searchLocal();
} }
public async buildHeaders() { public async buildHeaders() {
@ -194,7 +161,11 @@ export default class DiscoverView extends Vue {
const claimType = "claimType=PlanAction"; const claimType = "claimType=PlanAction";
const queryParams = [claimContents, claimType].join("&"); const queryParams = [claimContents, claimType].join("&");
this.isRemoteActive = true;
this.isLocalActive = false;
try { try {
this.isLoading = true;
const response = await fetch( const response = await fetch(
this.apiServer + "/api/v2/report/claims?" + queryParams, this.apiServer + "/api/v2/report/claims?" + queryParams,
{ {
@ -211,7 +182,8 @@ export default class DiscoverView extends Vue {
const results = await response.json(); const results = await response.json();
if (results.data) { if (results.data) {
console.log("Plans found in that search:", results.data); this.projects = results.data;
this.remoteCount = this.projects.length;
} else { } else {
throw JSON.stringify(results); throw JSON.stringify(results);
} }
@ -220,6 +192,8 @@ export default class DiscoverView extends Vue {
this.alertMessage = this.alertMessage =
e.userMessage || "There was an error retrieving projects."; e.userMessage || "There was an error retrieving projects.";
this.alertTitle = "Error"; this.alertTitle = "Error";
} finally {
this.isLoading = false;
} }
} }
@ -235,6 +209,9 @@ export default class DiscoverView extends Vue {
].join("&"); ].join("&");
try { try {
this.isLoading = true;
this.isLocalActive = true;
this.isRemoteActive = false;
const response = await fetch( const response = await fetch(
this.apiServer + "/api/v2/report/plansByLocation?" + queryParams, this.apiServer + "/api/v2/report/plansByLocation?" + queryParams,
{ {
@ -250,7 +227,8 @@ export default class DiscoverView extends Vue {
const results = await response.json(); const results = await response.json();
if (results.data) { if (results.data) {
console.log("Plans found in that location:", results.data); this.projects = results.data;
this.localCount = this.projects.length;
} else { } else {
throw JSON.stringify(results); throw JSON.stringify(results);
} }
@ -259,6 +237,8 @@ export default class DiscoverView extends Vue {
this.alertMessage = this.alertMessage =
e.userMessage || "There was an error retrieving projects."; e.userMessage || "There was an error retrieving projects.";
this.alertTitle = "Error"; this.alertTitle = "Error";
} finally {
this.isLoading = false;
} }
} }
@ -273,5 +253,37 @@ export default class DiscoverView extends Vue {
}; };
this.$router.push(route); this.$router.push(route);
} }
public computedLocalTabClassNames() {
return {
"inline-block": true,
"py-3": true,
"rounded-t-lg": true,
"border-b-2": true,
active: this.isLocalActive,
"text-blue-600": this.isLocalActive,
"border-blue-600": this.isLocalActive,
"font-semibold": this.isLocalActive,
"border-transparent": !this.isLocalActive,
"hover:text-slate-600": !this.isLocalActive,
"hover:border-slate-300": !this.isLocalActive,
};
}
public computedRemoteTabClassNames() {
return {
"inline-block": true,
"py-3": true,
"rounded-t-lg": true,
"border-b-2": true,
active: this.isRemoteActive,
"text-blue-600": this.isRemoteActive,
"border-blue-600": this.isRemoteActive,
"font-semibold": this.isRemoteActive,
"border-transparent": !this.isRemoteActive,
"hover:text-slate-600": !this.isRemoteActive,
"hover:border-slate-300": !this.isRemoteActive,
};
}
} }
</script> </script>

14
src/views/NewEditProjectView.vue

@ -83,14 +83,6 @@ import { useAppStore } from "@/store/app";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
interface VerifiableCredential {
"@context": string;
"@type": string;
name: string;
description: string;
identifier?: string;
}
@Component({ @Component({
components: { AlertMessage }, components: { AlertMessage },
}) })
@ -102,6 +94,8 @@ export default class NewEditProjectView extends Vue {
errorMessage = ""; errorMessage = "";
accounts: AccountsSchema; accounts: AccountsSchema;
numAccounts = 0; numAccounts = 0;
alertTitle = "";
alertMessage = "";
async beforeCreate() { async beforeCreate() {
accountsDB.open(); accountsDB.open();
@ -290,9 +284,5 @@ export default class NewEditProjectView extends Vue {
public onCancelClick() { public onCancelClick() {
this.$router.back(); this.$router.back();
} }
// This same popup code is in many files.
alertTitle = "";
alertMessage = "";
} }
</script> </script>

41
src/views/ProjectViewView.vue

@ -23,16 +23,15 @@
</h1> </h1>
</div> </div>
<div class="text-red-500">
{{ errorMessage }}
</div>
<!-- Project Details --> <!-- Project Details -->
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"> <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
<div> <div>
<h2 class="text-xl font-semibold">{{ name }}</h2> <h2 class="text-xl font-semibold">{{ name }}</h2>
<div class="flex justify-between gap-4 text-sm mb-3"> <div class="flex justify-between gap-4 text-sm mb-3">
<span><fa icon="user" class="fa-fw text-slate-400"></fa> Rotary</span> <span
><fa icon="user" class="fa-fw text-slate-400"></fa>
{{ issuer }}</span
>
<span <span
><fa icon="calendar" class="fa-fw text-slate-400"></fa ><fa icon="calendar" class="fa-fw text-slate-400"></fa
>{{ timeSince }} >{{ timeSince }}
@ -187,11 +186,11 @@ export default class ProjectViewView extends Vue {
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
apiServer = ""; apiServer = "";
description = ""; description = "";
errorMessage = "";
expanded = false; expanded = false;
givesToThis: Array<GiveServerRecord> = []; givesToThis: Array<GiveServerRecord> = [];
givesByThis: Array<GiveServerRecord> = []; givesByThis: Array<GiveServerRecord> = [];
name = ""; name = "";
issuer = "";
numAccounts = 0; numAccounts = 0;
projectId = localStorage.getItem("projectId") || ""; // handle ID projectId = localStorage.getItem("projectId") || ""; // handle ID
timeSince = ""; timeSince = "";
@ -280,30 +279,29 @@ export default class ProjectViewView extends Vue {
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
if (resp.status === 200) { if (resp.status === 200) {
// feel free to remove this; I haven't yet because it's helpful
console.log('Loaded project: ', resp.data);
const startTime = resp.data.startTime; const startTime = resp.data.startTime;
if (startTime != null) { if (startTime != null) {
const eventDate = new Date(startTime); const eventDate = new Date(startTime);
const now = moment.now(); const now = moment.now();
this.timeSince = moment.utc(now).to(eventDate); this.timeSince = moment.utc(now).to(eventDate);
} }
this.issuer = resp.data.issuer;
this.name = resp.data.claim?.name || "(no name)"; this.name = resp.data.claim?.name || "(no name)";
this.description = resp.data.claim?.description || "(no description)"; this.description = resp.data.claim?.description || "(no description)";
this.truncatedDesc = this.description.slice(0, this.truncateLength); this.truncatedDesc = this.description.slice(0, this.truncateLength);
} else if (resp.status === 404) { } else if (resp.status === 404) {
// actually, axios throws an error so we never get here // actually, axios throws an error so we never get here
this.errorMessage = "That project does not exist."; this.alertMessage = "That project does not exist.";
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
if (serverError.response?.status === 404) { if (serverError.response?.status === 404) {
this.errorMessage = "That project does not exist."; this.alertMessage = "That project does not exist.";
} else { } else {
this.errorMessage = this.alertMessage =
"Something went wrong retrieving that project." + "Something went wrong retrieving that project." +
" See logs for more info."; " See logs for more info.";
console.error("Error retrieving project:", error); console.error("Error retrieving project:", serverError.message);
} }
} }
@ -316,13 +314,16 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesToThis = resp.data.data; this.givesToThis = resp.data.data;
} else { } else {
this.errorMessage = "Failed to retrieve gives to this project."; this.alertMessage = "Failed to retrieve gives to this project.";
} }
} catch (error: unknown) { } catch (error: unknown) {
console.error("Error retrieving gives to this project:", error);
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.errorMessage = this.alertMessage =
"Something went wrong retrieving gives to this project."; "Something went wrong retrieving gives to this project.";
console.error(
"Error retrieving gives to this project:",
serverError.message,
);
} }
const givesOutUrl = const givesOutUrl =
@ -334,13 +335,15 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesByThis = resp.data.data; this.givesByThis = resp.data.data;
} else { } else {
this.errorMessage = "Failed to retrieve gives by this project."; this.alertMessage = "Failed to retrieve gives by this project.";
} }
} catch (error: unknown) { } catch (error: unknown) {
console.error("Error retrieving gives by this project:", error);
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.errorMessage = this.alertMessage = "Something went wrong retrieving gives by project.";
"Something went wrong retrieving gives by project."; console.error(
"Error retrieving gives by this project:",
serverError.message,
);
} }
} }

22
src/views/ProjectsView.vue

@ -83,28 +83,6 @@ import InfiniteScroll from "@/components/InfiniteScroll";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav"; import QuickNav from "@/components/QuickNav";
/**
* Represents data about a project
**/
interface ProjectData {
/**
* Name of the project
**/
name: string;
/**
* Description of the project
**/
description: string;
/**
* URL referencing information about the project
**/
handleId: string;
/**
* The Identier of the project
**/
rowid: string;
}
@Component({ @Component({
components: { InfiniteScroll, AlertMessage, QuickNav }, components: { InfiniteScroll, AlertMessage, QuickNav },
}) })

6
src/views/StatisticsView.vue

@ -47,11 +47,6 @@ import { World } from "@/components/World/World.js";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav"; import QuickNav from "@/components/QuickNav";
interface WorldProperties {
startTime?: string;
endTime?: string;
}
@Component({ components: { AlertMessage, World, QuickNav } }) @Component({ components: { AlertMessage, World, QuickNav } })
export default class StatisticsView extends Vue { export default class StatisticsView extends Vue {
world: World; world: World;
@ -62,7 +57,6 @@ export default class StatisticsView extends Vue {
mounted() { mounted() {
try { try {
const container = document.querySelector("#scene-container"); const container = document.querySelector("#scene-container");
console.log(container);
const newWorld = new World(container, this); const newWorld = new World(container, this);
newWorld.start(); newWorld.start();
this.world = newWorld; this.world = newWorld;

Loading…
Cancel
Save