update to new endpoints with the editable plan "handle" #7

Merged
trentlarson merged 3 commits from pull-only-plans into master 2 years ago
  1. 4
      README.md
  2. 30
      project.yaml
  3. 2
      src/constants/app.ts
  4. 56
      src/views/NewEditProjectView.vue
  5. 42
      src/views/ProjectViewView.vue
  6. 26
      src/views/ProjectsView.vue

4
README.md

@ -42,9 +42,9 @@ New users require registration. This can be done with a claim payload like this
}; };
``` ```
On the test server, the user starting 0x000 has rights to register others, so you can start playing one of two ways: On the test server, User #0 has rights to register others, so you can start playing one of two ways:
- Import the keys for that test User #0 by importing this seed phrase: `seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control` - Import the keys for that test User `did:ethr:0x000Ee5654b9742f6Fe18ea970e32b97ee2247B51` by importing this seed phrase: `seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control`
- Register someone else under User #0 on the `/account` page: - Register someone else under User #0 on the `/account` page:

30
project.yaml

@ -1,18 +1,36 @@
- top screens from img/screens.pdf : - top screens from img/screens.pdf milestone:2 :
- new
- make sure to save with plan identifier if it isn't already assignee:trent
- feedback to show saving
- edit
- make sure to save with plan identifier if it isn't already assignee:trent
- feedback to show saved
- view all : - view all :
- also extract plan identifier (instead of claim ID) assignee:trent - also extract plan identifier (instead of claim ID) assignee:trent
- search bar isn't highlighted & icon on right doesn't show - search bar isn't highlighted & icon on right doesn't show
- no tab bar across bottom - no tab bar across bottom
- add spinner
- view one - view one
- change lookup to pull from new /api/ext/plan with plan identifier assignee:trent - change lookup to pull from new /api/ext/plan with plan identifier assignee:trent
- edit - image
- make sure to save with plan identifier if it isn't already
- new
- make sure to save with plan identifier if it isn't already
blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time
- replace user-affecting console.logs with error messages - replace user-affecting console.logs with error messages (eg. catches)
- contacts
- commit screen - commit screen
- discover screen - discover screen
- backup all data
- Next Viable Product afterward
- Connect with phone contacts
- Multiple identities
- Peer DID
- DIDComm

2
src/constants/app.ts

@ -5,5 +5,5 @@ export enum AppString {
APP_NAME = "Kickstart for time", APP_NAME = "Kickstart for time",
VERSION = "0.1", VERSION = "0.1",
DEFAULT_ENDORSER_API_SERVER = "https://test.endorser.ch:8000", DEFAULT_ENDORSER_API_SERVER = "https://test.endorser.ch:8000",
DEFAULT_ENDORSER_VIEW_SERVER = "https://test.endorser.ch", //DEFAULT_ENDORSER_API_SERVER = "http://localhost:3000",
} }

56
src/views/NewEditProjectView.vue

