diff --git a/src/app/objectives/page.tsx b/src/app/objectives/page.tsx
index 14e98a1..bbe3bee 100644
--- a/src/app/objectives/page.tsx
+++ b/src/app/objectives/page.tsx
@@ -2,72 +2,8 @@ import { auth } from '@/lib/auth';
import { redirect } from 'next/navigation';
import Link from 'next/link';
import { getUserOKRs } from '@/services/okrs';
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
-import { Badge } from '@/components/ui';
-import { getGravatarUrl } from '@/lib/gravatar';
-import type { OKRStatus, KeyResultStatus } from '@/lib/types';
-import { OKR_STATUS_LABELS, KEY_RESULT_STATUS_LABELS } from '@/lib/types';
-
-// Helper functions for status colors
-function getOKRStatusColor(status: OKRStatus): { bg: string; color: string } {
- switch (status) {
- case 'NOT_STARTED':
- return {
- bg: 'color-mix(in srgb, #6b7280 15%, transparent)',
- color: '#6b7280',
- };
- case 'IN_PROGRESS':
- return {
- bg: 'color-mix(in srgb, #3b82f6 15%, transparent)',
- color: '#3b82f6',
- };
- case 'COMPLETED':
- return {
- bg: 'color-mix(in srgb, #10b981 15%, transparent)',
- color: '#10b981',
- };
- case 'CANCELLED':
- return {
- bg: 'color-mix(in srgb, #ef4444 15%, transparent)',
- color: '#ef4444',
- };
- default:
- return {
- bg: 'color-mix(in srgb, #6b7280 15%, transparent)',
- color: '#6b7280',
- };
- }
-}
-
-function getKeyResultStatusColor(status: KeyResultStatus): { bg: string; color: string } {
- switch (status) {
- case 'NOT_STARTED':
- return {
- bg: 'color-mix(in srgb, #6b7280 12%, transparent)',
- color: '#6b7280',
- };
- case 'IN_PROGRESS':
- return {
- bg: 'color-mix(in srgb, #3b82f6 12%, transparent)',
- color: '#3b82f6',
- };
- case 'COMPLETED':
- return {
- bg: 'color-mix(in srgb, #10b981 12%, transparent)',
- color: '#10b981',
- };
- case 'AT_RISK':
- return {
- bg: 'color-mix(in srgb, #f59e0b 12%, transparent)',
- color: '#f59e0b',
- };
- default:
- return {
- bg: 'color-mix(in srgb, #6b7280 12%, transparent)',
- color: '#6b7280',
- };
- }
-}
+import { Card } from '@/components/ui';
+import { ObjectivesList } from '@/components/okrs/ObjectivesList';
export default async function ObjectivesPage() {
const session = await auth();
@@ -120,8 +56,8 @@ export default async function ObjectivesPage() {
🎯
Aucun OKR défini
- Vous n'avez pas encore d'OKR défini. Contactez un administrateur d'équipe pour
- en créer.
+ Vous n'avez pas encore d'OKR défini. Contactez un administrateur d'équipe
+ pour en créer.
@@ -130,172 +66,8 @@ export default async function ObjectivesPage() {
) : (
-
- {periods.map((period) => {
- const periodOKRs = okrsByPeriod[period];
- const totalProgress =
- periodOKRs.reduce((sum, okr) => sum + (okr.progress || 0), 0) / periodOKRs.length;
-
- return (
-
- {/* Period Header */}
-
-
-
- {period}
-
-
- {periodOKRs.length} OKR{periodOKRs.length !== 1 ? 's' : ''}
-
-
-
- Progression moyenne: {Math.round(totalProgress)}%
-
-
-
- {/* OKRs Grid */}
-
- {periodOKRs.map((okr) => {
- const progress = okr.progress || 0;
- const progressColor =
- progress >= 75 ? '#10b981' : progress >= 25 ? '#f59e0b' : '#ef4444';
-
- return (
-
-
-
-
- {okr.objective}
-
- {OKR_STATUS_LABELS[okr.status]}
-
-
- {okr.description && (
- {okr.description}
- )}
-
- 👥
- {okr.team.name}
-
-
-
- {/* Progress Bar */}
-
-
- Progression
-
- {progress}%
-
-
-
-
-
- {/* Key Results Preview */}
- {okr.keyResults && okr.keyResults.length > 0 && (
-
-
- Key Results ({okr.keyResults.length})
-
-
- {okr.keyResults.slice(0, 3).map((kr) => {
- const krProgress =
- kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
- const krProgressColor =
- krProgress >= 100
- ? '#10b981'
- : krProgress >= 50
- ? '#f59e0b'
- : '#ef4444';
-
- return (
-
-
-
- {kr.title}
-
-
- {KEY_RESULT_STATUS_LABELS[kr.status]}
-
-
-
-
- {kr.currentValue} / {kr.targetValue} {kr.unit}
-
-
- {Math.round(krProgress)}%
-
-
-
-
- );
- })}
- {okr.keyResults.length > 3 && (
-
- +{okr.keyResults.length - 3} autre{okr.keyResults.length - 3 !== 1 ? 's' : ''}
-
- )}
-
-
- )}
-
- {/* Dates */}
-
-
- {new Date(okr.startDate).toLocaleDateString('fr-FR', {
- day: 'numeric',
- month: 'short',
- })}
-
- →
-
- {new Date(okr.endDate).toLocaleDateString('fr-FR', {
- day: 'numeric',
- month: 'short',
- year: 'numeric',
- })}
-
-
-
-
-
- );
- })}
-
-
- );
- })}
-
+
)}
);
}
-
diff --git a/src/components/okrs/OKRCard.tsx b/src/components/okrs/OKRCard.tsx
index 3ebd27a..8a83d24 100644
--- a/src/components/okrs/OKRCard.tsx
+++ b/src/components/okrs/OKRCard.tsx
@@ -1,11 +1,10 @@
'use client';
-import { useState, useTransition } from 'react';
+import { useTransition } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
import { Badge } from '@/components/ui';
-import { Button } from '@/components/ui';
import { getGravatarUrl } from '@/lib/gravatar';
import type { OKR, KeyResult, OKRStatus, KeyResultStatus } from '@/lib/types';
import { OKR_STATUS_LABELS, KEY_RESULT_STATUS_LABELS } from '@/lib/types';
@@ -75,9 +74,10 @@ interface OKRCardProps {
okr: OKR & { teamMember?: { user: { id: string; email: string; name: string | null } } };
teamId: string;
isAdmin?: boolean;
+ compact?: boolean;
}
-export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
+export function OKRCard({ okr, teamId, isAdmin = false, compact = false }: OKRCardProps) {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const progress = okr.progress || 0;
@@ -100,18 +100,128 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
if (!response.ok) {
const error = await response.json();
- alert(error.error || 'Erreur lors de la suppression de l\'OKR');
+ alert(error.error || "Erreur lors de la suppression de l'OKR");
return;
}
router.refresh();
} catch (error) {
console.error('Error deleting OKR:', error);
- alert('Erreur lors de la suppression de l\'OKR');
+ alert("Erreur lors de la suppression de l'OKR");
}
});
};
+ if (compact) {
+ return (
+
+
+
+
+
+
🎯
+
+
+ {okr.objective}
+
+ {okr.teamMember && (
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ {okr.teamMember.user.name || okr.teamMember.user.email}
+
+
+ )}
+
+
+
+ {isAdmin && (
+
+
+
+
+
+ )}
+
+ {okr.period}
+
+
+
+
+
+
+ {/* Progress Bar */}
+
+
+ Progression
+
+ {progress}%
+
+
+
+
+
+ {/* Status */}
+
+
+ {OKR_STATUS_LABELS[okr.status]}
+
+ {okr.keyResults && okr.keyResults.length > 0 && (
+
+ {okr.keyResults.length} KR{okr.keyResults.length !== 1 ? 's' : ''}
+
+ )}
+
+
+
+
+
+ );
+ }
+
return (
@@ -141,7 +251,7 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
-
+
{/* Action Zone */}
{isAdmin && (
@@ -208,9 +318,7 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
{/* Status */}
Statut:
-
- {OKR_STATUS_LABELS[okr.status]}
-
+ {OKR_STATUS_LABELS[okr.status]}
{/* Key Results List */}
@@ -223,7 +331,8 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
{okr.keyResults
.sort((a, b) => a.order - b.order)
.map((kr: KeyResult) => {
- const krProgress = kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
+ const krProgress =
+ kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
const krProgressColor =
krProgress >= 100
? 'var(--success)'
@@ -234,7 +343,9 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
return (
-
{kr.title}
+
+ {kr.title}
+
);
}
-
diff --git a/src/components/okrs/OKRsList.tsx b/src/components/okrs/OKRsList.tsx
index a387122..6a91579 100644
--- a/src/components/okrs/OKRsList.tsx
+++ b/src/components/okrs/OKRsList.tsx
@@ -1,12 +1,13 @@
'use client';
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
import { OKRCard } from './OKRCard';
-import { Card, ToggleGroup, type ToggleOption } from '@/components/ui';
+import { Card, ToggleGroup } from '@/components/ui';
import { getGravatarUrl } from '@/lib/gravatar';
import type { OKR } from '@/lib/types';
type ViewMode = 'grid' | 'grouped';
+type CardViewMode = 'detailed' | 'compact';
interface OKRsListProps {
okrsData: Array<{
@@ -21,8 +22,23 @@ interface OKRsListProps {
isAdmin?: boolean;
}
+const CARD_VIEW_STORAGE_KEY = 'okr-card-view-mode';
+
export function OKRsList({ okrsData, teamId, isAdmin = false }: OKRsListProps) {
const [viewMode, setViewMode] = useState('grouped');
+ const [cardViewMode, setCardViewMode] = useState(() => {
+ if (typeof window !== 'undefined') {
+ const stored = localStorage.getItem(CARD_VIEW_STORAGE_KEY);
+ return (stored as CardViewMode) || 'detailed';
+ }
+ return 'detailed';
+ });
+
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ localStorage.setItem(CARD_VIEW_STORAGE_KEY, cardViewMode);
+ }
+ }, [cardViewMode]);
// Flatten OKRs for grid view
const allOKRs = okrsData.flatMap((tm) =>
@@ -39,9 +55,7 @@ export function OKRsList({ okrsData, teamId, isAdmin = false }: OKRsListProps) {
🎯
Aucun OKR défini
-
- Aucun OKR n'a encore été défini pour cette équipe
-
+ Aucun OKR n'a encore été défini pour cette équipe
);
}
@@ -49,37 +63,78 @@ export function OKRsList({ okrsData, teamId, isAdmin = false }: OKRsListProps) {
return (
{/* View Toggle */}
-
+
OKRs
-
-
-
- ),
- },
- {
- value: 'grid',
- label: 'Grille',
- icon: (
-
-
-
- ),
- },
- ]}
- />
+
+
+
+
+ ),
+ },
+ {
+ value: 'compact',
+ label: 'Mini',
+ icon: (
+
+
+
+ ),
+ },
+ ]}
+ />
+
+
+
+ ),
+ },
+ {
+ value: 'grid',
+ label: 'Grille',
+ icon: (
+
+
+
+ ),
+ },
+ ]}
+ />
+
{/* Grouped View */}
@@ -113,7 +168,9 @@ export function OKRsList({ okrsData, teamId, isAdmin = false }: OKRsListProps) {
{/* OKRs Grid */}
-
+
{teamMember.okrs.map((okr) => (
))}
@@ -133,13 +191,20 @@ export function OKRsList({ okrsData, teamId, isAdmin = false }: OKRsListProps) {
) : (
/* Grid View */
-
+
{allOKRs.map((okr) => (
-
+
))}
)}
);
}
-
diff --git a/src/components/okrs/ObjectivesList.tsx b/src/components/okrs/ObjectivesList.tsx
new file mode 100644
index 0000000..2728f8c
--- /dev/null
+++ b/src/components/okrs/ObjectivesList.tsx
@@ -0,0 +1,122 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { OKRCard } from './OKRCard';
+import { ToggleGroup } from '@/components/ui';
+import type { OKR } from '@/lib/types';
+
+type CardViewMode = 'detailed' | 'compact';
+
+interface ObjectivesListProps {
+ okrsByPeriod: Record<
+ string,
+ Array
+ >;
+ periods: string[];
+}
+
+const CARD_VIEW_STORAGE_KEY = 'okr-card-view-mode';
+
+export function ObjectivesList({ okrsByPeriod, periods }: ObjectivesListProps) {
+ const [cardViewMode, setCardViewMode] = useState(() => {
+ if (typeof window !== 'undefined') {
+ const stored = localStorage.getItem(CARD_VIEW_STORAGE_KEY);
+ return (stored as CardViewMode) || 'detailed';
+ }
+ return 'detailed';
+ });
+
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ localStorage.setItem(CARD_VIEW_STORAGE_KEY, cardViewMode);
+ }
+ }, [cardViewMode]);
+
+ return (
+
+ {/* Global View Toggle */}
+
+
+
+
+ ),
+ },
+ {
+ value: 'compact',
+ label: 'Mini',
+ icon: (
+
+
+
+ ),
+ },
+ ]}
+ />
+
+
+ {periods.map((period) => {
+ const periodOKRs = okrsByPeriod[period];
+ const totalProgress =
+ periodOKRs.reduce((sum, okr) => sum + (okr.progress || 0), 0) / periodOKRs.length;
+
+ return (
+
+ {/* Period Header */}
+
+
+
+ {period}
+
+
+ {periodOKRs.length} OKR{periodOKRs.length !== 1 ? 's' : ''}
+
+
+
+ Progression moyenne:{' '}
+ {Math.round(totalProgress)}%
+
+
+
+ {/* OKRs Grid */}
+
+ {periodOKRs.map((okr) => (
+
+ ))}
+
+
+ );
+ })}
+
+ );
+}
diff --git a/src/components/okrs/index.ts b/src/components/okrs/index.ts
index 9fe390f..1730db1 100644
--- a/src/components/okrs/index.ts
+++ b/src/components/okrs/index.ts
@@ -2,4 +2,5 @@ export { OKRCard } from './OKRCard';
export { OKRForm } from './OKRForm';
export { KeyResultItem } from './KeyResultItem';
export { OKRsList } from './OKRsList';
+export { ObjectivesList } from './ObjectivesList';