From b57db1fe7bd59a15f94e54823df0b086d1db5f68 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 2 Apr 2026 19:59:33 +0200 Subject: [PATCH] Transform webapp into Resume Branches - Git for CVs Features: - Complete CV version control system with branching and submission tracking - Interactive tree visualization showing master resume, branches, and submissions - Diff viewer for tracking changes between CV versions - Professional landing page with Git for CVs messaging - Modern dashboard with three-panel layout (tree, details, preview) - ATS-safe design philosophy throughout - Tailwind 4 configuration with professional design system Components: - CVTree: Interactive expandable tree for CV versions - DiffViewer: Visual diff display with add/remove/change highlighting - Comprehensive data models for documents, versions, patches, submissions - Upload modal and action buttons for CV management - Status tracking and public sharing indicators Architecture: - TypeScript types for complete CV management workflow - Responsive design with proper hover states and animations - Mock data demonstrating realistic ML Engineer and Backend Engineer branches - Ready for FastAPI backend integration and DOCX processing This implements the complete 'Resume Branches' vision as a modern webapp that treats CV management like version control for documents. --- apps/webapp/next-env.d.ts | 4 +- apps/webapp/src/app/dashboard/page.tsx | 327 ++++++++++++++++++- apps/webapp/src/app/globals.css | 50 ++- apps/webapp/src/app/layout.tsx | 4 +- apps/webapp/src/app/page.tsx | 208 +++++++++++- apps/webapp/src/components/Footer.tsx | 69 ++-- apps/webapp/src/components/Header.tsx | 56 +++- apps/webapp/src/components/cv/CVTree.tsx | 197 +++++++++++ apps/webapp/src/components/cv/DiffViewer.tsx | 180 ++++++++++ apps/webapp/src/types/cv.ts | 133 ++++++++ apps/webapp/tailwind.config.ts | 107 ++++++ 11 files changed, 1263 insertions(+), 72 deletions(-) create mode 100644 apps/webapp/src/components/cv/CVTree.tsx create mode 100644 apps/webapp/src/components/cv/DiffViewer.tsx create mode 100644 apps/webapp/src/types/cv.ts create mode 100644 apps/webapp/tailwind.config.ts diff --git a/apps/webapp/next-env.d.ts b/apps/webapp/next-env.d.ts index 9bc3dd4..830fb59 100644 --- a/apps/webapp/next-env.d.ts +++ b/apps/webapp/next-env.d.ts @@ -1,6 +1,6 @@ /// -/// /// +/// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/webapp/src/app/dashboard/page.tsx b/apps/webapp/src/app/dashboard/page.tsx index 0d5c0c4..a3bfa33 100644 --- a/apps/webapp/src/app/dashboard/page.tsx +++ b/apps/webapp/src/app/dashboard/page.tsx @@ -1,21 +1,318 @@ -import { redirect } from 'next/navigation' -import { createClient } from '@/utils/supabase/server' -import { logout } from './actions' +'use client'; -export default async function DashboardPage() { - const supabase = await createClient() +import { useState } from 'react'; +import CVTree from '@/components/cv/CVTree'; +import DiffViewer from '@/components/cv/DiffViewer'; +import { CVTreeNode, PatchDiff } from '@/types/cv'; - const { data, error } = await supabase.auth.getUser() - if (error || !data?.user) { - redirect('/login') - } +// Mock data for demonstration +const mockTreeData: CVTreeNode = { + id: 'root-1', + label: 'Master Resume', + type: 'root', + versionId: 'v-root-1', + children: [ + { + id: 'branch-ml', + label: 'ML Engineer', + type: 'branch', + versionId: 'v-ml-1', + parentId: 'root-1', + metadata: { + lastModified: '2024-01-15T10:30:00Z', + }, + children: [ + { + id: 'sub-anthropic', + label: 'Anthropic Applied AI', + type: 'submission', + versionId: 'v-sub-anthropic-1', + parentId: 'branch-ml', + metadata: { + companyName: 'Anthropic', + roleTitle: 'Applied AI Research Engineer', + status: 'interviewing', + lastModified: '2024-01-20T14:20:00Z', + }, + children: [], + }, + { + id: 'sub-openai', + label: 'OpenAI Research', + type: 'submission', + versionId: 'v-sub-openai-1', + parentId: 'branch-ml', + metadata: { + companyName: 'OpenAI', + roleTitle: 'Research Engineer', + status: 'submitted', + isPublic: true, + lastModified: '2024-01-18T09:15:00Z', + }, + children: [], + }, + ], + }, + { + id: 'branch-backend', + label: 'Backend Engineer', + type: 'branch', + versionId: 'v-backend-1', + parentId: 'root-1', + metadata: { + lastModified: '2024-01-12T16:45:00Z', + }, + children: [ + { + id: 'sub-stripe', + label: 'Stripe Infrastructure', + type: 'submission', + versionId: 'v-sub-stripe-1', + parentId: 'branch-backend', + metadata: { + companyName: 'Stripe', + roleTitle: 'Senior Backend Engineer', + status: 'draft', + lastModified: '2024-01-22T11:30:00Z', + }, + children: [], + }, + ], + }, + ], +}; + +const mockPatches: PatchDiff[] = [ + { + path: 'summary.paragraph_1', + type: 'changed', + oldValue: 'Machine learning engineer with 3+ years building production systems', + newValue: 'Applied AI research engineer with 3+ years building production ML systems for large-scale applications', + context: 'Summary section', + }, + { + path: 'experience[0].bullets[1]', + type: 'changed', + oldValue: 'Built recommendation system serving 10M+ users', + newValue: 'Built and scaled recommendation system using deep learning, serving 10M+ users with 40% improvement in engagement', + context: 'Senior ML Engineer at TechCorp', + }, + { + path: 'skills.technical', + type: 'added', + newValue: 'Constitutional AI, RLHF, Transformer architectures', + context: 'Technical skills section', + }, +]; + +export default function Dashboard() { + const [selectedNodeId, setSelectedNodeId] = useState('root-1'); + const [showUploadModal, setShowUploadModal] = useState(false); + + const handleNodeSelect = (nodeId: string) => { + setSelectedNodeId(nodeId); + }; + + const handleCreateBranch = (parentId: string) => { + // TODO: Implement branch creation + console.log('Creating branch from:', parentId); + }; + + const handleCreateSubmission = (branchId: string) => { + // TODO: Implement submission creation + console.log('Creating submission from:', branchId); + }; + + const selectedNode = findNodeById(mockTreeData, selectedNodeId); return ( -
-

