Initial commit: Port Nimara CRM (Layers 0-4)
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled

Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:52:51 +01:00
commit 67d7e6e3d5
572 changed files with 86496 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
interface WebhookSecretDisplayProps {
/** Plaintext secret (shown once on creation). If undefined, shows masked. */
plaintext?: string;
/** Masked preview (always shown on view). */
masked: string;
}
export function WebhookSecretDisplay({ plaintext, masked }: WebhookSecretDisplayProps) {
const [copied, setCopied] = useState(false);
async function copySecret() {
if (!plaintext) return;
await navigator.clipboard.writeText(plaintext);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
if (plaintext) {
return (
<div className="space-y-2">
<div className="rounded-md bg-amber-50 border border-amber-200 p-3 text-sm text-amber-800">
<strong>Copy this secret now.</strong> It will not be shown again.
</div>
<div className="flex items-center gap-2">
<Input
readOnly
value={plaintext}
className="font-mono text-sm"
/>
<Button type="button" variant="outline" size="sm" onClick={copySecret}>
{copied ? 'Copied!' : 'Copy'}
</Button>
</div>
</div>
);
}
return (
<div className="flex items-center gap-2">
<Input
readOnly
value={masked}
className="font-mono text-sm text-muted-foreground"
/>
<span className="text-xs text-muted-foreground">Use &quot;Regenerate&quot; to get a new secret</span>
</div>
);
}