diff --git a/src/components/gif-mood/GifMoodCard.tsx b/src/components/gif-mood/GifMoodCard.tsx index 63787ef..2dffbb1 100644 --- a/src/components/gif-mood/GifMoodCard.tsx +++ b/src/components/gif-mood/GifMoodCard.tsx @@ -2,6 +2,7 @@ import { memo, useState, useTransition } from 'react'; import { updateGifMoodItem, deleteGifMoodItem } from '@/actions/gif-mood'; +import { IconClose } from '@/components/ui'; interface GifMoodCardProps { sessionId: string; @@ -83,9 +84,7 @@ export const GifMoodCard = memo(function GifMoodCard({ className="absolute top-2 right-2 p-1.5 rounded-full bg-black/50 text-white opacity-0 group-hover:opacity-100 hover:bg-black/70 transition-all backdrop-blur-sm" title="Supprimer ce GIF" > - - - + )} diff --git a/src/components/swot/SwotCard.tsx b/src/components/swot/SwotCard.tsx index 488df03..b2309d7 100644 --- a/src/components/swot/SwotCard.tsx +++ b/src/components/swot/SwotCard.tsx @@ -3,6 +3,7 @@ import { forwardRef, memo, useState, useTransition } from 'react'; import type { SwotItem, SwotCategory } from '@prisma/client'; import { updateSwotItem, deleteSwotItem, duplicateSwotItem } from '@/actions/swot'; +import { IconEdit, IconTrash, IconDuplicate, IconCheck } from '@/components/ui'; interface SwotCardProps { item: SwotItem; @@ -121,63 +122,21 @@ export const SwotCard = memo( className="rounded p-1 text-muted hover:bg-card-hover hover:text-foreground" aria-label="Modifier" > - - - + )} @@ -187,9 +146,7 @@ export const SwotCard = memo(
- - - +
)} diff --git a/src/components/swot/SwotQuadrant.tsx b/src/components/swot/SwotQuadrant.tsx index 7ea2f91..d657d98 100644 --- a/src/components/swot/SwotQuadrant.tsx +++ b/src/components/swot/SwotQuadrant.tsx @@ -4,6 +4,7 @@ import { forwardRef, useState, useTransition, useRef, ReactNode } from 'react'; import type { SwotCategory } from '@prisma/client'; import { createSwotItem } from '@/actions/swot'; import { QuadrantHelpPanel } from './QuadrantHelp'; +import { IconPlus, InlineFormActions } from '@/components/ui'; interface SwotQuadrantProps { category: SwotCategory; @@ -115,14 +116,7 @@ export const SwotQuadrant = forwardRef( `} aria-label={`Ajouter un item ${title}`} > - - - + @@ -152,28 +146,14 @@ export const SwotQuadrant = forwardRef( rows={2} disabled={isPending} /> -
- - -
+ { setIsAdding(false); setNewContent(''); }} + onSubmit={handleAdd} + isPending={isPending} + disabled={!newContent.trim()} + submitColorClass={`${styles.text} hover:bg-white/50`} + className="mt-1" + /> )} diff --git a/src/components/ui/EditableTitle.tsx b/src/components/ui/EditableTitle.tsx index 2bb5600..e72fda3 100644 --- a/src/components/ui/EditableTitle.tsx +++ b/src/components/ui/EditableTitle.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState, useTransition, useRef, useEffect, useMemo } from 'react'; +import { IconEdit } from './Icons'; interface EditableTitleProps { sessionId: string; @@ -92,19 +93,7 @@ export function EditableTitle({ sessionId, initialTitle, canEdit, onUpdate }: Ed title="Cliquez pour modifier" >

{title}

- - - + ); } diff --git a/src/components/ui/Icons.tsx b/src/components/ui/Icons.tsx new file mode 100644 index 0000000..7c474e4 --- /dev/null +++ b/src/components/ui/Icons.tsx @@ -0,0 +1,54 @@ +interface IconProps { + className?: string; +} + +const base = { fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor' } as const; +const path = { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2 } as const; + +export function IconEdit({ className = 'h-3.5 w-3.5' }: IconProps) { + return ( + + + + ); +} + +export function IconTrash({ className = 'h-3.5 w-3.5' }: IconProps) { + return ( + + + + ); +} + +export function IconDuplicate({ className = 'h-3.5 w-3.5' }: IconProps) { + return ( + + + + ); +} + +export function IconPlus({ className = 'h-5 w-5' }: IconProps) { + return ( + + + + ); +} + +export function IconCheck({ className = 'h-4 w-4' }: IconProps) { + return ( + + + + ); +} + +export function IconClose({ className = 'h-5 w-5' }: IconProps) { + return ( + + + + ); +} diff --git a/src/components/ui/InlineFormActions.tsx b/src/components/ui/InlineFormActions.tsx new file mode 100644 index 0000000..099f2f1 --- /dev/null +++ b/src/components/ui/InlineFormActions.tsx @@ -0,0 +1,39 @@ +interface InlineFormActionsProps { + onCancel: () => void; + onSubmit: () => void; + isPending: boolean; + disabled?: boolean; + submitLabel?: string; + submitColorClass?: string; + className?: string; +} + +export function InlineFormActions({ + onCancel, + onSubmit, + isPending, + disabled = false, + submitLabel = 'Ajouter', + submitColorClass = 'text-primary hover:bg-primary/10', + className = '', +}: InlineFormActionsProps) { + return ( +
+ + +
+ ); +} diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index db26c97..958a70f 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,6 +1,7 @@ 'use client'; import { Fragment, ReactNode, useEffect, useSyncExternalStore } from 'react'; +import { IconClose } from './Icons'; import { createPortal } from 'react-dom'; interface ModalProps { @@ -84,14 +85,7 @@ export function Modal({ isOpen, onClose, title, children, size = 'md' }: ModalPr className="rounded-lg p-1 text-muted hover:bg-card-hover hover:text-foreground transition-colors" aria-label="Fermer" > - - - + )} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index ec411b4..3f7e877 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -12,6 +12,8 @@ export { EditableWeatherTitle, EditableGifMoodTitle, } from './EditableTitles'; +export { IconEdit, IconTrash, IconDuplicate, IconPlus, IconCheck, IconClose } from './Icons'; +export { InlineFormActions } from './InlineFormActions'; export { PageHeader } from './PageHeader'; export { SessionPageHeader } from './SessionPageHeader'; export { Input } from './Input'; diff --git a/src/components/weekly-checkin/WeeklyCheckInCard.tsx b/src/components/weekly-checkin/WeeklyCheckInCard.tsx index a6fbb0e..bc2a5c8 100644 --- a/src/components/weekly-checkin/WeeklyCheckInCard.tsx +++ b/src/components/weekly-checkin/WeeklyCheckInCard.tsx @@ -4,6 +4,7 @@ import { forwardRef, memo, useState, useTransition } from 'react'; import type { WeeklyCheckInItem } from '@prisma/client'; import { updateWeeklyCheckInItem, deleteWeeklyCheckInItem } from '@/actions/weekly-checkin'; import { WEEKLY_CHECK_IN_BY_CATEGORY, EMOTION_BY_TYPE } from '@/lib/types'; +import { IconEdit, IconTrash, InlineFormActions } from '@/components/ui'; import { Select } from '@/components/ui/Select'; interface WeeklyCheckInCardProps { @@ -113,26 +114,13 @@ export const WeeklyCheckInCard = memo( label: `${em.icon} ${em.label}`, }))} /> -
- - -
+ { setContent(item.content); setEmotion(item.emotion); setIsEditing(false); }} + onSubmit={handleSave} + isPending={isPending} + disabled={!content.trim()} + submitLabel="Enregistrer" + /> ) : ( <> @@ -164,41 +152,14 @@ export const WeeklyCheckInCard = memo( className="rounded p-1 text-muted hover:bg-card-hover hover:text-foreground" aria-label="Modifier" > - - - + diff --git a/src/components/weekly-checkin/WeeklyCheckInSection.tsx b/src/components/weekly-checkin/WeeklyCheckInSection.tsx index 334696d..87c2cde 100644 --- a/src/components/weekly-checkin/WeeklyCheckInSection.tsx +++ b/src/components/weekly-checkin/WeeklyCheckInSection.tsx @@ -4,6 +4,7 @@ import { forwardRef, useState, useTransition, useRef, ReactNode } from 'react'; import type { WeeklyCheckInCategory } from '@prisma/client'; import { createWeeklyCheckInItem } from '@/actions/weekly-checkin'; import { WEEKLY_CHECK_IN_BY_CATEGORY, EMOTION_BY_TYPE } from '@/lib/types'; +import { IconPlus, InlineFormActions } from '@/components/ui'; import { Select } from '@/components/ui/Select'; interface WeeklyCheckInSectionProps { @@ -82,14 +83,7 @@ export const WeeklyCheckInSection = forwardRef - - - + @@ -138,29 +132,12 @@ export const WeeklyCheckInSection = forwardRef -
- - -
+ { setIsAdding(false); setNewContent(''); setNewEmotion('NONE'); }} + onSubmit={handleAdd} + isPending={isPending} + disabled={!newContent.trim()} + /> )} diff --git a/src/components/year-review/YearReviewCard.tsx b/src/components/year-review/YearReviewCard.tsx index 1135110..063bef9 100644 --- a/src/components/year-review/YearReviewCard.tsx +++ b/src/components/year-review/YearReviewCard.tsx @@ -4,6 +4,7 @@ import { forwardRef, memo, useState, useTransition } from 'react'; import type { YearReviewItem } from '@prisma/client'; import { updateYearReviewItem, deleteYearReviewItem } from '@/actions/year-review'; import { YEAR_REVIEW_BY_CATEGORY } from '@/lib/types'; +import { IconEdit, IconTrash } from '@/components/ui'; interface YearReviewCardProps { item: YearReviewItem; @@ -95,41 +96,14 @@ export const YearReviewCard = memo( className="rounded p-1 text-muted hover:bg-card-hover hover:text-foreground" aria-label="Modifier" > - - - + diff --git a/src/components/year-review/YearReviewSection.tsx b/src/components/year-review/YearReviewSection.tsx index ccdf8ce..ec80146 100644 --- a/src/components/year-review/YearReviewSection.tsx +++ b/src/components/year-review/YearReviewSection.tsx @@ -4,6 +4,7 @@ import { forwardRef, useState, useTransition, useRef, ReactNode } from 'react'; import type { YearReviewCategory } from '@prisma/client'; import { createYearReviewItem } from '@/actions/year-review'; import { YEAR_REVIEW_BY_CATEGORY } from '@/lib/types'; +import { IconPlus, InlineFormActions } from '@/components/ui'; interface YearReviewSectionProps { category: YearReviewCategory; @@ -77,14 +78,7 @@ export const YearReviewSection = forwardRef - - - + @@ -111,28 +105,13 @@ export const YearReviewSection = forwardRef -
- - -
+ { setIsAdding(false); setNewContent(''); }} + onSubmit={handleAdd} + isPending={isPending} + disabled={!newContent.trim()} + className="mt-1" + /> )}