feat(hq-web): module quote-content live print preview

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 1 week ago
parent b65e03e1c4
commit 0b340fd85f

@ -1,10 +1,11 @@
import { useState } from 'react'
import { useMemo, useState } from 'react'
import { formatINR, fromRupees } from '@sims/domain'
import { Badge, Button, DataTable, EmptyState, Field, Notice, PageHeader, Toolbar } from '@sims/ui'
import {
addPrice, createModule, getModules, getPrices, patchModule, role,
addPrice, createModule, getModules, getPrices, patchModule, previewSample, role,
KIND_LABEL, type Kind, type Module,
} from '../api'
import { LivePreview } from '../components/LivePreview'
import { useData } from './Clients'
const ALL_KINDS: Kind[] = ['one_time', 'monthly', 'yearly', 'usage']
@ -65,6 +66,8 @@ function QuoteContent(props: { module: Module; isOwner: boolean; onSaved: () =>
const [text, setText] = useState(props.module.quoteContent.join('\n'))
const [saved, setSaved] = useState(false)
const [error, setError] = useState<string | undefined>()
const [commitNonce, setCommitNonce] = useState(0)
const previewBody = useMemo(() => JSON.stringify({ contentLines: splitLines(text) }), [text])
const save = () => {
setError(undefined)
@ -77,16 +80,25 @@ function QuoteContent(props: { module: Module; isOwner: boolean; onSaved: () =>
<>
<h3 style={{ marginTop: 20 }}>Quote content {props.module.name}</h3>
{props.isOwner ? (
<div style={{ maxWidth: 560 }}>
<textarea
className="wf" rows={4} placeholder="One included item per line…"
value={text} onChange={(e) => { setText(e.target.value); setSaved(false) }}
/>
<Toolbar>
<Button tone="primary" onClick={save}>Save quote content</Button>
{saved && <Badge tone="ok">saved</Badge>}
</Toolbar>
{error !== undefined && <Notice tone="err">{error}</Notice>}
<div style={{ display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
<div style={{ flex: '0 0 560px', minWidth: 0 }}>
<textarea className="wf" rows={4} placeholder="One included item per line…"
value={text} onChange={(e) => { setText(e.target.value); setSaved(false) }} onBlur={() => setCommitNonce((n) => n + 1)} />
<Toolbar>
<Button tone="primary" onClick={save}>Save quote content</Button>
{saved && <Badge tone="ok">saved</Badge>}
</Toolbar>
{error !== undefined && <Notice tone="err">{error}</Notice>}
</div>
<div style={{ flex: 1, minWidth: 320, height: 420, background: 'var(--bg-sunken, #e9e9ee)', borderRadius: 8, padding: 12 }}>
<div style={{ width: '100%', height: '100%', background: '#fff', borderRadius: 4, boxShadow: '0 1px 6px rgba(0,0,0,.15)', overflow: 'hidden' }}>
<LivePreview
body={previewBody}
commitNonce={commitNonce}
fetchPreview={(b, signal) => previewSample(JSON.parse(b), signal).then((r) => ({ html: r.html }))}
/>
</div>
</div>
</div>
) : props.module.quoteContent.length === 0 ? (
<EmptyState>No quote content.</EmptyState>

Loading…
Cancel
Save