CRM pipeline
Contacts table plus a five-stage deal pipeline board with inline stage changes, all persisted locally.
A small CRM with two views over the same data: a searchable contacts table with stage selects and delete, and a kanban-style pipeline (Lead → Qualified → Proposal → Won/Lost) where deals move between columns with keyboard-accessible buttons. State lives in localStorage via a typed hook so the app works offline; swap src/store.ts for API calls when you add a backend.
- react
- crm
- pipeline
- kanban
- tailwind
This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.
Files14 files
import { useState, type FormEvent } from "react";
import { STAGES, type Contact, type Stage } from "./store";
export function ContactForm({ onSave, onCancel }: { onSave: (c: Omit<Contact, "id">) => void; onCancel: () => void }) {
const [form, setForm] = useState<Omit<Contact, "id">>({ name: "", company: "", email: "", stage: "Lead", value: 0, note: "" });
const [error, setError] = useState<string | null>(null);
const set = <K extends keyof typeof form>(k: K, v: (typeof form)[K]) => setForm((f) => ({ ...f, [k]: v }));
function submit(e: FormEvent) {
e.preventDefault();
if (!form.name.trim() || !form.company.trim()) return setError("Name and company are required.");
if (!form.email.includes("@")) return setError("Enter a valid email.");
onSave({ ...form, name: form.name.trim(), company: form.company.trim() });
}
return (
<div className="fixed inset-0 z-20 flex items-end justify-center bg-black/40 p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="contact-form-title">
<form onSubmit={submit} className="w-full max-w-md space-y-3 rounded-xl bg-white p-5 shadow-xl">
<h2 id="contact-form-title" className="text-lg font-semibold">
New contact
</h2>
<label className="block text-sm">
<span>Name</span>
<input value={form.name} onChange={(e) => set("name", e.target.value)} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" autoFocus />
</label>
<label className="block text-sm">
<span>Company</span>
<input value={form.company} onChange={(e) => set("company", e.target.value)} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
<label className="block text-sm">
<span>Email</span>
<input type="email" value={form.email} onChange={(e) => set("email", e.target.value)} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
<div className="grid grid-cols-2 gap-3">
<label className="block text-sm">
<span>Stage</span>
<select value={form.stage} onChange={(e) => set("stage", e.target.value as Stage)} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2">
{STAGES.map((s) => (
<option key={s}>{s}</option>
))}
</select>
</label>
<label className="block text-sm">
<span>Value ($)</span>
<input type="number" min={0} value={form.value} onChange={(e) => set("value", Number(e.target.value))} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
</div>
<label className="block text-sm">
<span>Note</span>
<textarea value={form.note} onChange={(e) => set("note", e.target.value)} rows={2} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
{error ? (
<p role="alert" className="text-sm text-red-600">
{error}
</p>
) : null}
<div className="flex justify-end gap-2 pt-2">
<button type="button" onClick={onCancel} className="rounded-md border border-slate-300 px-4 py-2 text-sm">
Cancel
</button>
<button type="submit" className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white">
Save
</button>
</div>
</form>
</div>
);
}
Dependencies
Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.
| Package | Version |
|---|---|
| react | ^19.1.0 |
| react-dom | ^19.1.0 |
| vite | ^6.3.0 |
| @vitejs/plugin-react | ^4.4.0 |
| tailwindcss | ^4.1.0 |
| @tailwindcss/vite | ^4.1.0 |
| typescript | ^5.8.0 |
How it runs
- Framework
- Vite + React + TypeScript
- Dev command
- npm run dev
- Build command
- npm run build
- Output directory
- dist
- Port
- 5173
- Opens at
- /
- Difficulty
- intermediate
Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.
Need the raw data? GET /api/v1/templates/vite-react-crm returns this template with its files.
Build on CRM pipeline
Create a free account, start from this template, and ask the AI agent to turn it into your app.