initial commit of v5

This commit is contained in:
Amruth Pillai
2026-01-19 23:31:54 +01:00
parent 55bdfd0067
commit cad390fa13
1132 changed files with 200807 additions and 165288 deletions
+380
View File
@@ -0,0 +1,380 @@
---
title: "Project Architecture"
description: "Understand the architecture and codebase structure of Reactive Resume"
---
This guide provides a comprehensive overview of Reactive Resume's architecture and codebase structure, helping you understand how different parts of the application work together.
---
## Tech Stack Overview
Reactive Resume is built with a modern, type-safe stack:
<CardGroup cols={2}>
<Card title="Frontend" icon="browser">
- **React 19** with TanStack Start
- **TypeScript** for type safety
- **Tailwind CSS** for styling
- **Radix UI** for accessible components
</Card>
<Card title="Backend" icon="server">
- **ORPC** for type-safe RPC
- **Drizzle ORM** with PostgreSQL
- **Better Auth** for authentication
- **Sharp** for image processing
</Card>
</CardGroup>
---
## Application Architecture
```mermaid
flowchart TD
%% Client Side
subgraph Client ["Client"]
Router["TanStack Router"]
Query["TanStack Query"]
ORPCClient["oRPC Client"]
end
%% Server Side
subgraph Server ["Server"]
ORPCRouter["oRPC Router"]
Auth["Better Auth"]
Services["Services Layer"]
Drizzle["Drizzle ORM"]
end
Database["PostgreSQL"]
Storage["File System OR S3-Compatible Storage"]
Printer["Gotenberg"]
Router --> ORPCClient
Query --> ORPCClient
ORPCClient -- calls --> ORPCRouter
ORPCRouter --> Services
Auth --> Services
Services --> Drizzle
Drizzle --> Database
Services --> Storage
Services --> Printer
```
**Diagram:** This flow shows data and control flow between the main architectural layers of Reactive Resume.
---
## Directory Structure
### Root Level
| Directory | Purpose |
|-----------|---------|
| `src/` | Main application source code |
| `public/` | Static assets served directly |
| `locales/` | Translation files (.po format) |
| `migrations/` | Database migration files |
| `docs/` | Mintlify documentation |
| `data/` | Local data storage (fonts, uploads) |
| `scripts/` | Utility scripts |
### Source Code (`src/`)
<AccordionGroup>
<Accordion title="components/" icon="cube">
Reusable React components organized by category:
- **`ui/`** — Base UI components (Button, Card, Dialog, etc.)
- **`resume/`** — Resume-specific components (sections, templates)
- **`input/`** — Form input components (ColorPicker, RichInput)
- **`layout/`** — Layout components (Sidebar, LoadingScreen)
- **`animation/`** — Animation components (Spotlight, TextMask)
- **`theme/`** — Theme management components
- **`typography/`** — Font management components
</Accordion>
<Accordion title="routes/" icon="route">
File-based routing using TanStack Router:
- **`__root.tsx`** — Root layout with providers
- **`_home/`** — Public home page routes
- **`auth/`** — Authentication routes (login, register, etc.)
- **`dashboard/`** — User dashboard routes
- **`builder/`** — Resume builder routes (the main editor)
- **`printer/`** — PDF printing route
- **`api/`** — API routes
</Accordion>
<Accordion title="integrations/" icon="plug">
Third-party service integrations:
- **`auth/`** — Better Auth client configuration
- **`drizzle/`** — Database schema and utilities
- **`orpc/`** — API router, client, and services
- **`ai/`** — AI service integrations
- **`import/`** — Resume import utilities
</Accordion>
<Accordion title="dialogs/" icon="window-maximize">
Modal dialog components:
- **`auth/`** — Authentication dialogs
- **`resume/`** — Resume management dialogs
- **`api-key/`** — API key management dialogs
- **`manager.tsx`** — Dialog manager component
- **`store.ts`** — Dialog state management (Zustand)
</Accordion>
<Accordion title="schema/" icon="file-code">
Zod schemas for validation:
- **`resume/`** — Resume data schemas
- **`icons.ts`** — Icon definitions
- **`templates.ts`** — Template definitions
</Accordion>
<Accordion title="hooks/" icon="hook">
Custom React hooks:
- `use-confirm.tsx` — Confirmation dialog hook
- `use-prompt.tsx` — Prompt dialog hook
- `use-mobile.tsx` — Mobile detection hook
- `use-safe-context.tsx` — Safe context consumption
</Accordion>
<Accordion title="utils/" icon="wrench">
Utility functions:
- `env.ts` — Environment variable validation
- `locale.ts` — Locale utilities
- `theme.ts` — Theme utilities
- `string.ts` — String manipulation
- `file.ts` — File handling utilities
</Accordion>
</AccordionGroup>
---
## Key Concepts
### File-Based Routing
Routes are automatically generated from the file structure in `src/routes/`. TanStack Router conventions:
| Pattern | Description | Example |
|---------|-------------|---------|
| `index.tsx` | Index route | `/dashboard` |
| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
| `_layout/` | Layout group (prefix) | `_home/` |
| `__root.tsx` | Root layout | Wraps all routes |
<Warning>
Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.
</Warning>
### API Layer (ORPC)
ORPC provides end-to-end type safety for API calls:
```
src/integrations/orpc/
├── client.ts # Client-side ORPC setup
├── router/ # API route definitions
│ ├── auth.ts # Authentication endpoints
│ ├── resume.ts # Resume CRUD operations
│ └── storage.ts # File storage operations
├── services/ # Business logic layer
└── helpers/ # Utility functions
```
**Using the API client:**
```tsx
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/integrations/orpc/client";
// Type-safe API calls with TanStack Query
const { data } = useQuery(orpc.resume.findMany.queryOptions());
```
### State Management
Reactive Resume uses a hybrid approach:
| Type | Tool | Use Case |
|------|------|----------|
| Server State | TanStack Query | API data, caching, sync |
| Client State | Zustand | UI state, dialogs, preferences |
| Form State | React Hook Form | Form inputs and validation |
### Database Schema
The database schema is defined using Drizzle ORM in `src/integrations/drizzle/schema.ts`:
```tsx
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const resume = pgTable("resume", {
id: uuid("id").primaryKey().defaultRandom(),
title: text("title").notNull(),
slug: text("slug").notNull(),
// ... more fields
});
```
---
## Common Development Patterns
### Adding a New Component
1. Create the component in the appropriate `src/components/` subdirectory
2. Export it from the directory's index file (if applicable)
3. Use TypeScript props interfaces for type safety
4. Follow existing patterns for consistency
```tsx
// src/components/ui/my-component.tsx
import { cn } from "@/utils/style";
interface MyComponentProps {
title: string;
className?: string;
}
export const MyComponent = ({ title, className }: MyComponentProps) => {
return <div className={cn("p-4", className)}>{title}</div>;
};
```
### Adding a New Route
1. Create a new file in `src/routes/` following TanStack Router conventions
2. The route tree auto-generates when you save
3. Use `createFileRoute` for type-safe routes
```tsx
// src/routes/my-page.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/my-page")({
component: MyPage,
});
function MyPage() {
return <div>My Page Content</div>;
}
```
### Adding an API Endpoint
1. Add the route handler in `src/integrations/orpc/router/`
2. Create service functions in `src/integrations/orpc/services/` if needed
3. The endpoint is automatically typed on the client
```tsx
// In router file
import { z } from "zod";
import { publicProcedure, router } from "../server";
export const myRouter = router({
hello: publicProcedure
.input(z.object({ name: z.string() }))
.handler(async ({ input }) => {
return { message: `Hello, ${input.name}!` };
}),
});
```
### Adding Translations
1. Wrap text with `t` macro or `<Trans>` component
2. Run `pnpm run lingui:extract` to update locale files
3. Edit the `.po` files in `locales/` to add translations
```tsx
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
// In component
const title = t`Welcome`;
<Trans>Click here to continue</Trans>
```
---
## Configuration Files
| File | Purpose |
|------|---------|
| `vite.config.ts` | Vite bundler configuration |
| `tsconfig.json` | TypeScript configuration |
| `biome.json` | Linter and formatter settings |
| `drizzle.config.ts` | Drizzle ORM configuration |
| `lingui.config.ts` | Lingui i18n configuration |
| `components.json` | shadcn/ui component configuration |
---
## Contributing Guidelines
<Steps>
<Step title="Fork & Clone">
Fork the repository on GitHub and clone your fork locally.
</Step>
<Step title="Create a Branch">
Create a feature branch from `main`:
```bash
git checkout -b feature/my-feature
```
</Step>
<Step title="Make Changes">
Implement your changes following the patterns described above.
</Step>
<Step title="Test Locally">
Ensure the app works correctly with your changes:
```bash
pnpm run dev
pnpm run lint
pnpm run typecheck
```
</Step>
<Step title="Commit & Push">
Write clear commit messages and push to your fork.
</Step>
<Step title="Open a Pull Request">
Open a PR against the main repository with a clear description of your changes.
</Step>
</Steps>
<Note>
Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.
</Note>
---
## Need Help?
<CardGroup cols={2}>
<Card
title="GitHub Discussions"
icon="comments"
href="https://github.com/AmruthPillai/Reactive-Resume/discussions"
>
Ask questions and discuss ideas with the community.
</Card>
<Card
title="GitHub Issues"
icon="bug"
href="https://github.com/AmruthPillai/Reactive-Resume/issues"
>
Report bugs or request features.
</Card>
</CardGroup>
+371
View File
@@ -0,0 +1,371 @@
---
title: "Development Setup"
description: "Set up a local development environment for Reactive Resume"
---
<Info>
**Prerequisites**:
- [Node.js](https://nodejs.org/) v20 or higher
- [pnpm](https://pnpm.io/) v10.28.0 or higher (package manager)
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
- [Git](https://git-scm.com/)
</Info>
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
---
## Setting Up Your Development Environment
<Steps>
<Step title="Clone the Repository">
```bash
git clone https://github.com/AmruthPillai/Reactive-Resume.git
cd Reactive-Resume
```
</Step>
<Step title="Install Dependencies">
This project uses [pnpm](https://pnpm.io/) as its package manager for its speed and efficiency.
```bash
# Install pnpm if you haven't already
npm install -g pnpm
# Install project dependencies
pnpm install
```
</Step>
<Step title="Start Infrastructure Services">
Start the required services using the development-specific Docker Compose file:
```bash
docker compose -f compose.dev.yml up -d
```
This starts the following infrastructure services:
- **PostgreSQL** — Database (port 5432)
- **SeaweedFS** — S3-compatible storage (port 8333)
- **Gotenberg** — PDF generation service (port 4000)
- **Mailpit** — Email testing server (SMTP on port 1025, UI on port 8025)
<Tip>
Use `compose.dev.yml` instead of `compose.yml` for local development. The development file only includes infrastructure services with ports exposed to your host machine, while the main `compose.yml` includes the full application stack intended for production deployments.
</Tip>
<Tip>
Wait for all services to be healthy before proceeding. Check with `docker compose -f compose.dev.yml ps`.
</Tip>
</Step>
<Step title="Configure Environment Variables">
Create a `.env` file in the project root:
```bash
# Server
APP_URL=http://localhost:3000
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
# Authentication
AUTH_SECRET=development-secret-change-in-production
# Storage (SeaweedFS)
S3_ACCESS_KEY_ID=seaweedfs
S3_SECRET_ACCESS_KEY=seaweedfs
S3_ENDPOINT=http://localhost:8333
S3_BUCKET=reactive-resume
S3_FORCE_PATH_STYLE=true
# PDF Printer (required for local development)
PRINTER_APP_URL=http://host.docker.internal:3000
# Email (Mailpit for local development)
SMTP_HOST=localhost
SMTP_PORT=1025
```
<Note>
**PDF Generation Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the Gotenberg PDF service is running inside Docker (which is the case when using `compose.dev.yml`). Gotenberg needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
</Note>
<Tip>
**Email Testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/), an email testing tool. All emails sent by the application will be captured and viewable at [http://localhost:8025](http://localhost:8025). No emails will actually be sent to real addresses during development.
</Tip>
</Step>
<Step title="Run Database Migrations">
Apply the database schema:
```bash
pnpm run db:migrate
```
</Step>
<Step title="Start the Development Server">
```bash
pnpm run dev
```
Your local Reactive Resume instance will be available at [http://localhost:3000](http://localhost:3000).
</Step>
</Steps>
---
## Available Scripts
Here are the most commonly used scripts during development:
### Development
| Command | Description |
|---------|-------------|
| `pnpm run dev` | Start the development server with hot reload |
| `pnpm run build` | Build the application for production |
| `pnpm run start` | Start the production server |
| `pnpm run lint` | Run Biome linter and formatter |
| `pnpm run typecheck` | Run TypeScript type checking |
### Database
| Command | Description |
|---------|-------------|
| `pnpm run db:generate` | Generate migration files from schema changes |
| `pnpm run db:migrate` | Apply pending migrations |
| `pnpm run db:push` | Push schema changes directly (dev only) |
| `pnpm run db:pull` | Pull schema from existing database |
| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
### Internationalization
| Command | Description |
|---------|-------------|
| `pnpm run lingui:extract` | Extract translatable strings from code |
### Documentation
| Command | Description |
|---------|-------------|
| `pnpm run docs` | Start the Mintlify docs development server |
---
## Understanding the Project Structure
Understanding the project structure will help you navigate the codebase:
```
reactive-resume/
├── src/
│ ├── components/ # Reusable React components
│ │ ├── ui/ # Base UI components (Button, Card, etc.)
│ │ ├── resume/ # Resume-specific components
│ │ └── ...
│ ├── dialogs/ # Modal dialogs
│ ├── hooks/ # Custom React hooks
│ ├── integrations/ # Third-party integrations
│ │ ├── auth/ # Better Auth integration
│ │ ├── drizzle/ # Database schema & utilities
│ │ └── orpc/ # API routes & services
│ ├── routes/ # File-based routing (TanStack Router)
│ │ ├── builder/ # Resume builder pages
│ │ ├── dashboard/ # User dashboard
│ │ ├── auth/ # Authentication pages
│ │ └── ...
│ ├── schema/ # Zod schemas for validation
│ ├── utils/ # Utility functions
│ └── styles/ # Global CSS styles
├── public/ # Static assets
├── locales/ # Translation files (.po format)
├── migrations/ # Database migrations
├── docs/ # Mintlify documentation
└── data/ # Local data (fonts, uploads)
```
---
## Working with the Database
### Viewing the Database
Use Drizzle Studio to explore and manage your database:
```bash
pnpm run db:studio
```
This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizzle.studio).
### Making Schema Changes
1. Edit the schema in `src/integrations/drizzle/schema.ts`
2. Generate a migration:
```bash
pnpm run db:generate
```
3. Apply the migration:
```bash
pnpm run db:migrate
```
<Warning>
Always review generated migrations before applying them, especially when working with existing data.
</Warning>
---
## Working with Translations
Reactive Resume uses [Lingui](https://lingui.dev/) for internationalization.
### Adding Translatable Text
Use the `t` macro for strings or `<Trans>` component for JSX:
```tsx
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
// For plain strings
const message = t`Hello, World!`;
// For JSX content
<Trans>Welcome to Reactive Resume</Trans>
```
### Extracting Translations
After adding new translatable text, extract them to the locale files:
```bash
pnpm run lingui:extract
```
Translation files are located in the `locales/` directory in `.po` format.
---
## Code Quality
### Linting & Formatting
Uses [Biome](https://biomejs.dev/) for linting and formatting:
```bash
# Check and auto-fix issues
pnpm run lint
```
### Type Checking
Run TypeScript type checking:
```bash
pnpm run typecheck
```
<Tip>
Configure your IDE to use Biome for automatic formatting on save. For VS Code, install the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome).
</Tip>
---
## Troubleshooting
<AccordionGroup>
<Accordion title="Port 3000 is already in use">
Another process is using port 3000. Either stop that process or start the dev server on a different port:
```bash
PORT=3001 pnpm run dev
```
</Accordion>
<Accordion title="Database connection refused">
Ensure Docker containers are running:
```bash
docker compose -f compose.dev.yml ps
docker compose -f compose.dev.yml up -d
```
Check that PostgreSQL is healthy and accessible on port 5432.
</Accordion>
<Accordion title="S3/Storage errors">
Verify SeaweedFS is running and the bucket exists:
```bash
docker compose -f compose.dev.yml logs seaweedfs
docker compose -f compose.dev.yml logs seaweedfs-create-bucket
```
If the bucket wasn't created, restart the bucket creation service:
```bash
docker compose -f compose.dev.yml restart seaweedfs-create-bucket
```
</Accordion>
<Accordion title="Type errors after pulling changes">
The route tree may need regeneration. Run the dev server which auto-generates routes:
```bash
pnpm run dev
```
Or run type checking to see specific errors:
```bash
pnpm run typecheck
```
</Accordion>
<Accordion title="Uploaded images not appearing in PDF exports">
When running in development mode (Docker services + app on host), images you upload to your resume may not appear in PDF exports. This is because the printer service runs inside Docker and cannot access URLs that resolve to `localhost` on your host machine.
**Why this happens:**
The app running on your host uploads images to SeaweedFS (S3 storage) using `localhost:8333`. When Gotenberg tries to fetch these images to render the PDF, it cannot resolve `localhost` the same way your host machine does — they're on different networks.
**Solutions:**
1. **Run the entire stack in Docker** (recommended for testing PDF exports):
Use the production `compose.yml` instead of `compose.dev.yml` to run all services, including the app, on the same Docker network:
```bash
docker compose up -d
```
This ensures all services can communicate with each other seamlessly.
2. **Use publicly accessible images**:
If you need to test PDF exports while developing on the host, use images hosted on publicly accessible URLs (e.g., images already hosted online) instead of uploading local files. The printer service can fetch these without network issues.
</Accordion>
</AccordionGroup>
---
## Next Steps
<CardGroup cols={2}>
<Card
title="Project Architecture"
icon="folder-open"
href="/contributing/architecture"
>
Deep dive into the project architecture and codebase structure.
</Card>
<Card
title="GitHub Repository"
icon="github"
href="https://github.com/AmruthPillai/Reactive-Resume"
>
View the source code and contribute to the project.
</Card>
</CardGroup>
+158
View File
@@ -0,0 +1,158 @@
---
title: "Contributing Translations"
description: "Help translate Reactive Resume into your language using Crowdin"
---
Reactive Resume is used by people all around the world, and translations help make the app accessible to everyone. If you speak a language other than English, you can help by contributing translations.
---
## How Translations Work
Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin provides a user-friendly interface where translators can contribute translations without needing to write code or work with files directly.
<Info>
The Reactive Resume Crowdin project is available at [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
</Info>
Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app.
---
## Getting Started
<Steps>
<Step title="Create a Crowdin Account">
If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
<Tip>
For detailed instructions on creating an account and getting started, see Crowdin's official [For Translators](https://support.crowdin.com/for-translators/) documentation.
</Tip>
</Step>
<Step title="Join the Reactive Resume Project">
Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join** to become a contributor.
</Step>
<Step title="Select Your Language">
From the project dashboard, click on the language you want to translate. You'll see a list of files that need translation along with the progress for each.
</Step>
<Step title="Start Translating">
Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for your translation on the right.
- Translate the text accurately while preserving any placeholders or formatting
- Use the suggestions from Translation Memory and Machine Translation as a starting point
- Vote on existing translations if you agree with them
</Step>
<Step title="Save Your Translations">
Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
</Step>
</Steps>
---
## Translation Guidelines
To maintain consistency across all translations, please follow these guidelines:
### Preserve Placeholders
Some strings contain placeholders like `{name}` or `{count}`. These must remain unchanged in your translation:
```
English: "Hello, {name}!"
Spanish: "¡Hola, {name}!"
```
### Keep Formatting
Preserve any HTML tags or markdown formatting in the source text:
```
English: "Click <1>here</1> to continue"
German: "Klicken Sie <1>hier</1>, um fortzufahren"
```
### Use Formal or Informal Tone Consistently
Choose either formal or informal language based on what's standard for software in your language, and stick with it throughout.
### Technical Terms
Some technical terms (like "PDF", "URL", "JSON") are often kept in English across languages. Use your judgment based on what's common in your language's software community.
---
## Requesting a New Language
If your language is not listed in the Crowdin project, you can request it to be added.
<Warning>
Before requesting a new language, please check if it's already available in the [Crowdin project](https://crowdin.com/project/reactive-resume).
</Warning>
To request a new language:
1. Go to the [GitHub Issues](https://github.com/AmruthPillai/Reactive-Resume/issues) page
2. Click **New Issue**
3. Select the appropriate template or create a blank issue
4. Title it something like: "Add [Language Name] to Reactive Resume"
5. Include the language name and locale code (e.g., "Japanese - ja-JP") in the issue description.
Once approved, the language will be added to Crowdin and you can begin translating.
---
## When Will My Translations Appear?
Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume.
<Info>
There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on the release cycle.
</Info>
---
## Tips for Effective Translation
<CardGroup cols={2}>
<Card title="Use Context" icon="eye">
Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app.
</Card>
<Card title="Check Existing Translations" icon="check">
Review translations by other contributors and vote for accurate ones to help maintain quality.
</Card>
<Card title="Ask Questions" icon="comment">
Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors.
</Card>
<Card title="Stay Consistent" icon="book">
Check the project glossary (if available) to ensure terminology is used consistently across the app.
</Card>
</CardGroup>
---
## Need Help?
<CardGroup cols={2}>
<Card
icon="book-open"
title="Crowdin Translator Docs"
href="https://support.crowdin.com/for-translators/"
>
Official Crowdin documentation for translators.
</Card>
<Card
icon="github"
title="GitHub Issues"
href="https://github.com/AmruthPillai/Reactive-Resume/issues"
>
Report issues or request new languages.
</Card>
</CardGroup>
---
Thank you for helping make Reactive Resume accessible to users worldwide!