Update meeting page controls with new icons and search functionality
Replaces existing edit and search controls with icon-based components, introducing a collapsible search input. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: ea995ab4-f97a-4789-a4b5-9635490a7ce0 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/jwG0a8d Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -41,6 +41,7 @@ import {
|
||||
MoreVertical,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Search,
|
||||
} from "lucide-react";
|
||||
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -2462,54 +2463,27 @@ function ScheduleSection({
|
||||
}
|
||||
data-testid="em-edit-mode-toggle"
|
||||
className={
|
||||
"inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-[#0B1E3F]/40 " +
|
||||
"inline-flex items-center justify-center rounded-md border w-8 h-8 transition-colors focus:outline-none focus:ring-2 focus:ring-[#0B1E3F]/40 " +
|
||||
(editMode
|
||||
? "bg-[#0B1E3F] text-white border-[#0B1E3F] hover:bg-[#0B1E3F]/90"
|
||||
: "bg-white text-[#0B1E3F] border-gray-300 hover:bg-gray-50")
|
||||
}
|
||||
>
|
||||
{editMode ? (
|
||||
<Check className="w-3.5 h-3.5" />
|
||||
<Check className="w-4 h-4" />
|
||||
) : (
|
||||
<Pencil className="w-3.5 h-3.5" />
|
||||
<Pencil className="w-4 h-4" />
|
||||
)}
|
||||
<span>
|
||||
{editMode
|
||||
? t("executiveMeetings.schedule.saveToggle")
|
||||
: t("executiveMeetings.schedule.editToggle")}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{/* #329: in-day search box. Sits next to the date picker so
|
||||
the user finds a meeting in the currently visible day
|
||||
without having to scroll. Hidden in print so PDFs still
|
||||
show the full schedule. */}
|
||||
<div className="relative print:hidden">
|
||||
<Input
|
||||
type="search"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={t("executiveMeetings.schedule.searchPlaceholder")}
|
||||
aria-label={t("executiveMeetings.schedule.searchAria")}
|
||||
data-testid="em-schedule-search"
|
||||
className="h-8 w-44 sm:w-56 text-sm bg-white"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearchQuery("")}
|
||||
aria-label={t("executiveMeetings.schedule.searchClear")}
|
||||
title={t("executiveMeetings.schedule.searchClear")}
|
||||
data-testid="em-schedule-search-clear"
|
||||
className={
|
||||
"absolute top-1/2 -translate-y-1/2 text-muted-foreground hover:text-[#0B1E3F] " +
|
||||
(isRtl ? "left-1.5" : "right-1.5")
|
||||
}
|
||||
>
|
||||
<X className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<SearchToggle
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
placeholder={t("executiveMeetings.schedule.searchPlaceholder")}
|
||||
ariaLabel={t("executiveMeetings.schedule.searchAria")}
|
||||
clearLabel={t("executiveMeetings.schedule.searchClear")}
|
||||
isRtl={isRtl}
|
||||
/>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<span className="text-muted-foreground">{t("executiveMeetings.date")}</span>
|
||||
<input
|
||||
@@ -6554,6 +6528,69 @@ function MeetingFormDialog({
|
||||
);
|
||||
}
|
||||
|
||||
function SearchToggle({
|
||||
searchQuery,
|
||||
onSearchChange,
|
||||
placeholder,
|
||||
ariaLabel,
|
||||
clearLabel,
|
||||
isRtl,
|
||||
}: {
|
||||
searchQuery: string;
|
||||
onSearchChange: (v: string) => void;
|
||||
placeholder: string;
|
||||
ariaLabel: string;
|
||||
clearLabel: string;
|
||||
isRtl: boolean;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) inputRef.current?.focus();
|
||||
}, [open]);
|
||||
|
||||
if (!open && !searchQuery) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
aria-label={ariaLabel}
|
||||
title={ariaLabel}
|
||||
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white text-[#0B1E3F] w-8 h-8 hover:bg-gray-50 transition-colors focus:outline-none focus:ring-2 focus:ring-[#0B1E3F]/40 print:hidden"
|
||||
>
|
||||
<Search className="w-4 h-4" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative print:hidden flex items-center gap-1">
|
||||
<Search className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="search"
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
aria-label={ariaLabel}
|
||||
data-testid="em-schedule-search"
|
||||
className="h-8 w-36 sm:w-48 text-sm bg-white"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { onSearchChange(""); setOpen(false); }}
|
||||
aria-label={clearLabel}
|
||||
title={clearLabel}
|
||||
data-testid="em-schedule-search-clear"
|
||||
className="text-muted-foreground hover:text-[#0B1E3F]"
|
||||
>
|
||||
<X className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FormRow({
|
||||
label,
|
||||
children,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user