forked from trent_larson/crowd-funder-for-time-pwa
add page for a printable certificate (which works but isn't too impressive yet)
This commit is contained in:
20
package-lock.json
generated
20
package-lock.json
generated
@@ -24,6 +24,7 @@
|
|||||||
"@simplewebauthn/browser": "^10.0.0",
|
"@simplewebauthn/browser": "^10.0.0",
|
||||||
"@simplewebauthn/server": "^10.0.0",
|
"@simplewebauthn/server": "^10.0.0",
|
||||||
"@tweenjs/tween.js": "^21.1.1",
|
"@tweenjs/tween.js": "^21.1.1",
|
||||||
|
"@types/qrcode": "^1.5.5",
|
||||||
"@veramo/core": "^5.6.0",
|
"@veramo/core": "^5.6.0",
|
||||||
"@veramo/credential-w3c": "^5.6.0",
|
"@veramo/credential-w3c": "^5.6.0",
|
||||||
"@veramo/data-store": "^5.6.0",
|
"@veramo/data-store": "^5.6.0",
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
"pina": "^0.20.2204228",
|
"pina": "^0.20.2204228",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"qr-code-generator-vue3": "^1.4.21",
|
"qr-code-generator-vue3": "^1.4.21",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"ramda": "^0.29.1",
|
"ramda": "^0.29.1",
|
||||||
"readable-stream": "^4.5.2",
|
"readable-stream": "^4.5.2",
|
||||||
"reflect-metadata": "^0.1.14",
|
"reflect-metadata": "^0.1.14",
|
||||||
@@ -9766,6 +9768,14 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/qrcode": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/ramda": {
|
"node_modules/@types/ramda": {
|
||||||
"version": "0.29.11",
|
"version": "0.29.11",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -13040,10 +13050,6 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encode-utf8": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -19720,11 +19726,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qrcode": {
|
"node_modules/qrcode": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.4",
|
||||||
"license": "MIT",
|
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dijkstrajs": "^1.0.1",
|
"dijkstrajs": "^1.0.1",
|
||||||
"encode-utf8": "^1.0.3",
|
|
||||||
"pngjs": "^5.0.0",
|
"pngjs": "^5.0.0",
|
||||||
"yargs": "^15.3.1"
|
"yargs": "^15.3.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"@simplewebauthn/browser": "^10.0.0",
|
"@simplewebauthn/browser": "^10.0.0",
|
||||||
"@simplewebauthn/server": "^10.0.0",
|
"@simplewebauthn/server": "^10.0.0",
|
||||||
"@tweenjs/tween.js": "^21.1.1",
|
"@tweenjs/tween.js": "^21.1.1",
|
||||||
|
"@types/qrcode": "^1.5.5",
|
||||||
"@veramo/core": "^5.6.0",
|
"@veramo/core": "^5.6.0",
|
||||||
"@veramo/credential-w3c": "^5.6.0",
|
"@veramo/credential-w3c": "^5.6.0",
|
||||||
"@veramo/data-store": "^5.6.0",
|
"@veramo/data-store": "^5.6.0",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"pina": "^0.20.2204228",
|
"pina": "^0.20.2204228",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"qr-code-generator-vue3": "^1.4.21",
|
"qr-code-generator-vue3": "^1.4.21",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"ramda": "^0.29.1",
|
"ramda": "^0.29.1",
|
||||||
"readable-stream": "^4.5.2",
|
"readable-stream": "^4.5.2",
|
||||||
"reflect-metadata": "^0.1.14",
|
"reflect-metadata": "^0.1.14",
|
||||||
|
|||||||
BIN
public/img/background/cert-frame-2.jpg
Normal file
BIN
public/img/background/cert-frame-2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
@@ -43,6 +43,7 @@ import {
|
|||||||
faEraser,
|
faEraser,
|
||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
|
faFileContract,
|
||||||
faFileLines,
|
faFileLines,
|
||||||
faFilter,
|
faFilter,
|
||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
@@ -117,6 +118,7 @@ library.add(
|
|||||||
faEraser,
|
faEraser,
|
||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
|
faFileContract,
|
||||||
faFileLines,
|
faFileLines,
|
||||||
faFilter,
|
faFilter,
|
||||||
faFloppyDisk,
|
faFloppyDisk,
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: "claim-add-raw",
|
name: "claim-add-raw",
|
||||||
component: () => import("../views/ClaimAddRawView.vue"),
|
component: () => import("../views/ClaimAddRawView.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/claim-cert/:id?",
|
||||||
|
name: "claim-cert",
|
||||||
|
component: () => import("../views/ClaimCertificateView.vue"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/confirm-contact",
|
path: "/confirm-contact",
|
||||||
name: "confirm-contact",
|
name: "confirm-contact",
|
||||||
|
|||||||
149
src/views/ClaimCertificateView.vue
Normal file
149
src/views/ClaimCertificateView.vue
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<section id="Content">
|
||||||
|
<div v-if="claimData">
|
||||||
|
<canvas ref="claimCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { nextTick } from "vue";
|
||||||
|
import QRCode from "qrcode";
|
||||||
|
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
|
import { retrieveSettingsForActiveAccount } from "@/db/index";
|
||||||
|
import * as endorserServer from "@/libs/endorserServer";
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class ClaimViewCertificate extends Vue {
|
||||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
|
activeDid = "";
|
||||||
|
allMyDids: Array<string> = [];
|
||||||
|
apiServer = "";
|
||||||
|
claimId = "";
|
||||||
|
claimData = null;
|
||||||
|
|
||||||
|
endorserServer = endorserServer;
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
|
this.activeDid = settings.activeDid || "";
|
||||||
|
this.apiServer = settings.apiServer || "";
|
||||||
|
const pathParams = window.location.pathname.substring(
|
||||||
|
"/claim-cert/".length,
|
||||||
|
);
|
||||||
|
this.claimId = pathParams;
|
||||||
|
await this.fetchClaim();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchClaim() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${this.apiServer}/api/claim/${this.claimId}`,
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
this.claimData = await response.json();
|
||||||
|
await nextTick(); // Wait for the DOM to update
|
||||||
|
this.drawCanvas();
|
||||||
|
} else {
|
||||||
|
throw new Error(`Error fetching claim: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load claim:", error);
|
||||||
|
this.$notify({
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error",
|
||||||
|
text: "There was a problem loading the claim.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCanvas() {
|
||||||
|
const canvas = this.$refs.claimCanvas as HTMLCanvasElement;
|
||||||
|
if (canvas) {
|
||||||
|
const CANVAS_WIDTH = 1100;
|
||||||
|
const CANVAS_HEIGHT = 850;
|
||||||
|
|
||||||
|
// size to approximate portrait of 8.5"x11"
|
||||||
|
canvas.width = CANVAS_WIDTH;
|
||||||
|
canvas.height = CANVAS_HEIGHT;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (ctx) {
|
||||||
|
// Load the background image
|
||||||
|
const backgroundImage = new Image();
|
||||||
|
backgroundImage.src = "/img/background/cert-frame-2.jpg";
|
||||||
|
backgroundImage.onload = async () => {
|
||||||
|
// Draw the background image
|
||||||
|
ctx.drawImage(backgroundImage, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||||
|
|
||||||
|
// Set font and styles
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
|
||||||
|
// Draw claim type
|
||||||
|
ctx.font = "bold 20px Arial";
|
||||||
|
const claimTypeText =
|
||||||
|
this.endorserServer.capitalizeAndInsertSpacesBeforeCaps(
|
||||||
|
this.claimData.claimType,
|
||||||
|
);
|
||||||
|
const claimTypeWidth = ctx.measureText(claimTypeText).width;
|
||||||
|
ctx.fillText(
|
||||||
|
claimTypeText,
|
||||||
|
(CANVAS_WIDTH - claimTypeWidth) / 2, // Center horizontally
|
||||||
|
CANVAS_HEIGHT * 0.35,
|
||||||
|
);
|
||||||
|
|
||||||
|
const descriptionText =
|
||||||
|
this.claimData.claim.description || this.claimData.claim.name;
|
||||||
|
if (descriptionText) {
|
||||||
|
const descriptionLine =
|
||||||
|
descriptionText.length > 50
|
||||||
|
? descriptionText.substring(0, 47) + "..."
|
||||||
|
: descriptionText;
|
||||||
|
ctx.font = "14px Arial";
|
||||||
|
const descriptionWidth = ctx.measureText(descriptionLine).width;
|
||||||
|
ctx.fillText(
|
||||||
|
descriptionLine,
|
||||||
|
(CANVAS_WIDTH - descriptionWidth) / 2,
|
||||||
|
CANVAS_HEIGHT * 0.45,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw claim ID
|
||||||
|
ctx.font = "14px Arial";
|
||||||
|
ctx.fillText(this.claimId, CANVAS_WIDTH * 0.3, CANVAS_HEIGHT * 0.62);
|
||||||
|
ctx.fillText(
|
||||||
|
"via EndorserSearch.com",
|
||||||
|
CANVAS_WIDTH * 0.3,
|
||||||
|
CANVAS_HEIGHT * 0.65,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate and draw QR code
|
||||||
|
const qrCodeCanvas = document.createElement("canvas");
|
||||||
|
await QRCode.toCanvas(qrCodeCanvas, window.location.href, {
|
||||||
|
width: 150,
|
||||||
|
color: { light: "#0000" /* Transparent background */ },
|
||||||
|
});
|
||||||
|
ctx.drawImage(
|
||||||
|
qrCodeCanvas,
|
||||||
|
CANVAS_WIDTH * 0.57,
|
||||||
|
CANVAS_HEIGHT * 0.55,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -157,6 +157,14 @@
|
|||||||
<fa icon="comment" class="text-slate-400" />
|
<fa icon="comment" class="text-slate-400" />
|
||||||
{{ issuerName }} posted that.
|
{{ issuerName }} posted that.
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
<div>
|
||||||
|
<router-link :to="'/claim-cert/' + encodeURIComponent(veriClaim.id)">
|
||||||
|
<fa icon="file-contract" class="text-slate-400" />
|
||||||
|
<span class="ml-2 text-blue-500">Printable Certificate</span>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<button
|
<button
|
||||||
@@ -885,7 +893,6 @@ export default class ClaimView extends Vue {
|
|||||||
this.veriClaim as GenericCredWrapper<OfferVerifiableCredential>,
|
this.veriClaim as GenericCredWrapper<OfferVerifiableCredential>,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
console.log("giver & dialog", giver, this.$refs.customGiveDialog);
|
|
||||||
(this.$refs.customGiveDialog as GiftedDialog).open(
|
(this.$refs.customGiveDialog as GiftedDialog).open(
|
||||||
giver,
|
giver,
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user