From 753012732db8acaf1f863419bdc125abd62d1cc0 Mon Sep 17 00:00:00 2001 From: "Kemas M. Aryadary Rasyad" <112590882+aryadary0706@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:21:16 +0700 Subject: [PATCH] feat: update navigation paths to use '/internship'; enhance certificate generation and user interface elements --- src/Pages/OurDeveloper.tsx | 2 +- src/Pages/internify/HomeCalendar.tsx | 16 ++++++++------ src/Pages/internify/LoginInternify.tsx | 8 ++++++- src/Pages/internify/intern/InternCertList.tsx | 2 +- src/Pages/internify/intern/ProjectsPage.tsx | 4 ++-- .../intern/components/CertificateAvailabe.tsx | 4 ++-- .../intern/components/HomeProjectList.tsx | 2 +- .../intern/components/ProjectTaskCard.tsx | 2 +- .../intern/components/ProjectsCard.tsx | 2 +- .../components/SubmissionStatusTable.tsx | 22 +++++++++++++++++-- .../intern/components/TaskForm-file.tsx | 9 +++++++- .../internify/mentor/BaseProjectsPage.tsx | 4 ++-- .../mentor/components/CertResultTable.tsx | 7 ------ .../mentor/components/EligibleInternTable.tsx | 8 ++++--- src/Pages/utils/Certificates.ts | 16 +++++++------- src/Pages/utils/SertificateGenerator.ts | 12 +++++----- src/types/certificate.types.ts | 4 ++++ 17 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/Pages/OurDeveloper.tsx b/src/Pages/OurDeveloper.tsx index ce4fc30..a6e3c04 100644 --- a/src/Pages/OurDeveloper.tsx +++ b/src/Pages/OurDeveloper.tsx @@ -74,7 +74,7 @@ const OurDeveloper = () => { link: "https://www.linkedin.com/in/reihanramadhana/", }, { - name: "Ayu", + name: "Nomi", role: "Web Designer", img: ayu, link: "https://www.linkedin.com/in/anom-ayu-radhaswasti/", diff --git a/src/Pages/internify/HomeCalendar.tsx b/src/Pages/internify/HomeCalendar.tsx index 2f81818..2e3cb05 100644 --- a/src/Pages/internify/HomeCalendar.tsx +++ b/src/Pages/internify/HomeCalendar.tsx @@ -143,21 +143,23 @@ export default function HomeCalendar() { ))} {currentDays.map(day => ( -
+
+
{isToday(day) ? ( - + {day} ) : ( + className={`w-full h-7 flex items-center justify-center text-xs + ${isBold(day) ? 'font-bold text-gray-800' : 'text-gray-700'} + ${hasDeadline(day) ? 'bg-red text-white rounded-full' : ''} + transition-colors`} + > {day} )} +
))} diff --git a/src/Pages/internify/LoginInternify.tsx b/src/Pages/internify/LoginInternify.tsx index 5b79567..ab642b5 100644 --- a/src/Pages/internify/LoginInternify.tsx +++ b/src/Pages/internify/LoginInternify.tsx @@ -52,7 +52,13 @@ export default function InternifyLogin() {
- Logo +

HUMIC LMS PORTAL diff --git a/src/Pages/internify/intern/InternCertList.tsx b/src/Pages/internify/intern/InternCertList.tsx index 50f95ad..5b6f2d0 100644 --- a/src/Pages/internify/intern/InternCertList.tsx +++ b/src/Pages/internify/intern/InternCertList.tsx @@ -56,7 +56,7 @@ export default function SertificateList() {

navigate(`/intern/certificates/${item.slug}`, { state: { project: item } })} + onClick={() => navigate(`/internship/certificates/${item.slug}`, { state: { project: item } })} > {/* Thumbnail */}
diff --git a/src/Pages/internify/intern/ProjectsPage.tsx b/src/Pages/internify/intern/ProjectsPage.tsx index 1136e05..ff9e8d1 100644 --- a/src/Pages/internify/intern/ProjectsPage.tsx +++ b/src/Pages/internify/intern/ProjectsPage.tsx @@ -22,9 +22,9 @@ export default function ProjectsPage() { if (projects.length === 0) { return ( -
+
-

Kamu belum tergabung ke projek apapun

+

Kamu belum tergabung ke projek apapun

); } diff --git a/src/Pages/internify/intern/components/CertificateAvailabe.tsx b/src/Pages/internify/intern/components/CertificateAvailabe.tsx index 80a56b9..3a59cdd 100644 --- a/src/Pages/internify/intern/components/CertificateAvailabe.tsx +++ b/src/Pages/internify/intern/components/CertificateAvailabe.tsx @@ -41,7 +41,7 @@ export default function CertificateAvailable({ project, certificate, templateUrl generateCertificate( resolveFileUrl(templateUrl), certificate.user.full_name, - certificate.project.project_name, + certificate.intern_position, certificate.certificate_no, duration, verifyUrl, @@ -139,7 +139,7 @@ export default function CertificateAvailable({ project, certificate, templateUrl className="flex justify-between items-center text-sm border-b border-card-outline pb-2 last:border-0" > {row.label} - {row.value} + {row.value}
))}
diff --git a/src/Pages/internify/intern/components/HomeProjectList.tsx b/src/Pages/internify/intern/components/HomeProjectList.tsx index 0373def..e8e2e7e 100644 --- a/src/Pages/internify/intern/components/HomeProjectList.tsx +++ b/src/Pages/internify/intern/components/HomeProjectList.tsx @@ -33,7 +33,7 @@ const HomeProjectList = () => { {projects.map((project) => (
navigate(`/intern/projects/${toSlug(project.slug)}`)} + onClick={() => navigate(`/internship/projects/${toSlug(project.slug)}`)} className="flex items-center gap-4 border border-card-outline rounded-lg px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors duration-150" >
navigate(`/intern/projects/${task.project_slug}/${task.slug}?type=${task.submission_type}`)} + onClick={() => navigate(`/internship/projects/${task.project_slug}/${task.slug}?type=${task.submission_type}`)} >
diff --git a/src/Pages/internify/intern/components/ProjectsCard.tsx b/src/Pages/internify/intern/components/ProjectsCard.tsx index aefac38..b7b15ea 100644 --- a/src/Pages/internify/intern/components/ProjectsCard.tsx +++ b/src/Pages/internify/intern/components/ProjectsCard.tsx @@ -12,7 +12,7 @@ const ProjectCard = (project: Project) => { return (
navigate(`/intern/projects/${project.slug}`)} + onClick={() => navigate(`/internship/projects/${project.slug}`)} className="w-[220px] min-h-[320px] flex flex-col bg-white rounded-xl overflow-hidden shadow-sm border border-gray-100 cursor-pointer hover:shadow-md transition-shadow duration-200" > {/* Thumbnail */} diff --git a/src/Pages/internify/intern/components/SubmissionStatusTable.tsx b/src/Pages/internify/intern/components/SubmissionStatusTable.tsx index 57d42e6..55522b8 100644 --- a/src/Pages/internify/intern/components/SubmissionStatusTable.tsx +++ b/src/Pages/internify/intern/components/SubmissionStatusTable.tsx @@ -15,13 +15,31 @@ interface SubmitStatusTableProps { function getTimeEarly(submittedAt: Date, deadline: Date): string { const diffMs = deadline.getTime() - submittedAt.getTime(); - if (diffMs <= 0) return `Submitted after deadline (${Math.abs(Math.floor(diffMs / (1000 * 60 * 60)))} hours late)`; const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); + + if (diffMs <= 0) { + const absHours = Math.abs(diffHours); + const days = Math.floor(absHours / 24); + const hours = absHours % 24; + const overdueParts = [] as string[]; + + if (days > 0) overdueParts.push(`${days} day${days > 1 ? "s" : ""}`); + if (hours > 0) overdueParts.push(`${hours} hour${hours > 1 ? "s" : ""}`); + + if (overdueParts.length === 0) { + return "Submitted after deadline (0 hours late)"; + } + + return `Submitted after deadline (${overdueParts.join(" ")} late)`; + } + + const parts = [] as string[]; const days = Math.floor(diffHours / 24); const hours = diffHours % 24; - const parts = []; + if (days > 0) parts.push(`${days} day${days > 1 ? "s" : ""}`); if (hours > 0) parts.push(`${hours} hour${hours > 1 ? "s" : ""}`); + return `Assignment was submitted ${parts.join(" ")} early`; } diff --git a/src/Pages/internify/intern/components/TaskForm-file.tsx b/src/Pages/internify/intern/components/TaskForm-file.tsx index a2d84aa..31ff80f 100644 --- a/src/Pages/internify/intern/components/TaskForm-file.tsx +++ b/src/Pages/internify/intern/components/TaskForm-file.tsx @@ -27,7 +27,14 @@ export default function TaskFormFile({ taskId, projectId, deadline, initialSubmi selectedFiles.forEach((file) => { const ext = file.name.split(".").pop()?.toLowerCase(); if (ext && allowedExtensions.includes(ext)) { - if (file.size <= 5 * 1024 * 1024) validFiles.push(file); + if (file.size <= 5 * 1024 * 1024) { + validFiles.push(file); + } else { + customToast.error( + "File terlalu besar!", + `File ${file.name} melebihi batas ukuran 5MB.` + ); + } } }); if (validFiles.length > 0) setFiles((prev) => [...prev, ...validFiles]); diff --git a/src/Pages/internify/mentor/BaseProjectsPage.tsx b/src/Pages/internify/mentor/BaseProjectsPage.tsx index b671553..cc39fe6 100644 --- a/src/Pages/internify/mentor/BaseProjectsPage.tsx +++ b/src/Pages/internify/mentor/BaseProjectsPage.tsx @@ -27,8 +27,8 @@ export default function MentorProjectsPage() { if (loading) { return (
- {[...Array(4)].map((_) => ( -
+ {[...Array(4)].map((_, index) => ( +
))}
diff --git a/src/Pages/internify/mentor/components/CertResultTable.tsx b/src/Pages/internify/mentor/components/CertResultTable.tsx index 89fd47e..cf916b9 100644 --- a/src/Pages/internify/mentor/components/CertResultTable.tsx +++ b/src/Pages/internify/mentor/components/CertResultTable.tsx @@ -132,13 +132,6 @@ export default function CertResultTable({ certificates, templateUrl }: Certifica : } -{/* - */}
)) diff --git a/src/Pages/internify/mentor/components/EligibleInternTable.tsx b/src/Pages/internify/mentor/components/EligibleInternTable.tsx index 686b414..85c373e 100644 --- a/src/Pages/internify/mentor/components/EligibleInternTable.tsx +++ b/src/Pages/internify/mentor/components/EligibleInternTable.tsx @@ -118,12 +118,14 @@ export default function EligibleInternTable({ id: intern.id_user, id_project: project.id, id_user: intern.id_user, - certificate_no: "No.2025/SPI3/CERT/yyddmm/X", + certificate_no: "No.2025/SPI3/CERT/MMDDYY/XXX", issued_at: new Date().toISOString().slice(0, 10), uuid: "HANYA SEBATAS PREVIEW", + intern_position: "[Posisi Intern]", user: { id: intern.id_user, full_name: intern.full_name, + intern_position: "[Posisi Intern]", email: intern.email, }, project: { @@ -185,12 +187,12 @@ export default function EligibleInternTable({ const blob = await generateCertificate( templateUrl, cert.user.full_name, - cert.project.project_name, + cert.intern_position, cert.certificate_no, duration, cert.uuid, ); - zip.file(`Sertifikat - ${cert.user.full_name}.png`, blob); + zip.file(`${cert.certificate_no}_${cert.user.full_name}_${cert.project.project_name}.png`, blob); }) ); })(), diff --git a/src/Pages/utils/Certificates.ts b/src/Pages/utils/Certificates.ts index 29d1e08..4118550 100644 --- a/src/Pages/utils/Certificates.ts +++ b/src/Pages/utils/Certificates.ts @@ -25,7 +25,7 @@ export const previewCertificate = async ( const blob = await generateCertificate( resolveFileUrl(templateUrl), cert.user.full_name, - cert.project.project_name, + cert.intern_position, cert.certificate_no, formatDateRange(cert.project.start_date, cert.project.end_date), cert.uuid, @@ -85,11 +85,11 @@ export const downloadCertificatePdf = async ( cert: Certificate, templateUrl: string, ): Promise => { - const fileName = `Sertifikat - ${cert.user.full_name} - ${cert.certificate_no}`; + const fileName = `${cert.certificate_no}_${cert.user.full_name}_${cert.project.project_name}`; const blob = await generateCertificate( resolveFileUrl(templateUrl), cert.user.full_name, - cert.project.project_name, + cert.intern_position, cert.certificate_no, formatDateRange(cert.project.start_date, cert.project.end_date), cert.uuid, @@ -125,11 +125,11 @@ export const downloadCertificateImage = async ( templateUrl: string, format: "png" | "jpg" = "png", ): Promise => { - const fileName = `Sertifikat - ${cert.user.full_name} - ${cert.certificate_no}`; + const fileName = `${cert.certificate_no}_${cert.user.full_name}_${cert.project.project_name}`; const blob = await generateCertificate( resolveFileUrl(templateUrl), cert.user.full_name, - cert.project.project_name, + cert.intern_position, cert.certificate_no, formatDateRange(cert.project.start_date, cert.project.end_date), cert.uuid, @@ -192,12 +192,12 @@ export const downloadAllCertificatesZip = async ( const blob = await generateCertificate( resolveFileUrl(templateUrl), cert.user.full_name, - cert.project.project_name, + cert.intern_position, cert.certificate_no, formatDateRange(cert.project.start_date, cert.project.end_date), cert.uuid, ); - zip.file(`Sertifikat - ${cert.user.full_name}.png`, blob); + zip.file(`${cert.certificate_no}_${projectName}_${cert.user.full_name}.png`, blob); onProgress?.(i + 1, total); }), ); @@ -205,7 +205,7 @@ export const downloadAllCertificatesZip = async ( const zipBlob = await zip.generateAsync({ type: "blob" }); const a = document.createElement("a"); a.href = URL.createObjectURL(zipBlob); - a.download = `Sertifikat - ${projectName}.zip`; + a.download = `Sertifikat_${projectName}_${new Date().toISOString().slice(0, 10)}.zip`; a.click(); URL.revokeObjectURL(a.href); }; diff --git a/src/Pages/utils/SertificateGenerator.ts b/src/Pages/utils/SertificateGenerator.ts index bf307fb..b6dbc20 100644 --- a/src/Pages/utils/SertificateGenerator.ts +++ b/src/Pages/utils/SertificateGenerator.ts @@ -23,13 +23,13 @@ async function ensureFontsLoaded(): Promise { // LAYOUT DEFAULT SERTIFIKAT YANG AKAN DIGENERATE const LAYOUT = { - internName: { x: 0.54, y: 0.5, size: 130, font: "Great Vibes", color: "#800000" }, + internName: { x: 0.54, y: 0.47, 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" }, + userposition: { x: 0.855, y: 0.557, size: 46, font: "Grenze", color: "#800000" }, + duration: { x: 0.7, y: 0.615, size: 48, 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 }, + qr: { x: 0.772, y: 0.715, size: 0.103 }, }; // HELPER: kurangi ukuran font 25% jika nama melebihi 22 karakter (termasuk spasi) @@ -48,7 +48,7 @@ function fitNameText(name: string, baseSize: number): number { export async function generateCertificate( templateUrl: string, internName: string, - projectName: string, + userposition: string, certificateNo: string, ProjectDuration: string, verifyUrl: string, @@ -76,7 +76,7 @@ export async function generateCertificate( text(LAYOUT.internName, internName); text(LAYOUT.internNameSmall, internName); - text(LAYOUT.projectName, projectName); + text(LAYOUT.userposition, userposition); text(LAYOUT.duration, ProjectDuration); text(LAYOUT.certNo, certificateNo); text(LAYOUT.certNoSmall, certificateNo); diff --git a/src/types/certificate.types.ts b/src/types/certificate.types.ts index ff0bc8e..91ea3d8 100644 --- a/src/types/certificate.types.ts +++ b/src/types/certificate.types.ts @@ -2,6 +2,7 @@ export interface CertificateUser { id: number; full_name: string; email: string; + intern_position: string; } export interface CertificateProject { @@ -19,6 +20,7 @@ export interface Certificate { id_user: number; certificate_no: string; issued_at: string; + intern_position: string; uuid: string; user: CertificateUser; project: CertificateProject; @@ -28,6 +30,7 @@ export interface Certificate { export interface CertificateVerification { uuid: string; intern_name: string; + intern_position: string; project_name: string; created_at: string; } @@ -49,5 +52,6 @@ export interface CertificateVerifyResult { uuid: string; intern_name: string; project: projectbysertif; + intern_position: string; created_at: string; } \ No newline at end of file