feat: show evolution indicators per person per axis in weather board

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 17:02:31 +01:00
parent 3b212d6dda
commit 3e869bf8ad

View File

@@ -5,7 +5,7 @@ import { createOrUpdateWeatherEntry } from '@/actions/weather';
import { Avatar } from '@/components/ui/Avatar'; import { Avatar } from '@/components/ui/Avatar';
import { Textarea } from '@/components/ui/Textarea'; import { Textarea } from '@/components/ui/Textarea';
import { Select } from '@/components/ui/Select'; import { Select } from '@/components/ui/Select';
import { WEATHER_EMOJIS } from '@/lib/weather-utils'; import { WEATHER_EMOJIS, getEmojiEvolution } from '@/lib/weather-utils';
interface WeatherEntry { interface WeatherEntry {
id: string; id: string;
@@ -37,6 +37,25 @@ interface WeatherCardProps {
previousEntry?: PreviousEntry | null; previousEntry?: PreviousEntry | null;
} }
function EvolutionIndicator({
current,
previous,
}: {
current: string | null;
previous: string | null | undefined;
}) {
const direction = getEmojiEvolution(current, previous);
if (direction === null) return null;
if (direction === 'up') {
return <span className="text-xs text-green-500 font-bold leading-none" title="Amélioration"></span>;
}
if (direction === 'down') {
return <span className="text-xs text-red-500 font-bold leading-none" title="Dégradation"></span>;
}
return <span className="text-xs text-muted font-bold leading-none" title="Stable"></span>;
}
export function WeatherCard({ sessionId, currentUserId, entry, canEdit, previousEntry }: WeatherCardProps) { export function WeatherCard({ sessionId, currentUserId, entry, canEdit, previousEntry }: WeatherCardProps) {
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const [notes, setNotes] = useState(entry.notes || ''); const [notes, setNotes] = useState(entry.notes || '');
@@ -112,9 +131,6 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit, previous
// For now, we'll use a placeholder - in real app, you'd pass user info as prop // For now, we'll use a placeholder - in real app, you'd pass user info as prop
const user = entry.user; const user = entry.user;
// previousEntry is available for future use in Task 6
void previousEntry;
return ( return (
<tr className={`border-b border-border ${isPending ? 'opacity-50' : ''}`}> <tr className={`border-b border-border ${isPending ? 'opacity-50' : ''}`}>
{/* User column */} {/* User column */}
@@ -130,64 +146,88 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit, previous
{/* Performance */} {/* Performance */}
<td className="w-24 px-2 py-3"> <td className="w-24 px-2 py-3">
{canEditThis ? ( {canEditThis ? (
<Select <div className="flex flex-col items-center gap-1">
value={performanceEmoji || ''} <Select
onChange={(e) => handleEmojiChange('performance', e.target.value || null)} value={performanceEmoji || ''}
options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))} onChange={(e) => handleEmojiChange('performance', e.target.value || null)}
size="sm" options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))}
wrapperClassName="!w-fit mx-auto" size="sm"
className="!w-16 min-w-16 text-center text-lg py-2.5" wrapperClassName="!w-fit mx-auto"
/> className="!w-16 min-w-16 text-center text-lg py-2.5"
/>
<EvolutionIndicator current={performanceEmoji} previous={previousEntry?.performanceEmoji ?? null} />
</div>
) : ( ) : (
<div className="text-2xl text-center">{performanceEmoji || '-'}</div> <div className="flex flex-col items-center gap-1">
<div className="text-2xl text-center">{performanceEmoji || '-'}</div>
<EvolutionIndicator current={performanceEmoji} previous={previousEntry?.performanceEmoji ?? null} />
</div>
)} )}
</td> </td>
{/* Moral */} {/* Moral */}
<td className="w-24 px-2 py-3"> <td className="w-24 px-2 py-3">
{canEditThis ? ( {canEditThis ? (
<Select <div className="flex flex-col items-center gap-1">
value={moralEmoji || ''} <Select
onChange={(e) => handleEmojiChange('moral', e.target.value || null)} value={moralEmoji || ''}
options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))} onChange={(e) => handleEmojiChange('moral', e.target.value || null)}
size="sm" options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))}
wrapperClassName="!w-fit mx-auto" size="sm"
className="!w-16 min-w-16 text-center text-lg py-2.5" wrapperClassName="!w-fit mx-auto"
/> className="!w-16 min-w-16 text-center text-lg py-2.5"
/>
<EvolutionIndicator current={moralEmoji} previous={previousEntry?.moralEmoji ?? null} />
</div>
) : ( ) : (
<div className="text-2xl text-center">{moralEmoji || '-'}</div> <div className="flex flex-col items-center gap-1">
<div className="text-2xl text-center">{moralEmoji || '-'}</div>
<EvolutionIndicator current={moralEmoji} previous={previousEntry?.moralEmoji ?? null} />
</div>
)} )}
</td> </td>
{/* Flux */} {/* Flux */}
<td className="w-24 px-2 py-3"> <td className="w-24 px-2 py-3">
{canEditThis ? ( {canEditThis ? (
<Select <div className="flex flex-col items-center gap-1">
value={fluxEmoji || ''} <Select
onChange={(e) => handleEmojiChange('flux', e.target.value || null)} value={fluxEmoji || ''}
options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))} onChange={(e) => handleEmojiChange('flux', e.target.value || null)}
size="sm" options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))}
wrapperClassName="!w-fit mx-auto" size="sm"
className="!w-16 min-w-16 text-center text-lg py-2.5" wrapperClassName="!w-fit mx-auto"
/> className="!w-16 min-w-16 text-center text-lg py-2.5"
/>
<EvolutionIndicator current={fluxEmoji} previous={previousEntry?.fluxEmoji ?? null} />
</div>
) : ( ) : (
<div className="text-2xl text-center">{fluxEmoji || '-'}</div> <div className="flex flex-col items-center gap-1">
<div className="text-2xl text-center">{fluxEmoji || '-'}</div>
<EvolutionIndicator current={fluxEmoji} previous={previousEntry?.fluxEmoji ?? null} />
</div>
)} )}
</td> </td>
{/* Création de valeur */} {/* Création de valeur */}
<td className="w-24 px-2 py-3"> <td className="w-24 px-2 py-3">
{canEditThis ? ( {canEditThis ? (
<Select <div className="flex flex-col items-center gap-1">
value={valueCreationEmoji || ''} <Select
onChange={(e) => handleEmojiChange('valueCreation', e.target.value || null)} value={valueCreationEmoji || ''}
options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))} onChange={(e) => handleEmojiChange('valueCreation', e.target.value || null)}
size="sm" options={WEATHER_EMOJIS.map(({ emoji }) => ({ value: emoji, label: emoji }))}
wrapperClassName="!w-fit mx-auto" size="sm"
className="!w-16 min-w-16 text-center text-lg py-2.5" wrapperClassName="!w-fit mx-auto"
/> className="!w-16 min-w-16 text-center text-lg py-2.5"
/>
<EvolutionIndicator current={valueCreationEmoji} previous={previousEntry?.valueCreationEmoji ?? null} />
</div>
) : ( ) : (
<div className="text-2xl text-center">{valueCreationEmoji || '-'}</div> <div className="flex flex-col items-center gap-1">
<div className="text-2xl text-center">{valueCreationEmoji || '-'}</div>
<EvolutionIndicator current={valueCreationEmoji} previous={previousEntry?.valueCreationEmoji ?? null} />
</div>
)} )}
</td> </td>