@ -10,13 +10,17 @@
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
><fa icon="chevron-left" class="fa-fw"></fa ><fa icon="chevron-left" class="fa-fw"></fa
></router-link> ></router-link>
[New/Edit] Project [New/Edit] Plan
</h1> </h1>
</div> </div>
<!-- Project Details --> <!-- Project Details -->
<!-- Image - (see design model) Empty --> <!-- Image - (see design model) Empty -->
<div>
{{ errorMessage }}
</div>
<input <input
type="text" type="text"
placeholder="Project Name" placeholder="Project Name"
@ -61,16 +65,23 @@ import * as didJwt from "did-jwt";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { useAppStore } from "@/store/app"; import { useAppStore } from "@/store/app";
interface VerifiableCredential {
"@context": string;
"@type": string;
name: string;
description: string;
identifier?: string;
}
@Options({ @Options({
components: {}, components: {},
}) })
export default class NewEditProjectView extends Vue { export default class NewEditProjectView extends Vue {
projectName = ""; projectName = "";
description = ""; description = "";
projectId = errorMessage = "";
localStorage.getItem("projectId") === null
? "" projectId = localStorage.getItem("projectId") || "";
: localStorage.getItem("projectId");
async created() { async created() {
if (this.projectId === "") { if (this.projectId === "") {
@ -90,7 +101,8 @@ export default class NewEditProjectView extends Vue {
async LoadProject(identity: IIdentifier) { async LoadProject(identity: IIdentifier) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
const url = endorserApiServer + "/api/claim/" + this.projectId; // eslint-disable-next-line prettier/prettier
const url = endorserApiServer + "/api/claim/byHandle/" + encodeURIComponent(this.projectId);
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -111,15 +123,17 @@ export default class NewEditProjectView extends Vue {
} }
private async SaveProject(identity: IIdentifier) { private async SaveProject(identity: IIdentifier) {
const address = identity.did;
// Make a claim // Make a claim
const vcClaim = { const vcClaim: VerifiableCredential = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "PlanAction", "@type": "PlanAction",
identifier: address,
name: this.projectName, name: this.projectName,
description: this.description, description: this.description,
identifier: this.projectId || undefined,
}; };
if (this.projectId) {
vcClaim.identifier = this.projectId;
}
// Make a payload for the claim // Make a payload for the claim
const vcPayload = { const vcPayload = {
sub: "PlanAction", sub: "PlanAction",
@ -149,7 +163,7 @@ export default class NewEditProjectView extends Vue {
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
const url = endorserApiServer + "/api/claim"; const url = endorserApiServer + "/api/v2/claim";
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -158,7 +172,10 @@ export default class NewEditProjectView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
useAppStore().setProjectId(resp.data); console.log("Got resp data:", resp.data);
if (resp.data?.success?.fullIri) {
this.errorMessage = "";
useAppStore().setProjectId(resp.data.success.fullIri);
setTimeout( setTimeout(
function (that: Vue) { function (that: Vue) {
console.log("THAT:", localStorage.getItem("projectId")); console.log("THAT:", localStorage.getItem("projectId"));
@ -171,8 +188,21 @@ export default class NewEditProjectView extends Vue {
2000, 2000,
this this
); );
} catch (error) { }
console.log(error); } catch (error: any) {
let userMessage = "There was an error. See logs for more info.";
const serverError = error.response?.data?.error;
if (serverError) {
if (serverError.message) {
userMessage = serverError.message; // This is info for the user.
} else {
console.log("Server gave an error:", serverError);
}
} else {
console.log("Here's the full error trying to save the claim:", error);
}
// Now set that error for the user to see.
this.errorMessage = userMessage;
} }
} }
} }

42
src/views/ProjectViewView.vue

@ -60,10 +60,14 @@
><fa icon="ellipsis-vertical" class="fa-fw"></fa ><fa icon="ellipsis-vertical" class="fa-fw"></fa
></a> ></a>
View Project View Plan
</h1> </h1>
</div> </div>
<div>
{{ 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>
@ -157,10 +161,8 @@ export default class ProjectViewView extends Vue {
truncatedDesc = ""; truncatedDesc = "";
truncateLength = 40; truncateLength = 40;
timeSince = ""; timeSince = "";
projectId = projectId = localStorage.getItem("projectId") || "";
localStorage.getItem("projectId") === null errorMessage = "";
? ""
: localStorage.getItem("projectId");
onEditClick() { onEditClick() {
localStorage.setItem("projectId", this.projectId as string); localStorage.setItem("projectId", this.projectId as string);
@ -181,7 +183,8 @@ export default class ProjectViewView extends Vue {
async LoadProject(identity: IIdentifier) { async LoadProject(identity: IIdentifier) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
const url = endorserApiServer + "/api/claim/" + this.projectId; // eslint-disable-next-line prettier/prettier
const url = endorserApiServer + "/api/claim/byHandle/" + encodeURIComponent(this.projectId);
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -190,19 +193,30 @@ export default class ProjectViewView extends Vue {
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
console.log(resp.status, resp.data); console.log("resp.status, resp.data", resp.status, resp.data);
if (resp.status === 200) { if (resp.status === 200) {
const claim = resp.data.claim; const startTime = resp.data.startTime;
const issuedAt = resp.data.issuedAt; if (startTime != null) {
const eventDate = new Date(issuedAt); 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.name = claim.name; }
this.description = claim.description; this.name = resp.data.claim?.name || "(no name)";
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) {
// actually, axios throws an error so we never get here
this.errorMessage = "That project does not exist.";
}
} catch (error: any) {
if (error?.response?.status === 404) {
this.errorMessage = "That project does not exist.";
} else {
this.errorMessage =
"Something went wrong retrieving that project." +
" See logs for more info.";
console.log("Error retrieving project:", error);
} }
} catch (error) {
console.log(error);
} }
} }

26
src/views/ProjectsView.vue

@ -2,7 +2,7 @@
<section id="Content" class="p-6 pb-24"> <section id="Content" class="p-6 pb-24">
<!-- Heading --> <!-- Heading -->
<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">
My Projects My Plans
</h1> </h1>
<!-- Quick Search --> <!-- Quick Search -->
@ -32,10 +32,10 @@
<li <li
class="border-b border-slate-300" class="border-b border-slate-300"
v-for="project in projects" v-for="project in projects"
:key="project.id" :key="project.handleId"
> >
<a <a
@click="onClickLoadProject(project.id)" @click="onClickLoadProject(project.handleId)"
class="block py-4 flex gap-4" class="block py-4 flex gap-4"
> >
<div class="flex-none w-12"> <div class="flex-none w-12">
@ -68,7 +68,7 @@ import { AppString } from "@/constants/app";
components: {}, components: {},
}) })
export default class ProjectsView extends Vue { export default class ProjectsView extends Vue {
projects: { id: string; name: string; description: string }[] = []; projects: { handleId: string; name: string; description: string }[] = [];
onClickLoadProject(id: string) { onClickLoadProject(id: string) {
console.log("projectId", id); console.log("projectId", id);
@ -82,10 +82,7 @@ export default class ProjectsView extends Vue {
async LoadProjects(identity: IIdentifier) { async LoadProjects(identity: IIdentifier) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
const url = const url = endorserApiServer + "/api/v2/report/plansByIssuer";
endorserApiServer +
"/api/claim/?claimType=PlanAction&issuer=" +
identity.did;
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -95,14 +92,13 @@ export default class ProjectsView 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) {
const claims = resp.data; const plans = resp.data.data;
for (let i = 0; i < claims.length; i++) { for (let i = 0; i < plans.length; i++) {
console.log(claims[i]); const plan = plans[i];
const claim = claims[i].claim;
const data = { const data = {
id: claims[i].id, name: plan.name,
name: claim.name, description: plan.description,
description: claim.description, handleId: plan.fullIri,
}; };
this.projects.push(data); this.projects.push(data);
} }

Loading…
Cancel
Save