sauvegarde boite a idée
This commit is contained in:
79
.gitignore
vendored
79
.gitignore
vendored
@@ -6,7 +6,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
*.next
|
||||
.next/
|
||||
.next/*
|
||||
|
||||
node_modules
|
||||
@@ -26,82 +26,7 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
/src/generated/prisma
|
||||
.next/dev/prerender-manifest.json
|
||||
.next/dev/*
|
||||
.next/dev/trace
|
||||
.next/dev/cache/turbopack/23c46498/CURRENT
|
||||
.next/dev/cache/turbopack/23c46498/LOG
|
||||
.next/dev/server/app-paths-manifest.json
|
||||
.next/dev/server/next-font-manifest.js
|
||||
.next/dev/server/next-font-manifest.json
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__661e4e50._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js.map
|
||||
.next/dev/static/chunks/\[root-of-the-server]__c391f813._.css
|
||||
.next/dev/static/chunks/\[root-of-the-server]__c391f813._.css.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_0ae2c1c3._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_0ae2c1c3._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_48e545ad._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_48e545ad._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_c4c2fd93._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_c4c2fd93._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_74b79b3f._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_app_globals_css_bad6b30c._.single.css
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_app_globals_css_bad6b30c._.single.css.map
|
||||
.next/dev/types/routes.d.ts
|
||||
.next/dev/types/validator.ts
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__661e4e50._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js.map
|
||||
.next/dev/prerender-manifest.json
|
||||
.next/dev/trace
|
||||
.next/dev/cache/turbopack/23c46498/CURRENT
|
||||
.next/dev/cache/turbopack/23c46498/LOG
|
||||
.next/dev/server/app-paths-manifest.json
|
||||
.next/dev/server/next-font-manifest.js
|
||||
.next/dev/server/next-font-manifest.json
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__43f27a2c._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__630db118._.js.map
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js
|
||||
.next/dev/server/chunks/\[root-of-the-server]__f694870c._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__2aea0639._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__661e4e50._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__bcada481._.js.map
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js
|
||||
.next/dev/server/chunks/ssr/\[root-of-the-server]__de10d535._.js.map
|
||||
.next/dev/static/chunks/\[root-of-the-server]__c391f813._.css
|
||||
.next/dev/static/chunks/\[root-of-the-server]__c391f813._.css.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_0ae2c1c3._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_0ae2c1c3._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_48e545ad._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_48e545ad._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_c4c2fd93._.js
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_c4c2fd93._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_74b79b3f._.js.map
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_app_globals_css_bad6b30c._.single.css
|
||||
.next/dev/static/chunks/Documents_00 - projet_plumeia_src_app_globals_css_bad6b30c._.single.css.map
|
||||
.next/dev/types/routes.d.ts
|
||||
.next/dev/types/validator.ts
|
||||
|
||||
BIN
.next/dev/cache/turbopack/23c46498/CURRENT
vendored
BIN
.next/dev/cache/turbopack/23c46498/CURRENT
vendored
Binary file not shown.
6
.next/dev/cache/turbopack/23c46498/LOG
vendored
6
.next/dev/cache/turbopack/23c46498/LOG
vendored
@@ -6110,3 +6110,9 @@ FAM | META SEQ | SST SEQ | RANGE
|
||||
0 | 00013727 | 00013726 SST | [=======================================================================] | 3aefa6fd5cf2deb4-f42f94001fcb5351 (0 MiB, fresh)
|
||||
2 | 00013728 | 00013724 SST | [=================================================================================================] | 024fd2c66c04979c-fbb97280b2255708 (0 MiB, fresh)
|
||||
1 | 00013729 | 00013725 SST | [=================================================================================================] | 024fd2c66c04979c-fbb97280b2255708 (0 MiB, fresh)
|
||||
Time 2026-03-05T11:47:26.3903621Z
|
||||
Commit 00013987 4 keys in 9ms 216µs 300ns
|
||||
FAM | META SEQ | SST SEQ | RANGE
|
||||
0 | 00013985 | 00013984 SST | [=======================================================================] | 3aefa6fd5cf2deb4-f42f94001fcb5351 (0 MiB, fresh)
|
||||
1 | 00013986 | 00013982 SST | O | 3ffdfb3b7d50fcf1-3ffdfb3b7d50fcf1 (0 MiB, fresh)
|
||||
2 | 00013987 | 00013983 SST | O | 3ffdfb3b7d50fcf1-3ffdfb3b7d50fcf1 (0 MiB, fresh)
|
||||
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -4,12 +4,15 @@ import IdeaBoard from '@/components/IdeaBoard';
|
||||
import { useProjectContext } from '@/providers/ProjectProvider';
|
||||
|
||||
export default function IdeasPage() {
|
||||
const { project, updateProject } = useProjectContext();
|
||||
const { project, projectId, createIdea, updateIdea, deleteIdea } = useProjectContext();
|
||||
|
||||
return (
|
||||
<IdeaBoard
|
||||
projectId={projectId}
|
||||
ideas={project.ideas || []}
|
||||
onUpdate={(ideas) => updateProject({ ideas })}
|
||||
onCreate={(data) => createIdea(projectId, data)}
|
||||
onUpdateIdea={(id, data) => updateIdea(projectId, id, data)}
|
||||
onDelete={(id) => deleteIdea(projectId, id)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ export default function ProjectLayout({ children }: { children: React.ReactNode
|
||||
const {
|
||||
projects, setCurrentProjectId,
|
||||
updateProject, updateChapter, addChapter,
|
||||
createEntity, updateEntity, deleteEntity, deleteProject
|
||||
createEntity, updateEntity, deleteEntity,
|
||||
createIdea, updateIdea, deleteIdea,
|
||||
deleteProject
|
||||
} = useProjects(user);
|
||||
const { chatHistory, isGenerating, sendMessage } = useChat();
|
||||
|
||||
@@ -111,6 +113,9 @@ export default function ProjectLayout({ children }: { children: React.ReactNode
|
||||
createEntity: (type, data) => createEntity(projectId, type, data),
|
||||
updateEntity: (entityId, data) => updateEntity(projectId, entityId, data),
|
||||
deleteEntity: (entityId) => deleteEntity(projectId, entityId),
|
||||
createIdea: (projectId, data) => createIdea(projectId, data),
|
||||
updateIdea: (projectId, ideaId, data) => updateIdea(projectId, ideaId, data),
|
||||
deleteIdea: (projectId, ideaId) => deleteIdea(projectId, ideaId),
|
||||
deleteProject: () => deleteProject(projectId),
|
||||
incrementUsage,
|
||||
}}>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Idea, IdeaStatus, IdeaCategory } from '@/lib/types';
|
||||
import { Plus, X, GripVertical, CheckCircle, Circle, Clock, Lightbulb, Search, Trash2, Edit3, Save } from 'lucide-react';
|
||||
import { Plus, X, GripVertical, CheckCircle, Circle, Clock, Lightbulb, Search, Trash2, Edit3, Save, Check, CheckCheck, Loader2 } from 'lucide-react';
|
||||
import { useLanguage } from '@/providers/LanguageProvider';
|
||||
import { TranslationKey } from '@/lib/i18n/translations';
|
||||
|
||||
interface IdeaBoardProps {
|
||||
projectId: string;
|
||||
ideas: Idea[];
|
||||
onUpdate: (ideas: Idea[]) => void;
|
||||
onCreate: (data: Partial<Idea>) => Promise<string>;
|
||||
onUpdateIdea: (id: string, data: Partial<Idea>) => Promise<void>;
|
||||
onDelete: (id: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const CATEGORIES: Record<IdeaCategory, { labelKey: string, color: string, icon: any }> = {
|
||||
@@ -26,10 +29,26 @@ const STATUS_LABELS: Record<IdeaStatus, string> = {
|
||||
|
||||
const MAX_DESCRIPTION_LENGTH = 500;
|
||||
|
||||
const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
const IdeaBoard: React.FC<IdeaBoardProps> = ({ projectId, ideas, onCreate, onUpdateIdea, onDelete }) => {
|
||||
const { t } = useLanguage();
|
||||
const [newIdeaTitle, setNewIdeaTitle] = useState('');
|
||||
const [newIdeaCategory, setNewIdeaCategory] = useState<IdeaCategory>('plot');
|
||||
const [localIdeas, setLocalIdeas] = useState<Idea[]>(ideas);
|
||||
|
||||
// Auto-Save State
|
||||
const [saveStatus, setSaveStatus] = useState<'saved_local' | 'saved_db' | 'saving' | 'unsaved'>('saved_db');
|
||||
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// Sync from parent
|
||||
useEffect(() => {
|
||||
setLocalIdeas(ideas);
|
||||
}, [ideas]);
|
||||
|
||||
const saveLocally = (updated: Idea[]) => {
|
||||
setLocalIdeas(updated);
|
||||
localStorage.setItem(`ideas_${projectId}`, JSON.stringify(updated));
|
||||
setSaveStatus('saved_local');
|
||||
};
|
||||
|
||||
// Drag and Drop State
|
||||
const [draggedIdeaId, setDraggedIdeaId] = useState<string | null>(null);
|
||||
@@ -39,12 +58,12 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
|
||||
// --- ACTIONS ---
|
||||
|
||||
const handleAddIdea = (e: React.FormEvent) => {
|
||||
const handleAddIdea = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!newIdeaTitle.trim()) return;
|
||||
|
||||
const newIdea: Idea = {
|
||||
id: `idea-${Date.now()}`,
|
||||
id: `temp-${Date.now()}`,
|
||||
title: newIdeaTitle,
|
||||
description: '',
|
||||
category: newIdeaCategory,
|
||||
@@ -52,36 +71,86 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
onUpdate([...ideas, newIdea]);
|
||||
saveLocally([...localIdeas, newIdea]);
|
||||
setNewIdeaTitle('');
|
||||
};
|
||||
setSaveStatus('saving');
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
if (confirm(t('ideaboard.delete') + " ?")) {
|
||||
onUpdate(ideas.filter(i => i.id !== id));
|
||||
if (editingItem?.id === id) setEditingItem(null);
|
||||
try {
|
||||
await onCreate({
|
||||
title: newIdea.title,
|
||||
category: newIdea.category,
|
||||
status: newIdea.status,
|
||||
});
|
||||
setSaveStatus('saved_db');
|
||||
} catch (err) {
|
||||
setSaveStatus('saved_local');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveEdit = () => {
|
||||
const handleDelete = async (id: string) => {
|
||||
if (confirm(t('ideaboard.delete') + " ?")) {
|
||||
saveLocally(localIdeas.filter(i => i.id !== id));
|
||||
if (editingItem?.id === id) setEditingItem(null);
|
||||
|
||||
setSaveStatus('saving');
|
||||
try {
|
||||
if (!id.startsWith('temp-')) {
|
||||
await onDelete(id);
|
||||
}
|
||||
setSaveStatus('saved_db');
|
||||
} catch (err) {
|
||||
setSaveStatus('saved_local');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveEdit = async () => {
|
||||
if (!editingItem || !editingItem.title?.trim()) return;
|
||||
|
||||
if (editingItem.id) {
|
||||
let isNew = !editingItem.id || editingItem.id.startsWith('temp-');
|
||||
|
||||
if (!isNew) {
|
||||
// Update existing
|
||||
onUpdate(ideas.map(i => i.id === editingItem.id ? { ...i, ...editingItem } as Idea : i));
|
||||
saveLocally(localIdeas.map(i => i.id === editingItem.id ? { ...i, ...editingItem } as Idea : i));
|
||||
setEditingItem(null);
|
||||
setSaveStatus('saving');
|
||||
try {
|
||||
await onUpdateIdea(editingItem.id!, {
|
||||
title: editingItem.title,
|
||||
description: editingItem.description,
|
||||
category: editingItem.category,
|
||||
status: editingItem.status
|
||||
});
|
||||
setSaveStatus('saved_db');
|
||||
} catch (err) {
|
||||
setSaveStatus('saved_local');
|
||||
}
|
||||
} else {
|
||||
// Create new from modal
|
||||
const newIdea: Idea = {
|
||||
id: `idea-${Date.now()}`,
|
||||
id: `temp-${Date.now()}`,
|
||||
title: editingItem.title || '',
|
||||
description: editingItem.description || '',
|
||||
category: editingItem.category || 'plot',
|
||||
status: editingItem.status || 'todo',
|
||||
createdAt: Date.now()
|
||||
};
|
||||
onUpdate([...ideas, newIdea]);
|
||||
}
|
||||
saveLocally([...localIdeas, newIdea]);
|
||||
setEditingItem(null);
|
||||
|
||||
setSaveStatus('saving');
|
||||
try {
|
||||
await onCreate({
|
||||
title: newIdea.title,
|
||||
description: newIdea.description,
|
||||
category: newIdea.category,
|
||||
status: newIdea.status,
|
||||
});
|
||||
setSaveStatus('saved_db');
|
||||
} catch (err) {
|
||||
setSaveStatus('saved_local');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const openQuickAdd = (status: IdeaStatus) => {
|
||||
@@ -104,14 +173,24 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent, status: IdeaStatus) => {
|
||||
const handleDrop = async (e: React.DragEvent, status: IdeaStatus) => {
|
||||
e.preventDefault();
|
||||
if (draggedIdeaId) {
|
||||
const updatedIdeas = ideas.map(idea =>
|
||||
saveLocally(localIdeas.map(idea =>
|
||||
idea.id === draggedIdeaId ? { ...idea, status } : idea
|
||||
);
|
||||
onUpdate(updatedIdeas);
|
||||
));
|
||||
const idToUpdate = draggedIdeaId;
|
||||
setDraggedIdeaId(null);
|
||||
|
||||
if (!idToUpdate.startsWith('temp-')) {
|
||||
setSaveStatus('saving');
|
||||
try {
|
||||
await onUpdateIdea(idToUpdate, { status });
|
||||
setSaveStatus('saved_db');
|
||||
} catch (err) {
|
||||
setSaveStatus('saved_local');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,7 +202,7 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
// --- RENDERERS ---
|
||||
|
||||
const Column = ({ title, status, icon: Icon }: { title: string, status: IdeaStatus, icon: any }) => {
|
||||
const columnIdeas = ideas.filter(i => i.status === status);
|
||||
const columnIdeas = localIdeas.filter(i => i.status === status);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -227,14 +306,31 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
|
||||
{/* Header & Add Form (Top Bar) */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 bg-theme-panel p-4 rounded-xl border border-theme-border shadow-sm shrink-0 transition-colors duration-300">
|
||||
<div className="flex justify-between items-center w-full md:w-auto">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-theme-text flex items-center gap-2">
|
||||
<h2 className="text-xl font-bold text-theme-text flex items-center gap-2">
|
||||
<Lightbulb className="text-yellow-500" /> {t('ideaboard.title')}
|
||||
</h2>
|
||||
<p className="text-theme-muted text-sm">{t('ideaboard.desc')}</p>
|
||||
<p className="text-theme-muted text-xs">{t('ideaboard.desc')}</p>
|
||||
</div>
|
||||
{/* Status Indicator for Mobile */}
|
||||
<div className="md:hidden flex items-center gap-2 text-xs font-medium text-slate-400">
|
||||
{saveStatus === 'saving' && <><Loader2 size={12} className="animate-spin text-blue-500" /></>}
|
||||
{saveStatus === 'saved_local' && <><Check size={14} className="text-green-500" /></>}
|
||||
{saveStatus === 'saved_db' && <><CheckCheck size={14} className="text-emerald-600" /></>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleAddIdea} className="flex-1 w-full md:w-auto max-w-2xl flex gap-2">
|
||||
<div className="flex items-center gap-4 w-full md:w-auto flex-1 justify-end">
|
||||
{/* Status Indicator for Desktop */}
|
||||
<div className="hidden md:flex items-center gap-2 text-xs font-medium text-slate-400">
|
||||
{saveStatus === 'saving' && <><Loader2 size={12} className="animate-spin text-blue-500" /> <span className="text-blue-500">Sauvegarde...</span></>}
|
||||
{saveStatus === 'saved_local' && <><Check size={14} className="text-green-500" /> <span className="text-green-500">Brouillon local</span></>}
|
||||
{saveStatus === 'saved_db' && <><CheckCheck size={14} className="text-emerald-600" /> <span className="text-emerald-600">Sauvegardé</span></>}
|
||||
{saveStatus === 'unsaved' && <span className="text-amber-500">Non sauvegardé...</span>}
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleAddIdea} className="flex-1 w-full md:w-auto max-w-lg flex gap-2">
|
||||
<select
|
||||
value={newIdeaCategory}
|
||||
onChange={(e) => setNewIdeaCategory(e.target.value as IdeaCategory)}
|
||||
@@ -260,6 +356,7 @@ const IdeaBoard: React.FC<IdeaBoardProps> = ({ ideas, onUpdate }) => {
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Kanban Board */}
|
||||
<div className="flex-1 grid grid-cols-1 md:grid-cols-3 gap-6 min-h-0">
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Entity,
|
||||
EntityType,
|
||||
UserProfile,
|
||||
Idea
|
||||
} from '@/lib/types';
|
||||
import api from '@/lib/api';
|
||||
import {
|
||||
@@ -293,6 +294,67 @@ export const useProjects = (user: UserProfile | null) => {
|
||||
}
|
||||
};
|
||||
|
||||
const createIdea = async (projectId: string, data: Partial<Idea>) => {
|
||||
try {
|
||||
const newIdea = await api.ideas.create({
|
||||
projectId,
|
||||
title: data.title || 'Nouveau',
|
||||
description: data.description || '',
|
||||
status: data.status || 'todo',
|
||||
category: data.category || 'plot',
|
||||
});
|
||||
|
||||
setProjects(prev => prev.map(p => {
|
||||
if (p.id !== projectId) return p;
|
||||
return {
|
||||
...p,
|
||||
ideas: [...(p.ideas || []), {
|
||||
...newIdea,
|
||||
createdAt: new Date(newIdea.createdAt).getTime()
|
||||
}]
|
||||
};
|
||||
}));
|
||||
return newIdea.id;
|
||||
} catch (err) {
|
||||
console.error("Failed to create idea", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const updateIdea = async (projectId: string, ideaId: string, data: Partial<Idea>) => {
|
||||
setProjects(prev => prev.map(p => {
|
||||
if (p.id !== projectId) return p;
|
||||
return {
|
||||
...p,
|
||||
ideas: (p.ideas || []).map(i => i.id === ideaId ? { ...i, ...data } : i)
|
||||
};
|
||||
}));
|
||||
|
||||
try {
|
||||
await api.ideas.update(ideaId, data);
|
||||
} catch (err) {
|
||||
console.error("Failed to update idea", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIdea = async (projectId: string, ideaId: string) => {
|
||||
setProjects(prev => prev.map(p => {
|
||||
if (p.id !== projectId) return p;
|
||||
return {
|
||||
...p,
|
||||
ideas: (p.ideas || []).filter(i => i.id !== ideaId)
|
||||
};
|
||||
}));
|
||||
|
||||
try {
|
||||
await api.ideas.delete(ideaId);
|
||||
} catch (err) {
|
||||
console.error("Failed to delete idea", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
projects,
|
||||
currentProjectId,
|
||||
@@ -304,6 +366,9 @@ export const useProjects = (user: UserProfile | null) => {
|
||||
createEntity,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
createIdea,
|
||||
updateIdea,
|
||||
deleteIdea,
|
||||
deleteProject: async (projectId: string) => {
|
||||
try {
|
||||
// Cascade delete is handled by Prisma, just delete the project
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { BookProject, UserProfile, Entity, EntityType } from '@/lib/types';
|
||||
import { BookProject, UserProfile, Entity, EntityType, Idea } from '@/lib/types';
|
||||
|
||||
interface ProjectContextType {
|
||||
project: BookProject;
|
||||
@@ -14,6 +14,9 @@ interface ProjectContextType {
|
||||
createEntity: (type: EntityType, initialData?: Partial<Entity>) => Promise<string>;
|
||||
updateEntity: (entityId: string, data: Partial<Entity>) => Promise<void>;
|
||||
deleteEntity: (entityId: string) => Promise<void>;
|
||||
createIdea: (projectId: string, data: Partial<Idea>) => Promise<string>;
|
||||
updateIdea: (projectId: string, ideaId: string, data: Partial<Idea>) => Promise<void>;
|
||||
deleteIdea: (projectId: string, ideaId: string) => Promise<void>;
|
||||
deleteProject: () => Promise<void>;
|
||||
incrementUsage: () => void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user