Skip to content

Add Dynamic Certificate Templates #3073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 12, 2025
Merged

Add Dynamic Certificate Templates #3073

merged 18 commits into from
Jun 12, 2025

Conversation

VNOsST
Copy link
Collaborator

@VNOsST VNOsST commented Jun 10, 2025

Summary by CodeRabbit

  • New Features

    • Introduced support for customizable certificate templates ("original", "modern", "elegant") for courses.
    • Added certificate template selection and live preview in the course creation and editing form.
    • Enabled dynamic PDF certificate generation and viewing with multiple template designs.
    • Added certificate preview and download options (PDF and PNG) for users.
  • Improvements

    • Enhanced localization with new strings for certificate template selection and preview in both English and Vietnamese.
    • Updated course and certificate management interfaces to support certificate template features.
  • Bug Fixes

    • Corrected a Vietnamese translation typo in the course deletion confirmation dialog.
  • Database

    • Added a new enum and column for certificate templates in the course database schema.

VNOsST and others added 3 commits June 10, 2025 21:08
Create 2 new certificate templates
Modify pdf viewer to only show pagination when there are more than 1 page
Refactor Certificate Viewer to view the react-pdf component instead of using a seperate file
Add certTemplate column and its logic
@VNOsST VNOsST self-assigned this Jun 10, 2025
Copy link

graphite-app bot commented Jun 10, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copy link
Contributor

coderabbitai bot commented Jun 10, 2025

Walkthrough

This update introduces support for multiple certificate templates in the platform. It adds a new database enum and schema field for selecting certificate designs, implements three distinct PDF certificate templates, updates the certificate generation and display logic to use the selected template, and enhances the UI with template selection and live preview features. Localization files and types are expanded accordingly.

Changes

Files / Grouped Files Change Summary
apps/db/supabase/migrations/20250612040712_add_certificate_templates.sql, packages/types/src/supabase.ts Add certificate_templates enum ('original', 'modern', 'elegant'); add cert_template field to workspace_courses table and types.
apps/upskii/messages/en.json, apps/upskii/messages/vi.json, apps/web/messages/en.json, apps/web/messages/vi.json Add new localization strings for certificate templates and preview controls; minor correction in Vietnamese translation.
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx Update certificate PDF generation to select and render the template based on course data.
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx, .../modern-certificate.tsx Add new React PDF components for "elegant" and "modern" certificate templates.
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx Refactor: rename and update font registration for the original certificate template.
apps/upskii/src/app/api/v1/certificates/[certId]/generate/types.ts Remove obsolete certificate data type definition.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx Refactor: generate certificate PDF server-side using selected template and display with new viewer.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/certificate-page.tsx Remove old React certificate display component.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx Update import for PDF download button to use shared UI component.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx, .../courses/page.tsx Pass enableCerts prop to enable certificate features in course table and form.
apps/upskii/src/lib/certificate-helper.ts Extend certificate details to include template; update query and types.
apps/upskii/src/lib/font-register-pdf.ts Add helper to register Roboto fonts for PDF rendering.
packages/ui/package.json Add export mapping for certificate types.
packages/ui/src/components/ui/custom/education/certificates/types.ts Add types for certificate details, data, and props.
packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx Add new component to display and download certificate PDFs and PNGs.
packages/ui/src/components/ui/custom/education/certificates/download-button-pdf.tsx Update to support direct PDF data URL downloads.
packages/ui/src/components/ui/custom/education/courses/course-form.tsx Add certificate template selection and live preview to course form; new enableCerts prop.
packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx Add enableCerts prop for row actions; pass to course form.
packages/ui/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx Remove fixed aspect ratio; hide pagination if only one page.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CourseForm
    participant DB
    participant CertificatePage
    participant PDFTemplates
    participant CertificateViewer

    User->>CourseForm: Open course form (with enableCerts)
    CourseForm->>DB: Fetch certificate template options
    User->>CourseForm: Select certificate template
    CourseForm->>DB: Save course with cert_template

    User->>CertificatePage: View certificate
    CertificatePage->>DB: Fetch certificate details (incl. cert_template)
    CertificatePage->>PDFTemplates: Render PDF with selected template
    PDFTemplates-->>CertificatePage: Return PDF buffer
    CertificatePage->>CertificateViewer: Display PDF (data URL)
    User->>CertificateViewer: Download as PDF/PNG
Loading

Possibly related PRs

Suggested labels

enhancement

Poem

