initial project setup

Fastify + Prisma backend, React + Vite frontend, Docker deployment.
Multi-board feedback platform with anonymous cookie auth, passkey
upgrade path, ALTCHA spam protection, plugin system, and full
privacy-first architecture.
This commit is contained in:
2026-03-19 18:05:16 +02:00
commit f07eddf29e
77 changed files with 7031 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
interface Props {
title?: string
message?: string
actionLabel?: string
onAction?: () => void
}
export default function EmptyState({
title = 'Nothing here yet',
message = 'Be the first to share feedback',
actionLabel = 'Create a post',
onAction,
}: Props) {
return (
<div className="flex flex-col items-center justify-center py-16 px-4 fade-in">
{/* Megaphone SVG */}
<svg
width="120"
height="120"
viewBox="0 0 120 120"
fill="none"
className="mb-6"
>
<circle cx="60" cy="60" r="55" stroke="var(--text-tertiary)" strokeWidth="1" strokeDasharray="4 4" />
<path
d="M75 35L45 50H35a5 5 0 00-5 5v10a5 5 0 005 5h10l30 15V35z"
stroke="var(--accent)"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
<path
d="M85 48a10 10 0 010 24"
stroke="var(--accent)"
strokeWidth="2.5"
strokeLinecap="round"
fill="none"
opacity="0.6"
/>
<path
d="M92 40a20 20 0 010 40"
stroke="var(--accent)"
strokeWidth="2"
strokeLinecap="round"
fill="none"
opacity="0.3"
/>
<path
d="M42 70v10a5 5 0 005 5h5a5 5 0 005-5v-7"
stroke="var(--text-tertiary)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
</svg>
<h3
className="text-lg font-semibold mb-2"
style={{ fontFamily: 'var(--font-heading)', color: 'var(--text)' }}
>
{title}
</h3>
<p className="text-sm mb-6" style={{ color: 'var(--text-tertiary)' }}>
{message}
</p>
{onAction && (
<button onClick={onAction} className="btn btn-primary">
{actionLabel}
</button>
)}
</div>
)
}