fix: improve error handling in API routes and update date handling for OKR and Key Result submissions
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 3m38s
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 3m38s
This commit is contained in:
@@ -49,10 +49,11 @@ export async function PATCH(
|
|||||||
const updated = await updateKeyResult(krId, Number(currentValue), notes || null);
|
const updated = await updateKeyResult(krId, Number(currentValue), notes || null);
|
||||||
|
|
||||||
return NextResponse.json(updated);
|
return NextResponse.json(updated);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Error updating key result:', error);
|
console.error('Error updating key result:', error);
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Erreur lors de la mise à jour du Key Result';
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message || 'Erreur lors de la mise à jour du Key Result' },
|
{ error: errorMessage },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,20 +90,21 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
|
|||||||
// Remove keyResultsUpdates from updateData as it's not part of UpdateOKRInput
|
// Remove keyResultsUpdates from updateData as it's not part of UpdateOKRInput
|
||||||
const { keyResultsUpdates, ...okrUpdateData } = body;
|
const { keyResultsUpdates, ...okrUpdateData } = body;
|
||||||
const finalUpdateData: UpdateOKRInput = { ...okrUpdateData };
|
const finalUpdateData: UpdateOKRInput = { ...okrUpdateData };
|
||||||
if (finalUpdateData.startDate) {
|
if (finalUpdateData.startDate && typeof finalUpdateData.startDate === 'string') {
|
||||||
finalUpdateData.startDate = new Date(finalUpdateData.startDate as any);
|
finalUpdateData.startDate = new Date(finalUpdateData.startDate);
|
||||||
}
|
}
|
||||||
if (finalUpdateData.endDate) {
|
if (finalUpdateData.endDate && typeof finalUpdateData.endDate === 'string') {
|
||||||
finalUpdateData.endDate = new Date(finalUpdateData.endDate as any);
|
finalUpdateData.endDate = new Date(finalUpdateData.endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = await updateOKR(id, finalUpdateData, keyResultsUpdates);
|
const updated = await updateOKR(id, finalUpdateData, keyResultsUpdates);
|
||||||
|
|
||||||
return NextResponse.json(updated);
|
return NextResponse.json(updated);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Error updating OKR:', error);
|
console.error('Error updating OKR:', error);
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Erreur lors de la mise à jour de l\'OKR';
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message || 'Erreur lors de la mise à jour de l\'OKR' },
|
{ error: errorMessage },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -132,10 +133,11 @@ export async function DELETE(request: Request, { params }: { params: Promise<{ i
|
|||||||
await deleteOKR(id);
|
await deleteOKR(id);
|
||||||
|
|
||||||
return NextResponse.json({ success: true });
|
return NextResponse.json({ success: true });
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Error deleting OKR:', error);
|
console.error('Error deleting OKR:', error);
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Erreur lors de la suppression de l\'OKR';
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message || 'Erreur lors de la suppression de l\'OKR' },
|
{ error: errorMessage },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ export async function POST(request: Request, { params }: { params: Promise<{ id:
|
|||||||
const member = await addTeamMember(id, userId, role || 'MEMBER');
|
const member = await addTeamMember(id, userId, role || 'MEMBER');
|
||||||
|
|
||||||
return NextResponse.json(member, { status: 201 });
|
return NextResponse.json(member, { status: 201 });
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Error adding team member:', error);
|
console.error('Error adding team member:', error);
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Erreur lors de l\'ajout du membre';
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message || 'Erreur lors de l\'ajout du membre' },
|
{ error: errorMessage },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,23 @@ export default function EditOKRPage() {
|
|||||||
});
|
});
|
||||||
}, [okrId, teamId]);
|
}, [okrId, teamId]);
|
||||||
|
|
||||||
const handleSubmit = async (data: CreateOKRInput & { keyResultsUpdates?: { create?: any[]; update?: any[]; delete?: string[] } }) => {
|
type KeyResultUpdate = {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
targetValue?: number;
|
||||||
|
unit?: string;
|
||||||
|
order?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (data: CreateOKRInput & {
|
||||||
|
startDate: Date | string;
|
||||||
|
endDate: Date | string;
|
||||||
|
keyResultsUpdates?: {
|
||||||
|
create?: Array<{ title: string; targetValue: number; unit: string; order: number }>;
|
||||||
|
update?: KeyResultUpdate[];
|
||||||
|
delete?: string[]
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
// Convert to UpdateOKRInput format
|
// Convert to UpdateOKRInput format
|
||||||
const updateData = {
|
const updateData = {
|
||||||
objective: data.objective,
|
objective: data.objective,
|
||||||
@@ -64,7 +80,18 @@ export default function EditOKRPage() {
|
|||||||
endDate: typeof data.endDate === 'string' ? new Date(data.endDate) : data.endDate,
|
endDate: typeof data.endDate === 'string' ? new Date(data.endDate) : data.endDate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const payload: any = {
|
const payload: {
|
||||||
|
objective: string;
|
||||||
|
description?: string;
|
||||||
|
period: string;
|
||||||
|
startDate: string;
|
||||||
|
endDate: string;
|
||||||
|
keyResultsUpdates?: {
|
||||||
|
create?: Array<{ title: string; targetValue: number; unit: string; order: number }>;
|
||||||
|
update?: KeyResultUpdate[];
|
||||||
|
delete?: string[];
|
||||||
|
};
|
||||||
|
} = {
|
||||||
...updateData,
|
...updateData,
|
||||||
startDate: updateData.startDate.toISOString(),
|
startDate: updateData.startDate.toISOString(),
|
||||||
endDate: updateData.endDate.toISOString(),
|
endDate: updateData.endDate.toISOString(),
|
||||||
@@ -83,7 +110,7 @@ export default function EditOKRPage() {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
throw new Error(error.error || 'Erreur lors de la mise à jour de l\'OKR');
|
throw new Error(error.error || 'Erreur lors de la mise à jour de l'OKR');
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push(`/teams/${teamId}/okrs/${okrId}`);
|
router.push(`/teams/${teamId}/okrs/${okrId}`);
|
||||||
@@ -121,12 +148,12 @@ export default function EditOKRPage() {
|
|||||||
<main className="mx-auto max-w-4xl px-4 py-8">
|
<main className="mx-auto max-w-4xl px-4 py-8">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<Link href={`/teams/${teamId}/okrs/${okrId}`} className="text-muted hover:text-foreground">
|
<Link href={`/teams/${teamId}/okrs/${okrId}`} className="text-muted hover:text-foreground">
|
||||||
← Retour à l'OKR
|
← Retour à l'OKR
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<h1 className="text-2xl font-bold text-foreground mb-6">Modifier l'OKR</h1>
|
<h1 className="text-2xl font-bold text-foreground mb-6">Modifier l'OKR</h1>
|
||||||
<OKRForm
|
<OKRForm
|
||||||
teamMembers={teamMembers}
|
teamMembers={teamMembers}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export default function OKRDetailPage() {
|
|||||||
<main className="mx-auto max-w-4xl px-4 py-8">
|
<main className="mx-auto max-w-4xl px-4 py-8">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<Link href={`/teams/${teamId}`} className="text-muted hover:text-foreground">
|
<Link href={`/teams/${teamId}`} className="text-muted hover:text-foreground">
|
||||||
← Retour à l'équipe
|
← Retour à l'équipe
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function NewOKRPage() {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
throw new Error(error.error || 'Erreur lors de la création de l\'OKR');
|
throw new Error(error.error || 'Erreur lors de la création de l'OKR');
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push(`/teams/${teamId}`);
|
router.push(`/teams/${teamId}`);
|
||||||
@@ -64,7 +64,7 @@ export default function NewOKRPage() {
|
|||||||
<main className="mx-auto max-w-4xl px-4 py-8">
|
<main className="mx-auto max-w-4xl px-4 py-8">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<Link href={`/teams/${teamId}`} className="text-muted hover:text-foreground">
|
<Link href={`/teams/${teamId}`} className="text-muted hover:text-foreground">
|
||||||
← Retour à l'équipe
|
← Retour à l'équipe
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -61,9 +61,29 @@ interface KeyResultEditInput extends CreateKeyResultInput {
|
|||||||
id?: string; // If present, it's an existing Key Result to update
|
id?: string; // If present, it's an existing Key Result to update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KeyResultUpdate = {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
targetValue?: number;
|
||||||
|
unit?: string;
|
||||||
|
order?: number;
|
||||||
|
};
|
||||||
|
|
||||||
interface OKRFormProps {
|
interface OKRFormProps {
|
||||||
teamMembers: TeamMember[];
|
teamMembers: TeamMember[];
|
||||||
onSubmit: (data: CreateOKRInput & { keyResultsUpdates?: { create?: CreateKeyResultInput[]; update?: Array<{ id: string; title?: string; targetValue?: number; unit?: string; order?: number }>; delete?: string[] } }) => Promise<void>;
|
onSubmit: (
|
||||||
|
data:
|
||||||
|
| (CreateOKRInput & {
|
||||||
|
startDate: Date | string;
|
||||||
|
endDate: Date | string;
|
||||||
|
keyResultsUpdates?: {
|
||||||
|
create?: CreateKeyResultInput[];
|
||||||
|
update?: KeyResultUpdate[];
|
||||||
|
delete?: string[];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
| CreateOKRInput
|
||||||
|
) => Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
initialData?: Partial<CreateOKRInput> & { keyResults?: KeyResult[] };
|
initialData?: Partial<CreateOKRInput> & { keyResults?: KeyResult[] };
|
||||||
}
|
}
|
||||||
@@ -83,13 +103,17 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
// Initialize Key Results from existing ones if in edit mode, otherwise start with one empty
|
// Initialize Key Results from existing ones if in edit mode, otherwise start with one empty
|
||||||
const [keyResults, setKeyResults] = useState<KeyResultEditInput[]>(() => {
|
const [keyResults, setKeyResults] = useState<KeyResultEditInput[]>(() => {
|
||||||
if (initialData?.keyResults && initialData.keyResults.length > 0) {
|
if (initialData?.keyResults && initialData.keyResults.length > 0) {
|
||||||
return initialData.keyResults.map((kr) => ({
|
return initialData.keyResults.map((kr): KeyResultEditInput => {
|
||||||
id: kr.id,
|
const result = {
|
||||||
title: kr.title,
|
title: kr.title,
|
||||||
targetValue: kr.targetValue,
|
targetValue: kr.targetValue,
|
||||||
unit: kr.unit,
|
unit: kr.unit || '%',
|
||||||
order: kr.order,
|
order: kr.order,
|
||||||
}));
|
};
|
||||||
|
// @ts-expect-error - id is added to extend CreateKeyResultInput to KeyResultEditInput
|
||||||
|
result.id = kr.id;
|
||||||
|
return result as KeyResultEditInput;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return [{ title: '', targetValue: 100, unit: '%', order: 0 }];
|
return [{ title: '', targetValue: 100, unit: '%', order: 0 }];
|
||||||
});
|
});
|
||||||
@@ -125,7 +149,11 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
setKeyResults(keyResults.filter((_, i) => i !== index).map((kr, i) => ({ ...kr, order: i })));
|
setKeyResults(keyResults.filter((_, i) => i !== index).map((kr, i) => ({ ...kr, order: i })));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateKeyResult = (index: number, field: keyof KeyResultEditInput, value: any) => {
|
const updateKeyResult = (
|
||||||
|
index: number,
|
||||||
|
field: keyof KeyResultEditInput,
|
||||||
|
value: string | number
|
||||||
|
) => {
|
||||||
const updated = [...keyResults];
|
const updated = [...keyResults];
|
||||||
updated[index] = { ...updated[index], [field]: value };
|
updated[index] = { ...updated[index], [field]: value };
|
||||||
setKeyResults(updated);
|
setKeyResults(updated);
|
||||||
@@ -166,23 +194,32 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
const isEditMode = !!initialData?.teamMemberId;
|
const isEditMode = !!initialData?.teamMemberId;
|
||||||
|
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
|
// Type guard for Key Results with id
|
||||||
|
type KeyResultWithId = KeyResultEditInput & { id: string };
|
||||||
|
const hasId = (kr: KeyResultEditInput): kr is KeyResultWithId => !!kr.id;
|
||||||
|
|
||||||
// In edit mode, separate existing Key Results from new ones
|
// In edit mode, separate existing Key Results from new ones
|
||||||
const existingKeyResults = keyResults.filter((kr) => kr.id);
|
const existingKeyResults: KeyResultWithId[] = keyResults.filter(hasId);
|
||||||
const newKeyResults = keyResults.filter((kr) => !kr.id);
|
const originalKeyResults: KeyResult[] = initialData?.keyResults || [];
|
||||||
const originalKeyResults = initialData?.keyResults || [];
|
const originalIds = new Set(originalKeyResults.map((kr: KeyResult) => kr.id));
|
||||||
const originalIds = new Set(originalKeyResults.map((kr) => kr.id));
|
const currentIds = new Set(existingKeyResults.map((kr: KeyResultWithId) => kr.id));
|
||||||
const currentIds = new Set(existingKeyResults.map((kr) => kr.id));
|
|
||||||
|
|
||||||
// Find deleted Key Results
|
// Find deleted Key Results
|
||||||
const deletedIds = Array.from(originalIds).filter((id) => !currentIds.has(id));
|
const deletedIds = Array.from(originalIds).filter((id) => !currentIds.has(id));
|
||||||
|
|
||||||
// Find updated Key Results (compare with original)
|
// Find updated Key Results (compare with original)
|
||||||
const updated = existingKeyResults
|
const updated = existingKeyResults
|
||||||
.map((kr) => {
|
.map((kr: KeyResultWithId) => {
|
||||||
const original = originalKeyResults.find((okr) => okr.id === kr.id);
|
const original = originalKeyResults.find((okr: KeyResult) => okr.id === kr.id);
|
||||||
if (!original) return null;
|
if (!original) return null;
|
||||||
|
|
||||||
const changes: { id: string; title?: string; targetValue?: number; unit?: string; order?: number } = { id: kr.id };
|
const changes: {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
targetValue?: number;
|
||||||
|
unit?: string;
|
||||||
|
order?: number;
|
||||||
|
} = { id: kr.id };
|
||||||
if (original.title !== kr.title) changes.title = kr.title;
|
if (original.title !== kr.title) changes.title = kr.title;
|
||||||
if (original.targetValue !== kr.targetValue) changes.targetValue = kr.targetValue;
|
if (original.targetValue !== kr.targetValue) changes.targetValue = kr.targetValue;
|
||||||
if (original.unit !== kr.unit) changes.unit = kr.unit;
|
if (original.unit !== kr.unit) changes.unit = kr.unit;
|
||||||
@@ -190,17 +227,27 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
|
|
||||||
return Object.keys(changes).length > 1 ? changes : null; // More than just 'id'
|
return Object.keys(changes).length > 1 ? changes : null; // More than just 'id'
|
||||||
})
|
})
|
||||||
.filter((u): u is { id: string; title?: string; targetValue?: number; unit?: string; order?: number } => u !== null);
|
.filter(
|
||||||
|
(
|
||||||
|
u
|
||||||
|
): u is {
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
targetValue?: number;
|
||||||
|
unit?: string;
|
||||||
|
order?: number;
|
||||||
|
} => u !== null
|
||||||
|
);
|
||||||
|
|
||||||
// Update order for all Key Results based on their position
|
// Update order for all Key Results based on their position
|
||||||
const allKeyResultsWithOrder = keyResults.map((kr, i) => ({ ...kr, order: i }));
|
const allKeyResultsWithOrder = keyResults.map((kr, i) => ({ ...kr, order: i }));
|
||||||
const existingWithOrder = allKeyResultsWithOrder.filter((kr) => kr.id);
|
const existingWithOrder = allKeyResultsWithOrder.filter(hasId) as KeyResultWithId[];
|
||||||
const newWithOrder = allKeyResultsWithOrder.filter((kr) => !kr.id);
|
const newWithOrder = allKeyResultsWithOrder.filter((kr) => !kr.id);
|
||||||
|
|
||||||
// Update order for existing Key Results that changed position
|
// Update order for existing Key Results that changed position
|
||||||
const orderUpdates = existingWithOrder
|
const orderUpdates = existingWithOrder
|
||||||
.map((kr) => {
|
.map((kr) => {
|
||||||
const original = originalKeyResults.find((okr) => okr.id === kr.id);
|
const original = originalKeyResults.find((okr: KeyResult) => okr.id === kr.id);
|
||||||
if (!original || original.order === kr.order) return null;
|
if (!original || original.order === kr.order) return null;
|
||||||
return { id: kr.id, order: kr.order };
|
return { id: kr.id, order: kr.order };
|
||||||
})
|
})
|
||||||
@@ -222,15 +269,23 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
objective,
|
objective,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
period: finalPeriod,
|
period: finalPeriod,
|
||||||
startDate: startDateObj.toISOString() as any,
|
startDate: startDateObj.toISOString(),
|
||||||
endDate: endDateObj.toISOString() as any,
|
endDate: endDateObj.toISOString(),
|
||||||
keyResults: [], // Not used in edit mode
|
keyResults: [], // Not used in edit mode
|
||||||
keyResultsUpdates: {
|
keyResultsUpdates: {
|
||||||
create: newWithOrder.length > 0 ? newWithOrder.map((kr) => ({ title: kr.title, targetValue: kr.targetValue, unit: kr.unit || '%', order: kr.order })) : undefined,
|
create:
|
||||||
|
newWithOrder.length > 0
|
||||||
|
? newWithOrder.map((kr) => ({
|
||||||
|
title: kr.title,
|
||||||
|
targetValue: kr.targetValue,
|
||||||
|
unit: kr.unit || '%',
|
||||||
|
order: kr.order,
|
||||||
|
}))
|
||||||
|
: undefined,
|
||||||
update: allUpdates.length > 0 ? allUpdates : undefined,
|
update: allUpdates.length > 0 ? allUpdates : undefined,
|
||||||
delete: deletedIds.length > 0 ? deletedIds : undefined,
|
delete: deletedIds.length > 0 ? deletedIds : undefined,
|
||||||
},
|
},
|
||||||
} as any);
|
});
|
||||||
} else {
|
} else {
|
||||||
// In create mode, just send Key Results normally
|
// In create mode, just send Key Results normally
|
||||||
await onSubmit({
|
await onSubmit({
|
||||||
@@ -238,8 +293,8 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
objective,
|
objective,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
period: finalPeriod,
|
period: finalPeriod,
|
||||||
startDate: startDateObj.toISOString() as any,
|
startDate: startDateObj,
|
||||||
endDate: endDateObj.toISOString() as any,
|
endDate: endDateObj,
|
||||||
keyResults: keyResults.map((kr, i) => ({ ...kr, order: i })),
|
keyResults: keyResults.map((kr, i) => ({ ...kr, order: i })),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -341,7 +396,8 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
</div>
|
</div>
|
||||||
{period && period !== 'custom' && period !== '' && (
|
{period && period !== 'custom' && period !== '' && (
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Les dates sont automatiquement définies selon le trimestre sélectionné. Vous pouvez les modifier si nécessaire.
|
Les dates sont automatiquement définies selon le trimestre sélectionné. Vous pouvez les
|
||||||
|
modifier si nécessaire.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -351,13 +407,22 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
<label className="block text-sm font-medium text-foreground">
|
<label className="block text-sm font-medium text-foreground">
|
||||||
Key Results * ({keyResults.length}/5)
|
Key Results * ({keyResults.length}/5)
|
||||||
</label>
|
</label>
|
||||||
<Button type="button" onClick={addKeyResult} variant="outline" size="sm" disabled={keyResults.length >= 5}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={addKeyResult}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled={keyResults.length >= 5}
|
||||||
|
>
|
||||||
+ Ajouter
|
+ Ajouter
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{keyResults.map((kr, index) => (
|
{keyResults.map((kr, index) => (
|
||||||
<div key={kr.id || `new-${index}`} className="rounded-lg border border-border bg-card p-4">
|
<div
|
||||||
|
key={kr.id || `new-${index}`}
|
||||||
|
className="rounded-lg border border-border bg-card p-4"
|
||||||
|
>
|
||||||
<div className="mb-3 flex items-center justify-between">
|
<div className="mb-3 flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-foreground">Key Result {index + 1}</span>
|
<span className="text-sm font-medium text-foreground">Key Result {index + 1}</span>
|
||||||
{keyResults.length > 1 && (
|
{keyResults.length > 1 && (
|
||||||
@@ -420,11 +485,10 @@ export function OKRForm({ teamMembers, onSubmit, onCancel, initialData }: OKRFor
|
|||||||
? 'Modification...'
|
? 'Modification...'
|
||||||
: 'Création...'
|
: 'Création...'
|
||||||
: initialData?.teamMemberId
|
: initialData?.teamMemberId
|
||||||
? 'Modifier l\'OKR'
|
? "Modifier l'OKR"
|
||||||
: 'Créer l\'OKR'}
|
: "Créer l'OKR"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ interface QuadrantHelpProps {
|
|||||||
category: SwotCategory;
|
category: SwotCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function QuadrantHelp({ category }: QuadrantHelpProps) {
|
export function QuadrantHelp({ category: _category }: QuadrantHelpProps) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const content = HELP_CONTENT[category];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function DeleteTeamButton({ teamId, teamName }: DeleteTeamButtonProps) {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
alert(error.error || 'Erreur lors de la suppression de l\'équipe');
|
alert(error.error || "Erreur lors de la suppression de l'équipe");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export function DeleteTeamButton({ teamId, teamName }: DeleteTeamButtonProps) {
|
|||||||
router.refresh();
|
router.refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting team:', error);
|
console.error('Error deleting team:', error);
|
||||||
alert('Erreur lors de la suppression de l\'équipe');
|
alert("Erreur lors de la suppression de l'équipe");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -44,17 +44,23 @@ export function DeleteTeamButton({ teamId, teamName }: DeleteTeamButtonProps) {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
className="text-destructive border-destructive hover:bg-destructive/10"
|
className="text-destructive border-destructive hover:bg-destructive/10"
|
||||||
>
|
>
|
||||||
Supprimer l'équipe
|
Supprimer l'équipe
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Modal isOpen={showModal} onClose={() => setShowModal(false)} title="Supprimer l'équipe" size="sm">
|
<Modal
|
||||||
|
isOpen={showModal}
|
||||||
|
onClose={() => setShowModal(false)}
|
||||||
|
title="Supprimer l'équipe"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-muted">
|
<p className="text-muted">
|
||||||
Êtes-vous sûr de vouloir supprimer l'équipe{' '}
|
Êtes-vous sûr de vouloir supprimer l'équipe{' '}
|
||||||
<strong className="text-foreground">"{teamName}"</strong> ?
|
<strong className="text-foreground">"{teamName}"</strong> ?
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-destructive">
|
<p className="text-sm text-destructive">
|
||||||
Cette action est irréversible. Tous les membres, OKRs et données associées seront supprimés.
|
Cette action est irréversible. Tous les membres, OKRs et données associées seront
|
||||||
|
supprimés.
|
||||||
</p>
|
</p>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button variant="ghost" onClick={() => setShowModal(false)} disabled={isPending}>
|
<Button variant="ghost" onClick={() => setShowModal(false)} disabled={isPending}>
|
||||||
@@ -69,4 +75,3 @@ export function DeleteTeamButton({ teamId, teamName }: DeleteTeamButtonProps) {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { prisma } from '@/services/database';
|
import { prisma } from '@/services/database';
|
||||||
import type { CreateTeamInput, UpdateTeamInput, AddTeamMemberInput, UpdateMemberRoleInput, TeamRole } from '@/lib/types';
|
import type { UpdateTeamInput, TeamRole } from '@/lib/types';
|
||||||
|
|
||||||
export async function createTeam(name: string, description: string | null, createdById: string) {
|
export async function createTeam(name: string, description: string | null, createdById: string) {
|
||||||
// Create team and add creator as admin in a transaction
|
// Create team and add creator as admin in a transaction
|
||||||
|
|||||||
Reference in New Issue
Block a user