In fields of code, three templates bloom,
"Modern," "Elegant," and "Original" loom.
Choose your style, preview with glee,
Download your proof, as PDF or PNG.
With fonts and colors, certificates shine—
A rabbit's hop through features,
All by design! 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:201:49)

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:201:49)

packages/ui/src/components/ui/custom/education/certificates/types.ts

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:201:49)

  • 2 others
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Commit Unit Tests in branch feat/certificate-template
  • Post Copyable Unit Tests in Comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Jun 10, 2025

Codecov Report

Attention: Patch coverage is 0.33632% with 889 lines in your changes missing coverage. Please review.

Project coverage is 0.83%. Comparing base (c23f322) to head (23e385d).
Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
.../v1/certificates/templates/elegant-certificate.tsx 0.00% 269 Missing and 1 partial ⚠️
...i/v1/certificates/templates/modern-certificate.tsx 0.00% 248 Missing and 1 partial ⚠️
...onents/ui/custom/education/courses/course-form.tsx 0.00% 136 Missing ⚠️
...stom/education/certificates/certificate-viewer.tsx 1.23% 80 Missing ⚠️
...oard)/[wsId]/certificates/[certificateId]/page.tsx 0.00% 51 Missing and 1 partial ⚠️
.../custom/education/modules/resources/pdf-viewer.tsx 0.00% 31 Missing ⚠️
...pp/api/v1/certificates/[certId]/generate/route.tsx 0.00% 28 Missing and 1 partial ⚠️
apps/upskii/src/lib/font-register-pdf.ts 5.55% 17 Missing ⚠️
...tom/education/certificates/download-button-pdf.tsx 0.00% 12 Missing ⚠️
...pp/[locale]/(dashboard)/[wsId]/courses/columns.tsx 0.00% 3 Missing ⚠️
... and 6 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3073      +/-   ##
==========================================
- Coverage    0.84%    0.83%   -0.01%     
==========================================
  Files        2488     2491       +3     
  Lines      306773   307328     +555     
  Branches     2990     2993       +3     
==========================================
+ Hits         2577     2581       +4     
- Misses     302068   302620     +552     
+ Partials     2128     2127       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@VNOsST VNOsST marked this pull request as ready for review June 11, 2025 07:39
@VNOsST VNOsST requested a review from vhpx June 11, 2025 07:39
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🔭 Outside diff range comments (2)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

12-15: 🛠️ Refactor suggestion

params should not be a Promise

Next Route Handlers pass a plain object, not a Promise.
Typing it as Promise<…> forces callers to await an ordinary object and breaks type-safety.

-{ params }: { params: Promise<{ certId: string }> }
+{ params }: { params: { certId: string } }
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (1)

13-18: ⚠️ Potential issue

params should not be typed as a Promise

params is synchronously supplied by the Next-JS App-Router. Declaring it as Promise<…> (and then await-ing) works at runtime only because await on a non-thenable is a no-op, but it’s misleading and breaks type-safety (TS will happily let you await any object).

-interface PageProps {
-  params: Promise<{
-    certificateId: string;
-    wsId: string;
-  }>;
-}
+interface PageProps {
+  params: {
+    certificateId: string;
+    wsId: string;
+  };
+}
♻️ Duplicate comments (1)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)

14-22: Same font-registration pitfalls as in Modern template

See previous comment – duplicate registration and non-numeric weights.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 14-22: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L14-L22
Added lines #L14 - L22 were not covered by tests

🧹 Nitpick comments (15)
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx (1)

14-14: Verify alias configuration and add test coverage for DownloadButtonPDF import
The absolute import is correct given the shared UI refactor, but please confirm your tsconfig.json path aliases include @tuturuuu/ui/custom/education/certificates/download-button-pdf. Static analysis flags this line as untested—add a unit or integration test to assert that the PDF download button renders and invokes the correct download logic.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 14-14: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx#L14
Added line #L14 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx (1)

60-60: Boolean prop can be expressed more idiomatically

enableCerts is a boolean that defaults to false; to pass true you can omit the explicit value and just write enableCerts.
No functional difference, but keeps the JSX a bit cleaner.

-          form={<CourseForm wsId={wsId} enableCerts={true} />}
+          form={<CourseForm wsId={wsId} enableCerts />}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 60-60: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx#L60
Added line #L60 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx (1)

94-96: Same nit ‒ shorthand boolean & keep config central

  1. JSX shorthand applies here too:
