Files
Mana-Loop/src/components/game/ConfirmDialog.tsx
T
Refactoring Agent 47c71e6f54
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 8m47s
feat(ui): complete Task 4 UI redesign — all sub-tasks 1-10
- Implemented complete design system with 40+ CSS custom properties
- Created 9 UI primitives (GameCard, SectionHeader, StatRow, ManaBar, ElementBadge, ValueDisplay, ActionButton, SkillRow, TooltipInfo)
- Redesigned all tabs: Spire, Skills, Stats, Equipment, Crafting, Attunements, Golemancy, Spells, Loot, Achievements, Lab, Debug
- Added toast notification system (GameToast) with success/warning/error/info types
- Added confirmation dialogs for destructive actions
- Removed all dev artifacts and component name labels
- Added empty states to all tabs
- Replaced emoji icons with Lucide React icons
- Added enchantPower placeholder to StatsTab and EquipmentTab
- Mobile audit passed at 375px viewport
- Build passes with 0 errors, lint passes with 0 errors

Sub-tasks completed:
- ST1: Design System Implementation
- ST2: Global Layout & Header
- ST3: Left Panel (Mana Display & Action Area)
- ST4: Skills Tab
- ST5: Spire Tab & Spire Mode UI
- ST6: Stats Tab
- ST7: Equipment & Crafting Tabs
- ST8: Attunements Tab
- ST9: Remaining Tabs
- ST10: Toast System & Confirmation Dialogs

Documentation: 15+ files in docs/task4/
2026-04-28 11:38:45 +02:00

185 lines
5.0 KiB
TypeScript

'use client';
import { useState, type ReactNode } from 'react';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { AlertTriangle, AlertCircle, Info, CheckCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
export type ConfirmDialogVariant = 'danger' | 'warning' | 'info' | 'success';
interface ConfirmDialogProps {
/** Whether the dialog is open */
open: boolean;
/** Callback when open state changes */
onOpenChange: (open: boolean) => void;
/** Dialog title */
title: string;
/** Dialog description/content */
description: ReactNode;
/** Cancel button text (default: "Cancel") */
cancelText?: string;
/** Confirm button text (default: "Confirm") */
confirmText?: string;
/** Dialog variant/type */
variant?: ConfirmDialogVariant;
/** Callback when user confirms */
onConfirm: () => void | Promise<void>;
/** Callback when user cancels */
onCancel?: () => void;
/** Whether the confirm action is destructive */
destructive?: boolean;
}
const VARIANT_ICONS = {
danger: AlertTriangle,
warning: AlertCircle,
info: Info,
success: CheckCircle,
};
const VARIANT_TITLE_COLORS = {
danger: 'text-[var(--color-danger)]',
warning: 'text-[var(--color-warning)]',
info: 'text-[var(--color-info)]',
success: 'text-[var(--color-success)]',
};
const VARIANT_ACTION_COLORS = {
danger: 'bg-[var(--color-danger)] hover:bg-[var(--interactive-danger-hover)] text-white',
warning: 'bg-[var(--color-warning)] hover:opacity-90 text-black',
info: 'bg-[var(--color-info)] hover:opacity-90 text-white',
success: 'bg-[var(--color-success)] hover:opacity-90 text-white',
};
/**
* Reusable confirmation dialog component.
* Uses the existing shadcn/ui AlertDialog.
*
* @example
* <ConfirmDialog
* open={showDialog}
* onOpenChange={setShowDialog}
* title="Delete Item"
* description="Are you sure you want to delete this item? This action cannot be undone."
* variant="danger"
* onConfirm={handleDelete}
* />
*/
export function ConfirmDialog({
open,
onOpenChange,
title,
description,
cancelText = 'Cancel',
confirmText = 'Confirm',
variant = 'warning',
onConfirm,
onCancel,
destructive = false,
}: ConfirmDialogProps) {
const [isLoading, setIsLoading] = useState(false);
const Icon = VARIANT_ICONS[variant];
const titleColor = VARIANT_TITLE_COLORS[variant];
const actionClass = destructive ? VARIANT_ACTION_COLORS.danger : VARIANT_ACTION_COLORS[variant];
const handleConfirm = async () => {
setIsLoading(true);
try {
await onConfirm();
onOpenChange(false);
} finally {
setIsLoading(false);
}
};
const handleCancel = () => {
onCancel?.();
onOpenChange(false);
};
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent className="bg-[var(--bg-elevated)] border-[var(--border-default)] text-[var(--text-primary)]">
<AlertDialogHeader>
<AlertDialogTitle className={cn('flex items-center gap-2', titleColor)}>
<Icon className="h-5 w-5" />
{title}
</AlertDialogTitle>
<AlertDialogDescription className="text-[var(--text-secondary)]">
{description}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
className="bg-[var(--bg-sunken)] border-[var(--border-default)] text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
onClick={handleCancel}
>
{cancelText}
</AlertDialogCancel>
<AlertDialogAction
className={cn(actionClass, isLoading && 'opacity-50 cursor-not-allowed')}
onClick={handleConfirm}
disabled={isLoading}
>
{isLoading ? 'Processing...' : confirmText}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
/**
* Hook to easily manage a confirmation dialog state.
*
* @example
* const { dialogProps, showConfirm } = useConfirmDialog();
*
* showConfirm({
* title: "Delete Item",
* description: "Are you sure?",
* onConfirm: () => deleteItem(),
* });
*/
export function useConfirmDialog() {
const [dialogState, setDialogState] = useState<{
open: boolean;
props: Omit<ConfirmDialogProps, 'open' | 'onOpenChange'>;
}>({
open: false,
props: {
title: '',
description: '',
onConfirm: () => {},
},
});
const showConfirm = (props: Omit<ConfirmDialogProps, 'open' | 'onOpenChange'>) => {
setDialogState({ open: true, props });
};
const dialogProps: ConfirmDialogProps = {
open: dialogState.open,
onOpenChange: (open: boolean) => setDialogState(prev => ({ ...prev, open })),
...dialogState.props,
};
return {
dialogProps,
showConfirm,
ConfirmDialogComponent: <ConfirmDialog {...dialogProps} />,
};
}
export default ConfirmDialog;