import { Alert, Collapse, Space } from "antd"; import queryString from "query-string"; import { Calendar } from "react-big-calendar"; import { Trans, useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectProblemJobs } from "../../redux/application/application.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import dayjs from "../../utils/day"; import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import Event from "../job-at-change/schedule-event.container"; import JobDetailCards from "../job-detail-cards/job-detail-cards.component"; import local from "./localizer"; import HeaderComponent from "./schedule-calendar-header.component"; import { logImEXEvent } from "../../firebase/firebase.utils"; import "./schedule-calendar.styles.scss"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, problemJobs: selectProblemJobs }); const localizer = local(dayjs); export function ScheduleCalendarWrapperComponent({ bodyshop, problemJobs, data, refetch, defaultView, setDateRangeCallback, date, ...otherProps }) { const search = queryString.parse(useLocation().search); const history = useNavigate(); const { t } = useTranslation(); // Determine current view to compute styles consistently const currentView = search.view || defaultView || "week"; const handleEventPropStyles = (event) => { const { color, block, arrived } = event ?? {}; const hasColor = Boolean(color?.hex || color); const useBg = currentView !== "agenda"; // Prioritize explicit blocked-day background to ensure red in all themes let bg; if (useBg) { bg = block ? "var(--event-block-bg)" : arrived ? "var(--event-arrived-bg)" : (color?.hex ?? color ?? "var(--event-bg-fallback)"); } const usedFallback = !hasColor && !block && !arrived; // only mark as fallback when not blocked or arrived const classes = [ "imex-event", arrived && "imex-event-arrived", block && "imex-event-block", usedFallback && "imex-event-fallback" ] .filter(Boolean) .join(" "); return { ...(bg ? { style: { backgroundColor: bg } } : {}), className: classes }; }; const selectedDate = new Date(date || dayjs(search.date) || Date.now()); return ( <> {HasFeatureAccess({ featureName: "smartscheduling", bodyshop }) && problemJobs && (problemJobs.length > 2 ? ( {t("appointments.labels.severalerrorsfound")} } > {problemJobs.map((problem) => ( ]} values={{ ro_number: problem.ro_number, code: problem.code }} /> } /> ))} ) : ( {problemJobs.map((problem) => ( ]} values={{ ro_number: problem.ro_number, code: problem.code }} /> } /> ))} ))} { search.date = date.toISOString().substr(0, 10); history({ search: queryString.stringify(search) }); }} onRangeChange={(start, end) => { if (setDateRangeCallback) setDateRangeCallback({ start, end }); }} onView={(view) => { search.view = view; logImEXEvent("schedule_change_view", { view }); history({ search: queryString.stringify(search) }); }} step={15} // timeslots={1} showMultiDayTimes localizer={localizer} min={bodyshop.schedule_start_time ? new Date(bodyshop.schedule_start_time) : new Date("2020-01-01T06:00:00")} max={bodyshop.schedule_end_time ? new Date(bodyshop.schedule_end_time) : new Date("2020-01-01T20:00:00")} eventPropGetter={handleEventPropStyles} components={{ event: (e) => Event({ bodyshop: bodyshop, event: e.event, refetch: refetch }), header: (p) => }} {...otherProps} /> ); } export default connect(mapStateToProps, null)(ScheduleCalendarWrapperComponent);