-      <WorkspaceCourseRowActions row={row} enableCerts={true} />
+      <WorkspaceCourseRowActions row={row} enableCerts />
  1. The feature flag is now duplicated in two places (page.tsx and this file).
    Consider lifting it to a single config/constant (e.g. CERTS_ENABLED) so it cannot drift.
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 94-96: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx#L94-L96
Added lines #L94 - L96 were not covered by tests

apps/web/messages/vi.json (1)

2008-2010: Translation accuracy: refine phrasing for certificate template keys.

The Vietnamese strings for "certificate_template" and "select_certificate_template" may read unnaturally as "Thiết kế bằng chứng chỉ" and "Chọn một mẫu thiết kế chứng chỉ". Consider using more idiomatic translations like "Mẫu chứng chỉ" and "Chọn mẫu chứng chỉ" (or "Chọn mẫu thiết kế chứng chỉ") to clearly convey “Certificate Template” and “Select a certificate template.”

apps/db/supabase/migrations/20250610140712_new_migration.sql (1)

1-1: Ensure enum creation is idempotent
To prevent failures if this migration is ever re-run, consider adding IF NOT EXISTS to the enum creation.

Example adjustment:

- create type "public"."certificate_templates" as enum ('original', 'modern', 'elegant');
+ create type if not exists "public"."certificate_templates" as enum ('original', 'modern', 'elegant');
packages/ui/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx (1)

50-52: Using height: 100 % relies on parent layout

The container is now h-full; if any ancestor lacks an explicit height the viewer collapses.
Consider either:

• keeping the previous intrinsic aspect-ratio approach, or
• using min-h-[calc(…)] / a fixed max-height, or
• documenting the height contract in a comment.

apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

55-69: Template selection is fine but misses exhaustive checking

Because certificate_templates is an enum with three variants, a switch without a never exhaustiveness check will silently default for future additions.

switch (certTemplate) {
  case 'elegant':
    
  case 'modern':
    
  case 'original':
    
  /* no default */
}

This causes a compile-time error when the enum grows — safer long-term.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 50-69: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L69
Added lines #L50 - L69 were not covered by tests

packages/ui/src/components/ui/custom/education/certificates/download-button-pdf.tsx (2)

26-36: Good UX – avoids redundant network hit

Using the data-URL when available removes an extra request.
Minor: add rel="noopener" and set style="display:none" on the temporary anchor to avoid visible layout shifts.

-const link = document.createElement('a');
+const link = document.createElement('a');
+link.style.display = 'none';
+link.rel = 'noopener';

70-70: Missing dependency warning suppressed

handleDownload closes over variant and className, but they’re not in the dependency array.
React’s ESLint rule will warn. Include them or mark as stable.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (1)

75-76: Consider streaming instead of base-64 inlining for large PDFs

Serialising the entire PDF to a base-64 data-URL inflates payload size (~33%) and may blow past 4 MB HTML limits on slower connections. If SEO/preview is not required, returning a Response with the correct Content-Type (or a presigned URL) and letting the CertificateViewer fetch it lazily will be far more memory-efficient.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 40-76: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L40-L76
Added lines #L40 - L76 were not covered by tests

apps/upskii/src/lib/certificate-helper.ts (1)

65-67: Replace console.log with structured logging / error reporting

console.log will be lost in production (depending on log sinks). Use your project’s logger or at least console.error so that the message is surfaced by APM tools.

-    console.log(error);
+    console.error('[getCertificateDetails] Supabase error:', error);
packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx (1)

195-202: Hard-coded English strings bypass localisation

'Hide' / 'Show' should come from next-intl, otherwise the form becomes partially untranslated.

-                  {showPreview ? 'Hide' : 'Show'} Certificate Preview
+                  {showPreview
+                    ? t('hide_certificate_preview')
+                    : t('show_certificate_preview')}
packages/ui/src/components/ui/custom/education/courses/course-form.tsx (2)

49-55: Use z.nativeEnum for cleaner enum validation

Constructing a tuple spread is brittle and loses exhaustiveness checks. Zod supports enums directly:

-cert_template: z
-  .enum([...certificateTemplateOptions] as [
-    CertificateTemplate,
-    ...CertificateTemplate[],
-  ])
-  .default('original'),
+cert_template: z
+  .nativeEnum(Constants.public.Enums.certificate_templates)
+  .default('original'),

This keeps the schema in sync even if new templates are added.


221-226: Missing priority or loading="lazy" on large preview image

The preview is 400 × 300 px. Mark it as loading="lazy" to avoid unnecessary bandwidth on first paint.

