Files
workavia-calendar-front/src/components/Menubar/Menubar.tsx
T
lenhanphung 57cc656d83 fix: sync calendar view state between Calendar and Menubar components
- Add onViewChange callback to CalendarAppProps interface
- Update CalendarLayout to manage currentView state and pass callbacks
- Modify Menubar to receive currentView from parent instead of local state
- Sync view changes when clicking day headers in week view
- Ensure dropdown in Menubar updates when calendar view changes
2025-09-25 17:11:24 +07:00

227 lines
6.1 KiB
TypeScript

import React, { useState } from "react";
import logo from "../../static/images/calendar.svg";
import AppsIcon from "@mui/icons-material/Apps";
import RefreshIcon from "@mui/icons-material/Refresh";
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import "./Menubar.css";
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import { stringToColor } from "../../features/Events/EventDisplay";
import {
Avatar,
IconButton,
Popover,
ButtonGroup,
Button,
Select,
MenuItem,
FormControl,
Typography,
} from "@mui/material";
import { push } from "redux-first-history";
import { CalendarApi } from "@fullcalendar/core";
export type AppIconProps = {
name: string;
link: string;
icon: string;
};
export type MenubarProps = {
calendarRef: React.RefObject<CalendarApi | null>;
onRefresh: () => void;
currentDate: Date;
onDateChange?: (date: Date) => void;
currentView: string;
onViewChange?: (view: string) => void;
};
export function Menubar({
calendarRef,
onRefresh,
currentDate,
onDateChange,
currentView,
onViewChange,
}: MenubarProps) {
const user = useAppSelector((state) => state.user.userData);
const applist: AppIconProps[] = (window as any).appList ?? [];
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const dispatch = useAppDispatch();
if (!user) {
dispatch(push("/"));
}
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleNavigation = (action: "prev" | "next" | "today") => {
if (!calendarRef.current) return;
switch (action) {
case "prev":
calendarRef.current.prev();
break;
case "next":
calendarRef.current.next();
break;
case "today":
calendarRef.current.today();
break;
}
// Notify parent about date change after navigation
if (onDateChange) {
const newDate = calendarRef.current.getDate();
onDateChange(newDate);
}
};
const handleViewChange = (view: string) => {
if (!calendarRef.current) return;
calendarRef.current.changeView(view);
// Notify parent about view change
if (onViewChange) {
onViewChange(view);
}
// Notify parent about date change after view change
if (onDateChange) {
const newDate = calendarRef.current.getDate();
onDateChange(newDate);
}
};
const handleRefresh = () => {
onRefresh();
};
const open = Boolean(anchorEl);
return (
<>
<header className="menubar">
<div className="left-menu">
<div className="menu-items">
<MainTitle />
</div>
<div className="menu-items">
<div className="navigation-controls">
<ButtonGroup variant="outlined" size="small">
<Button onClick={() => handleNavigation("prev")}>
<ChevronLeftIcon />
</Button>
<Button onClick={() => handleNavigation("today")}>Today</Button>
<Button onClick={() => handleNavigation("next")}>
<ChevronRightIcon />
</Button>
</ButtonGroup>
</div>
</div>
<div className="menu-items">
<div className="current-date-time">
<Typography variant="h6" component="div">
{currentDate.toLocaleDateString("en-US", {
month: "long",
year: "numeric",
})}
</Typography>
</div>
</div>
</div>
<div className="right-menu">
<div className="menu-items">
<FormControl size="small" sx={{ minWidth: 120, mr: 2 }}>
<Select
value={currentView}
onChange={(e) => handleViewChange(e.target.value)}
variant="outlined"
>
<MenuItem value="dayGridMonth">Month</MenuItem>
<MenuItem value="timeGridWeek">Week</MenuItem>
<MenuItem value="timeGridDay">Day</MenuItem>
</Select>
</FormControl>
</div>
<div className="menu-items">
{applist.length > 0 && (
<IconButton onClick={handleOpen} sx={{ mr: 1 }}>
<AppsIcon />
</IconButton>
)}
</div>
<div className="menu-items">
<Avatar
sx={{
bgcolor: stringToColor(
user && user.family_name
? user.family_name
: user && user.email
? user.email
: ""
),
}}
sizes="large"
>
{user?.name && user?.family_name
? `${user.name[0]}${user.family_name[0]}`
: (user?.email?.[0] ?? "")}
</Avatar>
</div>
</div>
</header>
<Popover
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<div className="app-grid">
{applist.map((prop: AppIconProps) => (
<AppIcon key={prop.name} prop={prop} />
))}
</div>
</Popover>
</>
);
}
export function MainTitle() {
return (
<div className="menubar-item tc-home">
<img className="logo" src={logo} alt="Calendar" />
<p>
<span className="twake">Twake</span>
<span className="calendar-text">Calendar</span>
</p>
</div>
);
}
function AppIcon({ prop }: { prop: AppIconProps }) {
return (
<a
href={prop.link}
target="_blank"
rel="noreferrer"
style={{ textDecoration: "none", color: "inherit" }}
>
<div>
<img src={prop.icon} alt={prop.name} />
<p>{prop.name}</p>
</div>
</a>
);
}