Files
hope-events/frontend/src/components/reports/EventsDropdown.tsx
T
joshuaandClaude Sonnet 5 1b4446c828 Hide dashboard sidebar on staff/supervisor/admin sub-pages; fix mobile overflow
- Sidebar now shows only on each role's root landing page
  (/dashboard/staff, /dashboard/supervisor, /dashboard/admin) and hides on
  every sub-page beneath them, not just Reports. Navbar's "Dashboard" link
  always leads back to the role root.
- Fixed two dropdown panels (EventsDropdown, the Email/WhatsApp Attendees
  recipient picker) that could extend past the viewport's right edge on
  narrow screens; both now stretch to their trigger's width like other
  dropdowns in the app already do.
- Wrapped the Admin Cashup event-costs table in the same overflow-auto
  container every sibling table on that page already uses, so it scrolls
  horizontally on narrow screens instead of squeezing its columns.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 15:30:56 +02:00

62 lines
2.5 KiB
TypeScript

"use client";
import React, { useEffect, useRef, useState } from "react";
import { ChevronDown } from "lucide-react";
export default function EventsDropdown({ options, value, onChange }: {
options: { value: string; label: string }[];
value: string[];
onChange: (v: string[]) => void;
}) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const onClick = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener("mousedown", onClick);
return () => document.removeEventListener("mousedown", onClick);
}, []);
const toggle = (v: string) => {
onChange(value.includes(v) ? value.filter(x => x !== v) : [...value, v]);
};
const summary = value.length === 0
? "No events selected"
: options.length > 0 && value.length === options.length
? "All events"
: value.length === 1
? (options.find(o => o.value === value[0])?.label || "1 event selected")
: `${value.length} events selected`;
return (
<div className="relative" ref={ref}>
<button
type="button"
onClick={() => setOpen(o => !o)}
className="w-full flex items-center justify-between gap-2 border rounded-lg px-3 py-2 text-sm bg-white hover:bg-gray-50"
>
<span className="truncate text-left">{summary}</span>
<ChevronDown className={"w-4 h-4 text-gray-400 shrink-0 transition-transform " + (open ? "rotate-180" : "")} />
</button>
{open && (
<div className="absolute z-20 mt-1 left-0 right-0 bg-white border rounded-lg shadow-lg max-h-64 overflow-auto p-1">
<div className="flex items-center justify-between px-2 py-1.5 text-xs text-gray-500 border-b mb-1">
<button type="button" className="hover:underline" onClick={() => onChange(options.map(o => o.value))}>Select all</button>
<button type="button" className="hover:underline" onClick={() => onChange([])}>Clear</button>
</div>
{options.map(opt => (
<label key={opt.value} className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-gray-50 cursor-pointer">
<input type="checkbox" checked={value.includes(opt.value)} onChange={() => toggle(opt.value)} />
<span className="truncate">{opt.label}</span>
</label>
))}
{options.length === 0 && <div className="px-2 py-1.5 text-xs text-gray-400">No events available.</div>}
</div>
)}
</div>
);
}