Theme swatches removed from inside scaffold components. Theme state
lifted to SurfaceSection which passes themeColor down as a prop.
Controls bar below the scaffold now has three rows:
1. Library tabs (shadcn / Mantine / HeroUI / Tremor etc.)
2. Color theme swatches — only shown when the active library has
theme variants (shadcn: 8, Mantine: 6, HeroUI: 5, Tremor: 5,
DaisyUI: 12, HeroUI marketing: 6)
3. Description + tags + Docs link + Lock in button
Scaffold renders cleanly with no UI chrome inside it.
Made-with: Cursor
1154 lines
70 KiB
TypeScript
1154 lines
70 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
/**
|
|
* Design scaffolds — styled React mockups showing what each UI library
|
|
* looks like for a given surface. Used in the Design page preview area.
|
|
*
|
|
* Each scaffold is rendered at its natural size inside a scaled container,
|
|
* giving the user a realistic feel for the library's visual language.
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Shared mock data
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const TABLE_ROWS = [
|
|
{ name: "Alice Martin", email: "alice@co.com", role: "Admin", status: "Active", date: "Jan 12" },
|
|
{ name: "Ben Walsh", email: "ben@co.com", role: "Member", status: "Pending", date: "Jan 14" },
|
|
{ name: "Clara Kim", email: "clara@co.com", role: "Member", status: "Active", date: "Jan 15" },
|
|
{ name: "David Osei", email: "david@co.com", role: "Viewer", status: "Inactive", date: "Jan 16" },
|
|
];
|
|
|
|
type Page = "Dashboard" | "Users" | "Settings";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Theme color definitions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface ThemeColor {
|
|
id: string;
|
|
label: string;
|
|
primary: string; // button bg, active dot, toggle on
|
|
primaryFg: string; // text on primary bg
|
|
activeBg: string; // sidebar active item bg
|
|
activeFg: string; // sidebar active item text
|
|
ring: string; // focus ring / subtle tint
|
|
// optional full-page palette (used by DaisyUI & dark themes)
|
|
bg?: string;
|
|
cardBg?: string;
|
|
textColor?: string;
|
|
borderColor?: string;
|
|
mutedText?: string;
|
|
}
|
|
|
|
export const SHADCN_THEMES: ThemeColor[] = [
|
|
{ id: "neutral", label: "Neutral", primary: "#18181b", primaryFg: "#fff", activeBg: "#f4f4f5", activeFg: "#18181b", ring: "#e4e4e7" },
|
|
{ id: "blue", label: "Blue", primary: "#3b82f6", primaryFg: "#fff", activeBg: "#eff6ff", activeFg: "#1d4ed8", ring: "#bfdbfe" },
|
|
{ id: "green", label: "Green", primary: "#22c55e", primaryFg: "#fff", activeBg: "#f0fdf4", activeFg: "#15803d", ring: "#bbf7d0" },
|
|
{ id: "orange", label: "Orange", primary: "#f97316", primaryFg: "#fff", activeBg: "#fff7ed", activeFg: "#c2410c", ring: "#fed7aa" },
|
|
{ id: "red", label: "Red", primary: "#ef4444", primaryFg: "#fff", activeBg: "#fef2f2", activeFg: "#b91c1c", ring: "#fecaca" },
|
|
{ id: "rose", label: "Rose", primary: "#f43f5e", primaryFg: "#fff", activeBg: "#fff1f2", activeFg: "#be123c", ring: "#fecdd3" },
|
|
{ id: "violet", label: "Violet", primary: "#8b5cf6", primaryFg: "#fff", activeBg: "#f5f3ff", activeFg: "#6d28d9", ring: "#ddd6fe" },
|
|
{ id: "yellow", label: "Yellow", primary: "#eab308", primaryFg: "#000", activeBg: "#fefce8", activeFg: "#a16207", ring: "#fef08a" },
|
|
];
|
|
|
|
export const MANTINE_THEMES: ThemeColor[] = [
|
|
{ id: "blue", label: "Blue", primary: "#228be6", primaryFg: "#fff", activeBg: "#e7f5ff", activeFg: "#1971c2", ring: "#a5d8ff" },
|
|
{ id: "teal", label: "Teal", primary: "#12b886", primaryFg: "#fff", activeBg: "#e6fcf5", activeFg: "#087f5b", ring: "#96f2d7" },
|
|
{ id: "violet", label: "Violet", primary: "#7950f2", primaryFg: "#fff", activeBg: "#f3f0ff", activeFg: "#5f3dc4", ring: "#d0bfff" },
|
|
{ id: "red", label: "Red", primary: "#fa5252", primaryFg: "#fff", activeBg: "#fff5f5", activeFg: "#c92a2a", ring: "#ffc9c9" },
|
|
{ id: "orange", label: "Orange", primary: "#fd7e14", primaryFg: "#fff", activeBg: "#fff4e6", activeFg: "#d9480f", ring: "#ffd8a8" },
|
|
{ id: "green", label: "Green", primary: "#40c057", primaryFg: "#fff", activeBg: "#ebfbee", activeFg: "#2f9e44", ring: "#b2f2bb" },
|
|
];
|
|
|
|
export const HEROUI_THEMES: ThemeColor[] = [
|
|
{ id: "violet", label: "Violet", primary: "#7c3aed", primaryFg: "#fff", activeBg: "rgba(124,58,237,0.3)", activeFg: "#c084fc", ring: "rgba(168,85,247,0.3)" },
|
|
{ id: "blue", label: "Blue", primary: "#2563eb", primaryFg: "#fff", activeBg: "rgba(37,99,235,0.3)", activeFg: "#93c5fd", ring: "rgba(59,130,246,0.3)" },
|
|
{ id: "rose", label: "Rose", primary: "#e11d48", primaryFg: "#fff", activeBg: "rgba(225,29,72,0.3)", activeFg: "#fda4af", ring: "rgba(244,63,94,0.3)" },
|
|
{ id: "green", label: "Green", primary: "#16a34a", primaryFg: "#fff", activeBg: "rgba(22,163,74,0.3)", activeFg: "#86efac", ring: "rgba(34,197,94,0.3)" },
|
|
{ id: "orange", label: "Orange", primary: "#ea580c", primaryFg: "#fff", activeBg: "rgba(234,88,12,0.3)", activeFg: "#fdba74", ring: "rgba(249,115,22,0.3)" },
|
|
];
|
|
|
|
// DaisyUI named themes — each has a full page palette
|
|
export const DAISY_THEMES: ThemeColor[] = [
|
|
{ id: "dark", label: "Dark", primary: "#793ef9", primaryFg: "#fff", activeBg: "rgba(121,62,249,0.2)", activeFg: "#a78bfa", ring: "#4c1d95", bg: "#1d232a", cardBg: "#191e24", textColor: "#a6adba", borderColor: "#2a323c", mutedText: "#6b7280" },
|
|
{ id: "light", label: "Light", primary: "#570df8", primaryFg: "#fff", activeBg: "#f3f0ff", activeFg: "#4c1d95", ring: "#ddd6fe", bg: "#fff", cardBg: "#fff", textColor: "#1f2937", borderColor: "#e5e7eb", mutedText: "#6b7280" },
|
|
{ id: "cupcake", label: "Cupcake", primary: "#65c3c8", primaryFg: "#291334", activeBg: "#d9f5f6", activeFg: "#0e6b70", ring: "#a7eaec", bg: "#faf7f5", cardBg: "#fff", textColor: "#291334", borderColor: "#e9e3df", mutedText: "#9ca3af" },
|
|
{ id: "bumblebee", label: "Bee", primary: "#e0a82e", primaryFg: "#1f2937", activeBg: "#fef9e5", activeFg: "#92650a", ring: "#fde68a", bg: "#fff", cardBg: "#fff", textColor: "#1f2937", borderColor: "#e5e7eb", mutedText: "#6b7280" },
|
|
{ id: "synthwave", label: "Synth", primary: "#e779c1", primaryFg: "#2d1b69", activeBg: "rgba(231,121,193,0.2)",activeFg: "#f0abdc", ring: "#701a75", bg: "#1a103c", cardBg: "#221551", textColor: "#e2e8f0", borderColor: "#4c3585", mutedText: "#a78bfa" },
|
|
{ id: "cyberpunk", label: "Cyber", primary: "#ff7598", primaryFg: "#1f2937", activeBg: "#fff0f3", activeFg: "#9d174d", ring: "#fca5a5", bg: "#ffee00", cardBg: "#fff", textColor: "#1f2937", borderColor: "#1f2937", mutedText: "#374151" },
|
|
{ id: "retro", label: "Retro", primary: "#ef9995", primaryFg: "#282425", activeBg: "#fde8e7", activeFg: "#7f1d1d", ring: "#fca5a5", bg: "#e4d8b4", cardBg: "#f7f0d8", textColor: "#282425", borderColor: "#d4b483", mutedText: "#6b5745" },
|
|
{ id: "dracula", label: "Dracula", primary: "#ff79c6", primaryFg: "#282a36", activeBg: "rgba(255,121,198,0.15)",activeFg: "#ff79c6",ring: "#bd93f9", bg: "#282a36", cardBg: "#343746", textColor: "#f8f8f2", borderColor: "#44475a", mutedText: "#6272a4" },
|
|
{ id: "night", label: "Night", primary: "#38bdf8", primaryFg: "#0c1a2b", activeBg: "rgba(56,189,248,0.15)",activeFg: "#38bdf8", ring: "#0284c7", bg: "#1d232a", cardBg: "#191e24", textColor: "#a6adba", borderColor: "#2a323c", mutedText: "#4b5563" },
|
|
{ id: "forest", label: "Forest", primary: "#1eb854", primaryFg: "#fff", activeBg: "rgba(30,184,84,0.15)", activeFg: "#1eb854", ring: "#15803d", bg: "#171212", cardBg: "#1d1d1d", textColor: "#d1d5db", borderColor: "#292929", mutedText: "#4b5563" },
|
|
{ id: "coffee", label: "Coffee", primary: "#db924b", primaryFg: "#20150e", activeBg: "rgba(219,146,75,0.15)",activeFg: "#db924b", ring: "#92400e", bg: "#20150e", cardBg: "#2a1c12", textColor: "#d1b59c", borderColor: "#3d261a", mutedText: "#78523a" },
|
|
{ id: "winter", label: "Winter", primary: "#047aed", primaryFg: "#fff", activeBg: "#e0f0ff", activeFg: "#0369a1", ring: "#bae6fd", bg: "#fff", cardBg: "#f0f9ff", textColor: "#1e3a5f", borderColor: "#bae6fd", mutedText: "#64748b" },
|
|
];
|
|
|
|
export const TREMOR_THEMES: ThemeColor[] = [
|
|
{ id: "blue", label: "Blue", primary: "#2563eb", primaryFg: "#fff", activeBg: "#eff6ff", activeFg: "#1d4ed8", ring: "#bfdbfe" },
|
|
{ id: "indigo", label: "Indigo", primary: "#4f46e5", primaryFg: "#fff", activeBg: "#eef2ff", activeFg: "#3730a3", ring: "#c7d2fe" },
|
|
{ id: "teal", label: "Teal", primary: "#0d9488", primaryFg: "#fff", activeBg: "#f0fdfa", activeFg: "#0f766e", ring: "#99f6e4" },
|
|
{ id: "rose", label: "Rose", primary: "#e11d48", primaryFg: "#fff", activeBg: "#fff1f2", activeFg: "#be123c", ring: "#fecdd3" },
|
|
{ id: "amber", label: "Amber", primary: "#d97706", primaryFg: "#fff", activeBg: "#fffbeb", activeFg: "#92400e", ring: "#fde68a" },
|
|
];
|
|
|
|
// Small swatch strip rendered inside each scaffold's header
|
|
function ThemeSwatches({ themes, selected, onSelect }: {
|
|
themes: ThemeColor[];
|
|
selected: ThemeColor;
|
|
onSelect: (t: ThemeColor) => void;
|
|
}) {
|
|
return (
|
|
<div className="flex items-center gap-1 flex-wrap">
|
|
{themes.map(t => (
|
|
<button
|
|
key={t.id}
|
|
title={t.label}
|
|
onClick={() => onSelect(t)}
|
|
className="w-4 h-4 rounded-full transition-transform hover:scale-110 border"
|
|
style={{
|
|
background: t.bg ? `linear-gradient(135deg, ${t.bg} 50%, ${t.primary} 50%)` : t.primary,
|
|
borderColor: selected.id === t.id ? t.primary : "transparent",
|
|
outline: selected.id === t.id ? `2px solid ${t.primary}` : "none",
|
|
outlineOffset: 2,
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// WEB APP — shadcn
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function ShadcnDashboard({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
|
<div className="grid grid-cols-3 gap-3 mb-4">
|
|
{["Total Revenue", "Active Users", "Conversions"].map((label, i) => (
|
|
<div key={label} className="bg-white rounded-lg border p-3">
|
|
<p className="text-[10px] text-zinc-500 mb-1">{label}</p>
|
|
<p className="text-lg font-bold text-zinc-900">{["$12,400", "2,841", "18.2%"][i]}</p>
|
|
<p className="text-[10px] text-zinc-400">+{[12, 8, 3][i]}% from last month</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="bg-white rounded-lg border mb-3">
|
|
<div className="px-4 py-3 border-b"><span className="text-xs font-semibold text-zinc-700">Revenue</span></div>
|
|
<div className="p-4 flex items-end gap-1.5 h-24">
|
|
{[40,60,45,75,65,85,70,90,55,80,75,95].map((h,i)=>(
|
|
<div key={i} className="flex-1 rounded-sm" style={{ height:`${h}%`, background: i===11 ? t.primary : t.ring }} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="bg-white rounded-lg border">
|
|
<div className="px-4 py-3 border-b"><span className="text-xs font-semibold text-zinc-700">Recent activity</span></div>
|
|
{TABLE_ROWS.slice(0,3).map(r=>(
|
|
<div key={r.name} className="flex items-center gap-3 px-4 py-2.5 border-b last:border-0">
|
|
<div className="w-6 h-6 rounded-full bg-zinc-200 shrink-0" />
|
|
<div className="flex-1"><p className="text-[11px] font-medium text-zinc-800">{r.name}</p><p className="text-[10px] text-zinc-400">{r.email}</p></div>
|
|
<span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:t.activeBg,color:t.activeFg}:{background:"#f4f4f5",color:"#a1a1aa"}}>{r.status}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ShadcnUsers({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
|
<div className="bg-white rounded-lg border">
|
|
<div className="px-4 py-3 border-b flex items-center justify-between">
|
|
<span className="text-xs font-semibold text-zinc-700">Team members</span>
|
|
<div className="flex gap-2">
|
|
<input className="h-7 px-2.5 rounded-md border border-zinc-200 text-[11px] outline-none w-32" placeholder="Search..." />
|
|
<div className="h-7 px-3 rounded-md text-[11px] flex items-center cursor-pointer" style={{ background: t.primary, color: t.primaryFg }}>Invite</div>
|
|
</div>
|
|
</div>
|
|
<table className="w-full text-[11px]">
|
|
<thead><tr className="border-b bg-zinc-50">{["Name","Role","Status","Joined",""].map(h=><th key={h} className="px-4 py-2 text-left text-[10px] text-zinc-500 font-medium">{h}</th>)}</tr></thead>
|
|
<tbody>{TABLE_ROWS.map(r=>(
|
|
<tr key={r.name} className="border-b last:border-0 hover:bg-zinc-50">
|
|
<td className="px-4 py-2.5">
|
|
<div className="flex items-center gap-2"><div className="w-5 h-5 rounded-full bg-zinc-200" /><div><p className="font-medium text-zinc-800">{r.name}</p><p className="text-[10px] text-zinc-400">{r.email}</p></div></div>
|
|
</td>
|
|
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded border border-zinc-200 text-[9px] text-zinc-600">{r.role}</span></td>
|
|
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:t.primary,color:t.primaryFg}:r.status==="Pending"?{background:"#f4f4f5",color:"#71717a"}:{background:"#fafafa",color:"#a1a1aa"}}>{r.status}</span></td>
|
|
<td className="px-4 py-2.5 text-zinc-400">{r.date}</td>
|
|
<td className="px-4 py-2.5"><div className="h-6 px-2 rounded border border-zinc-200 text-[9px] flex items-center text-zinc-500 cursor-pointer w-fit">···</div></td>
|
|
</tr>
|
|
))}</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ShadcnSettings({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
|
<div className="bg-white rounded-lg border mb-3">
|
|
<div className="px-4 py-3 border-b"><span className="text-xs font-semibold text-zinc-700">General</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{[{l:"Workspace name",v:"Acme Inc"},{l:"Slug",v:"acme-inc"},{l:"Email",v:"admin@acme.com"}].map(f=>(
|
|
<div key={f.l}>
|
|
<label className="text-[10px] font-medium text-zinc-600 block mb-1">{f.l}</label>
|
|
<input defaultValue={f.v} className="w-full h-8 px-3 rounded-md border border-zinc-200 text-[11px] outline-none" style={{ outlineColor: t.primary }} />
|
|
</div>
|
|
))}
|
|
<div className="pt-1"><div className="h-7 px-3 rounded-md text-[11px] inline-flex items-center cursor-pointer" style={{ background: t.primary, color: t.primaryFg }}>Save changes</div></div>
|
|
</div>
|
|
</div>
|
|
<div className="bg-white rounded-lg border">
|
|
<div className="px-4 py-3 border-b"><span className="text-xs font-semibold text-zinc-700">Notifications</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{["Email digests","Push notifications","Security alerts","Product updates"].map((label,i)=>(
|
|
<div key={label} className="flex items-center justify-between">
|
|
<div><p className="text-[11px] font-medium text-zinc-700">{label}</p><p className="text-[10px] text-zinc-400">Receive {label.toLowerCase()}</p></div>
|
|
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background: [true,false,true,false][i] ? t.primary : "#e4e4e7" }}>
|
|
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform: [true,false,true,false][i] ? "translateX(16px)" : "translateX(2px)" }} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function WebAppShadcn({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const [page, setPage] = useState<Page>("Dashboard");
|
|
const theme = themeColor ?? SHADCN_THEMES[0];
|
|
const NAV_ITEMS: { label: Page; icon: string }[] = [
|
|
{ label: "Dashboard", icon: "▦" },
|
|
{ label: "Users", icon: "◎" },
|
|
{ label: "Settings", icon: "⚙" },
|
|
];
|
|
return (
|
|
<div className="flex h-full bg-white font-sans text-sm">
|
|
<div className="w-44 border-r flex flex-col py-4 px-3 gap-1 bg-white shrink-0">
|
|
<div className="flex items-center gap-2 px-2 mb-4">
|
|
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
|
<span className="font-semibold text-zinc-900 text-xs">Acme Inc</span>
|
|
</div>
|
|
{NAV_ITEMS.map(({ label, icon }) => (
|
|
<button key={label} onClick={() => setPage(label)}
|
|
className="flex items-center gap-2 px-2 py-1.5 rounded-md text-xs cursor-pointer w-full text-left transition-colors"
|
|
style={page===label ? { background: theme.activeBg, color: theme.activeFg, fontWeight: 600 } : { color: "#71717a" }}>
|
|
<span className="text-[11px]">{icon}</span>{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="flex-1 flex flex-col min-w-0">
|
|
<div className="h-12 border-b flex items-center justify-between px-5 shrink-0">
|
|
<span className="font-semibold text-zinc-900 text-sm">{page}</span>
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-7 px-3 rounded-md border border-zinc-200 text-[11px] flex items-center text-zinc-600">Export</div>
|
|
<div className="h-7 px-3 rounded-md text-[11px] flex items-center" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</div>
|
|
</div>
|
|
</div>
|
|
{page === "Dashboard" && <ShadcnDashboard t={theme} />}
|
|
{page === "Users" && <ShadcnUsers t={theme} />}
|
|
{page === "Settings" && <ShadcnSettings t={theme} />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MantineDashboard({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
|
<div className="grid grid-cols-3 gap-3 mb-4">
|
|
{[{l:"Total Revenue",v:"$12,400",c:"#2f9e44"},{l:"Active Users",v:"2,841",c:"#228be6"},{l:"Conversions",v:"18.2%",c:"#e67700"}].map(item=>(
|
|
<div key={item.l} className="rounded-lg p-3" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<p className="text-[10px] mb-1" style={{ color:"#868e96" }}>{item.l}</p>
|
|
<p className="text-lg font-bold" style={{ color:"#212529" }}>{item.v}</p>
|
|
<p className="text-[10px]" style={{ color:item.c }}>↑ trending up</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="rounded-lg mb-3" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #e9ecef" }}><span className="text-xs font-semibold" style={{ color:"#212529" }}>Monthly revenue</span></div>
|
|
<div className="p-4 flex items-end gap-1.5 h-24">
|
|
{[40,60,45,75,65,85,70,90,55,80,75,95].map((h,i)=>(
|
|
<div key={i} className="flex-1 rounded-sm" style={{ height:`${h}%`, background:i===11?"#228be6":"#d0ebff" }} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="rounded-lg" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #e9ecef" }}><span className="text-xs font-semibold" style={{ color:"#212529" }}>Recent</span></div>
|
|
{TABLE_ROWS.slice(0,3).map(r=>(
|
|
<div key={r.name} className="flex items-center gap-3 px-4 py-2.5" style={{ borderBottom:"1px solid #f1f3f5" }}>
|
|
<div className="w-6 h-6 rounded-full shrink-0" style={{ background:"#d0ebff" }} />
|
|
<div className="flex-1"><p className="text-[11px] font-semibold" style={{ color:"#212529" }}>{r.name}</p><p className="text-[10px]" style={{ color:"#868e96" }}>{r.email}</p></div>
|
|
<span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:"#d3f9d8",color:"#2f9e44"}:{background:"#f1f3f5",color:"#868e96"}}>{r.status}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MantineUsers({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
|
<div className="rounded-lg" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<div className="px-4 py-3 flex items-center justify-between" style={{ borderBottom:"1px solid #e9ecef" }}>
|
|
<span className="text-xs font-semibold" style={{ color:"#212529" }}>Team members</span>
|
|
<div className="flex gap-2">
|
|
<input className="h-7 px-2.5 text-[11px] rounded" style={{ border:"1px solid #dee2e6", outline:"none", width:130 }} placeholder="Search members..." />
|
|
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ background: t.primary, color: t.primaryFg }}>+ Invite</button>
|
|
</div>
|
|
</div>
|
|
<table className="w-full text-[11px]">
|
|
<thead><tr style={{ borderBottom:"1px solid #e9ecef", background:"#f8f9fa" }}>{["Member","Role","Status","Joined","Actions"].map(h=><th key={h} className="px-4 py-2 text-left text-[10px] font-semibold" style={{ color:"#868e96" }}>{h}</th>)}</tr></thead>
|
|
<tbody>{TABLE_ROWS.map(r=>(
|
|
<tr key={r.name} style={{ borderBottom:"1px solid #f1f3f5" }}>
|
|
<td className="px-4 py-2.5"><div className="flex items-center gap-2"><div className="w-6 h-6 rounded-full" style={{ background: t.ring }} /><div><p className="font-semibold" style={{ color:"#212529" }}>{r.name}</p><p style={{ color:"#868e96" }}>{r.email}</p></div></div></td>
|
|
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px]" style={{ background: t.activeBg, color: t.activeFg }}>{r.role}</span></td>
|
|
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:"#d3f9d8",color:"#2f9e44"}:r.status==="Pending"?{background:"#fff3bf",color:"#e67700"}:{background:"#f1f3f5",color:"#868e96"}}>{r.status}</span></td>
|
|
<td className="px-4 py-2.5" style={{ color:"#adb5bd" }}>{r.date}</td>
|
|
<td className="px-4 py-2.5"><button className="text-[10px] px-2 py-1 rounded" style={{ border:"1px solid #dee2e6", color:"#495057" }}>Manage</button></td>
|
|
</tr>
|
|
))}</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MantineSettings({ t }: { t: ThemeColor }) {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
|
<div className="rounded-lg mb-3" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #e9ecef" }}><span className="text-xs font-semibold" style={{ color:"#212529" }}>Workspace</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{[{l:"Name",v:"Acme Inc"},{l:"Slug",v:"acme-inc"},{l:"Email",v:"admin@acme.com"}].map(f=>(
|
|
<div key={f.l}>
|
|
<label className="text-[10px] font-semibold block mb-1" style={{ color:"#495057" }}>{f.l}</label>
|
|
<input defaultValue={f.v} className="w-full h-8 px-3 rounded text-[11px]" style={{ border:"1px solid #dee2e6", outline:"none" }} />
|
|
</div>
|
|
))}
|
|
<button className="h-8 px-4 rounded text-[11px] font-medium" style={{ background: t.primary, color: t.primaryFg }}>Save</button>
|
|
</div>
|
|
</div>
|
|
<div className="rounded-lg" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #e9ecef" }}><span className="text-xs font-semibold" style={{ color:"#212529" }}>Notifications</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{["Email digests","Push notifications","Security alerts","Product updates"].map((label,i)=>(
|
|
<div key={label} className="flex items-center justify-between">
|
|
<div><p className="text-[11px] font-medium" style={{ color:"#212529" }}>{label}</p><p className="text-[10px]" style={{ color:"#868e96" }}>Get notified via {label.split(" ")[0].toLowerCase()}</p></div>
|
|
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background:[true,false,true,false][i] ? t.primary : "#dee2e6" }}>
|
|
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform:[true,false,true,false][i]?"translateX(16px)":"translateX(2px)" }} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function WebAppMantine({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const [page, setPage] = useState<Page>("Dashboard");
|
|
const theme = themeColor ?? MANTINE_THEMES[0];
|
|
const NAV: { label: Page; icon: string }[] = [
|
|
{ label: "Dashboard", icon: "▦" },
|
|
{ label: "Users", icon: "◎" },
|
|
{ label: "Settings", icon: "⚙" },
|
|
];
|
|
return (
|
|
<div className="flex h-full font-sans text-sm" style={{ background: "#f8f9fa" }}>
|
|
<div className="w-44 flex flex-col py-4 px-3 gap-0.5 shrink-0" style={{ background: "#fff", borderRight: "1px solid #e9ecef" }}>
|
|
<div className="flex items-center gap-2 px-2 mb-4">
|
|
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
|
<span className="font-bold text-xs" style={{ color: theme.activeFg }}>Acme Inc</span>
|
|
</div>
|
|
{NAV.map(({ label, icon }) => (
|
|
<button key={label} onClick={() => setPage(label)}
|
|
className="flex items-center gap-2 px-2 py-2 rounded text-xs cursor-pointer w-full text-left"
|
|
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"#495057"}}>
|
|
<span>{icon}</span>{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="flex-1 flex flex-col min-w-0">
|
|
<div className="h-12 flex items-center justify-between px-5 shrink-0" style={{ background:"#fff", borderBottom:"1px solid #e9ecef" }}>
|
|
<span className="font-bold text-sm" style={{ color: "#212529" }}>{page}</span>
|
|
<div className="flex items-center gap-2">
|
|
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ border:"1px solid #dee2e6", color:"#495057" }}>Export</button>
|
|
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
|
</div>
|
|
</div>
|
|
{page === "Dashboard" && <MantineDashboard t={theme} />}
|
|
{page === "Users" && <MantineUsers t={theme} />}
|
|
{page === "Settings" && <MantineSettings t={theme} />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HeroUIDashboard() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#fafafa" }}>
|
|
<div className="grid grid-cols-3 gap-3 mb-4">
|
|
{["Revenue","Users","Conversion"].map((label,i)=>(
|
|
<div key={label} className="rounded-2xl p-3" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<p className="text-[10px] mb-1" style={{ color:"#a1a1aa" }}>{label}</p>
|
|
<p className="text-lg font-bold" style={{ color:"#18181b" }}>{["$12,400","2,841","18.2%"][i]}</p>
|
|
<p className="text-[10px]" style={{ color:"#a855f7" }}>+{[12,8,3][i]}% this month</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="rounded-2xl mb-3" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #f4f4f5" }}><span className="text-xs font-semibold" style={{ color:"#18181b" }}>Revenue</span></div>
|
|
<div className="p-4 flex items-end gap-1.5 h-24">
|
|
{[40,60,45,75,65,85,70,90,55,80,75,95].map((h,i)=>(
|
|
<div key={i} className="flex-1 rounded-md" style={{ height:`${h}%`, background:i===11?"linear-gradient(180deg,#a855f7,#ec4899)":"rgba(168,85,247,0.15)" }} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="rounded-2xl" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #f4f4f5" }}><span className="text-xs font-semibold" style={{ color:"#18181b" }}>Recent activity</span></div>
|
|
{TABLE_ROWS.slice(0,3).map(r=>(
|
|
<div key={r.name} className="flex items-center gap-3 px-4 py-2.5" style={{ borderBottom:"1px solid #fafafa" }}>
|
|
<div className="w-7 h-7 rounded-full shrink-0" style={{ background:"linear-gradient(135deg,rgba(168,85,247,0.2),rgba(236,72,153,0.2))" }} />
|
|
<div className="flex-1"><p className="text-[11px] font-semibold" style={{ color:"#18181b" }}>{r.name}</p><p className="text-[10px]" style={{ color:"#a1a1aa" }}>{r.email}</p></div>
|
|
<span className="px-2 py-0.5 rounded-full text-[9px] font-semibold" style={r.status==="Active"?{background:"rgba(34,197,94,0.1)",color:"#16a34a"}:{background:"#f4f4f5",color:"#a1a1aa"}}>{r.status}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HeroUIUsers() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#fafafa" }}>
|
|
<div className="rounded-2xl" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<div className="px-4 py-3 flex items-center justify-between" style={{ borderBottom:"1px solid #f4f4f5" }}>
|
|
<span className="text-xs font-semibold" style={{ color:"#18181b" }}>Team members</span>
|
|
<div className="flex gap-2">
|
|
<input className="h-7 px-3 rounded-full text-[11px]" style={{ border:"1px solid #e4e4e7", outline:"none", width:130 }} placeholder="Search..." />
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-semibold text-white" style={{ background:"linear-gradient(135deg,#7c3aed,#ec4899)" }}>+ Invite</button>
|
|
</div>
|
|
</div>
|
|
<table className="w-full text-[11px]">
|
|
<thead><tr style={{ borderBottom:"1px solid #f4f4f5" }}>{["Member","Role","Status","Joined",""].map(h=><th key={h} className="px-4 py-2.5 text-left text-[10px] font-medium" style={{ color:"#a1a1aa" }}>{h}</th>)}</tr></thead>
|
|
<tbody>{TABLE_ROWS.map(r=>(
|
|
<tr key={r.name} style={{ borderBottom:"1px solid #fafafa" }}>
|
|
<td className="px-4 py-3"><div className="flex items-center gap-2.5"><div className="w-7 h-7 rounded-full" style={{ background:"linear-gradient(135deg,rgba(168,85,247,0.2),rgba(236,72,153,0.2))" }} /><div><p className="font-semibold" style={{ color:"#18181b" }}>{r.name}</p><p style={{ color:"#a1a1aa" }}>{r.email}</p></div></div></td>
|
|
<td className="px-4 py-3"><span className="px-2 py-0.5 rounded-full text-[9px]" style={{ background:"rgba(124,58,237,0.1)", color:"#7c3aed" }}>{r.role}</span></td>
|
|
<td className="px-4 py-3"><span className="px-2 py-0.5 rounded-full text-[9px] font-semibold" style={r.status==="Active"?{background:"rgba(34,197,94,0.1)",color:"#16a34a"}:r.status==="Pending"?{background:"rgba(234,179,8,0.1)",color:"#a16207"}:{background:"#f4f4f5",color:"#a1a1aa"}}>{r.status}</span></td>
|
|
<td className="px-4 py-3" style={{ color:"#d4d4d8" }}>{r.date}</td>
|
|
<td className="px-4 py-3"><button className="h-6 px-2.5 rounded-full text-[10px]" style={{ background:"rgba(124,58,237,0.08)", color:"#7c3aed" }}>Manage</button></td>
|
|
</tr>
|
|
))}</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function HeroUISettings() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#fafafa" }}>
|
|
<div className="rounded-2xl mb-3" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #f4f4f5" }}><span className="text-xs font-semibold" style={{ color:"#18181b" }}>Profile</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{[{l:"Workspace name",v:"Acme Inc"},{l:"Slug",v:"acme-inc"},{l:"Email",v:"admin@acme.com"}].map(f=>(
|
|
<div key={f.l}>
|
|
<label className="text-[10px] font-semibold block mb-1" style={{ color:"#71717a" }}>{f.l}</label>
|
|
<input defaultValue={f.v} className="w-full h-8 px-3 rounded-xl text-[11px]" style={{ border:"1px solid #e4e4e7", outline:"none" }} />
|
|
</div>
|
|
))}
|
|
<button className="h-8 px-4 rounded-full text-[11px] font-semibold text-white" style={{ background:"linear-gradient(135deg,#7c3aed,#ec4899)" }}>Save changes</button>
|
|
</div>
|
|
</div>
|
|
<div className="rounded-2xl" style={{ background:"#fff", boxShadow:"0 2px 8px rgba(0,0,0,0.06)", border:"1px solid #f4f4f5" }}>
|
|
<div className="px-4 py-3" style={{ borderBottom:"1px solid #f4f4f5" }}><span className="text-xs font-semibold" style={{ color:"#18181b" }}>Notifications</span></div>
|
|
<div className="p-4 space-y-3">
|
|
{["Email digests","Push notifications","Security alerts","Product updates"].map((label,i)=>(
|
|
<div key={label} className="flex items-center justify-between">
|
|
<p className="text-[11px]" style={{ color:"#18181b" }}>{label}</p>
|
|
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background:[true,false,true,false][i]?"linear-gradient(135deg,#7c3aed,#ec4899)":"#e4e4e7" }}>
|
|
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform:[true,false,true,false][i]?"translateX(16px)":"translateX(2px)" }} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function WebAppHeroUI({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const [page, setPage] = useState<Page>("Dashboard");
|
|
const theme = themeColor ?? HEROUI_THEMES[0];
|
|
const NAV: { label: Page; icon: string }[] = [
|
|
{ label: "Dashboard", icon: "▦" },
|
|
{ label: "Users", icon: "◎" },
|
|
{ label: "Settings", icon: "⚙" },
|
|
];
|
|
return (
|
|
<div className="flex h-full font-sans text-sm" style={{ background: "#fafafa" }}>
|
|
<div className="w-44 flex flex-col py-4 px-3 gap-1 shrink-0" style={{ background: "linear-gradient(180deg,#1a1a2e,#16213e)", color: "#fff" }}>
|
|
<div className="flex items-center gap-2 px-2 mb-4">
|
|
<div className="w-6 h-6 rounded-full" style={{ background: theme.primary }} />
|
|
<span className="font-bold text-xs text-white">Acme Inc</span>
|
|
</div>
|
|
{NAV.map(({ label, icon }) => (
|
|
<button key={label} onClick={() => setPage(label)}
|
|
className="flex items-center gap-2 px-2 py-2 rounded-xl text-xs cursor-pointer w-full text-left"
|
|
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"rgba(255,255,255,0.5)"}}>
|
|
<span>{icon}</span>{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="flex-1 flex flex-col min-w-0">
|
|
<div className="h-12 flex items-center justify-between px-5 bg-white shrink-0" style={{ borderBottom: "1px solid #f0f0f0" }}>
|
|
<span className="font-bold text-sm" style={{ color: theme.primary }}>{page}</span>
|
|
<div className="flex items-center gap-2">
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-medium" style={{ border:"1px solid #e4e4e7", color:"#71717a" }}>Export</button>
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-semibold" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
|
</div>
|
|
</div>
|
|
{page === "Dashboard" && <HeroUIDashboard />}
|
|
{page === "Users" && <HeroUIUsers />}
|
|
{page === "Settings" && <HeroUISettings />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TremorDashboard() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f9fafb" }}>
|
|
<div className="grid grid-cols-3 gap-3 mb-4">
|
|
{[{l:"Revenue",v:"$12,400",c:"#2563eb",bg:"#dbeafe",pct:65},{l:"Users",v:"2,841",c:"#7c3aed",bg:"#ede9fe",pct:48},{l:"Conversion",v:"18.2%",c:"#059669",bg:"#d1fae5",pct:72}].map(item=>(
|
|
<div key={item.l} className="rounded-xl p-3 bg-white" style={{ border:"1px solid #e5e7eb" }}>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<p className="text-[10px] font-medium" style={{ color:"#6b7280" }}>{item.l}</p>
|
|
<div className="w-5 h-5 rounded" style={{ background:item.bg, display:"flex", alignItems:"center", justifyContent:"center" }}><div className="w-2 h-2 rounded-sm" style={{ background:item.c }} /></div>
|
|
</div>
|
|
<p className="text-lg font-bold" style={{ color:"#111827" }}>{item.v}</p>
|
|
<div className="mt-2 h-1.5 rounded-full" style={{ background:"#f3f4f6" }}><div className="h-full rounded-full" style={{ width:`${item.pct}%`, background:item.c }} /></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="bg-white rounded-xl p-4 mb-3" style={{ border:"1px solid #e5e7eb" }}>
|
|
<p className="text-xs font-semibold mb-3" style={{ color:"#111827" }}>Revenue over time</p>
|
|
<div className="flex items-end gap-1.5 h-20">
|
|
{[40,65,55,80,70,90,75,85,60,95,80,100].map((h,i)=>(
|
|
<div key={i} className="flex-1 rounded-t" style={{ height:`${h}%`, background:i===11?"#2563eb":"#dbeafe" }} />
|
|
))}
|
|
</div>
|
|
<div className="flex justify-between mt-1">
|
|
{["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"].map(m=><span key={m} className="text-[8px]" style={{ color:"#9ca3af" }}>{m}</span>)}
|
|
</div>
|
|
</div>
|
|
<div className="bg-white rounded-xl p-3" style={{ border:"1px solid #e5e7eb" }}>
|
|
{TABLE_ROWS.slice(0,3).map(r=>(
|
|
<div key={r.name} className="flex items-center gap-3 py-2 border-b last:border-0" style={{ borderColor:"#f3f4f6" }}>
|
|
<div className="w-2 h-2 rounded-full" style={{ background:r.status==="Active"?"#059669":"#d1d5db" }} />
|
|
<span className="text-[11px] flex-1" style={{ color:"#111827" }}>{r.name}</span>
|
|
<span className="text-[10px]" style={{ color:"#6b7280" }}>{r.role}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TremorUsers() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f9fafb" }}>
|
|
<div className="grid grid-cols-2 gap-3 mb-4">
|
|
{[{l:"Total",v:"2,841",c:"#2563eb"},{l:"Active",v:"1,940",c:"#059669"},{l:"Pending",v:"647",c:"#d97706"},{l:"Inactive",v:"254",c:"#6b7280"}].map(s=>(
|
|
<div key={s.l} className="rounded-xl p-3 bg-white flex items-center gap-3" style={{ border:"1px solid #e5e7eb" }}>
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center" style={{ background:`${s.c}18` }}><div className="w-3 h-3 rounded-full" style={{ background:s.c }} /></div>
|
|
<div><p className="text-[10px]" style={{ color:"#6b7280" }}>{s.l}</p><p className="text-sm font-bold" style={{ color:"#111827" }}>{s.v}</p></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="bg-white rounded-xl" style={{ border:"1px solid #e5e7eb" }}>
|
|
<div className="px-4 py-3 flex items-center justify-between" style={{ borderBottom:"1px solid #f3f4f6" }}>
|
|
<span className="text-xs font-semibold" style={{ color:"#111827" }}>All users</span>
|
|
<input className="h-7 px-3 rounded-lg text-[11px]" style={{ border:"1px solid #e5e7eb", outline:"none", width:130 }} placeholder="Filter..." />
|
|
</div>
|
|
<table className="w-full text-[11px]">
|
|
<thead><tr style={{ borderBottom:"1px solid #f3f4f6" }}>{["Name","Role","Status","Date"].map(h=><th key={h} className="px-4 py-2 text-left text-[10px] font-medium" style={{ color:"#9ca3af" }}>{h}</th>)}</tr></thead>
|
|
<tbody>{TABLE_ROWS.map(r=>(
|
|
<tr key={r.name} style={{ borderBottom:"1px solid #f9fafb" }}>
|
|
<td className="px-4 py-2.5 font-medium" style={{ color:"#111827" }}>{r.name}</td>
|
|
<td className="px-4 py-2.5" style={{ color:"#6b7280" }}>{r.role}</td>
|
|
<td className="px-4 py-2.5"><div className="flex items-center gap-1.5"><div className="w-1.5 h-1.5 rounded-full" style={{ background:r.status==="Active"?"#059669":r.status==="Pending"?"#d97706":"#d1d5db" }} /><span style={{ color:"#374151" }}>{r.status}</span></div></td>
|
|
<td className="px-4 py-2.5" style={{ color:"#9ca3af" }}>{r.date}</td>
|
|
</tr>
|
|
))}</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function TremorSettings() {
|
|
return (
|
|
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f9fafb" }}>
|
|
<div className="bg-white rounded-xl mb-3 p-4" style={{ border:"1px solid #e5e7eb" }}>
|
|
<p className="text-xs font-semibold mb-3" style={{ color:"#111827" }}>Workspace</p>
|
|
<div className="space-y-3">
|
|
{[{l:"Name",v:"Acme Inc"},{l:"Domain",v:"acme.com"},{l:"Timezone",v:"UTC-8"}].map(f=>(
|
|
<div key={f.l}>
|
|
<label className="text-[10px] font-medium block mb-1" style={{ color:"#6b7280" }}>{f.l}</label>
|
|
<input defaultValue={f.v} className="w-full h-8 px-3 rounded-lg text-[11px]" style={{ border:"1px solid #e5e7eb", outline:"none" }} />
|
|
</div>
|
|
))}
|
|
<button className="h-8 px-4 rounded-lg text-[11px] font-medium text-white" style={{ background:"#2563eb" }}>Save</button>
|
|
</div>
|
|
</div>
|
|
<div className="bg-white rounded-xl p-4" style={{ border:"1px solid #e5e7eb" }}>
|
|
<p className="text-xs font-semibold mb-3" style={{ color:"#111827" }}>Alerts</p>
|
|
{["Email digests","Anomaly alerts","Weekly report","API warnings"].map((label,i)=>(
|
|
<div key={label} className="flex items-center justify-between py-2 border-b last:border-0" style={{ borderColor:"#f3f4f6" }}>
|
|
<p className="text-[11px]" style={{ color:"#374151" }}>{label}</p>
|
|
<div className="w-8 h-4 rounded-full relative" style={{ background:[true,true,false,true][i]?"#2563eb":"#e5e7eb" }}>
|
|
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform:[true,true,false,true][i]?"translateX(16px)":"translateX(2px)" }} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function WebAppTremor({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const [page, setPage] = useState<Page>("Dashboard");
|
|
const theme = themeColor ?? TREMOR_THEMES[0];
|
|
const NAV: { label: Page; icon: string }[] = [
|
|
{ label: "Dashboard", icon: "▦" },
|
|
{ label: "Users", icon: "◎" },
|
|
{ label: "Settings", icon: "⚙" },
|
|
];
|
|
return (
|
|
<div className="flex h-full font-sans text-sm bg-white">
|
|
<div className="w-44 border-r flex flex-col py-4 px-3 gap-1 shrink-0" style={{ background:"#fff" }}>
|
|
<div className="flex items-center gap-2 px-2 mb-4">
|
|
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
|
<span className="font-bold text-xs" style={{ color: theme.activeFg }}>Acme Inc</span>
|
|
</div>
|
|
{NAV.map(({ label, icon }) => (
|
|
<button key={label} onClick={() => setPage(label)}
|
|
className="flex items-center gap-2 px-2 py-2 rounded text-xs cursor-pointer w-full text-left"
|
|
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"#6b7280"}}>
|
|
<span>{icon}</span>{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="flex-1 flex flex-col min-w-0" style={{ background:"#f9fafb" }}>
|
|
<div className="h-12 flex items-center justify-between px-5 bg-white shrink-0" style={{ borderBottom:"1px solid #e5e7eb" }}>
|
|
<span className="font-bold text-sm" style={{ color:"#111827" }}>{page}</span>
|
|
<div className="flex items-center gap-2">
|
|
<button className="h-7 px-3 rounded-lg text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
|
</div>
|
|
</div>
|
|
{page === "Dashboard" && <TremorDashboard />}
|
|
{page === "Users" && <TremorUsers />}
|
|
{page === "Settings" && <TremorSettings />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MARKETING scaffolds
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function MarketingDaisy({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const theme = themeColor ?? DAISY_THEMES[0];
|
|
const textColor = theme.textColor ?? "#f8f8f2";
|
|
const mutedText = theme.mutedText ?? "rgba(255,255,255,0.5)";
|
|
const cardBg = theme.cardBg ?? "rgba(255,255,255,0.05)";
|
|
const borderColor = theme.borderColor ?? "rgba(255,255,255,0.1)";
|
|
const bg = theme.bg ?? "#1d232a";
|
|
|
|
return (
|
|
<div className="h-full font-sans" style={{ background: bg, color: textColor }}>
|
|
<nav className="flex items-center justify-between px-6 py-3" style={{ borderBottom:`1px solid ${borderColor}` }}>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
|
<span className="font-bold text-sm" style={{ color: textColor }}>Acme</span>
|
|
</div>
|
|
<div className="flex gap-4 text-xs" style={{ color: mutedText }}>
|
|
{["Features","Pricing","Docs","Blog"].map(i=><span key={i}>{i}</span>)}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-bold" style={{ background:`${borderColor}`, color: textColor }}>Login</button>
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-bold" style={{ background: theme.primary, color: theme.primaryFg }}>Get started</button>
|
|
</div>
|
|
</nav>
|
|
<div className="px-10 pt-10 pb-6 text-center">
|
|
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-bold mb-4" style={{ background:`${theme.primary}22`, color: theme.primary, border:`1px solid ${theme.primary}44` }}>
|
|
✦ {theme.label} theme — v2.0
|
|
</div>
|
|
<h1 className="text-2xl font-black mb-2" style={{ color: textColor }}>Build faster,<br />ship smarter</h1>
|
|
<p className="text-xs mb-6 mx-auto" style={{ color: mutedText, maxWidth:280 }}>The all-in-one platform that helps teams build, launch, and scale their products.</p>
|
|
<div className="flex gap-2 justify-center mb-6">
|
|
<button className="h-9 px-5 rounded-full text-xs font-bold" style={{ background: theme.primary, color: theme.primaryFg }}>Start for free</button>
|
|
<button className="h-9 px-5 rounded-full text-xs font-bold" style={{ border:`1px solid ${borderColor}`, color: textColor }}>See demo →</button>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-3 px-8">
|
|
{[{icon:"⚡",title:"Lightning fast",desc:"Deploy in seconds, not hours"},{icon:"🔒",title:"Secure by default",desc:"Enterprise-grade security built in"},{icon:"📈",title:"Scales with you",desc:"From zero to millions of users"}].map(f=>(
|
|
<div key={f.title} className="p-3 rounded-xl text-center" style={{ background: cardBg, border:`1px solid ${borderColor}` }}>
|
|
<div className="text-xl mb-1">{f.icon}</div>
|
|
<p className="text-[11px] font-bold mb-0.5" style={{ color: textColor }}>{f.title}</p>
|
|
<p className="text-[10px]" style={{ color: mutedText }}>{f.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="px-8 mt-4">
|
|
<div className="rounded-xl p-4" style={{ background: cardBg, border:`1px solid ${borderColor}` }}>
|
|
<div className="flex items-center gap-3 mb-3">
|
|
<div className="flex-1 h-8 rounded-lg" style={{ background: borderColor }} />
|
|
<button className="h-8 px-3 rounded-lg text-[11px] font-bold" style={{ background: theme.primary, color: theme.primaryFg }}>Subscribe</button>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
{["badge","badge-outline","badge-soft"].map((b,i)=>(
|
|
<span key={b} className="px-2 py-0.5 rounded-full text-[9px] font-bold" style={i===0?{background:theme.primary,color:theme.primaryFg}:i===1?{border:`1px solid ${theme.primary}`,color:theme.primary}:{background:`${theme.primary}22`,color:theme.primary}}>{["Primary","Outline","Soft"][i]}</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Extended HeroUI marketing themes — these match heroui.com/themes prebuilt options
|
|
const HEROUI_MARKETING_THEMES: ThemeColor[] = [
|
|
{ id: "purple", label: "Purple", primary: "#7c3aed", primaryFg: "#fff", activeBg: "rgba(124,58,237,0.08)", activeFg: "#7c3aed", ring: "rgba(124,58,237,0.15)", bg: "#fff" },
|
|
{ id: "blue", label: "Blue", primary: "#2563eb", primaryFg: "#fff", activeBg: "rgba(37,99,235,0.08)", activeFg: "#2563eb", ring: "rgba(37,99,235,0.15)", bg: "#fff" },
|
|
{ id: "teal", label: "Teal", primary: "#0d9488", primaryFg: "#fff", activeBg: "rgba(13,148,136,0.08)", activeFg: "#0d9488", ring: "rgba(13,148,136,0.15)", bg: "#fff" },
|
|
{ id: "rose", label: "Rose", primary: "#e11d48", primaryFg: "#fff", activeBg: "rgba(225,29,72,0.08)", activeFg: "#e11d48", ring: "rgba(225,29,72,0.15)", bg: "#fff" },
|
|
{ id: "dark", label: "Dark", primary: "#7c3aed", primaryFg: "#fff", activeBg: "rgba(124,58,237,0.2)", activeFg: "#c084fc", ring: "rgba(124,58,237,0.3)", bg: "#09090b", cardBg: "#18181b", textColor: "#f4f4f5", borderColor: "#27272a", mutedText: "#71717a" },
|
|
{ id: "modern", label: "Modern", primary: "#06b6d4", primaryFg: "#fff", activeBg: "rgba(6,182,212,0.08)", activeFg: "#06b6d4", ring: "rgba(6,182,212,0.15)", bg: "#fff" },
|
|
];
|
|
|
|
export function MarketingHeroUI({ themeColor }: { themeColor?: ThemeColor }) {
|
|
const theme = themeColor ?? HEROUI_MARKETING_THEMES[0];
|
|
const isDark = theme.id === "dark";
|
|
const bg = theme.bg ?? "#fff";
|
|
const textColor = theme.textColor ?? "#18181b";
|
|
const mutedText = theme.mutedText ?? "#71717a";
|
|
const cardBg = theme.cardBg ?? "#fff";
|
|
const borderColor = theme.borderColor ?? "#f4f4f5";
|
|
|
|
return (
|
|
<div className="h-full font-sans" style={{ background: bg }}>
|
|
<nav className="flex items-center justify-between px-6 py-3" style={{ borderBottom:`1px solid ${borderColor}` }}>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-6 h-6 rounded-full" style={{ background: theme.primary }} />
|
|
<span className="font-bold text-xs" style={{ color: textColor }}>Acme</span>
|
|
</div>
|
|
<div className="flex gap-4 text-xs" style={{ color: mutedText }}>
|
|
{["Features","Pricing","Docs","Blog"].map(i=><span key={i}>{i}</span>)}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<button className="h-7 px-3 rounded-full text-[11px]" style={{ border:`1px solid ${borderColor}`, color: mutedText }}>Login</button>
|
|
<button className="h-7 px-3 rounded-full text-[11px] font-semibold text-white" style={{ background: theme.primary }}>Get started</button>
|
|
</div>
|
|
</nav>
|
|
<div className="px-10 pt-10 pb-4 text-center">
|
|
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-semibold mb-4" style={{ background: theme.activeBg, color: theme.activeFg, border:`1px solid ${theme.ring}` }}>
|
|
🚀 {theme.label} theme — v2.0
|
|
</div>
|
|
<h1 className="text-2xl font-black mb-2" style={{ color: theme.primary }}>
|
|
Build faster,<br />ship smarter
|
|
</h1>
|
|
<p className="text-xs mb-5 mx-auto" style={{ color: mutedText, maxWidth:280 }}>The all-in-one platform for teams that move fast.</p>
|
|
<div className="flex gap-2 justify-center mb-5">
|
|
<button className="h-9 px-5 rounded-full text-xs font-semibold shadow-lg" style={{ background: theme.primary, color: theme.primaryFg }}>Start for free</button>
|
|
<button className="h-9 px-5 rounded-full text-xs font-medium" style={{ border:`1px solid ${borderColor}`, color: mutedText }}>Live demo →</button>
|
|
</div>
|
|
<div className="rounded-2xl mx-auto overflow-hidden shadow-xl" style={{ maxWidth:400, border:`1px solid ${borderColor}`, background: cardBg }}>
|
|
<div className="h-6 flex items-center gap-1.5 px-3" style={{ background: isDark ? "#27272a" : "#fafafa", borderBottom:`1px solid ${borderColor}` }}>
|
|
{["#ff5f56","#ffbd2e","#27c93f"].map(c=><div key={c} className="w-2 h-2 rounded-full" style={{ background:c }} />)}
|
|
</div>
|
|
<div className="p-4">
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{["Revenue","Users","Growth","Churn"].map((m,i)=>(
|
|
<div key={m} className="rounded-xl p-2.5" style={{ background: theme.activeBg, border:`1px solid ${theme.ring}` }}>
|
|
<p className="text-[9px]" style={{ color: mutedText }}>{m}</p>
|
|
<p className="text-sm font-bold" style={{ color: textColor }}>{["$12k","2.8k","+24%","2.1%"][i]}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function MarketingAceternity() {
|
|
return (
|
|
<div className="h-full font-sans" style={{ background:"#030303" }}>
|
|
<nav className="flex items-center justify-between px-6 py-3" style={{ borderBottom:"1px solid rgba(255,255,255,0.06)" }}>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-6 h-6 rounded" style={{ background:"linear-gradient(135deg,#a855f7,#3b82f6)" }} />
|
|
<span className="font-bold text-white text-sm">Acme</span>
|
|
</div>
|
|
<div className="flex gap-4 text-xs" style={{ color:"rgba(255,255,255,0.4)" }}>
|
|
{["Features","Pricing","Docs"].map(i=><span key={i}>{i}</span>)}
|
|
</div>
|
|
<button className="h-7 px-3 rounded text-[11px] font-medium text-white" style={{ background:"rgba(168,85,247,0.2)", border:"1px solid rgba(168,85,247,0.3)" }}>Get started</button>
|
|
</nav>
|
|
<div className="relative px-10 pt-10 text-center overflow-hidden">
|
|
<div className="absolute inset-0 pointer-events-none" style={{ background:"radial-gradient(ellipse 60% 40% at 50% 0%,rgba(168,85,247,0.15),transparent)" }} />
|
|
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-medium mb-4" style={{ border:"1px solid rgba(168,85,247,0.3)", color:"rgba(168,85,247,0.9)" }}>
|
|
✦ Open source · 12k GitHub stars
|
|
</div>
|
|
<h1 className="text-2xl font-black mb-2" style={{ background:"linear-gradient(180deg,#fff 0%,rgba(255,255,255,0.5) 100%)", WebkitBackgroundClip:"text", WebkitTextFillColor:"transparent" }}>
|
|
Build the future<br />of the web
|
|
</h1>
|
|
<p className="text-xs mb-6 mx-auto" style={{ color:"rgba(255,255,255,0.35)", maxWidth:260 }}>Beautifully crafted components built with Tailwind CSS and Framer Motion.</p>
|
|
<div className="flex gap-2 justify-center mb-6">
|
|
<button className="h-9 px-5 rounded text-xs font-semibold text-white relative overflow-hidden" style={{ background:"linear-gradient(135deg,#a855f7,#3b82f6)" }}>Get started →</button>
|
|
<button className="h-9 px-5 rounded text-xs font-medium" style={{ border:"1px solid rgba(255,255,255,0.1)", color:"rgba(255,255,255,0.6)" }}>View components</button>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-3 mx-auto" style={{ maxWidth:480 }}>
|
|
{["Animated","Accessible","Open source"].map((f,i)=>(
|
|
<div key={f} className="p-3 rounded-xl text-left" style={{ background:"rgba(255,255,255,0.03)", border:"1px solid rgba(255,255,255,0.06)" }}>
|
|
<div className="w-6 h-6 rounded mb-1.5 flex items-center justify-center" style={{ background:["rgba(168,85,247,0.15)","rgba(59,130,246,0.15)","rgba(34,197,94,0.15)"][i] }}>
|
|
<div className="w-2.5 h-2.5 rounded-full" style={{ background:["#a855f7","#3b82f6","#22c55e"][i] }} />
|
|
</div>
|
|
<p className="text-[10px] font-semibold text-white mb-0.5">{f}</p>
|
|
<p className="text-[9px]" style={{ color:"rgba(255,255,255,0.3)" }}>Built for production</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function MarketingTailwind() {
|
|
return (
|
|
<div className="h-full font-sans bg-white">
|
|
<nav className="flex items-center justify-between px-6 py-3 border-b">
|
|
<span className="font-black text-sm text-zinc-900">acme</span>
|
|
<div className="flex gap-4 text-xs text-zinc-500">
|
|
{["Features","Pricing","Blog"].map(i=><span key={i}>{i}</span>)}
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<button className="h-7 px-3 text-[11px] text-zinc-600">Log in</button>
|
|
<button className="h-7 px-3 rounded-md text-[11px] font-medium bg-zinc-900 text-white">Sign up</button>
|
|
</div>
|
|
</nav>
|
|
<div className="px-10 pt-10 pb-6">
|
|
<div className="max-w-sm">
|
|
<span className="text-[10px] font-semibold text-violet-600 tracking-widest uppercase">Now in public beta</span>
|
|
<h1 className="text-2xl font-black text-zinc-900 mt-1 mb-2 leading-tight">The platform<br />built for scale</h1>
|
|
<p className="text-xs text-zinc-500 mb-5 leading-relaxed">Everything your team needs to build, deploy, and monitor production applications.</p>
|
|
<div className="flex gap-2 mb-8">
|
|
<button className="h-8 px-4 rounded-md text-xs font-medium bg-zinc-900 text-white">Get started free</button>
|
|
<button className="h-8 px-4 rounded-md text-xs font-medium border border-zinc-200 text-zinc-600">Documentation →</button>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{["99.9% uptime SLA","10ms avg latency","SOC2 compliant","GDPR ready"].map(f=>(
|
|
<div key={f} className="flex items-center gap-1.5 text-[10px] text-zinc-500">
|
|
<div className="w-3.5 h-3.5 rounded-full bg-zinc-900 flex items-center justify-center">
|
|
<div className="w-1.5 h-1 border-l border-b border-white -rotate-45 translate-y-px" />
|
|
</div>
|
|
{f}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ADMIN scaffolds
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function AdminMantine() { return <WebAppMantine />; }
|
|
export function AdminShadcn() { return <WebAppShadcn />; }
|
|
export function AdminTremor() { return <WebAppTremor />; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MOBILE scaffolds
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function MobileFrame({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<div className="flex items-center justify-center h-full" style={{ background:"#f4f4f5" }}>
|
|
<div className="relative" style={{ width:200, height:380 }}>
|
|
<div className="absolute inset-0 rounded-3xl" style={{ background:"#1a1a1a", boxShadow:"0 20px 60px rgba(0,0,0,0.3)" }} />
|
|
<div className="absolute top-3 left-1/2 -translate-x-1/2 w-16 h-4 rounded-full bg-black z-10" />
|
|
<div className="absolute inset-2 rounded-3xl overflow-hidden bg-white">
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function MobileNativewind() {
|
|
return (
|
|
<MobileFrame>
|
|
<div className="h-full flex flex-col bg-white text-xs">
|
|
<div className="pt-6 pb-3 px-3" style={{ background:"#18181b" }}>
|
|
<p className="text-[8px] text-zinc-400">Good morning</p>
|
|
<p className="text-sm font-bold text-white">Dashboard</p>
|
|
</div>
|
|
<div className="flex-1 overflow-auto p-3 space-y-2" style={{ background:"#fafafa" }}>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{[{l:"Revenue",v:"$4.2k"},{l:"Users",v:"184"}].map(c=>(
|
|
<div key={c.l} className="bg-white rounded-xl p-2.5" style={{ border:"1px solid #e4e4e7" }}>
|
|
<p className="text-[8px] text-zinc-400">{c.l}</p>
|
|
<p className="text-sm font-bold text-zinc-900">{c.v}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{["Fix login bug","Update pricing","Review PR #42"].map((t,i)=>(
|
|
<div key={t} className="bg-white rounded-xl p-2.5 flex items-center gap-2" style={{ border:"1px solid #e4e4e7" }}>
|
|
<div className="w-4 h-4 rounded border border-zinc-300 shrink-0" style={i===0?{background:"#18181b",border:"none"}:{}} />
|
|
<p className="text-[9px] text-zinc-700" style={i===0?{textDecoration:"line-through",color:"#a1a1aa"}:{}}>{t}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex border-t bg-white" style={{ padding:"6px 0 10px" }}>
|
|
{["Home","Projects","Chat","Profile"].map((l,i)=>(
|
|
<div key={l} className="flex-1 flex flex-col items-center gap-0.5">
|
|
<div className="w-4 h-4 rounded" style={{ background:i===0?"#18181b":"#e4e4e7" }} />
|
|
<span className="text-[7px]" style={{ color:i===0?"#18181b":"#a1a1aa" }}>{l}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</MobileFrame>
|
|
);
|
|
}
|
|
|
|
export function MobileGluestack() {
|
|
return (
|
|
<MobileFrame>
|
|
<div className="h-full flex flex-col text-xs">
|
|
<div className="pt-6 pb-3 px-3 flex items-center justify-between" style={{ background:"#1976d2" }}>
|
|
<p className="text-sm font-bold text-white">Dashboard</p>
|
|
<div className="w-5 h-5 rounded-full bg-white/20" />
|
|
</div>
|
|
<div className="flex-1 overflow-auto p-3 space-y-2" style={{ background:"#f5f5f5" }}>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{[{l:"Revenue",v:"$4.2k",c:"#1976d2"},{l:"Users",v:"184",c:"#7b1fa2"}].map(c=>(
|
|
<div key={c.l} className="rounded-lg p-2.5" style={{ background:c.c }}>
|
|
<p className="text-[8px] text-white/70">{c.l}</p>
|
|
<p className="text-sm font-bold text-white">{c.v}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{["Fix login bug","Update pricing","Review PR #42"].map((t,i)=>(
|
|
<div key={t} className="bg-white rounded-lg p-2.5 flex items-center gap-2 shadow-sm">
|
|
<div className="w-4 h-4 rounded-full border-2 shrink-0" style={i===0?{background:"#1976d2",borderColor:"#1976d2"}:{borderColor:"#bdbdbd"}} />
|
|
<p className="text-[9px]" style={{ color:i===0?"#9e9e9e":"#212121", textDecoration:i===0?"line-through":"none" }}>{t}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex bg-white shadow-sm" style={{ padding:"6px 0 10px", borderTop:"1px solid #e0e0e0" }}>
|
|
{["Home","Tasks","Chat","Profile"].map((l,i)=>(
|
|
<div key={l} className="flex-1 flex flex-col items-center gap-0.5">
|
|
<div className="w-4 h-4 rounded" style={{ background:i===0?"#1976d2":"#e0e0e0" }} />
|
|
<span className="text-[7px]" style={{ color:i===0?"#1976d2":"#9e9e9e" }}>{l}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</MobileFrame>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// EMAIL scaffolds
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function EmailReactEmail() {
|
|
return (
|
|
<div className="flex items-start justify-center h-full py-6 font-sans" style={{ background:"#f6f9fc" }}>
|
|
<div className="w-full max-w-md" style={{ background:"#fff", borderRadius:8, boxShadow:"0 2px 8px rgba(0,0,0,0.08)" }}>
|
|
<div className="px-8 py-5" style={{ borderBottom:"1px solid #e6e6e6" }}>
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<div className="w-5 h-5 rounded" style={{ background:"#000" }} />
|
|
<span className="font-bold text-xs text-zinc-900">Acme</span>
|
|
</div>
|
|
</div>
|
|
<div className="px-8 py-6">
|
|
<p className="text-base font-bold text-zinc-900 mb-2">Welcome to Acme! 🎉</p>
|
|
<p className="text-xs text-zinc-500 mb-4 leading-relaxed">Hi Alice, thanks for signing up. Your account is ready and you can start building right away.</p>
|
|
<div className="text-center mb-4">
|
|
<button className="h-9 px-6 rounded text-xs font-semibold text-white" style={{ background:"#000" }}>Get started →</button>
|
|
</div>
|
|
<div className="rounded-lg p-3 mb-4" style={{ background:"#f6f9fc", border:"1px solid #e6e6e6" }}>
|
|
<p className="text-[10px] font-semibold text-zinc-700 mb-1">Your account details</p>
|
|
{["Plan: Starter","Workspace: alice-workspace","Status: Active"].map(d=>(
|
|
<p key={d} className="text-[10px] text-zinc-500">{d}</p>
|
|
))}
|
|
</div>
|
|
<p className="text-[10px] text-zinc-400 leading-relaxed">If you have any questions, reply to this email or visit our help center. We're here to help.</p>
|
|
</div>
|
|
<div className="px-8 py-4 text-center" style={{ borderTop:"1px solid #e6e6e6", background:"#fafafa" }}>
|
|
<p className="text-[9px] text-zinc-400">Acme Inc · 123 Main St · San Francisco CA · Unsubscribe</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DOCS scaffolds
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function DocsNextra() {
|
|
return (
|
|
<div className="flex h-full font-sans text-xs bg-white">
|
|
<div className="w-44 border-r flex flex-col py-4 px-3" style={{ background:"#fafafa" }}>
|
|
<div className="mb-4">
|
|
<p className="text-[9px] font-semibold text-zinc-400 uppercase tracking-wide mb-2">Getting Started</p>
|
|
{["Introduction","Installation","Quick Start"].map((item,i)=>(
|
|
<div key={item} className={`px-2 py-1 rounded text-[10px] cursor-pointer mb-0.5 ${i===0?"bg-zinc-900 text-white font-medium":"text-zinc-500 hover:text-zinc-700"}`}>{item}</div>
|
|
))}
|
|
</div>
|
|
<div className="mb-4">
|
|
<p className="text-[9px] font-semibold text-zinc-400 uppercase tracking-wide mb-2">Components</p>
|
|
{["Button","Card","Input","Modal","Table"].map(item=>(
|
|
<div key={item} className="px-2 py-1 rounded text-[10px] cursor-pointer text-zinc-500 hover:text-zinc-700 mb-0.5">{item}</div>
|
|
))}
|
|
</div>
|
|
<div>
|
|
<p className="text-[9px] font-semibold text-zinc-400 uppercase tracking-wide mb-2">API Reference</p>
|
|
{["REST API","Webhooks"].map(item=>(
|
|
<div key={item} className="px-2 py-1 text-[10px] text-zinc-500 mb-0.5">{item}</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="flex-1 overflow-auto">
|
|
<div className="max-w-lg px-8 py-6">
|
|
<div className="flex items-center gap-1.5 text-[9px] text-zinc-400 mb-4">
|
|
<span>Docs</span><span>/</span><span>Getting Started</span><span>/</span><span className="text-zinc-600">Introduction</span>
|
|
</div>
|
|
<h1 className="text-base font-bold text-zinc-900 mb-2">Introduction</h1>
|
|
<p className="text-[11px] text-zinc-600 mb-3 leading-relaxed">Acme UI is a collection of re-usable components that you can copy and paste into your web apps. Built with Radix UI and Tailwind CSS.</p>
|
|
<div className="rounded-lg p-3 mb-3" style={{ background:"#18181b" }}>
|
|
<p className="text-[10px] font-mono" style={{ color:"#10b981" }}>$ npm install acme-ui</p>
|
|
</div>
|
|
<h2 className="text-sm font-semibold text-zinc-900 mb-1.5">Key features</h2>
|
|
{["Accessible","Customisable","Open source","TypeScript ready"].map(f=>(
|
|
<div key={f} className="flex items-center gap-1.5 text-[10px] text-zinc-600 mb-1">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-zinc-400" />{f}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function DocsShadcnCustom() {
|
|
return (
|
|
<div className="flex h-full font-sans text-xs bg-white">
|
|
<div className="w-44 border-r flex flex-col py-4 px-3">
|
|
<div className="mb-4 pb-3 border-b">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-5 h-5 rounded bg-zinc-900" />
|
|
<span className="font-bold text-zinc-900 text-[11px]">Acme Docs</span>
|
|
</div>
|
|
</div>
|
|
{[{section:"Guides",items:["Introduction","Installation","Theming"]},{section:"Components",items:["Button","Input","Select","Dialog"]}].map(g=>(
|
|
<div key={g.section} className="mb-3">
|
|
<p className="text-[9px] font-semibold text-zinc-400 uppercase tracking-wide mb-1.5">{g.section}</p>
|
|
{g.items.map((item,i)=>(
|
|
<div key={item} className={`px-2 py-1 rounded-md text-[10px] mb-0.5 cursor-pointer ${i===0&&g.section==="Guides"?"bg-zinc-100 text-zinc-900 font-medium":"text-zinc-500"}`}>{item}</div>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex-1 px-8 py-6 overflow-auto">
|
|
<h1 className="text-base font-bold text-zinc-900 mb-1">Introduction</h1>
|
|
<p className="text-[11px] text-zinc-500 mb-4 leading-relaxed">A set of beautifully designed components built with Radix UI and Tailwind CSS.</p>
|
|
<div className="rounded-lg border overflow-hidden mb-4">
|
|
<div className="flex items-center gap-2 px-3 py-2 border-b bg-zinc-50">
|
|
<span className="text-[9px] font-medium text-zinc-500">bash</span>
|
|
</div>
|
|
<div className="px-3 py-2.5 bg-zinc-950">
|
|
<p className="text-[10px] font-mono text-emerald-400">npx shadcn@latest init</p>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{["Button","Card","Input","Badge"].map(c=>(
|
|
<div key={c} className="rounded-lg border p-3 cursor-pointer hover:bg-zinc-50 transition-colors">
|
|
<p className="text-[10px] font-semibold text-zinc-700 mb-0.5">{c}</p>
|
|
<p className="text-[9px] text-zinc-400">View component →</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Registry — maps surface+theme to scaffold component
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const SCAFFOLD_REGISTRY: Record<string, Record<string, React.ComponentType<{ themeColor?: ThemeColor }>>> = {
|
|
"web-app": {
|
|
"shadcn": WebAppShadcn,
|
|
"mantine": WebAppMantine,
|
|
"hero-ui": WebAppHeroUI,
|
|
"tremor": WebAppTremor,
|
|
},
|
|
"marketing": {
|
|
"daisy-ui": MarketingDaisy,
|
|
"hero-ui": MarketingHeroUI,
|
|
"aceternity": MarketingAceternity,
|
|
"tailwind-only": MarketingTailwind,
|
|
},
|
|
"admin": {
|
|
"mantine": AdminMantine,
|
|
"shadcn": AdminShadcn,
|
|
"tremor": AdminTremor,
|
|
},
|
|
"mobile": {
|
|
"nativewind": MobileNativewind,
|
|
"gluestack": MobileGluestack,
|
|
},
|
|
"email": {
|
|
"react-email": EmailReactEmail,
|
|
},
|
|
"docs": {
|
|
"nextra": DocsNextra,
|
|
"shadcn": DocsShadcnCustom,
|
|
},
|
|
};
|
|
|
|
// Maps surface → library → available theme colors for that library
|
|
export const THEME_REGISTRY: Record<string, Record<string, ThemeColor[]>> = {
|
|
"web-app": {
|
|
"shadcn": SHADCN_THEMES,
|
|
"mantine": MANTINE_THEMES,
|
|
"hero-ui": HEROUI_THEMES,
|
|
"tremor": TREMOR_THEMES,
|
|
},
|
|
"marketing": {
|
|
"daisy-ui": DAISY_THEMES,
|
|
"hero-ui": HEROUI_MARKETING_THEMES,
|
|
},
|
|
};
|