mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-20 01:09:26 +00:00
Fix delete button behavior in Project Settings (#699)
This commit is contained in:
commit
330fa3b762
3 changed files with 290 additions and 262 deletions
|
|
@ -1,249 +1,273 @@
|
|||
import { useState, useEffect, useRef } from "react";
|
||||
import { ProjectGroup } from "@/types/history";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogContentSection,
|
||||
DialogFooter,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogContentSection,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import TaskItem from "./TaskItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Hash, CheckCircle, Clock, Pin, Loader2, LoaderCircle } from "lucide-react";
|
||||
import {
|
||||
Hash,
|
||||
CheckCircle,
|
||||
Clock,
|
||||
Pin,
|
||||
Loader2,
|
||||
LoaderCircle,
|
||||
} from "lucide-react";
|
||||
import { useProjectStore } from "@/store/projectStore";
|
||||
|
||||
interface ProjectDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
project: ProjectGroup;
|
||||
onProjectRename: (projectId: string, newName: string) => void;
|
||||
onTaskSelect: (projectId: string, question: string, historyId: string) => void;
|
||||
onTaskDelete: (taskId: string) => void;
|
||||
onTaskShare: (taskId: string) => void;
|
||||
activeTaskId?: string;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
project: ProjectGroup;
|
||||
onProjectRename: (projectId: string, newName: string) => void;
|
||||
onTaskSelect: (
|
||||
projectId: string,
|
||||
question: string,
|
||||
historyId: string
|
||||
) => void;
|
||||
onTaskDelete: (taskId: string) => void;
|
||||
onTaskShare: (taskId: string) => void;
|
||||
activeTaskId?: string;
|
||||
}
|
||||
|
||||
export default function ProjectDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
project,
|
||||
onProjectRename,
|
||||
onTaskSelect,
|
||||
onTaskDelete,
|
||||
onTaskShare,
|
||||
activeTaskId,
|
||||
open,
|
||||
onOpenChange,
|
||||
project,
|
||||
onProjectRename,
|
||||
onTaskSelect,
|
||||
onTaskDelete,
|
||||
onTaskShare,
|
||||
activeTaskId,
|
||||
}: ProjectDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const projectStore = useProjectStore();
|
||||
const [projectName, setProjectName] = useState(project.project_name || t("layout.new-project"));
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const lastSavedNameRef = useRef<string>(project.project_name || t("layout.new-project"));
|
||||
const { t } = useTranslation();
|
||||
const projectStore = useProjectStore();
|
||||
const [projectName, setProjectName] = useState(
|
||||
project.project_name || t("layout.new-project")
|
||||
);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const lastSavedNameRef = useRef<string>(
|
||||
project.project_name || t("layout.new-project")
|
||||
);
|
||||
|
||||
// Update state when project changes
|
||||
useEffect(() => {
|
||||
const name = project.project_name || t("layout.new-project");
|
||||
setProjectName(name);
|
||||
lastSavedNameRef.current = name;
|
||||
}, [project.project_name, project.project_id, t]);
|
||||
// Update state when project changes
|
||||
useEffect(() => {
|
||||
const name = project.project_name || t("layout.new-project");
|
||||
setProjectName(name);
|
||||
lastSavedNameRef.current = name;
|
||||
}, [project.project_name, project.project_id, t]);
|
||||
|
||||
// Auto-save with debouncing
|
||||
useEffect(() => {
|
||||
// Clear any existing timeout
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
// Auto-save with debouncing
|
||||
useEffect(() => {
|
||||
// Clear any existing timeout
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
|
||||
const trimmedName = projectName.trim();
|
||||
|
||||
// Only save if the name has actually changed and is not empty
|
||||
if (trimmedName && trimmedName !== lastSavedNameRef.current) {
|
||||
setIsSaving(true);
|
||||
|
||||
// Debounce: wait 800ms after user stops typing
|
||||
saveTimeoutRef.current = setTimeout(() => {
|
||||
// Update via callback (for history API)
|
||||
onProjectRename(project.project_id, trimmedName);
|
||||
|
||||
// Also update in projectStore if the project exists there
|
||||
const storeProject = projectStore.getProjectById(project.project_id);
|
||||
if (storeProject) {
|
||||
projectStore.updateProject(project.project_id, { name: trimmedName });
|
||||
}
|
||||
|
||||
lastSavedNameRef.current = trimmedName;
|
||||
setIsSaving(false);
|
||||
}, 800);
|
||||
} else if (!trimmedName) {
|
||||
// If empty, don't show saving state
|
||||
setIsSaving(false);
|
||||
}
|
||||
const trimmedName = projectName.trim();
|
||||
|
||||
// Cleanup timeout on unmount or when projectName changes
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [projectName, project.project_id, onProjectRename, projectStore]);
|
||||
// Only save if the name has actually changed and is not empty
|
||||
if (trimmedName && trimmedName !== lastSavedNameRef.current) {
|
||||
setIsSaving(true);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
// Debounce: wait 800ms after user stops typing
|
||||
saveTimeoutRef.current = setTimeout(() => {
|
||||
// Update via callback (for history API)
|
||||
onProjectRename(project.project_id, trimmedName);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
size="md"
|
||||
className="max-h-[80vh] flex flex-col h-full"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DialogHeader
|
||||
title={t("layout.project-settings")}
|
||||
subtitle={t("layout.manage-project-details")}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
/>
|
||||
// Also update in projectStore if the project exists there
|
||||
const storeProject = projectStore.getProjectById(project.project_id);
|
||||
if (storeProject) {
|
||||
projectStore.updateProject(project.project_id, { name: trimmedName });
|
||||
}
|
||||
|
||||
<DialogContentSection
|
||||
className="flex flex-col overflow-y-auto scrollbar"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Project Name Section - Inline Edit with Auto-Save */}
|
||||
<div
|
||||
className="flex flex-col gap-sm mb-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<label className="text-text-label font-bold text-label-sm">
|
||||
{t("layout.project-name")}
|
||||
</label>
|
||||
<div
|
||||
className="flex items-center gap-sm"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Input
|
||||
value={projectName}
|
||||
onChange={(e) => setProjectName(e.target.value)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onFocus={(e) => e.stopPropagation()}
|
||||
placeholder={t("layout.enter-project-name")}
|
||||
/>
|
||||
{isSaving ? (
|
||||
<Loader2 className="w-4 h-4 text-icon-action animate-spin flex-shrink-0" />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
lastSavedNameRef.current = trimmedName;
|
||||
setIsSaving(false);
|
||||
}, 800);
|
||||
} else if (!trimmedName) {
|
||||
// If empty, don't show saving state
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
{/* Project Stats */}
|
||||
<div className="grid grid-cols-4 gap-lg border-solid border-t-0 border-x-0 border-border-disabled pb-md">
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.total-tokens")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<Hash className="w-4 h-4 text-icon-primary" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_tokens.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
// Cleanup timeout on unmount or when projectName changes
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [projectName, project.project_id, onProjectRename, projectStore]);
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.total-tasks")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<Pin className="w-4 h-4 text-icon-primary" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.task_count}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.completed")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<CheckCircle className="w-4 h-4 text-icon-success" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_completed_tasks}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
size="md"
|
||||
className="max-h-[80vh] flex flex-col h-full"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DialogHeader
|
||||
title={t("layout.project-settings")}
|
||||
subtitle={t("layout.manage-project-details")}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.ongoing")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<LoaderCircle className="w-4 h-4 text-icon-information" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_ongoing_tasks}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogContentSection
|
||||
className="flex flex-col overflow-y-auto scrollbar"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Project Name Section - Inline Edit with Auto-Save */}
|
||||
<div
|
||||
className="flex flex-col gap-sm mb-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<label className="text-text-label font-bold text-label-sm">
|
||||
{t("layout.project-name")}
|
||||
</label>
|
||||
<div
|
||||
className="flex items-center gap-sm"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Input
|
||||
disabled={true}
|
||||
value={projectName}
|
||||
onChange={(e) => setProjectName(e.target.value)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onFocus={(e) => e.stopPropagation()}
|
||||
placeholder={t("layout.enter-project-name")}
|
||||
/>
|
||||
{isSaving ? (
|
||||
<Loader2 className="w-4 h-4 text-icon-action animate-spin flex-shrink-0" />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tasks List */}
|
||||
<div className="flex flex-col h-full gap-sm overflow-y-auto scrollbar mt-4">
|
||||
<div className="flex flex-col h-full gap-sm overflow-y-auto scrollbar">
|
||||
{project.tasks.length > 0 ? (
|
||||
project.tasks.map((task, index) => (
|
||||
<TaskItem
|
||||
key={task.id}
|
||||
task={task}
|
||||
isActive={activeTaskId === task.id.toString()}
|
||||
onSelect={() => onTaskSelect(project.project_id, task.question, task.id.toString())}
|
||||
onDelete={() => onTaskDelete(task.id.toString())}
|
||||
onShare={() => onTaskShare(task.id.toString())}
|
||||
isLast={index === project.tasks.length - 1}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="
|
||||
{/* Project Stats */}
|
||||
<div className="grid grid-cols-4 gap-lg border-solid border-t-0 border-x-0 border-border-disabled pb-md">
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.total-tokens")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<Hash className="w-4 h-4 text-icon-primary" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_tokens.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.total-tasks")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<Pin className="w-4 h-4 text-icon-primary" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.task_count}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.completed")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<CheckCircle className="w-4 h-4 text-icon-success" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_completed_tasks}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-xs">
|
||||
<span className="text-text-label text-label-sm font-normal">
|
||||
{t("layout.ongoing")}
|
||||
</span>
|
||||
<div className="flex flex-row items-center gap-sm">
|
||||
<LoaderCircle className="w-4 h-4 text-icon-information" />
|
||||
<span className="text-text-heading text-body-lg font-bold">
|
||||
{project.total_ongoing_tasks}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tasks List */}
|
||||
<div className="flex flex-col h-full gap-sm overflow-y-auto scrollbar mt-4">
|
||||
<div className="flex flex-col h-full gap-sm overflow-y-auto scrollbar">
|
||||
{project.tasks.length > 0 ? (
|
||||
project.tasks.map((task, index) => (
|
||||
<TaskItem
|
||||
key={task.id}
|
||||
task={task}
|
||||
isActive={activeTaskId === task.id.toString()}
|
||||
onSelect={() =>
|
||||
onTaskSelect(
|
||||
project.project_id,
|
||||
task.question,
|
||||
task.id.toString()
|
||||
)
|
||||
}
|
||||
onDelete={() => onTaskDelete(task.id.toString())}
|
||||
onShare={() => onTaskShare(task.id.toString())}
|
||||
isLast={index === project.tasks.length - 1}
|
||||
showActions={false}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div
|
||||
className="
|
||||
text-center py-lg
|
||||
text-text-label text-sm
|
||||
">
|
||||
<Clock className="w-8 h-8 mx-auto mb-sm text-icon-secondary opacity-50" />
|
||||
{t("layout.no-tasks-in-project")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContentSection>
|
||||
"
|
||||
>
|
||||
<Clock className="w-8 h-8 mx-auto mb-sm text-icon-secondary opacity-50" />
|
||||
{t("layout.no-tasks-in-project")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContentSection>
|
||||
|
||||
<DialogFooter
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
>
|
||||
{t("layout.close")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
<DialogFooter
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
>
|
||||
{t("layout.close")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ interface TaskItemProps {
|
|||
isOngoing?: boolean;
|
||||
onPause?: () => void;
|
||||
onResume?: () => void;
|
||||
showActions?: boolean;
|
||||
}
|
||||
|
||||
export default function TaskItem({
|
||||
|
|
@ -34,7 +35,8 @@ export default function TaskItem({
|
|||
isLast,
|
||||
isOngoing = false,
|
||||
onPause,
|
||||
onResume
|
||||
onResume,
|
||||
showActions = true
|
||||
}: TaskItemProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
|
@ -146,23 +148,40 @@ export default function TaskItem({
|
|||
</Tag>
|
||||
)}
|
||||
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
variant="ghost"
|
||||
className="rounded-full"
|
||||
{showActions && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
variant="ghost"
|
||||
className="rounded-full"
|
||||
>
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="end"
|
||||
className="w-[98px] p-sm rounded-[12px] bg-dropdown-bg border border-solid border-dropdown-border"
|
||||
>
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="end"
|
||||
className="w-[98px] p-sm rounded-[12px] bg-dropdown-bg border border-solid border-dropdown-border"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
{!isOngoing && (
|
||||
<div className="space-y-1">
|
||||
{!isOngoing && (
|
||||
<PopoverClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-start"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onShare();
|
||||
}}
|
||||
>
|
||||
<Share size={14} />
|
||||
{t("layout.share")}
|
||||
</Button>
|
||||
</PopoverClose>
|
||||
)}
|
||||
|
||||
<PopoverClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
@ -170,35 +189,20 @@ export default function TaskItem({
|
|||
className="w-full justify-start"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onShare();
|
||||
onDelete();
|
||||
}}
|
||||
>
|
||||
<Share size={14} />
|
||||
{t("layout.share")}
|
||||
<Trash2
|
||||
size={14}
|
||||
className="text-icon-primary group-hover:text-icon-cuation"
|
||||
/>
|
||||
{t("layout.delete")}
|
||||
</Button>
|
||||
</PopoverClose>
|
||||
)}
|
||||
|
||||
<PopoverClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-start"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete();
|
||||
}}
|
||||
>
|
||||
<Trash2
|
||||
size={14}
|
||||
className="text-icon-primary group-hover:text-icon-cuation"
|
||||
/>
|
||||
{t("layout.delete")}
|
||||
</Button>
|
||||
</PopoverClose>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default function ConfirmModal({
|
|||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-white/5 backdrop-blur-sm z-100 alert-dialog"
|
||||
className="fixed inset-0 bg-white/5 z-100 alert-dialog"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export default function ConfirmModal({
|
|||
initial={{ opacity: 0, scale: 0.9, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.9, y: 20 }}
|
||||
className="fixed max-w-md alert-dialog-wrapper rounded-xl backdrop-blur-xl shadow-perfect"
|
||||
className="fixed max-w-md alert-dialog-wrapper rounded-xl shadow-perfect"
|
||||
>
|
||||
<div className="p-6">
|
||||
<span className="text-body-lg font-bold text-text-primary mb-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue