Browse Source

add page for a printable certificate (which works but isn't too impressive yet)

split_build_process
Trent Larson 2 months ago
parent
commit
e6f5511dbb
  1. 20
      package-lock.json
  2. 2
      package.json
  3. BIN
      public/img/background/cert-frame-2.jpg
  4. 2
      src/main.ts
  5. 5
      src/router/index.ts
  6. 149
      src/views/ClaimCertificateView.vue
  7. 9
      src/views/ClaimView.vue

20
package-lock.json

@ -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"
}, },

2
package.json

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

2
src/main.ts

@ -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,

5
src/router/index.ts

@ -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

@ -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>

9
src/views/ClaimView.vue

@ -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,

Loading…
Cancel
Save