feat: add linked item management to action updates in SWOT analysis
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 5s

This commit is contained in:
Julien Froidefond
2025-12-15 13:34:09 +01:00
parent 0cf7437efe
commit d735e1c4c5
3 changed files with 87 additions and 1 deletions

View File

@@ -183,6 +183,7 @@ export async function updateAction(
description?: string;
priority?: number;
status?: string;
linkedItemIds?: string[];
}
) {
const session = await auth();

View File

@@ -66,6 +66,7 @@ export function ActionPanel({
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [priority, setPriority] = useState(1);
const [editingSelectedItems, setEditingSelectedItems] = useState<string[]>([]);
function openCreateModal() {
if (selectedItems.length < 2) {
@@ -75,6 +76,7 @@ export function ActionPanel({
setTitle('');
setDescription('');
setPriority(1);
setEditingSelectedItems([]);
setEditingAction(null);
setShowModal(true);
}
@@ -83,6 +85,7 @@ export function ActionPanel({
setTitle(action.title);
setDescription(action.description || '');
setPriority(action.priority);
setEditingSelectedItems(action.links.map((link) => link.swotItemId));
setEditingAction(action);
setShowModal(true);
}
@@ -90,9 +93,16 @@ export function ActionPanel({
function closeModal() {
setShowModal(false);
setEditingAction(null);
setEditingSelectedItems([]);
onExitLinkMode();
}
function toggleEditingItem(itemId: string) {
setEditingSelectedItems((prev) =>
prev.includes(itemId) ? prev.filter((id) => id !== itemId) : [...prev, itemId]
);
}
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
@@ -100,10 +110,15 @@ export function ActionPanel({
startTransition(async () => {
if (editingAction) {
if (editingSelectedItems.length < 2) {
alert('Une action doit être liée à au moins 2 items SWOT');
return;
}
await updateAction(editingAction.id, sessionId, {
title: title.trim(),
description: description.trim() || undefined,
priority,
linkedItemIds: editingSelectedItems,
});
} else {
await createAction(sessionId, {
@@ -302,6 +317,48 @@ export function ActionPanel({
</div>
)}
{editingAction && (
<div className="mb-4">
<p className="mb-2 text-sm font-medium text-foreground">
Items liés ({editingSelectedItems.length}) :
</p>
<div className="max-h-48 space-y-2 overflow-y-auto rounded-lg border border-border bg-background p-3">
{allItems.map((item) => {
const isSelected = editingSelectedItems.includes(item.id);
return (
<label
key={item.id}
className={`
flex cursor-pointer items-center gap-2 rounded-lg border p-2 transition-colors
${
isSelected
? 'border-primary bg-primary/10'
: 'border-border bg-card hover:bg-card-hover'
}
`}
>
<input
type="checkbox"
checked={isSelected}
onChange={() => toggleEditingItem(item.id)}
className="h-4 w-4 rounded border-border text-primary focus:ring-primary"
/>
<Badge variant={categoryBadgeVariant[item.category]} className="shrink-0">
{categoryShort[item.category]}
</Badge>
<span className="text-sm text-foreground">{item.content}</span>
</label>
);
})}
</div>
{editingSelectedItems.length < 2 && (
<p className="mt-2 text-xs text-destructive">
Sélectionnez au moins 2 items SWOT
</p>
)}
</div>
)}
<div className="space-y-4">
<Input
label="Titre de l'action"

View File

@@ -305,11 +305,39 @@ export async function updateAction(
priority?: number;
status?: string;
dueDate?: Date | null;
linkedItemIds?: string[];
}
) {
const { linkedItemIds, ...updateData } = data;
// If linkedItemIds is provided, update the links
if (linkedItemIds !== undefined) {
// Delete all existing links
await prisma.actionLink.deleteMany({
where: { actionId },
});
// Create new links
if (linkedItemIds.length > 0) {
await prisma.actionLink.createMany({
data: linkedItemIds.map((swotItemId) => ({
actionId,
swotItemId,
})),
});
}
}
return prisma.action.update({
where: { id: actionId },
data,
data: updateData,
include: {
links: {
include: {
swotItem: true,
},
},
},
});
}