diff --git a/src/Pages/internify/mentor/BaseProjectsPage.tsx b/src/Pages/internify/mentor/BaseProjectsPage.tsx
index 0baa0be..b671553 100644
--- a/src/Pages/internify/mentor/BaseProjectsPage.tsx
+++ b/src/Pages/internify/mentor/BaseProjectsPage.tsx
@@ -5,13 +5,6 @@ import CreateProjectModal from './components/CreateProjectDialog';
import { useProjectsByRole } from '../../../hooks/useListProjects';
import TeamPana from '../../../assets/Team-pana.svg';
-function MentorProjectCardSkeleton() {
- return (
-
-
- )
-}
-
export default function MentorProjectsPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
const { projects, loading, error, refetch } = useProjectsByRole();
@@ -35,7 +28,8 @@ export default function MentorProjectsPage() {
return (
{[...Array(4)].map((_) => (
-
+
+
))}
);
diff --git a/src/Pages/internify/mentor/components/EligibleInternTable.tsx b/src/Pages/internify/mentor/components/EligibleInternTable.tsx
index 5cbe202..686b414 100644
--- a/src/Pages/internify/mentor/components/EligibleInternTable.tsx
+++ b/src/Pages/internify/mentor/components/EligibleInternTable.tsx
@@ -118,7 +118,7 @@ export default function EligibleInternTable({
id: intern.id_user,
id_project: project.id,
id_user: intern.id_user,
- certificate_no: "CERT/yyddmm/X",
+ certificate_no: "No.2025/SPI3/CERT/yyddmm/X",
issued_at: new Date().toISOString().slice(0, 10),
uuid: "HANYA SEBATAS PREVIEW",
user: {
diff --git a/src/Pages/utils/SertificateGenerator.ts b/src/Pages/utils/SertificateGenerator.ts
index f1469b9..bf307fb 100644
--- a/src/Pages/utils/SertificateGenerator.ts
+++ b/src/Pages/utils/SertificateGenerator.ts
@@ -1,7 +1,8 @@
import { resolveFileUrl } from "./resolveFileFromUrl";
import QRCode from "qrcode";
-export function loadImage(src: string): Promise
{
+//LOAD IMAGE TEMPLATE
+function loadImage(src: string): Promise {
return new Promise((resolve, reject) => {
const img = new Image();
if (!src.startsWith("data:")) img.crossOrigin = "anonymous";
@@ -11,7 +12,8 @@ export function loadImage(src: string): Promise {
});
}
-export async function ensureFontsLoaded(): Promise {
+//LOAD FONT UNTUK MENCEGAH FONT TERGANTI
+async function ensureFontsLoaded(): Promise {
await Promise.all([
document.fonts.load('105px "Great Vibes"'),
document.fonts.load('54px "Grenze"'),
@@ -19,16 +21,30 @@ export async function ensureFontsLoaded(): Promise {
await document.fonts.ready;
}
+// LAYOUT DEFAULT SERTIFIKAT YANG AKAN DIGENERATE
const LAYOUT = {
- internName: { x: 0.57, y: 0.48, size: 140, font: "Great Vibes", color: "#800000" },
+ internName: { x: 0.54, y: 0.5, size: 130, font: "Great Vibes", color: "#800000" },
internNameSmall: { x: 0.82, y: 0.889, size: 47, font: "Great Vibes", color: "#090909" },
projectName: { x: 0.815, y: 0.598, size: 46, font: "Grenze", color: "#800000" },
duration: { x: 0.7, y: 0.645, size: 48, font: "Grenze", color: "#090909" },
- certNo: { x: 0.597, y: 0.305, size: 50, font: "Grenze", color: "#090909" },
+ certNo: { x: 0.56, y: 0.305, size: 50, font: "Grenze", color: "#090909" },
certNoSmall: { x: 0.827, y: 0.92, size: 32, font: "Grenze", color: "#090909"},
qr: { x: 0.77, y: 0.715, size: 0.103 },
};
+// HELPER: kurangi ukuran font 25% jika nama melebihi 22 karakter (termasuk spasi)
+function fitNameText(name: string, baseSize: number): number {
+ const SAFE_LENGTH = 24;
+ const REDUCTION_PER_CHAR = 0.032;
+ const MIN_SCALE = 0.75;
+
+ const length = name.length;
+ if (length <= SAFE_LENGTH) return baseSize;
+ const scale = Math.max(1 - (length - SAFE_LENGTH) * REDUCTION_PER_CHAR, MIN_SCALE);
+ return baseSize * scale;
+}
+
+//MAIN FUNCTION: generasi sertifikat intern formatted
export async function generateCertificate(
templateUrl: string,
internName: string,
@@ -52,7 +68,8 @@ export async function generateCertificate(
cfg: { x: number; y: number; size: number; font: string; color: string },
value: string,
) => {
- ctx.font = `400 ${cfg.size}px "${cfg.font}", serif`;
+ const size = cfg === LAYOUT.internName ? fitNameText(value, cfg.size) : cfg.size;
+ ctx.font = `400 ${size}px "${cfg.font}", serif`;
ctx.fillStyle = cfg.color;
ctx.fillText(value, canvas.width * cfg.x, canvas.height * cfg.y);
};
@@ -62,9 +79,8 @@ export async function generateCertificate(
text(LAYOUT.projectName, projectName);
text(LAYOUT.duration, ProjectDuration);
text(LAYOUT.certNo, certificateNo);
- text(LAYOUT.certNoSmall, "No.2026/SPI3/" + certificateNo);
+ text(LAYOUT.certNoSmall, certificateNo);
- // QR code — nanti akan diganti ke alamat domain
const qrSize = Math.round(canvas.width * LAYOUT.qr.size);
const qrDataUrl = await QRCode.toDataURL(
`${window.location.origin}/verify-certificate/${verifyUrl}`,
diff --git a/src/Pages/utils/testlayout.html b/src/Pages/utils/testlayout.html
index d55f2bc..36127b1 100644
--- a/src/Pages/utils/testlayout.html
+++ b/src/Pages/utils/testlayout.html
@@ -5,29 +5,48 @@
+
+
-
+