feat(pitch-deck): change preferred_lang for existing investors from detail page
Build pitch-deck / build-push-deploy (push) Successful in 1m27s
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 33s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 34s
Build pitch-deck / build-push-deploy (push) Successful in 1m27s
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 33s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 34s
- GET /api/admin/investors/:id now returns preferred_lang - PATCH /api/admin/investors/:id accepts preferred_lang (de/en), validates value - Investor detail page: DE/EN toggle in the Pitch Version card, instant save on click Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ export async function GET(request: NextRequest, ctx: RouteContext) {
|
|||||||
pool.query(
|
pool.query(
|
||||||
`SELECT i.id, i.email, i.name, i.company, i.status, i.last_login_at, i.login_count,
|
`SELECT i.id, i.email, i.name, i.company, i.status, i.last_login_at, i.login_count,
|
||||||
i.created_at, i.updated_at, i.first_activity_at, i.data_masked_at,
|
i.created_at, i.updated_at, i.first_activity_at, i.data_masked_at,
|
||||||
i.assigned_version_id, i.is_showcase,
|
i.assigned_version_id, i.is_showcase, i.preferred_lang,
|
||||||
v.name AS version_name, v.status AS version_status
|
v.name AS version_name, v.status AS version_status
|
||||||
FROM pitch_investors i
|
FROM pitch_investors i
|
||||||
LEFT JOIN pitch_versions v ON v.id = i.assigned_version_id
|
LEFT JOIN pitch_versions v ON v.id = i.assigned_version_id
|
||||||
@@ -68,14 +68,14 @@ export async function PATCH(request: NextRequest, ctx: RouteContext) {
|
|||||||
|
|
||||||
const { id } = await ctx.params
|
const { id } = await ctx.params
|
||||||
const body = await request.json().catch(() => ({}))
|
const body = await request.json().catch(() => ({}))
|
||||||
const { name, company, assigned_version_id, is_showcase } = body
|
const { name, company, assigned_version_id, is_showcase, preferred_lang } = body
|
||||||
|
|
||||||
if (name === undefined && company === undefined && assigned_version_id === undefined && is_showcase === undefined) {
|
if (name === undefined && company === undefined && assigned_version_id === undefined && is_showcase === undefined && preferred_lang === undefined) {
|
||||||
return NextResponse.json({ error: 'name, company, assigned_version_id, or is_showcase required' }, { status: 400 })
|
return NextResponse.json({ error: 'name, company, assigned_version_id, is_showcase, or preferred_lang required' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = await pool.query(
|
const before = await pool.query(
|
||||||
`SELECT name, company, assigned_version_id, is_showcase FROM pitch_investors WHERE id = $1`,
|
`SELECT name, company, assigned_version_id, is_showcase, preferred_lang FROM pitch_investors WHERE id = $1`,
|
||||||
[id],
|
[id],
|
||||||
)
|
)
|
||||||
if (before.rows.length === 0) {
|
if (before.rows.length === 0) {
|
||||||
@@ -98,8 +98,8 @@ export async function PATCH(request: NextRequest, ctx: RouteContext) {
|
|||||||
|
|
||||||
// Use null to clear version assignment, undefined to leave unchanged
|
// Use null to clear version assignment, undefined to leave unchanged
|
||||||
const versionValue = assigned_version_id === undefined ? before.rows[0].assigned_version_id : (assigned_version_id || null)
|
const versionValue = assigned_version_id === undefined ? before.rows[0].assigned_version_id : (assigned_version_id || null)
|
||||||
|
|
||||||
const showcaseValue = is_showcase !== undefined ? Boolean(is_showcase) : before.rows[0].is_showcase
|
const showcaseValue = is_showcase !== undefined ? Boolean(is_showcase) : before.rows[0].is_showcase
|
||||||
|
const langValue = preferred_lang === 'en' || preferred_lang === 'de' ? preferred_lang : before.rows[0].preferred_lang
|
||||||
|
|
||||||
const { rows } = await pool.query(
|
const { rows } = await pool.query(
|
||||||
`UPDATE pitch_investors SET
|
`UPDATE pitch_investors SET
|
||||||
@@ -107,10 +107,11 @@ export async function PATCH(request: NextRequest, ctx: RouteContext) {
|
|||||||
company = COALESCE($2, company),
|
company = COALESCE($2, company),
|
||||||
assigned_version_id = $4,
|
assigned_version_id = $4,
|
||||||
is_showcase = $5,
|
is_showcase = $5,
|
||||||
|
preferred_lang = $6,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = $3
|
WHERE id = $3
|
||||||
RETURNING id, email, name, company, status, assigned_version_id, is_showcase`,
|
RETURNING id, email, name, company, status, assigned_version_id, is_showcase, preferred_lang`,
|
||||||
[name ?? null, company ?? null, id, versionValue, showcaseValue],
|
[name ?? null, company ?? null, id, versionValue, showcaseValue, langValue],
|
||||||
)
|
)
|
||||||
|
|
||||||
const action = assigned_version_id !== undefined && assigned_version_id !== before.rows[0].assigned_version_id
|
const action = assigned_version_id !== undefined && assigned_version_id !== before.rows[0].assigned_version_id
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ interface InvestorDetail {
|
|||||||
version_name: string | null
|
version_name: string | null
|
||||||
version_status: string | null
|
version_status: string | null
|
||||||
is_showcase: boolean
|
is_showcase: boolean
|
||||||
|
preferred_lang: 'de' | 'en'
|
||||||
}
|
}
|
||||||
sessions: Array<{
|
sessions: Array<{
|
||||||
id: string
|
id: string
|
||||||
@@ -324,6 +325,39 @@ export default function InvestorDetailPage() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Default language */}
|
||||||
|
<div className="mt-4 pt-4 border-t border-white/[0.06]">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm text-white font-medium">Default pitch language</div>
|
||||||
|
<div className="text-xs text-white/40 mt-0.5">Language the deck opens in when the investor clicks the link</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex rounded-lg border border-white/10 overflow-hidden">
|
||||||
|
{(['de', 'en'] as const).map(l => (
|
||||||
|
<button
|
||||||
|
key={l}
|
||||||
|
disabled={busy}
|
||||||
|
onClick={async () => {
|
||||||
|
if (inv.preferred_lang === l) return
|
||||||
|
setBusy(true)
|
||||||
|
const res = await fetch(`/api/admin/investors/${id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ preferred_lang: l }),
|
||||||
|
})
|
||||||
|
setBusy(false)
|
||||||
|
if (res.ok) { flashToast(`Language set to ${l.toUpperCase()}`); load() }
|
||||||
|
else { flashToast('Update failed') }
|
||||||
|
}}
|
||||||
|
className={`px-4 py-1.5 text-sm font-medium transition-colors disabled:opacity-50 ${inv.preferred_lang === l ? 'bg-indigo-600 text-white' : 'bg-black/20 text-white/50 hover:text-white/80'}`}
|
||||||
|
>
|
||||||
|
{l === 'de' ? '🇩🇪 DE' : '🇬🇧 EN'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Showcase toggle */}
|
{/* Showcase toggle */}
|
||||||
<div className="flex items-center justify-between mt-4 pt-4 border-t border-white/[0.06]">
|
<div className="flex items-center justify-between mt-4 pt-4 border-t border-white/[0.06]">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user