Skip to content

Commit a7fc199

Browse files
authored
feat: add VeriWorkly cover letter template with PDF and web rendering (#72)
- Introduced a new cover letter template "VeriWorkly Special" and "Professional" with a two-column layout. - Implemented PDF rendering for the cover letter using React PDF. - Developed web rendering for the cover letter, including dynamic content handling. - Updated the cover letter preview and HTML generation functions to support the new template. - Refactored existing resume template metadata to align with new TemplateMeta type. - Removed obsolete ResumeTemplateMeta interface to streamline template management.
1 parent 43a9f6d commit a7fc199

31 files changed

Lines changed: 3278 additions & 155 deletions

File tree

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,36 @@ All while maintaining **100% open-source transparency** and enabling self-hostin
4242

4343
## Templates
4444

45+
### Resume Templates
46+
4547
<table>
4648
<tr>
4749
<td align="center">
48-
<img src="apps/studio/public/templates/precision-ats.png" alt="Precision ATS" width="300" />
50+
<img src="apps/studio/public/templates/resume/precision-ats.png" alt="Precision ATS" width="300" />
4951
<br /><sub><b>Precision ATS</b></sub>
5052
</td>
5153
<td align="center">
52-
<img src="apps/studio/public/templates/executive-clarity.png" alt="Executive Clarity" width="300" />
54+
<img src="apps/studio/public/templates/resume/executive-clarity.png" alt="Executive Clarity" width="300" />
5355
<br /><sub><b>Executive Clarity</b></sub>
5456
</td>
5557
</tr>
5658
</table>
5759

60+
### Cover Letter Templates
61+
62+
<table>
63+
<tr>
64+
<td align="center">
65+
<img src="apps/studio/public/templates/cover-letter/professional.png" alt="Professional" width="300" />
66+
<br /><sub><b>Professional</b></sub>
67+
</td>
68+
<td align="center">
69+
<img src="apps/studio/public/templates/cover-letter/veriworkly-special.png" alt="Veriworkly Special" width="300" />
70+
<br /><sub><b>Veriworkly Special</b></sub>
71+
</td>
72+
</tr>
73+
</table>
74+
5875
## Architecture and Technology Stack
5976

6077
VeriWorkly utilizes a modern, type-safe monorepo architecture to ensure service isolation and scalability.

apps/studio/components/dashboard/StudioShell.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import {
1717
} from "@/components/dashboard/StudioNavigation";
1818
import { ThemeToggle } from "@/components/dashboard/ThemeToggle";
1919
import { AccountMenu } from "@/components/dashboard/AccountMenu";
20-
import { NewDocumentButton } from "@/components/dashboard/NewDocumentModal";
20+
import { NewDocumentButton, NewDocumentModal } from "@/components/dashboard/NewDocumentModal";
2121
import { WorkspaceSearchModal } from "@/components/dashboard/WorkspaceSearchModal";
2222

2323
import { createResume } from "@/features/resume/services/resume-service";
2424
import { signOutCurrentUser } from "@/features/auth/services/current-user";
25+
import { createDocument } from "@/features/documents/services/document-workspace-service";
26+
import type { DocumentType } from "@/features/documents/core/document-types";
2527

2628
import { cn } from "@/lib/utils";
2729

@@ -43,13 +45,21 @@ const StudioShell = ({ children, mainClassName }: StudioShellProps) => {
4345
const [collapsed, setCollapsed] = useState(false);
4446
const [searchOpen, setSearchOpen] = useState(false);
4547
const [mobileNavOpen, setMobileNavOpen] = useState(false);
48+
const [newDocumentOpen, setNewDocumentOpen] = useState(false);
4649

4750
const email = user?.email || "No account connected";
4851
const displayName = user?.name || user?.email?.split("@")[0] || "Local builder";
4952

50-
const createNewResume = () => {
51-
const resume = createResume();
52-
router.push(`/editor/resume/${resume.id}`);
53+
const createNewDocument = (type: DocumentType) => {
54+
if (type === "RESUME") {
55+
const resume = createResume();
56+
router.push(`/editor/resume/${resume.id}`);
57+
58+
return;
59+
}
60+
61+
const document = createDocument(type);
62+
router.push(`/editor/${type.toLowerCase()}/${document.id}`);
5363
};
5464

5565
const handleLogout = async () => {
@@ -126,7 +136,7 @@ const StudioShell = ({ children, mainClassName }: StudioShellProps) => {
126136
</div>
127137

128138
<div className="px-2">
129-
<NewDocumentButton collapsed={collapsed} onClick={createNewResume} />
139+
<NewDocumentButton collapsed={collapsed} onClick={() => setNewDocumentOpen(true)} />
130140
</div>
131141

132142
{!collapsed ? (
@@ -187,7 +197,7 @@ const StudioShell = ({ children, mainClassName }: StudioShellProps) => {
187197
</Link>
188198

189199
<div className="flex items-center gap-2">
190-
<NewDocumentButton compact onClick={createNewResume} />
200+
<NewDocumentButton compact onClick={() => setNewDocumentOpen(true)} />
191201

192202
<button
193203
type="button"
@@ -231,6 +241,12 @@ const StudioShell = ({ children, mainClassName }: StudioShellProps) => {
231241
onOpenDocument={(id) => router.push(`/editor/resume/${id}`)}
232242
/>
233243

244+
<NewDocumentModal
245+
open={newDocumentOpen}
246+
onCreate={createNewDocument}
247+
onClose={() => setNewDocumentOpen(false)}
248+
/>
249+
234250
<div
235251
className={cn(
236252
"relative min-w-0 flex-1 bg-[radial-gradient(circle_at_top_left,color-mix(in_oklab,var(--accent)_13%,transparent),transparent_28rem),linear-gradient(180deg,color-mix(in_oklab,var(--card)_62%,var(--background)),var(--background)_18rem)] p-4 sm:p-6 xl:p-8",

apps/studio/features/cover-letter/defaults.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,81 @@ import type { BaseDocument } from "@/features/documents/core/types";
22

33
import type { CoverLetterContent } from "./types";
44

5-
export const COVER_LETTER_TEMPLATE_ID = "cover-letter-classic";
5+
export const COVER_LETTER_TEMPLATE_ID = "professional";
66

77
export function createDefaultCoverLetter(id: string): BaseDocument<CoverLetterContent> {
88
const now = new Date().toISOString();
99
return {
1010
id,
1111
type: "COVER_LETTER",
12-
title: "Cover Letter",
12+
title: "Product Engineer - Veriworkly",
1313
templateId: COVER_LETTER_TEMPLATE_ID,
14+
1415
updatedAt: now,
16+
1517
sync: {
1618
enabled: false,
1719
status: "local-only",
1820
cloudDocumentId: null,
1921
lastSyncedAt: null,
2022
revision: 1,
2123
},
24+
2225
content: {
23-
recipientName: "",
24-
recipientTitle: "",
25-
companyName: "",
26-
subject: "",
27-
greeting: "Dear Hiring Manager,",
28-
opening: "",
29-
body: "",
26+
senderName: "Veriworkly User",
27+
senderTitle: "Product Engineer",
28+
senderEmail: "user@veriworkly.com",
29+
senderPhone: "+1 (555) 010-2026",
30+
senderLocation: "Remote",
31+
senderWebsite: "veriworkly.com",
32+
senderLinks: "",
33+
34+
links: {
35+
displayMode: "icon-username",
36+
items: [],
37+
},
38+
39+
date: new Intl.DateTimeFormat("en", {
40+
year: "numeric",
41+
month: "long",
42+
day: "numeric",
43+
}).format(new Date()),
44+
45+
recipientName: "Veriworkly Hiring Team",
46+
recipientTitle: "Product and Engineering",
47+
48+
companyName: "Veriworkly",
49+
companyLocation: "Remote",
50+
51+
jobTitle: "Product Engineer",
52+
subject: "Application for Product Engineer at Veriworkly",
53+
54+
greeting: "Dear Veriworkly Hiring Team,",
55+
56+
opening:
57+
"I am excited to apply for the Product Engineer role at Veriworkly. The product mission is close to the work I care about: helping people present credible professional documents, move faster through career workflows, and keep their materials consistent across resume, cover letter, and document experiences.",
58+
59+
body: "I would bring a practical product engineering mindset to Veriworkly. I enjoy building polished document editors, export flows, cloud sync, sharing, and preview experiences that feel dependable for real users. I care about details like autosave behavior, hydration-safe rendering, reusable document actions, and templates that help users start with strong defaults instead of a blank page.\n\nVeriworkly stands out because it combines user-facing craft with systems that need to be trustworthy: local drafts, public links, document exports, and collaboration between dashboard and editor surfaces. I would be excited to help make those workflows sharper, faster, and easier to maintain.",
60+
61+
highlights:
62+
"- Built React and TypeScript document workflows with live preview, export, and autosave behavior\n- Improved shared UI actions so resumes, cover letters, and other documents behave consistently\n- Debugged hydration, routing, and local-storage edge cases in Next.js applications",
63+
3064
closing: "Sincerely,",
31-
signature: "",
65+
signature: "Veriworkly User",
66+
67+
postscript:
68+
"I would welcome the chance to help Veriworkly make professional document creation feel faster, clearer, and more reliable.",
69+
70+
appearance: {
71+
fontFamily: "geist",
72+
pageMargin: 40,
73+
paragraphSpacing: 10,
74+
lineHeight: 1.5,
75+
accentColor: "#2563eb",
76+
sidebarColor: "#f8fafc",
77+
pageColor: "#ffffff",
78+
textColor: "#18181b",
79+
},
3280
},
3381
};
3482
}

0 commit comments

Comments
 (0)