|
|
|
<template>
|
|
|
|
<!-- QUICK NAV -->
|
|
|
|
<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>
|
|
|
|
<!-- Projects -->
|
|
|
|
<li class="basis-1/5 rounded-md bg-slate-400 text-white">
|
|
|
|
<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">
|
|
|
|
<!-- Heading -->
|
|
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
|
|
Your Plans
|
|
|
|
</h1>
|
|
|
|
|
|
|
|
<!-- Quick Search -->
|
|
|
|
<form id="QuickSearch" class="mb-4 flex">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
placeholder="Search…"
|
|
|
|
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2"
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
class="px-4 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
|
|
|
>
|
|
|
|
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
|
|
|
</button>
|
|
|
|
</form>
|
|
|
|
|
|
|
|
<!-- New Project -->
|
|
|
|
<button
|
|
|
|
class="fixed right-6 bottom-24 text-center text-4xl leading-none bg-blue-600 text-white w-14 py-2.5 rounded-full"
|
|
|
|
@click="onClickNewProject()"
|
|
|
|
>
|
|
|
|
<fa icon="plus" class="fa-fw"></fa>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<!-- Results List -->
|
|
|
|
<InfiniteScroll @reached-bottom="loadMoreData">
|
|
|
|
<ul>
|
|
|
|
<li
|
|
|
|
class="border-b border-slate-300"
|
|
|
|
v-for="project in projects"
|
|
|
|
:key="project.handleId"
|
|
|
|
>
|
|
|
|
<a
|
|
|
|
@click="onClickLoadProject(project.handleId)"
|
|
|
|
class="block py-4 flex gap-4"
|
|
|
|
>
|
|
|
|
<div class="flex-none w-12">
|
|
|
|
<img
|
|
|
|
src="https://picsum.photos/200/200?random=1"
|
|
|
|
class="w-full rounded"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="grow overflow-hidden">
|
|
|
|
<h2 class="text-base font-semibold">{{ project.name }}</h2>
|
|
|
|
<div class="text-sm truncate">
|
|
|
|
{{ project.description }}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</InfiniteScroll>
|
|
|
|
</section>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
import * as R from "ramda";
|
|
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
|
|
|
|
|
|
import { accountsDB, db } from "@/db";
|
|
|
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|
|
|
import { accessToken } from "@/libs/crypto";
|
|
|
|
import { IIdentifier } from "@veramo/core";
|
|
|
|
import InfiniteScroll from "@/components/InfiniteScroll";
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
components: { InfiniteScroll },
|
|
|
|
})
|
|
|
|
export default class ProjectsView extends Vue {
|
|
|
|
apiServer = "";
|
|
|
|
projects: { handleId: string; name: string; description: string }[] = [];
|
|
|
|
current: IIdentifier;
|
|
|
|
isDataLoaded = false;
|
|
|
|
|
|
|
|
async loadMoreData(payload: boolean) {
|
|
|
|
console.log("loadMoreData");
|
|
|
|
console.log(payload);
|
|
|
|
if (this.projects.length > 0) {
|
|
|
|
console.log(this.projects[this.projects.length - 1]);
|
|
|
|
const afterurl = this.projects[this.projects.length - 1]["handleId"];
|
|
|
|
const regex = /([0-9A-Z]+)$/; // Regular expression to match one or more digits at the end
|
|
|
|
const matches = afterurl.match(regex);
|
|
|
|
|
|
|
|
let afterId = "";
|
|
|
|
if (matches && matches.length > 1) {
|
|
|
|
afterId = matches[1];
|
|
|
|
console.log(afterId); // Output: 01H3HBVZGH3YE0K27X35S15QT1
|
|
|
|
} else {
|
|
|
|
console.log("No digits found at the end of the URL.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const url =
|
|
|
|
this.apiServer + "/api/v2/report/plansByIssuer?afterId=" + afterId;
|
|
|
|
const token = await accessToken(this.current);
|
|
|
|
const headers = {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
Authorization: "Bearer " + token,
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
const resp = await this.axios.get(url, { headers });
|
|
|
|
if (resp.status === 200) {
|
|
|
|
const plans = resp.data.data;
|
|
|
|
for (let i = 0; i < plans.length; i++) {
|
|
|
|
const plan = plans[i];
|
|
|
|
const data = {
|
|
|
|
name: plan.name,
|
|
|
|
description: plan.description,
|
|
|
|
// handleId is new in server v release-1.6.0; remove fullIri when that
|
|
|
|
// version shows up here: https://endorser.ch:3000/api-docs/
|
|
|
|
handleId: plan.handleId || plan.fullIri,
|
|
|
|
};
|
|
|
|
this.projects.push(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Got error loading projects: ", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onClickLoadProject(id: string) {
|
|
|
|
localStorage.setItem("projectId", id);
|
|
|
|
const route = {
|
|
|
|
name: "project",
|
|
|
|
};
|
|
|
|
this.$router.push(route);
|
|
|
|
}
|
|
|
|
|
|
|
|
async LoadProjects(identity: IIdentifier) {
|
|
|
|
console.log("LoadProjects");
|
|
|
|
const url = this.apiServer + "/api/v2/report/plansByIssuer";
|
|
|
|
const token = await accessToken(identity);
|
|
|
|
const headers = {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
Authorization: "Bearer " + token,
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
const resp = await this.axios.get(url, { headers });
|
|
|
|
if (resp.status === 200) {
|
|
|
|
const plans = resp.data.data;
|
|
|
|
for (let i = 0; i < plans.length; i++) {
|
|
|
|
const plan = plans[i];
|
|
|
|
const data = {
|
|
|
|
name: plan.name,
|
|
|
|
description: plan.description,
|
|
|
|
// handleId is new in server v release-1.6.0; remove fullIri when that
|
|
|
|
// version shows up here: https://endorser.ch:3000/api-docs/
|
|
|
|
handleId: plan.handleId || plan.fullIri,
|
|
|
|
};
|
|
|
|
this.projects.push(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.isDataLoaded = true;
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Got error loading projects: ", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 'created' hook runs when the Vue instance is first created
|
|
|
|
async created() {
|
|
|
|
await db.open();
|
|
|
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
|
|
|
const activeDid = settings?.activeDid || "";
|
|
|
|
this.apiServer = settings?.apiServer || "";
|
|
|
|
|
|
|
|
await accountsDB.open();
|
|
|
|
const num_accounts = await accountsDB.accounts.count();
|
|
|
|
if (num_accounts === 0) {
|
|
|
|
console.error("Problem! Should have a profile!");
|
|
|
|
} else {
|
|
|
|
const accounts = await accountsDB.accounts.toArray();
|
|
|
|
const account = R.find((acc) => acc.did === activeDid, accounts);
|
|
|
|
const identity = JSON.parse(account?.identity || "undefined");
|
|
|
|
this.current = identity;
|
|
|
|
this.LoadProjects(identity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onClickNewProject(): void {
|
|
|
|
localStorage.removeItem("projectId");
|
|
|
|
const route = {
|
|
|
|
name: "new-edit-project",
|
|
|
|
};
|
|
|
|
this.$router.push(route);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|