@mytec: iter1.1.1 ready for testing
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProjectsStore } from '@/store/projects.ts';
|
||||
import { useToastStore } from '@/components/ui/Toast.tsx';
|
||||
import { useDirtyStore } from '@/hooks/useUnsavedChanges.ts';
|
||||
import Button from '@/components/ui/Button.tsx';
|
||||
import ConfirmDialog from '@/components/ui/ConfirmDialog.tsx';
|
||||
import { logger } from '@/utils/logger.ts';
|
||||
|
||||
export default function ProjectPanel() {
|
||||
@@ -12,11 +14,19 @@ export default function ProjectPanel() {
|
||||
const loadProject = useProjectsStore((s) => s.loadProject);
|
||||
const deleteProject = useProjectsStore((s) => s.deleteProject);
|
||||
const addToast = useToastStore((s) => s.addToast);
|
||||
const isDirty = useDirtyStore((s) => s.isDirty);
|
||||
|
||||
const [projectName, setProjectName] = useState('');
|
||||
const [showSaveForm, setShowSaveForm] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// Confirm dialog state
|
||||
const [confirmAction, setConfirmAction] = useState<{
|
||||
type: 'load' | 'delete';
|
||||
id: string;
|
||||
name: string;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadProjects();
|
||||
}, [loadProjects]);
|
||||
@@ -30,6 +40,7 @@ export default function ProjectPanel() {
|
||||
|
||||
try {
|
||||
await saveProject(name);
|
||||
useDirtyStore.getState().markClean();
|
||||
addToast(`Project "${name}" saved`, 'success');
|
||||
setProjectName('');
|
||||
setShowSaveForm(false);
|
||||
@@ -39,11 +50,20 @@ export default function ProjectPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoad = async (id: string, name: string) => {
|
||||
const handleLoadRequest = (id: string, name: string) => {
|
||||
if (isDirty) {
|
||||
setConfirmAction({ type: 'load', id, name });
|
||||
} else {
|
||||
executeLoad(id, name);
|
||||
}
|
||||
};
|
||||
|
||||
const executeLoad = async (id: string, name: string) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const project = await loadProject(id);
|
||||
if (project) {
|
||||
useDirtyStore.getState().markClean();
|
||||
addToast(`Loaded project "${name}" (${project.sites.length} sites)`, 'success');
|
||||
} else {
|
||||
addToast('Project not found', 'error');
|
||||
@@ -56,7 +76,11 @@ export default function ProjectPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string, name: string) => {
|
||||
const handleDeleteRequest = (id: string, name: string) => {
|
||||
setConfirmAction({ type: 'delete', id, name });
|
||||
};
|
||||
|
||||
const executeDelete = async (id: string, name: string) => {
|
||||
try {
|
||||
await deleteProject(id);
|
||||
addToast(`Project "${name}" deleted`, 'info');
|
||||
@@ -66,6 +90,18 @@ export default function ProjectPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!confirmAction) return;
|
||||
const { type, id, name } = confirmAction;
|
||||
setConfirmAction(null);
|
||||
|
||||
if (type === 'load') {
|
||||
await executeLoad(id, name);
|
||||
} else {
|
||||
await executeDelete(id, name);
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (timestamp: number) => {
|
||||
return new Date(timestamp).toLocaleDateString(undefined, {
|
||||
month: 'short',
|
||||
@@ -142,7 +178,7 @@ export default function ProjectPanel() {
|
||||
</div>
|
||||
<div className="flex gap-1 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => handleLoad(project.id, project.name)}
|
||||
onClick={() => handleLoadRequest(project.id, project.name)}
|
||||
disabled={isLoading}
|
||||
className="px-2 py-1 text-xs text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded
|
||||
min-w-[40px] min-h-[32px] flex items-center justify-center disabled:opacity-50"
|
||||
@@ -150,7 +186,7 @@ export default function ProjectPanel() {
|
||||
Load
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(project.id, project.name)}
|
||||
onClick={() => handleDeleteRequest(project.id, project.name)}
|
||||
className="px-2 py-1 text-xs text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded
|
||||
min-w-[32px] min-h-[32px] flex items-center justify-center"
|
||||
>
|
||||
@@ -161,6 +197,27 @@ export default function ProjectPanel() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Confirmation dialog */}
|
||||
{confirmAction && (
|
||||
<ConfirmDialog
|
||||
title={
|
||||
confirmAction.type === 'delete'
|
||||
? 'Delete Project?'
|
||||
: 'Unsaved Changes'
|
||||
}
|
||||
message={
|
||||
confirmAction.type === 'delete'
|
||||
? `Delete project "${confirmAction.name}"? This cannot be undone.`
|
||||
: `You have unsaved changes. Load project "${confirmAction.name}" anyway?`
|
||||
}
|
||||
confirmLabel={confirmAction.type === 'delete' ? 'Delete' : 'Load'}
|
||||
cancelLabel="Cancel"
|
||||
variant={confirmAction.type === 'delete' ? 'danger' : 'warning'}
|
||||
onConfirm={handleConfirm}
|
||||
onCancel={() => setConfirmAction(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user