-                  width={400}
-                  height={300}
+                  width={400}
+                  height={300}
+                  loading="lazy"
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)

7-10: Consider typing completionDate as Date instead of string

Using Date (or a branded ISO-string type) prevents accidental locale-formatted strings leaking into storage and makes date-logic safer.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 413d338 and 78e4b7c.

⛔ Files ignored due to path filters (6)
  • apps/upskii/public/media/certificate-previews/en/elegant-preview.png is excluded by !**/*.png
  • apps/upskii/public/media/certificate-previews/en/modern-preview.png is excluded by !**/*.png
  • apps/upskii/public/media/certificate-previews/en/original-preview.png is excluded by !**/*.png
  • apps/upskii/public/media/certificate-previews/vi/elegant-preview.png is excluded by !**/*.png
  • apps/upskii/public/media/certificate-previews/vi/modern-preview.png is excluded by !**/*.png
  • apps/upskii/public/media/certificate-previews/vi/original-preview.png is excluded by !**/*.png
📒 Files selected for processing (24)
  • apps/db/supabase/migrations/20250610140712_new_migration.sql (1 hunks)
  • apps/upskii/messages/en.json (1 hunks)
  • apps/upskii/messages/vi.json (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (3 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/certificate-page.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (2 hunks)
  • apps/upskii/src/app/api/v1/certificates/[certId]/generate/types.ts (0 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (2 hunks)
  • apps/upskii/src/lib/certificate-helper.ts (4 hunks)
  • apps/web/messages/en.json (1 hunks)
  • apps/web/messages/vi.json (1 hunks)
  • packages/types/src/supabase.ts (20 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/certificates/download-button-pdf.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/education/certificates/types.ts (1 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-form.tsx (3 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx (3 hunks)
💤 Files with no reviewable changes (2)
  • apps/upskii/src/app/api/v1/certificates/[certId]/generate/types.ts
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/certificate-page.tsx
🧰 Additional context used
🧬 Code Graph Analysis (7)
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx (1)
packages/ui/src/components/ui/custom/education/courses/course-form.tsx (1)
  • CourseForm (57-234)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (4)
packages/types/src/supabase.ts (1)
  • Database (9-7840)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)
  • ElegantCertificateDocument (286-315)
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1)
  • ModernCertificateDocument (284-300)
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (1)
  • OGCertificateDocument (170-186)
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx (1)
packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (1)
  • WorkspaceCourseRowActions (26-137)
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (1)
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)
  • CertificateData (12-20)
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (7)
apps/upskii/src/lib/certificate-helper.ts (1)
  • getCertificateDetails (31-82)
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)
  • CertificateData (12-20)
packages/types/src/supabase.ts (1)
  • Database (9-7840)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)
  • ElegantCertificateDocument (286-315)
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1)
  • ModernCertificateDocument (284-300)
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (1)
  • OGCertificateDocument (170-186)
packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx (1)
  • CertificateViewer (25-98)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (2)
apps/web/src/constants/common.ts (1)
  • BASE_URL (11-13)
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)
  • CertificateData (12-20)
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (2)
apps/web/src/constants/common.ts (1)
  • BASE_URL (11-13)
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)
  • CertificateData (12-20)
🪛 GitHub Check: codecov/patch
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx

[warning] 14-14: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/page.tsx#L14
Added line #L14 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx

[warning] 60-60: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx#L60
Added line #L60 was not covered by tests

apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx

[warning] 2-3: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 7-8: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L7-L8
Added lines #L7 - L8 were not covered by tests


[warning] 50-69: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L69
Added lines #L50 - L69 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx

[warning] 94-96: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx#L94-L96
Added lines #L94 - L96 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx

[warning] 2-3: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 5-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L5
Added line #L5 was not covered by tests


[warning] 7-10: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L7-L10
Added lines #L7 - L10 were not covered by tests


[warning] 23-23: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L23
Added line #L23 was not covered by tests


[warning] 34-34: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L34
Added line #L34 was not covered by tests


[warning] 40-76: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L40-L76
Added lines #L40 - L76 were not covered by tests


[warning] 78-82: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L78-L82
Added lines #L78 - L82 were not covered by tests


[warning] 85-85: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L85
Added line #L85 was not covered by tests

apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx

[warning] 2-2: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L2
Added line #L2 was not covered by tests


[warning] 14-22: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L14-L22
Added lines #L14 - L22 were not covered by tests


[warning] 25-25: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L25
Added line #L25 was not covered by tests


