fix(05-04): replace locked client display with editable email input

- Remove isLocked variable and read-only div for assigned client
- Add primaryEmail state pre-filled with assigned client's email, user-editable
- Show client name as helper text below the input for reference
- Update buildEmailAddresses() to use primaryEmail when assignedClientId is set
- Update button disabled logic to gate on primaryEmail when assigned client exists
This commit is contained in:
Chandler Copeland
2026-03-20 00:41:49 -06:00
parent f0ecfd1545
commit 73ba6d5a0d

View File

@@ -40,15 +40,15 @@ export function PreparePanel({
}: PreparePanelProps) { }: PreparePanelProps) {
const router = useRouter(); const router = useRouter();
// If the document already has an explicit assigned client, lock to that client.
// Otherwise default to the document owner (defaultClientId) but allow changing.
const isLocked = assignedClientId !== null;
// selectedClientId: id from the dropdown (empty string = "pick manually by email") // selectedClientId: id from the dropdown (empty string = "pick manually by email")
const [selectedClientId, setSelectedClientId] = useState<string>( const [selectedClientId, setSelectedClientId] = useState<string>(
assignedClientId ?? defaultClientId, assignedClientId ?? defaultClientId,
); );
// primaryEmail: editable email for the assigned client (pre-filled, can be overridden)
const assignedClient = clients.find(c => c.id === assignedClientId);
const [primaryEmail, setPrimaryEmail] = useState(assignedClient?.email ?? '');
// emailInput: raw text for manual email entry (used when selectedClientId is '' or // emailInput: raw text for manual email entry (used when selectedClientId is '' or
// as additional CC addresses) // as additional CC addresses)
const [emailInput, setEmailInput] = useState(''); const [emailInput, setEmailInput] = useState('');
@@ -61,12 +61,15 @@ export function PreparePanel({
const canPrepare = currentStatus === 'Draft'; const canPrepare = currentStatus === 'Draft';
// The email addresses that will be sent with the prepare request. // The email addresses that will be sent with the prepare request.
// If a known client is selected, start with that client's email. // When an assigned client exists, use the (editable) primaryEmail.
// Otherwise use the dropdown-selected client's email.
// Any manually-entered addresses are appended. // Any manually-entered addresses are appended.
function buildEmailAddresses(): string[] { function buildEmailAddresses(): string[] {
const addresses: string[] = []; const addresses: string[] = [];
if (selectedClientId) { if (assignedClientId && primaryEmail.trim()) {
addresses.push(primaryEmail.trim());
} else if (selectedClientId) {
const client = clients.find((c) => c.id === selectedClientId); const client = clients.find((c) => c.id === selectedClientId);
if (client?.email) addresses.push(client.email); if (client?.email) addresses.push(client.email);
} }
@@ -136,22 +139,25 @@ export function PreparePanel({
<div className="rounded-lg border border-gray-200 p-4 space-y-4"> <div className="rounded-lg border border-gray-200 p-4 space-y-4">
<h2 className="font-semibold text-gray-900">Prepare Document</h2> <h2 className="font-semibold text-gray-900">Prepare Document</h2>
{/* Client / recipient selection */} {/* Primary recipient */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
{isLocked ? 'Assigned client (locked)' : 'Assign to client'} Recipient email
</label> </label>
{assignedClientId ? (
{isLocked ? ( <>
// Document already has an assigned client — show read-only info <input
<div className="w-full border rounded px-2 py-1.5 text-sm bg-gray-50 text-gray-600"> type="email"
{(() => { value={primaryEmail}
const c = clients.find((c) => c.id === assignedClientId); onChange={(e) => setPrimaryEmail(e.target.value)}
return c ? `${c.name} (${c.email})` : assignedClientId; className="w-full border rounded px-2 py-1.5 text-sm"
})()} placeholder="recipient@example.com"
</div> />
<p className="text-xs text-gray-400 mt-0.5">
Assigned client: {clients.find(c => c.id === assignedClientId)?.name ?? assignedClientId}
</p>
</>
) : ( ) : (
// No explicit assignment yet — allow choosing from list or entering email manually
<select <select
value={selectedClientId} value={selectedClientId}
onChange={(e) => setSelectedClientId(e.target.value)} onChange={(e) => setSelectedClientId(e.target.value)}
@@ -168,17 +174,15 @@ export function PreparePanel({
{/* Additional / manual email addresses */} {/* Additional / manual email addresses */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
{isLocked {assignedClientId || selectedClientId
? 'Additional email addresses (optional, comma-separated)' ? 'Additional email addresses (optional, comma-separated)'
: selectedClientId : 'Email address(es) — required if no client selected above'}
? 'Additional email addresses (optional, comma-separated)'
: 'Email address(es) — required if no client selected above'}
</label> </label>
<textarea <textarea
value={emailInput} value={emailInput}
onChange={(e) => setEmailInput(e.target.value)} onChange={(e) => setEmailInput(e.target.value)}
placeholder={ placeholder={
isLocked || selectedClientId assignedClientId || selectedClientId
? 'e.g. cc@example.com, another@example.com' ? 'e.g. cc@example.com, another@example.com'
: 'Enter recipient email, e.g. client@example.com' : 'Enter recipient email, e.g. client@example.com'
} }
@@ -197,7 +201,7 @@ export function PreparePanel({
<button <button
onClick={handlePrepare} onClick={handlePrepare}
disabled={loading || (!selectedClientId && parseEmails(emailInput).length === 0)} disabled={loading || (assignedClientId ? !primaryEmail.trim() : (!selectedClientId && parseEmails(emailInput).length === 0))}
className="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium" className="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium"
type="button" type="button"
> >