feat(uat-batch-4): a11y form primitives + click-to-preview + EOI empty-state + lint guards
- FieldError primitive (role=alert, aria-live) — used by Wave 3 form-error UX work. - FieldLabel primitive (Label + Info-tooltip slot) — foundational for the platform-wide admin-settings tooltip audit. - ESLint guard against em-dash in user-facing JSX text inside src/components + src/app (warning, not error; 111 existing instances flagged for follow-up sweep). - FileGrid card body becomes click-to-preview button (was hidden under a kebab); aria-label per row; kebab keeps Download/Rename/Delete. - DocumentList: title cell on rows with signedFileId opens FilePreviewDialog; kebab gains Download action (was missing per UAT). Single FilePreviewDialog instance lifted to the parent. - DocumentList type extended with signedFileId. - EOI empty state: third ghost button "Mark signed without file" wired to existing MarkExternallySignedDialog (parity with reservation tab). - Watcher empty-state padding fix on document-detail. tsc clean. 1419/1419 vitest. lint clean on touched files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { EoiCancelDialog } from '@/components/documents/eoi-cancel-dialog';
|
||||
import { EoiGenerateDialog } from '@/components/documents/eoi-generate-dialog';
|
||||
import { ExternalEoiUploadDialog } from '@/components/interests/external-eoi-upload-dialog';
|
||||
import { MarkExternallySignedDialog } from '@/components/interests/mark-externally-signed-dialog';
|
||||
import { SigningProgress } from '@/components/documents/signing-progress';
|
||||
import { FilePreviewDialog } from '@/components/files/file-preview-dialog';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
@@ -104,6 +105,7 @@ export function InterestEoiTab({ interestId, clientId }: InterestEoiTabProps) {
|
||||
const portSlug = useUIStore((s) => s.currentPortSlug);
|
||||
const [generateOpen, setGenerateOpen] = useState(false);
|
||||
const [uploadSignedOpen, setUploadSignedOpen] = useState(false);
|
||||
const [markSignedOpen, setMarkSignedOpen] = useState(false);
|
||||
// Lifted preview state so the View button on every signed-PDF row opens
|
||||
// the in-app preview dialog rather than navigating to a presigned URL
|
||||
// (which the storage backend serves with Content-Disposition=attachment,
|
||||
@@ -137,6 +139,7 @@ export function InterestEoiTab({ interestId, clientId }: InterestEoiTabProps) {
|
||||
<EmptyEoiState
|
||||
onGenerate={() => setGenerateOpen(true)}
|
||||
onUploadSigned={() => setUploadSignedOpen(true)}
|
||||
onMarkSigned={() => setMarkSignedOpen(true)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -197,6 +200,13 @@ export function InterestEoiTab({ interestId, clientId }: InterestEoiTabProps) {
|
||||
interestId={interestId}
|
||||
/>
|
||||
|
||||
<MarkExternallySignedDialog
|
||||
open={markSignedOpen}
|
||||
onOpenChange={setMarkSignedOpen}
|
||||
interestId={interestId}
|
||||
docType="eoi"
|
||||
/>
|
||||
|
||||
<FilePreviewDialog
|
||||
open={!!previewFile}
|
||||
onOpenChange={(o) => {
|
||||
@@ -559,9 +569,11 @@ function SignedPdfPreview({ fileId }: { fileId: string }) {
|
||||
function EmptyEoiState({
|
||||
onGenerate,
|
||||
onUploadSigned,
|
||||
onMarkSigned,
|
||||
}: {
|
||||
onGenerate: () => void;
|
||||
onUploadSigned: () => void;
|
||||
onMarkSigned: () => void;
|
||||
}) {
|
||||
return (
|
||||
<section className="rounded-xl border border-dashed bg-muted/20 p-8 text-center">
|
||||
@@ -572,7 +584,7 @@ function EmptyEoiState({
|
||||
No EOI in flight for this interest
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Generate the EOI to send it for signing — the signing service handles the signing chain. You
|
||||
Generate the EOI to send it for signing. The signing service handles the signing chain. You
|
||||
can also upload a paper-signed copy if it was signed outside the system.
|
||||
</p>
|
||||
<div className="mt-5 flex flex-wrap items-center justify-center gap-2">
|
||||
@@ -584,6 +596,10 @@ function EmptyEoiState({
|
||||
<Upload className="size-4" aria-hidden />
|
||||
Upload paper-signed copy
|
||||
</Button>
|
||||
<Button onClick={onMarkSigned} variant="ghost" size="sm" className="gap-1.5">
|
||||
<CheckCircle2 className="size-4" aria-hidden />
|
||||
Mark signed without file
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user