[warning] 27-39: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L27-L39
Added lines #L27 - L39 were not covered by tests


[warning] 42-73: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L42-L73
Added lines #L42 - L73 were not covered by tests


[warning] 75-96: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L75-L96
Added lines #L75 - L96 were not covered by tests


[warning] 98-106: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L98-L106
Added lines #L98 - L106 were not covered by tests


[warning] 109-129: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L109-L129
Added lines #L109 - L129 were not covered by tests


[warning] 132-139: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L132-L139
Added lines #L132 - L139 were not covered by tests


[warning] 141-166: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L141-L166
Added lines #L141 - L166 were not covered by tests


[warning] 168-198: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L168-L198
Added lines #L168 - L198 were not covered by tests


[warning] 201-234: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L201-L234
Added lines #L201 - L234 were not covered by tests


[warning] 237-243: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L237-L243
Added lines #L237 - L243 were not covered by tests


[warning] 246-250: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L246-L250
Added lines #L246 - L250 were not covered by tests


[warning] 253-261: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L253-L261
Added lines #L253 - L261 were not covered by tests


[warning] 263-268: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L263-L268
Added lines #L263 - L268 were not covered by tests

🪛 Biome (1.9.4)
packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx

[error] 36-36: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (14)
apps/upskii/messages/en.json (2)

3542-3543: New localization keys follow conventions and are syntactically correct.

The certificate_template and select_certificate_template entries are named consistently with existing keys and correctly punctuated.


3536-3552: Verify translations exist in other locales.

Ensure that these new keys are also added to all other language JSON files (e.g., vi.json) so that non-English users see the corresponding labels.

apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (2)

11-11: Centralize typing for certificate data
Importing CertificateData from the shared UI package ensures consistent type definitions across all certificate templates and UI components. Please confirm that your tsconfig.json is configured to resolve the @tuturuuu/ui/custom/education/certificates/types path correctly.


170-174:

✅ Verification successful

Disambiguate component name to OGCertificateDocument
Renaming the component to OGCertificateDocument aligns it with the file name and differentiates it from other certificate variants. Verify that all imports and usages in the PDF generation route and certificate viewer have been updated accordingly.

Run this script to catch any lingering references or stale local imports:


🏁 Script executed:

#!/bin/bash
# Check for old component imports or relative types
rg -n "CertificateDocument" -n "from '\\./types'" apps/upskii/src/app/api/v1/certificates/templates

Length of output: 598


🏁 Script executed:

#!/bin/bash
# Search for any lingering imports of the old 'CertificateDocument'
rg -n "import.*CertificateDocument" -g "*.tsx" apps/upskii/src

# Search for any direct references to 'CertificateDocument' outside of template definitions
rg -n "CertificateDocument" apps/upskii/src | grep -v "OGCertificateDocument" | grep -v "ElegantCertificateDocument" | grep -v "ModernCertificateDocument"

# Verify usages of the new 'OGCertificateDocument'
rg -n "OGCertificateDocument" apps/upskii/src

# Ensure 'CertificateData' type is being imported correctly
rg -n "CertificateData" apps/upskii/src

Length of output: 4412


All references updated to OGCertificateDocument

  • No lingering CertificateDocument imports or usages found in apps/upskii/src.
  • OGCertificateDocument is correctly imported and used in:
    • apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx
    • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx
  • CertificateData type imports remain consistent across all templates.

No further action required.

apps/web/messages/en.json (1)

1997-1999: New localization keys for certificate templates
The entries "certificate_template": "Certificate Design" and "select_certificate_template": "Choose a certificate template" have been added under ws-courses. They align with the new certificate template selection feature. Ensure these keys are also present in all other locale files for full translation coverage.

packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (2)

23-29: Prop wiring looks good

enableCerts is optional, defaults to false, and is forwarded to CourseForm.
No issues spotted.


110-111: Confirm CourseForm supports the new prop

Please verify that CourseForm accepts enableCerts?: boolean or the prop will be silently dropped.

apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

50-52: Potential camelCase / snake_case mismatch

certData.certTemplate assumes the helper returns camelCase.
The DB column name is cert_template; if the helper does not rename it this line will be undefined, falling through to the default template every time.

const certTemplate =
  (certData.certTemplate ?? certData.cert_template) as Database['public']['Enums']['certificate_templates'];

Double-check getCertificateDetails.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 50-69: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L69
Added lines #L50 - L69 were not covered by tests

apps/upskii/src/lib/certificate-helper.ts (1)

74-81: cert_template might be null – add a fallback

