* Done! I've successfully updated the CreatePRDialog to use the Alert component pattern, matching the style used in DeleteTaskConfirmationDialog: - Added `Alert` import from `@/components/ui/alert` - Replaced the custom error div with `<Alert variant="destructive">` - Added error clearing when starting PR creation for better UX The error messages now use a consistent, accessible Alert component across the application. * Cleanup script changes for task attempt 3757c2b1-becb-4b8f-80ab-03be9bc3f096 * Perfect! I've successfully refactored all dialog error messages to use the consistent Alert component pattern: ## ✅ Completed Changes: **1. [TaskTemplateEditDialog.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/3757-update-create-gi/frontend/src/components/dialogs/tasks/TaskTemplateEditDialog.tsx)** - Added `Alert` and `AlertDescription` imports - Replaced simple error div with proper Alert component **2. [GitHubLoginDialog.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/3757-update-create-gi/frontend/src/components/dialogs/auth/GitHubLoginDialog.tsx)** - Added `Alert` and `AlertDescription` imports - Replaced both custom styled error divs with Alert components - Maintained the same error display locations for proper context ## 🎯 Benefits Achieved: - **Consistent UI/UX**: All dialogs now use the same error styling - **Better Accessibility**: Semantic alert components with proper roles - **Unified Theming**: All errors follow the same design system - **Maintainability**: Single pattern for error display across the app All dialog error messages now follow the same pattern as DeleteTaskConfirmationDialog and the updated CreatePRDialog. * Done! I've simplified all Alert components to use the same pattern as CreatePRDialog: - **Removed AlertDescription wrapper** from all error messages - **Updated imports** to only import Alert - **Consistent pattern**: `<Alert variant="destructive">{error}</Alert>` across all dialogs Now all dialog error messages use the exact same simple Alert pattern throughout the application. * Cleanup script changes for task attempt 3757c2b1-becb-4b8f-80ab-03be9bc3f096
178 lines
5.6 KiB
TypeScript
178 lines
5.6 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Alert } from '@/components/ui/alert';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter,
|
|
} from '@/components/ui/dialog';
|
|
import { Loader2 } from 'lucide-react';
|
|
import { templatesApi } from '@/lib/api';
|
|
import type {
|
|
TaskTemplate,
|
|
CreateTaskTemplate,
|
|
UpdateTaskTemplate,
|
|
} from 'shared/types';
|
|
import NiceModal, { useModal } from '@ebay/nice-modal-react';
|
|
|
|
export interface TaskTemplateEditDialogProps {
|
|
template?: TaskTemplate | null; // null for create mode
|
|
projectId?: string;
|
|
isGlobal?: boolean;
|
|
}
|
|
|
|
export type TaskTemplateEditResult = 'saved' | 'canceled';
|
|
|
|
export const TaskTemplateEditDialog =
|
|
NiceModal.create<TaskTemplateEditDialogProps>(
|
|
({ template, projectId, isGlobal = false }) => {
|
|
const modal = useModal();
|
|
const [formData, setFormData] = useState({
|
|
template_name: '',
|
|
title: '',
|
|
description: '',
|
|
});
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const isEditMode = Boolean(template);
|
|
|
|
useEffect(() => {
|
|
if (template) {
|
|
setFormData({
|
|
template_name: template.template_name,
|
|
title: template.title,
|
|
description: template.description || '',
|
|
});
|
|
} else {
|
|
setFormData({
|
|
template_name: '',
|
|
title: '',
|
|
description: '',
|
|
});
|
|
}
|
|
setError(null);
|
|
}, [template]);
|
|
|
|
const handleSave = async () => {
|
|
if (!formData.template_name.trim() || !formData.title.trim()) {
|
|
setError('Template name and title are required');
|
|
return;
|
|
}
|
|
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
try {
|
|
if (isEditMode && template) {
|
|
const updateData: UpdateTaskTemplate = {
|
|
template_name: formData.template_name,
|
|
title: formData.title,
|
|
description: formData.description || null,
|
|
};
|
|
await templatesApi.update(template.id, updateData);
|
|
} else {
|
|
const createData: CreateTaskTemplate = {
|
|
project_id: isGlobal ? null : projectId || null,
|
|
template_name: formData.template_name,
|
|
title: formData.title,
|
|
description: formData.description || null,
|
|
};
|
|
await templatesApi.create(createData);
|
|
}
|
|
|
|
modal.resolve('saved' as TaskTemplateEditResult);
|
|
modal.hide();
|
|
} catch (err: any) {
|
|
setError(err.message || 'Failed to save template');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
modal.resolve('canceled' as TaskTemplateEditResult);
|
|
modal.hide();
|
|
};
|
|
|
|
const handleOpenChange = (open: boolean) => {
|
|
if (!open) {
|
|
handleCancel();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={modal.visible} onOpenChange={handleOpenChange}>
|
|
<DialogContent className="sm:max-w-[500px]">
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{isEditMode ? 'Edit Template' : 'Create Template'}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="space-y-4 py-4">
|
|
<div>
|
|
<Label htmlFor="template-name">Template Name</Label>
|
|
<Input
|
|
id="template-name"
|
|
value={formData.template_name}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, template_name: e.target.value })
|
|
}
|
|
placeholder="e.g., Bug Fix, Feature Request"
|
|
disabled={saving}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="template-title">Default Title</Label>
|
|
<Input
|
|
id="template-title"
|
|
value={formData.title}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, title: e.target.value })
|
|
}
|
|
placeholder="e.g., Fix bug in..."
|
|
disabled={saving}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="template-description">
|
|
Default Description
|
|
</Label>
|
|
<Textarea
|
|
id="template-description"
|
|
value={formData.description}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, description: e.target.value })
|
|
}
|
|
placeholder="Enter a default description for tasks created with this template"
|
|
rows={4}
|
|
disabled={saving}
|
|
/>
|
|
</div>
|
|
{error && <Alert variant="destructive">{error}</Alert>}
|
|
</div>
|
|
<DialogFooter>
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleCancel}
|
|
disabled={saving}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button onClick={handleSave} disabled={saving}>
|
|
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
{isEditMode ? 'Update' : 'Create'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
);
|