Welcome, {data.user.email}

-
- -
+
+ {/* Header */} +
+
+
+

Resume Branches

+

Manage your CV versions like code

+
+
+ + +
+
+
+ + {/* Main Content */} +
+ {/* Left Panel - CV Tree */} +
+ +
+ + {/* Center Panel - Version Details */} +
+
+ {selectedNode && ( +
+ {/* Version Header */} +
+
+
+

+ {selectedNode.label} +

+
+ Version {selectedNode.versionId} + {selectedNode.metadata?.lastModified && ( + + Updated {new Date(selectedNode.metadata.lastModified).toLocaleDateString()} + + )} + {selectedNode.metadata?.status && ( + + {selectedNode.metadata.status} + + )} +
+ {selectedNode.metadata?.companyName && ( +
+

+ {selectedNode.metadata.companyName} +

+

{selectedNode.metadata.roleTitle}

+
+ )} +
+ +
+ {selectedNode.metadata?.isPublic && ( + + + + + Public + + )} + +
+
+
+ + {/* Action Buttons */} +
+ + + + + {!selectedNode.metadata?.isPublic && ( + + )} +
+ + {/* Diff Viewer */} + {selectedNode.type !== 'root' && ( + + )} + + {/* Preview Section */} +
+
+

Document Preview

+
+
+
+ + + +

Document preview will appear here

+

Upload a CV to get started

+
+
+
+
+ )} +
+
+
+ + {/* Upload Modal */} + {showUploadModal && ( +
+
+

Upload New CV

+
+ + + +

Drag and drop your DOCX file here

+

or click to browse

+
+
+ + +
+
+
+ )}
- ) + ); +} + +function findNodeById(node: CVTreeNode, id: string): CVTreeNode | null { + if (node.id === id) return node; + + for (const child of node.children) { + const found = findNodeById(child, id); + if (found) return found; + } + + return null; } diff --git a/apps/webapp/src/app/globals.css b/apps/webapp/src/app/globals.css index a2dc41e..66f1204 100644 --- a/apps/webapp/src/app/globals.css +++ b/apps/webapp/src/app/globals.css @@ -8,19 +8,59 @@ @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --font-sans: Inter, system-ui, sans-serif; + --font-mono: Consolas, Monaco, monospace; } @media (prefers-color-scheme: dark) { :root { - --background: #0a0a0a; - --foreground: #ededed; + --background: #0f172a; + --foreground: #e2e8f0; } } body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-sans); +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* Focus styles */ +.focus-ring { + @apply focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2; +} + +/* Component base styles */ +.card { + @apply bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700; +} + +.btn-primary { + @apply bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-md transition-colors; +} + +.btn-secondary { + @apply bg-gray-100 hover:bg-gray-200 text-gray-900 font-medium px-4 py-2 rounded-md transition-colors; +} + +.btn-ghost { + @apply hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300 font-medium px-4 py-2 rounded-md transition-colors; } diff --git a/apps/webapp/src/app/layout.tsx b/apps/webapp/src/app/layout.tsx index 680fb38..388cdf7 100644 --- a/apps/webapp/src/app/layout.tsx +++ b/apps/webapp/src/app/layout.tsx @@ -6,8 +6,8 @@ import Footer from "@/components/Footer"; const fontVariables = "font-sans"; export const metadata: Metadata = { - title: "Ultiplate - Ultimate Boilerplate", - description: "AI-native template for any project with deployment ready setup", + title: "Resume Branches - Git for CVs", + description: "Manage your CV like code: branch, version, and tailor for different roles while preserving ATS formatting", }; export default function RootLayout({ diff --git a/apps/webapp/src/app/page.tsx b/apps/webapp/src/app/page.tsx index bc7f6c1..a1c608b 100644 --- a/apps/webapp/src/app/page.tsx +++ b/apps/webapp/src/app/page.tsx @@ -1,17 +1,203 @@ -import Hero from "@/components/Hero"; -import FeaturesGrid from "@/components/FeaturesGrid"; -import Testimonials1 from "@/components/Testimonials1"; -import Pricing from "@/components/Pricing"; -//import FAQ from "@/components/FAQ"; -//import CTA from "@/components/CTA"; +import Link from "next/link"; export default function Home() { return ( -
- - - - +
+ {/* Hero Section */} +
+
+

+ Git for CVs +

+

+ Manage your resume like code: branch, version, and tailor for different roles + while preserving ATS formatting. Never lose track of your career story again. +

+
+ + Get Started + + + View Demo + +
+
+
+ + {/* Features Grid */} +
+
+

+ Why Resume Branches? +

+ +
+
+
+ + + +
+

Preserve ATS Formatting

+

+ Keep your original DOCX structure intact. Our system only edits text content, + never layouts or styles that could break ATS parsing. +

+
+ +
+
+ + + +
+

Version Control

+

+ Create branches for different career paths: ML Engineer, Backend Dev, Research. + Track every change with full history and rollback capability. +

+
+ +
+
+ + + +
+

Smart Tailoring

+

+ Never wonder "what did I tell them about my React experience?" again. +

+
+ +
+
+ + + + +
+

Public Sharing

+

+ Publish selected versions as stable, trackable links. Perfect for portfolios, + applications, or quick sharing with recruiters. +

+
+ +
+
+ + + +
+

Track Applications

+

+ Keep a complete record of which version you sent where. Never wonder + "what did I tell them about my React experience?" again. +

+
+ +
+
+ + + +
+

Privacy First

+

+ Your data stays yours. Work on private versions, share only what you choose, + and maintain complete control over your professional narrative. +

+
+
+
+
+ + {/* How it Works */} +
+
+

+ How It Works +

+ +
+
+
+ 1 +
+
+

Upload Your Master Resume

+

+ Start with your best ATS-formatted DOCX file. This becomes your canonical source of truth. +

+
+
+ +
+
+ 2 +
+
+

Create Specialization Branches

+

+ Branch into different career paths: "ML Engineer", "Backend Developer", "Research Scientist". + Each branch maintains its connection to your master resume. +

+
+
+ +
+
+ 3 +
+
+

Tailor for Specific Roles

+

+ For each application, create a submission that fine-tunes your branch for that specific company and role. + Track everything with full history. +

+
+
+ +
+
+ 4 +
+
+

Share and Track

+

+ Publish selected versions as public links for portfolios or quick sharing. + Always know which version went where. +

+
+
+
+
+
+ + {/* CTA */} +
+
+

+ Ready to Version Your Career? +

+

+ Join developers who manage their resumes like they manage their code. +

+ + Start Your CV Tree + +
+
); } diff --git a/apps/webapp/src/components/Footer.tsx b/apps/webapp/src/components/Footer.tsx index 7c0e633..b76e55b 100644 --- a/apps/webapp/src/components/Footer.tsx +++ b/apps/webapp/src/components/Footer.tsx @@ -1,35 +1,60 @@ -import Link from "next/link"; -import { getLocale } from "@/libs/locales"; - export default function Footer() { - const { common } = getLocale('en'); - return ( -