fix(uat-batch-1): wave-1 blocker bugs — supplemental gate, file FK, downloads, search dedup, notes stale, expense form, vocab
Surgical fixes for the 7 UAT blockers that prevent productive forward
testing. Each item has a corresponding entry in alpha-uat-master.md.
- supplemental-info route relocated out of (portal) so it bypasses the
isPortalDisabledGlobally() kill-switch. URL unchanged.
- file upload service derives client_id/company_id/yacht_id from
(entityType, entityId) when not explicitly passed, so interest-tab
uploads no longer land with client_id=NULL and stay visible in the
Attachments list.
- triggerBlobDownload / triggerUrlDownload helpers in src/lib/utils
attach the anchor to the DOM before click so Chromium honours the
download attribute; 7 sites refactored, file-named downloads stop
arriving as bare UUIDs.
- search-nav-catalog dedupes by href at the result-collection layer so
the same href can no longer surface twice in the command-K dropdown
(kills the React duplicate-key warning); /admin/templates entries
merged into a single richer-keyword variant.
- NotesList gains a parentInvalidateKey prop, wired through all five
callers (interest, client, yacht, company, residential client/
interest) so the Overview "Latest note" teaser refreshes when a note
is added in the Notes tab.
- expense-form-dialog: setValue('receiptFileIds') / setValue(
'noReceiptAcknowledged') on upload/clear/checkbox so the schema-level
refine sees the field and Create stops silently no-op'ing on submit.
- bulk-add-berths-wizard: side-pontoon dropdown now reads through
useVocabulary('berth_side_pontoon_options') instead of a wrong local
enum ('Port', 'Starboard', 'Bow', 'Stern') — wizard data now matches
the rest of the platform + honours admin-editable per-port overrides.
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:
@@ -16,6 +16,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import { toastError } from '@/lib/api/toast-error';
|
||||
import { triggerUrlDownload } from '@/lib/utils/download';
|
||||
|
||||
interface BackupJob {
|
||||
id: string;
|
||||
@@ -87,10 +88,7 @@ export function BackupAdminPanel() {
|
||||
async function download(id: string) {
|
||||
try {
|
||||
const res = await apiFetch<{ data: { url: string } }>(`/api/v1/admin/backup/${id}/download`);
|
||||
const a = document.createElement('a');
|
||||
a.href = res.data.url;
|
||||
a.download = `backup-${id}.dump`;
|
||||
a.click();
|
||||
triggerUrlDownload(res.data.url, `backup-${id}.dump`);
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
}
|
||||
|
||||
@@ -35,12 +35,11 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import { toastError } from '@/lib/api/toast-error';
|
||||
import { useVocabulary } from '@/hooks/use-vocabulary';
|
||||
|
||||
const DOCK_LETTERS = ['A', 'B', 'C', 'D', 'E'] as const;
|
||||
type DockLetter = (typeof DOCK_LETTERS)[number];
|
||||
|
||||
const SIDE_PONTOON_OPTIONS = ['Port', 'Starboard', 'Bow', 'Stern', ''] as const;
|
||||
|
||||
interface RowDraft {
|
||||
mooringNumber: string;
|
||||
area: string;
|
||||
@@ -77,6 +76,10 @@ export function BulkAddBerthsWizard() {
|
||||
const params = useParams<{ portSlug: string }>();
|
||||
const portSlug = params?.portSlug ?? '';
|
||||
const router = useRouter();
|
||||
// Canonical, admin-editable side-pontoon vocabulary (per-port overrides
|
||||
// honoured). Falls back to BERTH_SIDE_PONTOON_OPTIONS defaults when the
|
||||
// /api/v1/vocabularies request hasn't resolved yet.
|
||||
const sidePontoonOptions = useVocabulary('berth_side_pontoon_options');
|
||||
|
||||
const [step, setStep] = useState<'sequence' | 'edit'>('sequence');
|
||||
|
||||
@@ -261,7 +264,7 @@ export function BulkAddBerthsWizard() {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">(none)</SelectItem>
|
||||
{SIDE_PONTOON_OPTIONS.filter(Boolean).map((p) => (
|
||||
{sidePontoonOptions.filter(Boolean).map((p) => (
|
||||
<SelectItem key={p} value={p}>
|
||||
{p}
|
||||
</SelectItem>
|
||||
@@ -331,7 +334,7 @@ export function BulkAddBerthsWizard() {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">—</SelectItem>
|
||||
{SIDE_PONTOON_OPTIONS.filter(Boolean).map((p) => (
|
||||
{sidePontoonOptions.filter(Boolean).map((p) => (
|
||||
<SelectItem key={p} value={p}>
|
||||
{p}
|
||||
</SelectItem>
|
||||
|
||||
Reference in New Issue
Block a user