If existing rows pre-date the migration the column can be NULL. Returning null will later hit a switch that expects concrete strings. Either coerce to 'original' or validate here.

-    certTemplate: certificate.workspace_courses.cert_template,
+    certTemplate:
+      certificate.workspace_courses.cert_template ?? 'original',
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1)

36-42: gap style may break on older @react-pdf versions

gap was introduced only in v3.1+. If the runtime still uses ≤3.0, PDFs will fail to compile with Unknown prop. Verify the deployed version or replace with margin* utilities.

apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)

132-139: gap compatibility check

Same caveat as modern template – ensure renderer ≥3.1 or replace.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 132-139: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L132-L139
Added lines #L132 - L139 were not covered by tests

packages/types/src/supabase.ts (3)

5043-5065: New cert_template column is non-nullable – double-check all inserts

The Row type rightly models the DB default and makes cert_template mandatory, while Insert/Update keep it optional.
Any code that previously inserted into workspace_courses without specifying this field will now fail type-checking (and will also violate the DB constraint if you bypass the types). Make sure every supabase.from('workspace_courses').insert(...) path either:

  1. Omits the property entirely (and lets Postgres default to 'original'), or
  2. Supplies one of the enum values explicitly.

7805-7808: Enum wiring is consistent – good!

certificate_templates is exposed in Enums and in Constants.public.Enums, keeping generated types and helper arrays in sync. No action required.

Also applies to: 7960-7961


7335-7362:

✅ Verification successful

Function signatures changed – audit call-sites

count_search_users, create_ai_chat, generate_cross_app_token, etc., now accept extended parameter objects (additional optional filters / session data). Any stale call-sites will compile-fail or, worse, pass incorrect positional arguments at runtime if any is involved.


🏁 Script executed:

#!/bin/bash
# Scan for outdated positional calls to a few updated functions.
rg --line-number -e 'create_ai_chat\(' -e 'generate_cross_app_token\(' -e 'count_search_users\(' | head

Length of output: 1069


No outdated RPC call-sites detected; TS unions preserve backward compatibility

I ran a search for positional calls to create_ai_chat(...), generate_cross_app_token(...), and count_search_users(...) in the codebase and only found their SQL definitions in migration files—no JS/TS call-sites using positional arguments. Since the TypeScript definitions now use unioned object shapes (with optional extras), existing calls passing just the required fields still satisfy one of the unions and will continue to compile and run correctly.

@VNOsST VNOsST added the upskii label Jun 11, 2025
@VNOsST VNOsST removed the request for review from vhpx June 11, 2025 07:57
@vhpx vhpx marked this pull request as draft June 11, 2025 08:57
@vhpx vhpx changed the title Dynamic Certificate Templates Add Dynamic Certificate Templates Jun 11, 2025
@VNOsST VNOsST marked this pull request as ready for review June 11, 2025 12:43
@VNOsST VNOsST requested a review from vhpx June 11, 2025 12:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

14-19: ⚠️ Potential issue

Incorrect params typing – drops type-safety and misleads readers

Next.js passes { params: { certId: string } } synchronously.
Typing it as Promise<...> and await-ing it works at runtime (a non-promise is awaited as-is) but fools TypeScript and future readers.

-export async function POST(
-  req: NextRequest,
-  { params }: { params: Promise<{ certId: string }> }
+export async function POST(
+  req: NextRequest,
+  { params }: { params: { certId: string } }
 )

This restores the correct signature and eliminates the unnecessary await params.

🧹 Nitpick comments (2)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

55-74: Reduce repetition with a template map

The switch duplicates renderToStream calls. A map keeps this O(1) to extend and removes the default fall-through.

-let stream;
-switch (certTemplate) {
-  case 'elegant':
-    stream = await renderToStream(<ElegantCertificateDocument data={data} />);
-    break;
-  case 'modern':
-    stream = await renderToStream(<ModernCertificateDocument data={data} />);
-    break;
-  case 'original':
-    stream = await renderToStream(<OGCertificateDocument data={data} />);
-    break;
-  default:
-    console.log('Unhandled template:', certTemplate);
-    console.log('Using original template as fallback');
-    stream = await renderToStream(<OGCertificateDocument data={data} />);
-    break;
-}
+const templateMap = {
+  elegant: ElegantCertificateDocument,
+  modern: ModernCertificateDocument,
+  original: OGCertificateDocument,
+} as const;
+
+const Component = templateMap[certTemplate] ?? OGCertificateDocument;
+const stream = await renderToStream(<Component data={data} />);

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 50-74: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L74
Added lines #L50 - L74 were not covered by tests

</details>

</details>

</blockquote></details>
<details>
<summary>apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)</summary><blockquote>

