fix(pitch-admin): render JSONB arrays as inline table editors
Some checks failed
Build pitch-deck / build-and-push (push) Failing after 57s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 32s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 30s
CI / Deploy (push) Failing after 3s
Some checks failed
Build pitch-deck / build-and-push (push) Failing after 57s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 32s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 30s
CI / Deploy (push) Failing after 3s
Arrays of objects (funding_schedule, founder_salary_schedule, etc.) now render as editable tables with per-field inputs, add/remove row buttons, instead of a raw JSON string in a single text input. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,92 @@ export default function EditScenarioPage() {
|
||||
<div className="space-y-3">
|
||||
{items.map(a => {
|
||||
const isEdited = edits[a.id] !== undefined
|
||||
// Detect arrays of objects for structured editing
|
||||
const isObjectArray = Array.isArray(a.value) && a.value.length > 0 && typeof a.value[0] === 'object' && a.value[0] !== null
|
||||
|
||||
if (isObjectArray) {
|
||||
const rows = isEdited ? (JSON.parse(edits[a.id]) as Record<string, unknown>[]) : (a.value as Record<string, unknown>[])
|
||||
const cols = Object.keys(rows[0] || {})
|
||||
|
||||
return (
|
||||
<div key={a.id} className="border border-white/[0.06] rounded-xl overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2.5 bg-white/[0.02]">
|
||||
<div>
|
||||
<span className="text-sm text-white/90">{a.label_en || a.label_de}</span>
|
||||
<span className="text-xs text-white/40 font-mono ml-2">{a.key}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newRow: Record<string, unknown> = {}
|
||||
cols.forEach(c => { newRow[c] = typeof rows[0][c] === 'number' ? 0 : '' })
|
||||
const updated = [...rows, newRow]
|
||||
setEdit(a.id, JSON.stringify(updated))
|
||||
}}
|
||||
className="text-[10px] px-2 py-1 rounded bg-white/[0.06] text-white/60 hover:text-white hover:bg-white/[0.1]"
|
||||
>
|
||||
+ Row
|
||||
</button>
|
||||
{isEdited && (
|
||||
<button
|
||||
onClick={() => saveAssumption(a)}
|
||||
disabled={savingId === a.id}
|
||||
className="bg-indigo-500 hover:bg-indigo-600 text-white text-[10px] px-2.5 py-1 rounded flex items-center gap-1 disabled:opacity-50"
|
||||
>
|
||||
<Save className="w-3 h-3" /> Save
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="border-b border-white/[0.06]">
|
||||
{cols.map(c => (
|
||||
<th key={c} className="text-left py-2 px-3 text-white/40 font-medium uppercase tracking-wider">{c}</th>
|
||||
))}
|
||||
<th className="w-8" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, ri) => (
|
||||
<tr key={ri} className="border-b border-white/[0.04] hover:bg-white/[0.02]">
|
||||
{cols.map(c => (
|
||||
<td key={c} className="py-1.5 px-3">
|
||||
<input
|
||||
type={typeof row[c] === 'number' ? 'number' : 'text'}
|
||||
value={row[c] as string | number}
|
||||
onChange={e => {
|
||||
const updated = rows.map((r, i) => {
|
||||
if (i !== ri) return r
|
||||
const val = typeof r[c] === 'number' ? Number(e.target.value) || 0 : e.target.value
|
||||
return { ...r, [c]: val }
|
||||
})
|
||||
setEdit(a.id, JSON.stringify(updated))
|
||||
}}
|
||||
className="w-full bg-transparent border-b border-transparent hover:border-white/10 focus:border-indigo-500/50 text-white font-mono py-0.5 focus:outline-none"
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
<td className="py-1.5 px-1">
|
||||
<button
|
||||
onClick={() => {
|
||||
const updated = rows.filter((_, i) => i !== ri)
|
||||
setEdit(a.id, JSON.stringify(updated))
|
||||
}}
|
||||
className="text-white/30 hover:text-rose-400 p-1"
|
||||
title="Remove row"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const currentValue = isEdited
|
||||
? edits[a.id]
|
||||
: typeof a.value === 'object'
|
||||
|
||||
Reference in New Issue
Block a user