feat(hq-web): owner company profile editor and client 360 aws usage trend
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/client-detail-redesign
parent
2acca18fc9
commit
dbccb8f954
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { Button, Field, Notice, PageHeader } from '@sims/ui'
|
||||
import { getCompanyProfile, putCompanyProfile, role } from '../api'
|
||||
|
||||
const FIELDS: { key: string; setting: string; label: string }[] = [
|
||||
{ key: 'name', setting: 'company.name', label: 'Company name' },
|
||||
{ key: 'address', setting: 'company.address', label: 'Address' },
|
||||
{ key: 'gstin', setting: 'company.gstin', label: 'GSTIN' },
|
||||
{ key: 'stateCode', setting: 'company.state_code', label: 'State code' },
|
||||
{ key: 'phone', setting: 'company.phone', label: 'Phone' },
|
||||
{ key: 'email', setting: 'company.email', label: 'Email' },
|
||||
{ key: 'bank', setting: 'company.bank', label: 'Bank details' },
|
||||
]
|
||||
|
||||
/** Owner-only editor for the company.* settings the letterhead PDFs read. */
|
||||
export function CompanyProfile() {
|
||||
const [f, setF] = useState<Record<string, string>>({})
|
||||
const [msg, setMsg] = useState<{ tone: 'ok' | 'err'; text: string } | undefined>()
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
getCompanyProfile()
|
||||
.then((c) => setF(Object.fromEntries(FIELDS.map((x) => [x.key, c[x.setting] ?? '']))))
|
||||
.catch((e: Error) => setMsg({ tone: 'err', text: e.message }))
|
||||
}, [])
|
||||
|
||||
if (role() !== 'owner') return <Navigate to="/" replace />
|
||||
|
||||
const save = () => {
|
||||
setSaving(true); setMsg(undefined)
|
||||
putCompanyProfile(f)
|
||||
.then((c) => { setF(Object.fromEntries(FIELDS.map((x) => [x.key, c[x.setting] ?? '']))); setMsg({ tone: 'ok', text: 'Saved. PDFs use the new details immediately.' }) })
|
||||
.catch((e: Error) => setMsg({ tone: 'err', text: e.message }))
|
||||
.finally(() => setSaving(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wf-page">
|
||||
<PageHeader title="Company profile" desc="These details appear on every quotation, invoice, and receipt." />
|
||||
{msg !== undefined && <Notice tone={msg.tone}>{msg.text}</Notice>}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, maxWidth: 520, marginTop: 10 }}>
|
||||
{FIELDS.map((x) => (
|
||||
<Field key={x.key} label={x.label}>
|
||||
{x.key === 'address' || x.key === 'bank'
|
||||
? <textarea className="wf" rows={2} style={{ width: '100%' }} value={f[x.key] ?? ''} onChange={(e) => setF((p) => ({ ...p, [x.key]: e.target.value }))} />
|
||||
: <input className="wf" style={{ width: '100%' }} value={f[x.key] ?? ''} onChange={(e) => setF((p) => ({ ...p, [x.key]: e.target.value }))} />}
|
||||
</Field>
|
||||
))}
|
||||
<div><Button tone="primary" onClick={save}>{saving ? 'Saving…' : 'Save company profile'}</Button></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue