copy-schema-button.tsx54 lines · main
1'use client';
2
3import { useState } from 'react';
4
5export function CopySchemaButton({ projectId }: { projectId: string }) {
6 const [state, setState] = useState<'idle' | 'loading' | 'copied' | 'error'>('idle');
7 const [errorMsg, setErrorMsg] = useState<string | null>(null);
8
9 async function onClick() {
10 setState('loading');
11 setErrorMsg(null);
12 try {
13 const res = await fetch(`/api/v1/projects/${projectId}/studio/schema.ts`);
14 if (!res.ok) {
15 const body = await res.text().catch(() => '');
16 throw new Error(body || `http ${res.status}`);
17 }
18 const text = await res.text();
19 await navigator.clipboard.writeText(text);
20 setState('copied');
21 setTimeout(() => setState('idle'), 2000);
22 } catch (err) {
23 setState('error');
24 setErrorMsg(err instanceof Error ? err.message : 'copy failed');
25 setTimeout(() => setState('idle'), 3000);
26 }
27 }
28
29 const label =
30 state === 'loading'
31 ? 'generating…'
32 : state === 'copied'
33 ? 'copied!'
34 : state === 'error'
35 ? 'failed'
36 : 'copy as schema.ts';
37
38 return (
39 <div className="flex flex-col items-end gap-1">
40 <button
41 type="button"
42 onClick={onClick}
43 disabled={state === 'loading'}
44 className="rounded-md border border-[var(--color-border)] px-3 py-1.5 font-mono text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text)] disabled:opacity-50"
45 title="generate the equivalent briven/schema.ts and copy to clipboard"
46 >
47 {label}
48 </button>
49 {errorMsg ? (
50 <p className="font-mono text-[10px] text-red-400">{errorMsg}</p>
51 ) : null}
52 </div>
53 );
54}