feat: replace individual workshop buttons with a dropdown for creating new workshops in SessionsPage and update WorkshopTabs for improved tab management

This commit is contained in:
Julien Froidefond
2026-02-17 09:30:46 +01:00
parent e8282bb118
commit a8f53bfe2a
4 changed files with 215 additions and 105 deletions

View File

@@ -30,6 +30,15 @@ const VALID_TABS: WorkshopType[] = [
'byPerson',
];
const TYPE_TABS: { value: WorkshopType; icon: string; label: string }[] = [
{ value: 'all', icon: '📋', label: 'Tous' },
{ value: 'swot', icon: '📊', label: 'SWOT' },
{ value: 'motivators', icon: '🎯', label: 'Motivators' },
{ value: 'year-review', icon: '📅', label: 'Year Review' },
{ value: 'weekly-checkin', icon: '📝', label: 'Check-in' },
{ value: 'weather', icon: '🌤️', label: 'Météo' },
];
interface ShareUser {
id: string;
name: string | null;
@@ -199,6 +208,7 @@ export function WorkshopTabs({
}: WorkshopTabsProps) {
const searchParams = useSearchParams();
const router = useRouter();
const [typeDropdownOpen, setTypeDropdownOpen] = useState(false);
// Get tab from URL or default to 'all'
const tabParam = searchParams.get('tab');
@@ -251,7 +261,7 @@ export function WorkshopTabs({
return (
<div className="space-y-6">
{/* Tabs */}
<div className="flex gap-2 border-b border-border pb-4 flex-wrap">
<div className="flex gap-2 border-b border-border pb-4 flex-wrap items-center">
<TabButton
active={activeTab === 'all'}
onClick={() => setActiveTab('all')}
@@ -266,40 +276,18 @@ export function WorkshopTabs({
label="Par personne"
count={sessionsByPerson.size}
/>
<TabButton
active={activeTab === 'swot'}
onClick={() => setActiveTab('swot')}
icon="📊"
label="SWOT"
count={swotSessions.length}
/>
<TabButton
active={activeTab === 'motivators'}
onClick={() => setActiveTab('motivators')}
icon="🎯"
label="Moving Motivators"
count={motivatorSessions.length}
/>
<TabButton
active={activeTab === 'year-review'}
onClick={() => setActiveTab('year-review')}
icon="📅"
label="Year Review"
count={yearReviewSessions.length}
/>
<TabButton
active={activeTab === 'weekly-checkin'}
onClick={() => setActiveTab('weekly-checkin')}
icon="📝"
label="Weekly Check-in"
count={weeklyCheckInSessions.length}
/>
<TabButton
active={activeTab === 'weather'}
onClick={() => setActiveTab('weather')}
icon="🌤️"
label="Météo"
count={weatherSessions.length}
<TypeFilterDropdown
activeTab={activeTab}
setActiveTab={setActiveTab}
open={typeDropdownOpen}
onOpenChange={setTypeDropdownOpen}
counts={{
swot: swotSessions.length,
motivators: motivatorSessions.length,
'year-review': yearReviewSessions.length,
'weekly-checkin': weeklyCheckInSessions.length,
weather: weatherSessions.length,
}}
/>
</div>
@@ -367,6 +355,92 @@ export function WorkshopTabs({
);
}
function TypeFilterDropdown({
activeTab,
setActiveTab,
open,
onOpenChange,
counts,
}: {
activeTab: WorkshopType;
setActiveTab: (t: WorkshopType) => void;
open: boolean;
onOpenChange: (v: boolean) => void;
counts: Record<string, number>;
}) {
const typeTabs = TYPE_TABS.filter((t) => t.value !== 'all');
const current = TYPE_TABS.find((t) => t.value === activeTab) ?? TYPE_TABS[0];
const isTypeSelected = activeTab !== 'all' && activeTab !== 'byPerson';
const totalCount = typeTabs.reduce((s, t) => s + (counts[t.value] ?? 0), 0);
return (
<div className="relative">
<button
type="button"
onClick={() => onOpenChange(!open)}
onBlur={() => setTimeout(() => onOpenChange(false), 150)}
className={`
flex items-center gap-2 px-3 py-2 rounded-lg font-medium text-sm transition-colors
${isTypeSelected ? 'bg-primary text-primary-foreground' : 'text-muted hover:bg-card-hover hover:text-foreground'}
`}
>
<span>{current.icon}</span>
<span>{current.label}</span>
<Badge variant={isTypeSelected ? 'default' : 'primary'} className="ml-1 text-xs">
{isTypeSelected ? counts[activeTab] ?? 0 : totalCount}
</Badge>
<svg
className={`h-4 w-4 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 left-0 z-20 mt-2 w-44 rounded-lg border border-border bg-card py-1 shadow-lg">
<button
type="button"
onClick={() => {
setActiveTab('all');
onOpenChange(false);
}}
className="flex w-full items-center justify-between gap-2 px-4 py-2 text-left text-sm text-foreground hover:bg-card-hover border-b border-border"
>
<span className="flex items-center gap-2">
<span>📋</span>
<span>Tous</span>
</span>
<Badge variant="primary" className="text-xs">
{totalCount}
</Badge>
</button>
{typeTabs.map((t) => (
<button
key={t.value}
type="button"
onClick={() => {
setActiveTab(t.value);
onOpenChange(false);
}}
className="flex w-full items-center justify-between gap-2 px-4 py-2 text-left text-sm text-foreground hover:bg-card-hover"
>
<span className="flex items-center gap-2">
<span>{t.icon}</span>
<span>{t.label}</span>
</span>
<Badge variant="primary" className="text-xs">
{counts[t.value] ?? 0}
</Badge>
</button>
))}
</div>
)}
</div>
);
}
function TabButton({
active,
onClick,
@@ -382,9 +456,10 @@ function TabButton({
}) {
return (
<button
type="button"
onClick={onClick}
className={`
flex items-center gap-2 px-4 py-2 rounded-lg font-medium transition-colors
flex items-center gap-2 px-3 py-2 rounded-lg font-medium text-sm transition-colors
${
active
? 'bg-primary text-primary-foreground'
@@ -394,7 +469,7 @@ function TabButton({
>
<span>{icon}</span>
<span>{label}</span>
<Badge variant={active ? 'default' : 'primary'} className="ml-1">
<Badge variant={active ? 'default' : 'primary'} className="ml-1 text-xs">
{count}
</Badge>
</button>