feat(ui): yacht transfer dialog with atomic ownership change
Replaces the Task 5.3 stub with a real YachtTransferDialog backed by
OwnerPicker, a date input, reason select, and notes textarea. Submits to
POST /api/v1/yachts/{id}/transfer, invalidates yacht + ownership-history
queries on success, and surfaces API errors (same-owner 400, cross-tenant
404, no-permission 403) as form-level messages. Transfer button is now
gated by PermissionGate resource="yachts" action="transfer".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,16 +9,10 @@ import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { ArchiveConfirmDialog } from '@/components/shared/archive-confirm-dialog';
|
||||
import { PermissionGate } from '@/components/shared/permission-gate';
|
||||
import { YachtForm } from '@/components/yachts/yacht-form';
|
||||
import { YachtTransferDialog } from '@/components/yachts/yacht-transfer-dialog';
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
|
||||
interface YachtDetailHeaderYacht {
|
||||
@@ -174,15 +168,17 @@ export function YachtDetailHeader({ yacht }: YachtDetailHeaderProps) {
|
||||
<Pencil className="mr-1.5 h-3.5 w-3.5" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setTransferOpen(true)}
|
||||
disabled={isArchived}
|
||||
>
|
||||
<ArrowRightLeft className="mr-1.5 h-3.5 w-3.5" />
|
||||
Transfer
|
||||
</Button>
|
||||
<PermissionGate resource="yachts" action="transfer">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setTransferOpen(true)}
|
||||
disabled={isArchived}
|
||||
>
|
||||
<ArrowRightLeft className="mr-1.5 h-3.5 w-3.5" />
|
||||
Transfer
|
||||
</Button>
|
||||
</PermissionGate>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -234,30 +230,12 @@ export function YachtDetailHeader({ yacht }: YachtDetailHeaderProps) {
|
||||
isLoading={archiveMutation.isPending}
|
||||
/>
|
||||
|
||||
{/* TODO(Task 5.5): Replace with real YachtTransferDialog component. */}
|
||||
<Dialog open={transferOpen} onOpenChange={setTransferOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Transfer Ownership</DialogTitle>
|
||||
<DialogDescription>
|
||||
The yacht ownership transfer flow will be implemented in Task 5.5.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="py-2 text-sm text-muted-foreground">
|
||||
This stub will be replaced with a form that lets you pick a new owner, effective date,
|
||||
reason, and notes — then calls{' '}
|
||||
<code className="rounded bg-muted px-1 text-xs">
|
||||
POST /api/v1/yachts/{'{id}'}/transfer
|
||||
</code>
|
||||
.
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setTransferOpen(false)}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<YachtTransferDialog
|
||||
open={transferOpen}
|
||||
onOpenChange={setTransferOpen}
|
||||
yachtId={yacht.id}
|
||||
currentOwner={{ type: yacht.currentOwnerType, id: yacht.currentOwnerId }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user