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

This commit is contained in:
2024-12-01 20:20:03 -07:00
parent 8bd1a6668c
commit 2b802f01f8
7 changed files with 179 additions and 8 deletions

View File

@@ -43,6 +43,7 @@ import {
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,
@@ -117,6 +118,7 @@ library.add(
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,

View File

@@ -43,6 +43,11 @@ const routes: Array<RouteRecordRaw> = [
name: "claim-add-raw",
component: () => import("../views/ClaimAddRawView.vue"),
},
{
path: "/claim-cert/:id?",
name: "claim-cert",
component: () => import("../views/ClaimCertificateView.vue"),
},
{
path: "/confirm-contact",
name: "confirm-contact",

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

View File

@@ -157,6 +157,14 @@
<fa icon="comment" class="text-slate-400" />
{{ issuerName }} posted that.
</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">
<button
@@ -885,7 +893,6 @@ export default class ClaimView extends Vue {
this.veriClaim as GenericCredWrapper<OfferVerifiableCredential>,
),
};
console.log("giver & dialog", giver, this.$refs.customGiveDialog);
(this.$refs.customGiveDialog as GiftedDialog).open(
giver,
undefined,