`147-153`: **Potential runtime issue – mixed numeric & string padding**

`paddingBottom: '4pt'` and `paddingHorizontal: '16pt'` are accepted, but keeping units consistent (numbers) avoids accidental string coercion and makes math easier later.

```diff
-  paddingBottom: '4pt',
-  paddingHorizontal: '16pt',
+  paddingBottom: 4,
+  paddingHorizontal: 16,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee6d19 and fefcb7e.

📒 Files selected for processing (13)
  • apps/upskii/messages/en.json (1 hunks)
  • apps/upskii/messages/vi.json (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (2 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1 hunks)
  • apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (2 hunks)
  • apps/upskii/src/lib/font-register-pdf.ts (1 hunks)
  • apps/web/messages/en.json (1 hunks)
  • apps/web/messages/vi.json (1 hunks)
  • packages/types/src/supabase.ts (7 hunks)
  • packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-form.tsx (3 hunks)
  • packages/ui/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/upskii/src/lib/font-register-pdf.ts
  • apps/web/messages/vi.json
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/ui/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx
  • apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx
  • packages/types/src/supabase.ts
  • apps/upskii/messages/en.json
  • packages/ui/src/components/ui/custom/education/courses/course-form.tsx
  • packages/ui/src/components/ui/custom/education/certificates/certificate-viewer.tsx
  • apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx
  • apps/web/messages/en.json
  • apps/upskii/messages/vi.json
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (4)
packages/types/src/supabase.ts (1)
  • Database (9-7867)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)
  • ElegantCertificateDocument (283-312)
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1)
  • ModernCertificateDocument (275-291)
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (1)
  • OGCertificateDocument (161-177)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (3)
apps/upskii/src/lib/font-register-pdf.ts (1)
  • registerRobotoFonts (7-21)
packages/ui/src/components/ui/custom/education/certificates/types.ts (1)
  • CertificateData (12-20)
apps/web/src/constants/common.ts (1)
  • BASE_URL (11-13)
🪛 GitHub Check: codecov/patch
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx

[warning] 2-3: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 7-8: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L7-L8
Added lines #L7 - L8 were not covered by tests


[warning] 50-74: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L74
Added lines #L50 - L74 were not covered by tests

apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx

[warning] 2-3: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 13-13: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L13
Added line #L13 was not covered by tests


[warning] 16-16: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L16
Added line #L16 was not covered by tests


[warning] 18-32: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L18-L32
Added lines #L18 - L32 were not covered by tests


[warning] 35-66: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L35-L66
Added lines #L35 - L66 were not covered by tests


[warning] 68-89: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L68-L89
Added lines #L68 - L89 were not covered by tests


[warning] 91-99: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L91-L99
Added lines #L91 - L99 were not covered by tests


[warning] 102-122: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L102-L122
Added lines #L102 - L122 were not covered by tests


[warning] 125-132: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L125-L132
Added lines #L125 - L132 were not covered by tests


[warning] 134-161: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L134-L161
Added lines #L134 - L161 were not covered by tests


[warning] 163-195: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L163-L195
Added lines #L163 - L195 were not covered by tests


[warning] 198-231: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L198-L231
Added lines #L198 - L231 were not covered by tests


[warning] 234-240: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L234-L240
Added lines #L234 - L240 were not covered by tests


[warning] 243-247: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L243-L247
Added lines #L243 - L247 were not covered by tests


[warning] 250-258: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L250-L258
Added lines #L250 - L258 were not covered by tests


[warning] 260-265: apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx#L260-L265
Added lines #L260 - L265 were not covered by tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/db/supabase/migrations/20250612040712_add_certificate_templates.sql (2)

1-1: Use IF NOT EXISTS for enum creation
To avoid migration failures if the enum already exists (e.g., on repeat runs), prefix with IF NOT EXISTS.

Apply:

- create type "public"."certificate_templates" as enum ('original', 'modern', 'elegant');
+ create type if not exists "public"."certificate_templates" as enum ('original', 'modern', 'elegant');

3-3: Verify RLS policies and consider indexing
Adding a non-nullable column under RLS may block inserts/updates if policies aren’t updated; please confirm your policies allow operations on cert_template. Additionally, if you’ll frequently filter by this field in queries, an index could improve performance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eced612 and 64fb84b.

📒 Files selected for processing (1)
  • apps/db/supabase/migrations/20250612040712_add_certificate_templates.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Prettier Check (23)
  • GitHub Check: Verify generated types

Copy link
Member

@vhpx vhpx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks @VNOsST.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (1)

50-51: ⚠️ Potential issue

Guard against null / undefined enum values

certData.certTemplate can still be null if the DB column is nullable.
Cast-away typing (CertificateTemplate) hides that possibility and the switch
below would immediately hit the default branch. Add a defensive fallback
before the switch.

-const certTemplate: CertificateTemplate = certData.certTemplate;
+const certTemplate: CertificateTemplate =
+  (certData.certTemplate ?? 'original') as CertificateTemplate;
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 50-73: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L73
Added lines #L50 - L73 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (1)

58-71: Explicitly handle 'original' case instead of lumping it into default
Having a dedicated case 'original': improves readability and guarantees the
compiler warns on future enum additions. This was flagged earlier and still
applies.

🧹 Nitpick comments (3)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (2)

52-52: Missing type for stream obscures API contract

let stream; is implicitly any. Declare an explicit type
(ReadableStream<Uint8Array> or the concrete type returned by
renderToStream) to preserve type-safety and future refactor confidence.

-let stream;
+let stream: ReadableStream<Uint8Array>;

69-71: Use console.error (or a logger) for unexpected templates

Unknown templates represent an anomaly; surface them at an error level rather
than console.log to aid alerting / monitoring.

-        console.log('Unhandled template:', certTemplate);
-        console.log('Using original template as fallback');
+        console.error(`Unhandled certificate template: ${certTemplate}`);
+        console.info('Falling back to original template');
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (1)

73-75: Base64-encoding large PDFs causes avoidable memory pressure

pdfBuffer.toString('base64') doubles memory usage for each request and pushes
the entire file through the HTML. Consider:

  1. Streaming the PDF to the client (especially if you already expose the /generate endpoint), or
  2. Storing the buffer in object storage and serving a signed URL.

Both approaches cut server memory spikes and reduce page payload.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 40-75: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L40-L75
Added lines #L40 - L75 were not covered by tests

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64fb84b and 23e385d.

📒 Files selected for processing (5)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx (3 hunks)
  • apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (2 hunks)
  • apps/upskii/src/lib/certificate-helper.ts (5 hunks)
  • packages/types/src/db.ts (1 hunks)
  • packages/ui/src/components/ui/custom/education/certificates/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/types/src/db.ts
  • packages/ui/src/components/ui/custom/education/certificates/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/upskii/src/lib/certificate-helper.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx (4)
packages/types/src/db.ts (1)
  • CertificateTemplate (143-144)
apps/upskii/src/app/api/v1/certificates/templates/elegant-certificate.tsx (1)
  • ElegantCertificateDocument (283-312)
apps/upskii/src/app/api/v1/certificates/templates/modern-certificate.tsx (1)
  • ModernCertificateDocument (275-291)
apps/upskii/src/app/api/v1/certificates/templates/og-certificate.tsx (1)
  • OGCertificateDocument (161-177)
🪛 GitHub Check: codecov/patch
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx

[warning] 2-3: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 5-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L5
Added line #L5 was not covered by tests


[warning] 7-10: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L7-L10
Added lines #L7 - L10 were not covered by tests


[warning] 23-23: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L23
Added line #L23 was not covered by tests


[warning] 34-34: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L34
Added line #L34 was not covered by tests


[warning] 40-75: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L40-L75
Added lines #L40 - L75 were not covered by tests


[warning] 77-81: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L77-L81
Added lines #L77 - L81 were not covered by tests


[warning] 84-84: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/certificates/[certificateId]/page.tsx#L84
Added line #L84 was not covered by tests

apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx

[warning] 2-3: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L2-L3
Added lines #L2 - L3 were not covered by tests


[warning] 7-8: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L7-L8
Added lines #L7 - L8 were not covered by tests


[warning] 50-73: apps/upskii/src/app/api/v1/certificates/[certId]/generate/route.tsx#L50-L73
Added lines #L50 - L73 were not covered by tests

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Deploy-Preview
  • GitHub Check: Verify generated types

@vhpx vhpx enabled auto-merge June 12, 2025 03:46
@vhpx vhpx merged commit bb5b31f into main Jun 12, 2025
19 checks passed
@vhpx vhpx deleted the feat/certificate-template branch June 12, 2025 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants