refactor(ui): unify low-level controls and expand design system
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m57s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m57s
This commit is contained in:
@@ -4,7 +4,7 @@ import { useState } from 'react';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { RocketIcon } from '@/components/ui';
|
||||
import { Button, Input, RocketIcon } from '@/components/ui';
|
||||
|
||||
export default function LoginPage() {
|
||||
const router = useRouter();
|
||||
@@ -62,42 +62,32 @@ export default function LoginPage() {
|
||||
)}
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
required
|
||||
autoComplete="email"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="vous@exemple.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="password" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Mot de passe
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
label="Mot de passe"
|
||||
required
|
||||
autoComplete="current-password"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full rounded-lg bg-primary px-4 py-2.5 font-semibold text-primary-foreground transition-colors hover:bg-primary-hover disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<Button type="submit" disabled={loading} loading={loading} className="w-full">
|
||||
{loading ? 'Connexion...' : 'Se connecter'}
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<p className="mt-6 text-center text-sm text-muted">
|
||||
Pas encore de compte ?{' '}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState } from 'react';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { RocketIcon } from '@/components/ui';
|
||||
import { Button, Input, RocketIcon } from '@/components/ui';
|
||||
|
||||
export default function RegisterPage() {
|
||||
const router = useRouter();
|
||||
@@ -91,74 +91,55 @@ export default function RegisterPage() {
|
||||
)}
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="name" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Nom
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
label="Nom"
|
||||
autoComplete="name"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="Jean Dupont"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
required
|
||||
autoComplete="email"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="vous@exemple.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="password" className="mb-2 block text-sm font-medium text-foreground">
|
||||
Mot de passe
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
label="Mot de passe"
|
||||
required
|
||||
autoComplete="new-password"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label
|
||||
htmlFor="confirmPassword"
|
||||
className="mb-2 block text-sm font-medium text-foreground"
|
||||
>
|
||||
Confirmer le mot de passe
|
||||
</label>
|
||||
<input
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
label="Confirmer le mot de passe"
|
||||
required
|
||||
autoComplete="new-password"
|
||||
className="w-full rounded-lg border border-input-border bg-input px-4 py-2.5 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full rounded-lg bg-primary px-4 py-2.5 font-semibold text-primary-foreground transition-colors hover:bg-primary-hover disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<Button type="submit" disabled={loading} loading={loading} className="w-full">
|
||||
{loading ? 'Création...' : 'Créer mon compte'}
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<p className="mt-6 text-center text-sm text-muted">
|
||||
Déjà un compte ?{' '}
|
||||
|
||||
525
src/app/design-system/page.tsx
Normal file
525
src/app/design-system/page.tsx
Normal file
@@ -0,0 +1,525 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CollaboratorDisplay,
|
||||
DateInput,
|
||||
Disclosure,
|
||||
DropdownMenu,
|
||||
EditableGifMoodTitle,
|
||||
EditableMotivatorTitle,
|
||||
EditableSessionTitle,
|
||||
EditableTitle,
|
||||
EditableWeatherTitle,
|
||||
EditableWeeklyCheckInTitle,
|
||||
EditableYearReviewTitle,
|
||||
InlineFormActions,
|
||||
Input,
|
||||
IconCheck,
|
||||
IconClose,
|
||||
IconDuplicate,
|
||||
IconEdit,
|
||||
IconButton,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
Modal,
|
||||
ModalFooter,
|
||||
PageHeader,
|
||||
ParticipantInput,
|
||||
RocketIcon,
|
||||
Select,
|
||||
SegmentedControl,
|
||||
SessionPageHeader,
|
||||
Textarea,
|
||||
ToggleGroup,
|
||||
FormField,
|
||||
NumberInput,
|
||||
} from '@/components/ui';
|
||||
|
||||
const BUTTON_VARIANTS = [
|
||||
'primary',
|
||||
'secondary',
|
||||
'outline',
|
||||
'ghost',
|
||||
'destructive',
|
||||
'brand',
|
||||
] as const;
|
||||
const BUTTON_SIZES = ['sm', 'md', 'lg'] as const;
|
||||
|
||||
const BADGE_VARIANTS = [
|
||||
'default',
|
||||
'primary',
|
||||
'strength',
|
||||
'weakness',
|
||||
'opportunity',
|
||||
'threat',
|
||||
'success',
|
||||
'warning',
|
||||
'destructive',
|
||||
'accent',
|
||||
] as const;
|
||||
|
||||
const SELECT_OPTIONS = [
|
||||
{ value: 'editor', label: 'Editeur' },
|
||||
{ value: 'viewer', label: 'Lecteur' },
|
||||
{ value: 'admin', label: 'Admin' },
|
||||
];
|
||||
|
||||
const SECTION_LINKS = [
|
||||
{ id: 'buttons', label: 'Buttons' },
|
||||
{ id: 'badges', label: 'Badges' },
|
||||
{ id: 'icon-button', label: 'IconButton' },
|
||||
{ id: 'form-inputs', label: 'Form Inputs' },
|
||||
{ id: 'select-toggle', label: 'Select & Toggle' },
|
||||
{ id: 'form-field', label: 'FormField / Date / Number' },
|
||||
{ id: 'cards', label: 'Cards' },
|
||||
{ id: 'avatars', label: 'Avatar & Collaborators' },
|
||||
{ id: 'disclosure-dropdown', label: 'Disclosure & Dropdown' },
|
||||
{ id: 'menu', label: 'Menu' },
|
||||
{ id: 'editable-titles', label: 'Editable Titles' },
|
||||
{ id: 'session-header', label: 'Session Header' },
|
||||
{ id: 'participant-input', label: 'ParticipantInput' },
|
||||
{ id: 'icons', label: 'Icons' },
|
||||
{ id: 'modal', label: 'Modal' },
|
||||
] as const;
|
||||
|
||||
export default function DesignSystemPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [toggleValue, setToggleValue] = useState<'cards' | 'table' | 'list'>('cards');
|
||||
const [selectMd, setSelectMd] = useState('editor');
|
||||
const [selectSm, setSelectSm] = useState('viewer');
|
||||
const [selectXs, setSelectXs] = useState('admin');
|
||||
const [selectLg, setSelectLg] = useState('editor');
|
||||
const [menuCount, setMenuCount] = useState(0);
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-7xl px-4 py-8">
|
||||
<PageHeader
|
||||
emoji="🎨"
|
||||
title="Design System"
|
||||
subtitle="Guide visuel des composants UI et de leurs variantes"
|
||||
actions={
|
||||
<Button variant="brand" size="sm">
|
||||
Action principale
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="grid items-start gap-8" style={{ gridTemplateColumns: '240px minmax(0, 1fr)' }}>
|
||||
<aside>
|
||||
<Card className="sticky top-20 p-4">
|
||||
<p className="mb-3 text-sm font-medium text-foreground">Menu de la page</p>
|
||||
<nav className="flex flex-col gap-1.5">
|
||||
{SECTION_LINKS.map((section) => (
|
||||
<a
|
||||
key={section.id}
|
||||
href={`#${section.id}`}
|
||||
className="rounded-md px-2.5 py-1.5 text-sm text-muted transition-colors hover:bg-card-hover hover:text-foreground"
|
||||
>
|
||||
{section.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</Card>
|
||||
</aside>
|
||||
|
||||
<div className="space-y-8">
|
||||
<Card id="buttons" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Buttons</h2>
|
||||
<div className="space-y-4">
|
||||
{BUTTON_SIZES.map((size) => (
|
||||
<div key={size} className="flex flex-wrap items-center gap-3">
|
||||
<span className="w-12 text-xs uppercase tracking-wide text-muted">{size}</span>
|
||||
{BUTTON_VARIANTS.map((variant) => (
|
||||
<Button key={`${size}-${variant}`} variant={variant} size={size}>
|
||||
{variant}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-wrap items-center gap-3 border-t border-border pt-4">
|
||||
<Button loading>Chargement</Button>
|
||||
<Button disabled>Desactive</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="badges" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Badges</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{BADGE_VARIANTS.map((variant) => (
|
||||
<Badge key={variant} variant={variant}>
|
||||
{variant}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="icon-button" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">IconButton</h2>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<IconButton icon={<IconEdit />} label="Edit" />
|
||||
<IconButton icon={<IconDuplicate />} label="Duplicate" variant="primary" />
|
||||
<IconButton icon={<IconTrash />} label="Delete" variant="destructive" />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="form-inputs" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Form Inputs</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<Input label="Input standard" placeholder="Votre texte" />
|
||||
<Input label="Input avec erreur" defaultValue="Valeur invalide" error="Champ invalide" />
|
||||
<Textarea label="Textarea standard" placeholder="Votre description" rows={3} />
|
||||
<Textarea
|
||||
label="Textarea avec erreur"
|
||||
defaultValue="Texte"
|
||||
rows={3}
|
||||
error="Description trop courte"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="select-toggle" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Select & ToggleGroup</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="space-y-3">
|
||||
<Select
|
||||
label="Select XS"
|
||||
size="xs"
|
||||
value={selectXs}
|
||||
onChange={(e) => setSelectXs(e.target.value)}
|
||||
options={SELECT_OPTIONS}
|
||||
/>
|
||||
<Select
|
||||
label="Select SM"
|
||||
size="sm"
|
||||
value={selectSm}
|
||||
onChange={(e) => setSelectSm(e.target.value)}
|
||||
options={SELECT_OPTIONS}
|
||||
/>
|
||||
<Select
|
||||
label="Select MD"
|
||||
size="md"
|
||||
value={selectMd}
|
||||
onChange={(e) => setSelectMd(e.target.value)}
|
||||
options={SELECT_OPTIONS}
|
||||
/>
|
||||
<Select
|
||||
label="Select LG"
|
||||
size="lg"
|
||||
value={selectLg}
|
||||
onChange={(e) => setSelectLg(e.target.value)}
|
||||
options={SELECT_OPTIONS}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm font-medium text-foreground">Toggle group</p>
|
||||
<ToggleGroup
|
||||
value={toggleValue}
|
||||
onChange={setToggleValue}
|
||||
options={[
|
||||
{ value: 'cards', label: 'Cards' },
|
||||
{ value: 'table', label: 'Table' },
|
||||
{ value: 'list', label: 'List' },
|
||||
]}
|
||||
/>
|
||||
<p className="text-sm text-muted">Valeur active: {toggleValue}</p>
|
||||
<p className="pt-2 text-sm font-medium text-foreground">Segmented control</p>
|
||||
<SegmentedControl
|
||||
value={toggleValue}
|
||||
onChange={setToggleValue}
|
||||
options={[
|
||||
{ value: 'cards', label: 'Cards' },
|
||||
{ value: 'table', label: 'Table' },
|
||||
{ value: 'list', label: 'List' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="form-field" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">FormField / Date / Number</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<FormField label="FormField">
|
||||
<Input placeholder="Control custom" />
|
||||
</FormField>
|
||||
<DateInput label="DateInput" defaultValue="2026-03-03" />
|
||||
<NumberInput label="NumberInput" defaultValue={42} />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="cards" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Cards & Header blocks</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<Card hover>
|
||||
<CardHeader>
|
||||
<CardTitle>Card title</CardTitle>
|
||||
<CardDescription>Description secondaire</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted">Contenu principal de la card.</p>
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end">
|
||||
<Button size="sm" variant="outline">
|
||||
Annuler
|
||||
</Button>
|
||||
<Button size="sm">Valider</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<h3 className="mb-3 font-medium text-foreground">Inline actions</h3>
|
||||
<Input placeholder="Exemple inline" className="mb-2" />
|
||||
<InlineFormActions
|
||||
onCancel={() => {}}
|
||||
onSubmit={() => {}}
|
||||
isPending={false}
|
||||
submitLabel="Ajouter"
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="avatars" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Avatar & Collaborators</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar email="jane.doe@example.com" name="Jane Doe" size={40} />
|
||||
<Avatar email="john.smith@example.com" name="John Smith" size={32} />
|
||||
<Avatar email="team@example.com" size={24} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<CollaboratorDisplay
|
||||
collaborator={{
|
||||
raw: 'Jane Doe',
|
||||
matchedUser: {
|
||||
id: '1',
|
||||
email: 'jane.doe@example.com',
|
||||
name: 'Jane Doe',
|
||||
},
|
||||
}}
|
||||
showEmail
|
||||
/>
|
||||
<CollaboratorDisplay
|
||||
collaborator={{
|
||||
raw: 'Intervenant externe',
|
||||
matchedUser: null,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="disclosure-dropdown" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Disclosure & Dropdown</h2>
|
||||
<div className="space-y-4">
|
||||
<Disclosure title="Panneau pliable" subtitle="Composant Disclosure">
|
||||
<p className="text-sm text-muted">Contenu du panneau.</p>
|
||||
</Disclosure>
|
||||
<DropdownMenu
|
||||
panelClassName="mt-2 w-56 rounded-lg border border-border bg-card p-2 shadow-lg"
|
||||
trigger={({ open, toggle }) => (
|
||||
<Button type="button" variant="outline" onClick={toggle}>
|
||||
Menu demo {open ? '▲' : '▼'}
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
{({ close }) => (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setMenuCount((prev) => prev + 1);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Incrementer ({menuCount})
|
||||
</Button>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="menu" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Menu</h2>
|
||||
<DropdownMenu
|
||||
panelClassName="mt-2 w-64 overflow-hidden rounded-lg border border-border bg-card py-1 shadow-lg"
|
||||
trigger={({ open, toggle }) => (
|
||||
<Button type="button" variant="outline" onClick={toggle}>
|
||||
Ouvrir le menu {open ? '▲' : '▼'}
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
{({ close }) => (
|
||||
<>
|
||||
<div className="border-b border-border px-4 py-2">
|
||||
<p className="text-xs text-muted">MENU DE DEMO</p>
|
||||
<p className="text-sm font-medium text-foreground">Navigation rapide</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={close}
|
||||
className="block w-full px-4 py-2 text-left text-sm text-foreground hover:bg-card-hover"
|
||||
>
|
||||
👤 Mon profil
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={close}
|
||||
className="block w-full px-4 py-2 text-left text-sm text-foreground hover:bg-card-hover"
|
||||
>
|
||||
👥 Equipes
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={close}
|
||||
className="block w-full px-4 py-2 text-left text-sm text-destructive hover:bg-card-hover"
|
||||
>
|
||||
Se deconnecter
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</Card>
|
||||
|
||||
<Card id="editable-titles" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Editable Titles</h2>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<p className="mb-2 text-sm font-medium text-foreground">EditableTitle (base)</p>
|
||||
<EditableTitle
|
||||
sessionId="demo-editable-title"
|
||||
initialTitle="Titre modifiable (cliquez pour tester)"
|
||||
canEdit
|
||||
onUpdate={async () => ({ success: true })}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<EditableSessionTitle
|
||||
sessionId="demo-session-title"
|
||||
initialTitle="Session title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
<EditableMotivatorTitle
|
||||
sessionId="demo-motivator-title"
|
||||
initialTitle="Motivator title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
<EditableYearReviewTitle
|
||||
sessionId="demo-year-review-title"
|
||||
initialTitle="Year review title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
<EditableWeatherTitle
|
||||
sessionId="demo-weather-title"
|
||||
initialTitle="Weather title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
<EditableWeeklyCheckInTitle
|
||||
sessionId="demo-weekly-checkin-title"
|
||||
initialTitle="Weekly check-in title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
<EditableGifMoodTitle
|
||||
sessionId="demo-gif-mood-title"
|
||||
initialTitle="Gif mood title wrapper"
|
||||
canEdit={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="session-header" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Session Header</h2>
|
||||
<SessionPageHeader
|
||||
workshopType="swot"
|
||||
sessionId="demo-session"
|
||||
sessionTitle="Atelier de demonstration"
|
||||
isOwner={true}
|
||||
canEdit={false}
|
||||
ownerUser={{ name: 'Jane Doe', email: 'jane.doe@example.com' }}
|
||||
date={new Date()}
|
||||
collaborator={{
|
||||
raw: 'Jane Doe',
|
||||
matchedUser: {
|
||||
id: '1',
|
||||
email: 'jane.doe@example.com',
|
||||
name: 'Jane Doe',
|
||||
},
|
||||
}}
|
||||
badges={<Badge variant="primary">DEMO</Badge>}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card id="participant-input" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">ParticipantInput</h2>
|
||||
<ParticipantInput name="participant" />
|
||||
</Card>
|
||||
|
||||
<Card id="icons" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Icons</h2>
|
||||
<div className="flex flex-wrap items-center gap-4 text-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<IconEdit />
|
||||
<span className="text-sm text-muted">Edit</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconTrash />
|
||||
<span className="text-sm text-muted">Trash</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconDuplicate />
|
||||
<span className="text-sm text-muted">Duplicate</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconPlus />
|
||||
<span className="text-sm text-muted">Plus</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconCheck />
|
||||
<span className="text-sm text-muted">Check</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<IconClose />
|
||||
<span className="text-sm text-muted">Close</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RocketIcon className="h-5 w-5" />
|
||||
<span className="text-sm text-muted">Rocket</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card id="modal" className="p-6">
|
||||
<h2 className="mb-4 text-xl font-semibold text-foreground">Modal</h2>
|
||||
<Button onClick={() => setModalOpen(true)}>Ouvrir la popup</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal isOpen={modalOpen} onClose={() => setModalOpen(false)} title="Exemple de popup" size="md">
|
||||
<p className="text-sm text-muted">
|
||||
Ceci est un exemple de modal avec ses actions standardisees.
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button variant="outline" onClick={() => setModalOpen(false)}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button onClick={() => setModalOpen(false)}>Confirmer</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CardDescription,
|
||||
CardContent,
|
||||
Button,
|
||||
DateInput,
|
||||
Input,
|
||||
} from '@/components/ui';
|
||||
import { createGifMoodSession } from '@/actions/gif-mood';
|
||||
@@ -78,20 +79,14 @@ export default function NewGifMoodPage() {
|
||||
required
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label htmlFor="date" className="block text-sm font-medium text-foreground mb-1">
|
||||
Date
|
||||
</label>
|
||||
<input
|
||||
id="date"
|
||||
name="date"
|
||||
type="date"
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
required
|
||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<DateInput
|
||||
id="date"
|
||||
name="date"
|
||||
label="Date"
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="rounded-lg border border-border bg-card-hover p-4">
|
||||
<h3 className="font-medium text-foreground mb-2">Comment ça marche ?</h3>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { auth } from '@/lib/auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { getUserOKRs } from '@/services/okrs';
|
||||
import { Card, PageHeader } from '@/components/ui';
|
||||
import { Card, PageHeader, getButtonClassName } from '@/components/ui';
|
||||
import { ObjectivesList } from '@/components/okrs/ObjectivesList';
|
||||
import { comparePeriods } from '@/lib/okr-utils';
|
||||
|
||||
@@ -46,10 +46,11 @@ export default async function ObjectivesPage() {
|
||||
Vous n'avez pas encore d'OKR défini. Contactez un administrateur d'équipe
|
||||
pour en créer.
|
||||
</p>
|
||||
<Link href="/teams">
|
||||
<span className="inline-block rounded-lg bg-[var(--purple)] px-4 py-2 text-white hover:opacity-90">
|
||||
Voir mes équipes
|
||||
</span>
|
||||
<Link
|
||||
href="/teams"
|
||||
className={getButtonClassName({ variant: 'brand' })}
|
||||
>
|
||||
Voir mes équipes
|
||||
</Link>
|
||||
</Card>
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { getButtonClassName } from '@/components/ui';
|
||||
import { WORKSHOPS, getSessionsTabUrl } from '@/lib/workshops';
|
||||
|
||||
export default function Home() {
|
||||
@@ -888,14 +889,16 @@ function WorkshopCard({
|
||||
<div className="flex gap-3">
|
||||
<Link
|
||||
href={newHref}
|
||||
className="flex-1 rounded-lg px-4 py-2.5 text-center font-medium text-white transition-colors"
|
||||
className={getButtonClassName({
|
||||
className: 'flex-1 border-transparent text-white',
|
||||
})}
|
||||
style={{ backgroundColor: accentColor }}
|
||||
>
|
||||
Démarrer
|
||||
</Link>
|
||||
<Link
|
||||
href={href}
|
||||
className="rounded-lg border border-border px-4 py-2.5 font-medium text-foreground transition-colors hover:bg-card-hover"
|
||||
className={getButtonClassName({ variant: 'outline' })}
|
||||
>
|
||||
Mes sessions
|
||||
</Link>
|
||||
|
||||
@@ -39,29 +39,20 @@ export function PasswordForm() {
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="currentPassword"
|
||||
className="mb-1.5 block text-sm font-medium text-foreground"
|
||||
>
|
||||
Mot de passe actuel
|
||||
</label>
|
||||
<Input
|
||||
id="currentPassword"
|
||||
type="password"
|
||||
value={currentPassword}
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
id="currentPassword"
|
||||
type="password"
|
||||
label="Mot de passe actuel"
|
||||
value={currentPassword}
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label htmlFor="newPassword" className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
Nouveau mot de passe
|
||||
</label>
|
||||
<Input
|
||||
id="newPassword"
|
||||
type="password"
|
||||
label="Nouveau mot de passe"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
required
|
||||
@@ -71,15 +62,10 @@ export function PasswordForm() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="confirmPassword"
|
||||
className="mb-1.5 block text-sm font-medium text-foreground"
|
||||
>
|
||||
Confirmer le nouveau mot de passe
|
||||
</label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
label="Confirmer le nouveau mot de passe"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
|
||||
@@ -39,31 +39,23 @@ export function ProfileForm({ initialData }: ProfileFormProps) {
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="name" className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
Nom
|
||||
</label>
|
||||
<Input
|
||||
id="name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Votre nom"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
id="name"
|
||||
type="text"
|
||||
label="Nom"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Votre nom"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
Email
|
||||
</label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
{message && (
|
||||
<p
|
||||
|
||||
@@ -1,46 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui';
|
||||
import { Button, DropdownMenu } from '@/components/ui';
|
||||
import { WORKSHOPS } from '@/lib/workshops';
|
||||
import { useClickOutside } from '@/hooks/useClickOutside';
|
||||
|
||||
export function NewWorkshopDropdown() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
useClickOutside(containerRef, () => setOpen(false), open);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="relative">
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="gap-1.5"
|
||||
>
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
Nouvel atelier
|
||||
<svg
|
||||
className={`h-3.5 w-3.5 transition-transform ${open ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</Button>
|
||||
{open && (
|
||||
<div className="absolute right-0 z-20 mt-2 w-60 rounded-xl border border-border bg-card py-1.5 shadow-lg">
|
||||
<DropdownMenu
|
||||
panelClassName="absolute right-0 z-20 mt-2 w-60 rounded-xl border border-border bg-card py-1.5 shadow-lg"
|
||||
trigger={({ open, toggle }) => (
|
||||
<Button type="button" variant="primary" size="sm" onClick={toggle} className="gap-1.5">
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
Nouvel atelier
|
||||
<svg
|
||||
className={`h-3.5 w-3.5 transition-transform ${open ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
{({ close }) => (
|
||||
<>
|
||||
{WORKSHOPS.map((w) => (
|
||||
<Link
|
||||
key={w.id}
|
||||
href={w.newPath}
|
||||
className="flex items-center gap-3 px-4 py-2.5 text-sm text-foreground hover:bg-card-hover transition-colors"
|
||||
onClick={() => setOpen(false)}
|
||||
onClick={close}
|
||||
>
|
||||
<span
|
||||
className="flex h-8 w-8 items-center justify-center rounded-lg text-base flex-shrink-0"
|
||||
@@ -54,8 +46,8 @@ export function NewWorkshopDropdown() {
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,16 @@
|
||||
|
||||
import { useState, useTransition } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Button, Modal, ModalFooter, Input, CollaboratorDisplay } from '@/components/ui';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalFooter,
|
||||
Input,
|
||||
CollaboratorDisplay,
|
||||
IconButton,
|
||||
IconEdit,
|
||||
IconTrash,
|
||||
} from '@/components/ui';
|
||||
import { deleteSwotSession, updateSwotSession } from '@/actions/session';
|
||||
import { deleteMotivatorSession, updateMotivatorSession } from '@/actions/moving-motivators';
|
||||
import { deleteYearReviewSession, updateYearReviewSession } from '@/actions/year-review';
|
||||
@@ -211,25 +220,21 @@ export function SessionCard({
|
||||
<>
|
||||
{(session.isOwner || session.role === 'EDITOR' || session.isTeamCollab) && (
|
||||
<div className={`absolute flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-20 ${view === 'table' ? 'top-1/2 -translate-y-1/2 right-3' : 'top-2.5 right-2.5'}`}>
|
||||
<button
|
||||
<IconButton
|
||||
onClick={(e) => { e.preventDefault(); e.stopPropagation(); openEditModal(); }}
|
||||
className="p-1.5 rounded-lg bg-card border border-border text-muted hover:text-primary hover:bg-primary/5 shadow-sm"
|
||||
title="Modifier"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
</button>
|
||||
label="Modifier"
|
||||
icon={<IconEdit />}
|
||||
variant="primary"
|
||||
className="bg-card shadow-sm"
|
||||
/>
|
||||
{(session.isOwner || session.isTeamCollab) && (
|
||||
<button
|
||||
<IconButton
|
||||
onClick={(e) => { e.preventDefault(); e.stopPropagation(); setShowDeleteModal(true); }}
|
||||
className="p-1.5 rounded-lg bg-card border border-border text-muted hover:text-destructive hover:bg-destructive/5 shadow-sm"
|
||||
title="Supprimer"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
label="Supprimer"
|
||||
icon={<IconTrash />}
|
||||
variant="destructive"
|
||||
className="bg-card shadow-sm"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -247,19 +252,15 @@ export function SessionCard({
|
||||
|
||||
<Modal isOpen={showEditModal} onClose={() => setShowEditModal(false)} title="Modifier l'atelier" size="sm">
|
||||
<form onSubmit={(e) => { e.preventDefault(); handleEdit(); }} className="space-y-4">
|
||||
<Input id="edit-title" label="Titre" value={editTitle} onChange={(e) => setEditTitle(e.target.value)} placeholder="Titre de l'atelier" required />
|
||||
<div>
|
||||
<label htmlFor="edit-title" className="block text-sm font-medium text-foreground mb-1">Titre</label>
|
||||
<Input id="edit-title" value={editTitle} onChange={(e) => setEditTitle(e.target.value)} placeholder="Titre de l'atelier" required />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="edit-participant" className="block text-sm font-medium text-foreground mb-1">{workshop.participantLabel}</label>
|
||||
{!isWeather && !isGifMood && (
|
||||
<Input id="edit-participant" value={editParticipant} onChange={(e) => setEditParticipant(e.target.value)}
|
||||
<Input id="edit-participant" label={workshop.participantLabel} value={editParticipant} onChange={(e) => setEditParticipant(e.target.value)}
|
||||
placeholder={isSwot ? 'Nom du collaborateur' : 'Nom du participant'} required />
|
||||
)}
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button type="button" variant="ghost" onClick={() => setShowEditModal(false)} disabled={isPending}>Annuler</Button>
|
||||
<Button type="button" variant="outline" onClick={() => setShowEditModal(false)} disabled={isPending}>Annuler</Button>
|
||||
<Button type="submit" disabled={isPending || !editTitle.trim() || (!isWeather && !isGifMood && !editParticipant.trim())}>
|
||||
{isPending ? 'Enregistrement...' : 'Enregistrer'}
|
||||
</Button>
|
||||
@@ -272,7 +273,7 @@ export function SessionCard({
|
||||
<p className="text-muted">Êtes-vous sûr de vouloir supprimer <strong className="text-foreground">"{session.title}"</strong> ?</p>
|
||||
<p className="text-sm text-destructive">Cette action est irréversible. Toutes les données seront perdues.</p>
|
||||
<ModalFooter>
|
||||
<Button variant="ghost" onClick={() => setShowDeleteModal(false)} disabled={isPending}>Annuler</Button>
|
||||
<Button variant="outline" onClick={() => setShowDeleteModal(false)} disabled={isPending}>Annuler</Button>
|
||||
<Button variant="destructive" onClick={handleDelete} disabled={isPending}>{isPending ? 'Suppression...' : 'Supprimer'}</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@ export default async function TeamDetailPage({ params }: TeamDetailPageProps) {
|
||||
isAdmin ? (
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href={`/teams/${id}/okrs/new`}>
|
||||
<Button className="bg-[var(--purple)] text-white hover:opacity-90 border-transparent">
|
||||
<Button variant="brand" size="sm">
|
||||
Définir un OKR
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function NewTeamPage() {
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="bg-[var(--purple)] text-white hover:opacity-90 border-transparent"
|
||||
variant="brand"
|
||||
>
|
||||
{submitting ? 'Création...' : "Créer l'équipe"}
|
||||
</Button>
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function TeamsPage() {
|
||||
subtitle={`${teams.length} équipe${teams.length !== 1 ? 's' : ''} · Collaborez et définissez vos OKRs`}
|
||||
actions={
|
||||
<Link href="/teams/new">
|
||||
<Button className="bg-[var(--purple)] text-white hover:opacity-90 border-transparent">
|
||||
<Button variant="brand" size="sm">
|
||||
Créer une équipe
|
||||
</Button>
|
||||
</Link>
|
||||
@@ -44,7 +44,7 @@ export default async function TeamsPage() {
|
||||
Créez votre première équipe pour commencer à définir des OKRs
|
||||
</div>
|
||||
<Link href="/teams/new" className="mt-6">
|
||||
<Button className="!bg-[var(--purple)] !text-white hover:!bg-[var(--purple)]/90">
|
||||
<Button variant="brand">
|
||||
Créer une équipe
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CardDescription,
|
||||
CardContent,
|
||||
Button,
|
||||
DateInput,
|
||||
Input,
|
||||
} from '@/components/ui';
|
||||
import { createWeatherSession } from '@/actions/weather';
|
||||
@@ -93,20 +94,14 @@ export default function NewWeatherPage() {
|
||||
required
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label htmlFor="date" className="block text-sm font-medium text-foreground mb-1">
|
||||
Date de la météo
|
||||
</label>
|
||||
<input
|
||||
id="date"
|
||||
name="date"
|
||||
type="date"
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
required
|
||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<DateInput
|
||||
id="date"
|
||||
name="date"
|
||||
label="Date de la météo"
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="rounded-lg border border-border bg-card-hover p-4">
|
||||
<h3 className="font-medium text-foreground mb-2">Comment ça marche ?</h3>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CardDescription,
|
||||
CardContent,
|
||||
Button,
|
||||
DateInput,
|
||||
Input,
|
||||
ParticipantInput,
|
||||
} from '@/components/ui';
|
||||
@@ -98,20 +99,14 @@ export default function NewWeeklyCheckInPage() {
|
||||
|
||||
<ParticipantInput name="participant" required />
|
||||
|
||||
<div>
|
||||
<label htmlFor="date" className="block text-sm font-medium text-foreground mb-1">
|
||||
Date du check-in
|
||||
</label>
|
||||
<input
|
||||
id="date"
|
||||
name="date"
|
||||
type="date"
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
required
|
||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<DateInput
|
||||
id="date"
|
||||
name="date"
|
||||
label="Date du check-in"
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="rounded-lg border border-border bg-card-hover p-4">
|
||||
<h3 className="font-medium text-foreground mb-2">Comment ça marche ?</h3>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CardDescription,
|
||||
CardContent,
|
||||
Button,
|
||||
NumberInput,
|
||||
Input,
|
||||
ParticipantInput,
|
||||
} from '@/components/ui';
|
||||
@@ -79,21 +80,15 @@ export default function NewYearReviewPage() {
|
||||
|
||||
<ParticipantInput name="participant" required />
|
||||
|
||||
<div>
|
||||
<label htmlFor="year" className="block text-sm font-medium text-foreground mb-1">
|
||||
Année du bilan
|
||||
</label>
|
||||
<input
|
||||
id="year"
|
||||
name="year"
|
||||
type="number"
|
||||
min="2000"
|
||||
max="2100"
|
||||
defaultValue={currentYear}
|
||||
required
|
||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
<NumberInput
|
||||
id="year"
|
||||
name="year"
|
||||
label="Année du bilan"
|
||||
min="2000"
|
||||
max="2100"
|
||||
defaultValue={currentYear}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="rounded-lg border border-border bg-card-hover p-4">
|
||||
<h3 className="font-medium text-foreground mb-2">Comment ça marche ?</h3>
|
||||
|
||||
Reference in New Issue
Block a user