@fayz-ai/plugin-agenda 0.23.3 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAoBnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,2BAA2B,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAA;AAChG,YAAY,EACV,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,yBAAyB,CAAA;AAMhC,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAuehF;AAGD,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAGtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAC5E,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAC1I,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAA;AAGlG,OAAO,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AAIvK,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAoBnD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAE,2BAA2B,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAA;AAChG,YAAY,EACV,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,yBAAyB,CAAA;AAMhC,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAgfhF;AAGD,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAGtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAC5E,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAC1I,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAA;AAGlG,OAAO,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AAIvK,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createMockAgendaProvider, createSupabasePublicBookingProvider } from '.
2
2
  export { createMockAgendaProvider } from './chunk-A77PUFY5.js';
3
3
  import React23, { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
4
4
  import { tl, recordRailFields, RECORD_GROUP, activeFacet, connectionStatus, createConnectionStore, getActiveTenantId, createSafeDataProvider, todayLocal, getSupabaseClientOptional, registerTranslations, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, getActiveUnitId, assertWrote, emitDataChanged, globalCache, usePluginRuntimeOptional, useTranslation, useActiveUnitId, emitDomainEvent, getCurrentLocale, localDateKey, useDataChanged, describeError, formatCurrency, resolveDataProvider } from '@fayz-ai/core';
5
- import { createPluginContext, fayzConnectorOAuth, UNIT_DETAIL_ZONES, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, adminNavigateTo, createArchetypeLookup, dedup, usePluginPrefs, PersonLink, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, usePermissionOptional, PageActions, useRightRailStore, useFieldRules, deriveEntityKey, openEntity, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
5
+ import { createPluginContext, fayzConnectorOAuth, UNIT_DETAIL_ZONES, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, adminNavigateTo, createArchetypeLookup, dedup, usePluginPrefs, PersonLink, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, usePermissionOptional, useAdminPath, PageActions, useRightRailStore, useFieldRules, deriveEntityKey, openEntity, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
6
6
  import FullCalendar from '@fullcalendar/react';
7
7
  import dayGridPlugin from '@fullcalendar/daygrid';
8
8
  import timeGridPlugin from '@fullcalendar/timegrid';
@@ -420,7 +420,7 @@ var css = (
420
420
 
421
421
  /* Today's column header */
422
422
  .agenda-calendar .fc-day-today .fc-col-header-cell-cushion {
423
- color: hsl(var(--primary));
423
+ color: hsl(var(--primary-ink, var(--primary)));
424
424
  font-weight: 600;
425
425
  }
426
426
 
@@ -648,7 +648,7 @@ var css = (
648
648
  font-weight: 600;
649
649
  font-size: 0.68rem;
650
650
  letter-spacing: 0.08em;
651
- color: hsl(var(--primary));
651
+ color: hsl(var(--primary-ink, var(--primary)));
652
652
  }
653
653
 
654
654
  .agenda-calendar .fc-day-today.fc-col-header-cell[data-date] > .fc-scrollgrid-sync-inner {
@@ -677,6 +677,42 @@ var css = (
677
677
  text-transform: none;
678
678
  letter-spacing: 0;
679
679
  }
680
+
681
+ /* The day header fills its column, so a holiday name ellipses inside it
682
+ instead of pushing the day out. */
683
+ .agenda-calendar .fc-col-header-cell[data-date] .fc-col-header-cell-cushion {
684
+ display: block;
685
+ max-width: 100%;
686
+ overflow: hidden;
687
+ }
688
+
689
+ /* A day of the week opens that day. */
690
+ .agenda-calendar .agenda-day-link {
691
+ cursor: pointer;
692
+ border-radius: 0.5rem;
693
+ padding: 2px 4px;
694
+ transition: background-color 120ms ease-out;
695
+ }
696
+ .agenda-calendar .agenda-day-link:hover,
697
+ .agenda-calendar .agenda-day-link:focus-visible {
698
+ background: hsl(var(--foreground) / 0.07);
699
+ outline: none;
700
+ }
701
+
702
+ /* No schedule in the period: the whole grid reads as closed, in the same
703
+ hatch as time outside a schedule \u2014 a blank grid looks bookable. On the slot
704
+ rows, because with no schedule there may be no column to paint. */
705
+ .agenda-calendar.agenda-unscheduled .fc-timegrid-slots td.fc-timegrid-slot-lane {
706
+ background-color: hsl(var(--muted-foreground) / 0.2);
707
+ background-image: repeating-linear-gradient(
708
+ 45deg,
709
+ transparent,
710
+ transparent 4px,
711
+ hsl(var(--muted-foreground) / 0.12) 4px,
712
+ hsl(var(--muted-foreground) / 0.12) 8px
713
+ );
714
+ background-attachment: fixed;
715
+ }
680
716
  `
681
717
  );
682
718
  var injected = false;
@@ -695,6 +731,38 @@ function injectCalendarStyles() {
695
731
  document.head.appendChild(style);
696
732
  injected = true;
697
733
  }
734
+
735
+ // src/lib/agenda-url.ts
736
+ var VIEWS = ["day", "week", "month", "list"];
737
+ function parseAgendaPath(path) {
738
+ const parts = (path.split("?")[0] ?? "").split("/").filter(Boolean);
739
+ if (parts[0] !== "agenda") return null;
740
+ const view = parts[1];
741
+ if (!view || !VIEWS.includes(view)) return null;
742
+ if (parts.length === 2) return { view };
743
+ if (parts.length !== 5) return null;
744
+ const [y, m, d] = parts.slice(2).map(Number);
745
+ const date = new Date(y, m - 1, d);
746
+ if (date.getFullYear() !== y || date.getMonth() !== m - 1 || date.getDate() !== d) return null;
747
+ return { view, date };
748
+ }
749
+ function agendaPath(view, date) {
750
+ if (!date) return `/agenda/${view}`;
751
+ const pad = (n) => String(n).padStart(2, "0");
752
+ return `/agenda/${view}/${date.getFullYear()}/${pad(date.getMonth() + 1)}/${pad(date.getDate())}`;
753
+ }
754
+ function urlViewOf(viewType) {
755
+ if (viewType.startsWith("list")) return "list";
756
+ if (viewType.endsWith("Day")) return "day";
757
+ if (viewType.endsWith("Week")) return "week";
758
+ return "month";
759
+ }
760
+ function calendarViewFor(view, resourceCapable) {
761
+ if (view === "day") return resourceCapable ? "resourceTimeGridDay" : "timeGridDay";
762
+ if (view === "week") return resourceCapable ? "resourceTimeGridWeek" : "timeGridWeek";
763
+ if (view === "list") return resourceCapable ? "listWeek" : "listMonth";
764
+ return "dayGridMonth";
765
+ }
698
766
  var DEFAULTS = {
699
767
  startTime: "07:00",
700
768
  endTime: "21:00"
@@ -4428,7 +4496,9 @@ function CalendarView() {
4428
4496
  // Como a SEMANA se agrupa: 'day' põe o dia em cima e as pessoas dentro
4429
4497
  // dele; 'professional' inverte. 'auto' é o padrão e deixa a agenda decidir
4430
4498
  // pelo tamanho da equipe — ver `weekGrouping` mais abaixo.
4431
- weekGrouping: "auto"
4499
+ weekGrouping: "auto",
4500
+ // Which weekdays the WEEK draws: 'auto', or the shown days (0 = Sunday) — see `shownWeekDays`.
4501
+ weekDays: "auto"
4432
4502
  });
4433
4503
  const weekGroupingPref = prefs.get("weekGrouping") ?? "auto";
4434
4504
  const agendaSettings = useAgendaSettings();
@@ -4549,6 +4619,16 @@ function CalendarView() {
4549
4619
  const days2 = /* @__PURE__ */ new Set();
4550
4620
  const cursor = new Date(visibleRange.start);
4551
4621
  const end = new Date(visibleRange.end);
4622
+ if (currentView.endsWith("Week")) {
4623
+ cursor.setDate(cursor.getDate() - cursor.getDay());
4624
+ end.setTime(cursor.getTime());
4625
+ end.setDate(end.getDate() + 7);
4626
+ return {
4627
+ daysOfWeek: [0, 1, 2, 3, 4, 5, 6],
4628
+ from: localDateKey(cursor),
4629
+ to: localDateKey(end)
4630
+ };
4631
+ }
4552
4632
  for (let i = 0; cursor < end && i < 62; i++) {
4553
4633
  days2.add(cursor.getDay());
4554
4634
  cursor.setDate(cursor.getDate() + 1);
@@ -4558,7 +4638,7 @@ function CalendarView() {
4558
4638
  from: visibleRange.start.slice(0, 10),
4559
4639
  to: visibleRange.end.slice(0, 10)
4560
4640
  };
4561
- }, [visibleRange]);
4641
+ }, [visibleRange, currentView]);
4562
4642
  const wantedSchedulesKey = useMemo(
4563
4643
  () => scheduleQuery ? "agenda:schedules:" + JSON.stringify(scheduleQuery) : null,
4564
4644
  [scheduleQuery]
@@ -4633,6 +4713,37 @@ function CalendarView() {
4633
4713
  const rows = scheduleIndex.get(profId);
4634
4714
  return !!rows && opensOnDate(rows, iso);
4635
4715
  }, [scheduleIndex]);
4716
+ const weekDaysPref = prefs.get("weekDays");
4717
+ const lastAutoWeekDays = useRef("0123456");
4718
+ const shownWeekDaysKey = useMemo(() => {
4719
+ const all = [0, 1, 2, 3, 4, 5, 6];
4720
+ if (Array.isArray(weekDaysPref)) {
4721
+ const chosen = all.filter((d) => weekDaysPref.includes(d));
4722
+ return (chosen.length > 0 ? chosen : all).join("");
4723
+ }
4724
+ const settled = currentView.endsWith("Week") && !schedulesLoading && schedulesKey === wantedSchedulesKey;
4725
+ if (!settled) return lastAutoWeekDays.current;
4726
+ if (!visibleRange || scheduleIndex.size === 0) return lastAutoWeekDays.current = all.join("");
4727
+ const sunday = new Date(visibleRange.start);
4728
+ sunday.setDate(sunday.getDate() - sunday.getDay());
4729
+ const works = (dow2) => {
4730
+ const day = new Date(sunday);
4731
+ day.setDate(day.getDate() + dow2);
4732
+ const iso = localDateKey(day);
4733
+ for (const [profId] of scheduleIndex) if (opensOn(profId, iso, dow2)) return true;
4734
+ return false;
4735
+ };
4736
+ return lastAutoWeekDays.current = all.filter((d) => d !== 0 && d !== 6 || works(d)).join("");
4737
+ }, [weekDaysPref, visibleRange, scheduleIndex, opensOn, currentView, schedulesLoading, schedulesKey, wantedSchedulesKey]);
4738
+ const shownWeekDays = useMemo(() => shownWeekDaysKey.split("").map(Number), [shownWeekDaysKey]);
4739
+ const weekViewOptions = useMemo(() => {
4740
+ const hiddenDays = [0, 1, 2, 3, 4, 5, 6].filter((d) => !shownWeekDays.includes(d));
4741
+ return { resourceTimeGridWeek: { hiddenDays }, timeGridWeek: { hiddenDays } };
4742
+ }, [shownWeekDays]);
4743
+ const toggleWeekDay = useCallback((dow2) => {
4744
+ const next = shownWeekDays.includes(dow2) ? shownWeekDays.filter((d) => d !== dow2) : [...shownWeekDays, dow2].sort();
4745
+ if (next.length > 0) prefs.set("weekDays", next);
4746
+ }, [shownWeekDays, prefs]);
4636
4747
  const relevantProfIds = useMemo(() => {
4637
4748
  const ids = /* @__PURE__ */ new Set();
4638
4749
  for (const [profId] of scheduleIndex) {
@@ -4911,13 +5022,49 @@ function CalendarView() {
4911
5022
  return [...bookingEvents, ...disponibilidade, ...blockEvents];
4912
5023
  }, [bookingEvents, availabilityEvents, blockEvents, currentView]);
4913
5024
  const storedView = prefs.get("calendarView");
4914
- const effectiveInitialView = resourceCapable ? viewForResources(storedView) : viewForNoResources(storedView);
5025
+ const adminPath = useAdminPath();
5026
+ const urlTarget = useMemo(() => parseAgendaPath(adminPath), [adminPath]);
5027
+ const effectiveInitialView = urlTarget ? calendarViewFor(urlTarget.view, resourceCapable) : resourceCapable ? viewForResources(storedView) : viewForNoResources(storedView);
4915
5028
  const handleDatesSet = useCallback((arg) => {
4916
5029
  setCalendarTitle(arg.view.title);
4917
5030
  setView(arg.view.type);
4918
5031
  setVisibleRange({ start: arg.startStr, end: arg.endStr });
4919
5032
  fetchBookings({ start: arg.startStr, end: arg.endStr });
5033
+ const here = window.location.hash.replace(/^#/, "");
5034
+ if (parseAgendaPath(here)) {
5035
+ const api = calendarRef.current?.getApi();
5036
+ const next = agendaPath(urlViewOf(arg.view.type), api?.getDate());
5037
+ if (next !== here) {
5038
+ try {
5039
+ window.history.replaceState(null, "", `#${next}`);
5040
+ } catch {
5041
+ }
5042
+ }
5043
+ }
4920
5044
  }, [fetchBookings, setView]);
5045
+ useEffect(() => {
5046
+ if (!urlTarget) return;
5047
+ const api = calendarRef.current?.getApi();
5048
+ if (!api) return;
5049
+ const view = calendarViewFor(urlTarget.view, resourceCapable);
5050
+ const sameDay = !urlTarget.date || api.getDate().toDateString() === urlTarget.date.toDateString();
5051
+ if (api.view.type === view && sameDay) return;
5052
+ api.changeView(view);
5053
+ if (urlTarget.date) {
5054
+ api.gotoDate(urlTarget.date);
5055
+ setSelectedDate(urlTarget.date);
5056
+ }
5057
+ }, [urlTarget, resourceCapable]);
5058
+ const openDay = useCallback((date) => {
5059
+ const api = calendarRef.current?.getApi();
5060
+ if (api && !parseAgendaPath(window.location.hash.replace(/^#/, ""))) {
5061
+ try {
5062
+ window.history.replaceState(null, "", `#${agendaPath(urlViewOf(api.view.type), api.getDate())}`);
5063
+ } catch {
5064
+ }
5065
+ }
5066
+ adminNavigateTo(agendaPath("day", date));
5067
+ }, []);
4921
5068
  const handleEventDidMount = useCallback((arg) => {
4922
5069
  const booking = arg.event.extendedProps?.booking;
4923
5070
  if (booking) arg.el.setAttribute("data-booking-id", booking.id);
@@ -5266,11 +5413,23 @@ function CalendarView() {
5266
5413
  const iso = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
5267
5414
  const noMes = arg.view?.type === "dayGridMonth";
5268
5415
  const feriado = noMes ? void 0 : holidayByDate.get(iso);
5269
- const wrapStyle = { display: "flex", flexDirection: "column", alignItems: "center", lineHeight: 1.15, maxWidth: "100%", minWidth: 0 };
5416
+ const wrapStyle = { display: "flex", flexDirection: "column", alignItems: "center", lineHeight: 1.15, width: "fit-content", maxWidth: "100%", minWidth: 0, margin: "0 auto", boxSizing: "border-box" };
5417
+ const dayLink = String(arg.view?.type ?? "").endsWith("Week") ? {
5418
+ role: "link",
5419
+ tabIndex: 0,
5420
+ className: "agenda-day-link",
5421
+ onClick: () => openDay(date),
5422
+ onKeyDown: (e) => {
5423
+ if (e.key === "Enter" || e.key === " ") {
5424
+ e.preventDefault();
5425
+ openDay(date);
5426
+ }
5427
+ }
5428
+ } : {};
5270
5429
  const dateStyle = { fontSize: "0.65rem", opacity: 0.75 };
5271
5430
  if (dayHeaderCompact) {
5272
5431
  const letter = date.toLocaleDateString(dateLocale, { weekday: "narrow" });
5273
- return /* @__PURE__ */ jsxs("div", { style: wrapStyle, title: feriado ? feriado.names.join(" \xB7 ") : void 0, children: [
5432
+ return /* @__PURE__ */ jsxs("div", { style: wrapStyle, title: feriado ? feriado.names.join(" \xB7 ") : void 0, ...dayLink, children: [
5274
5433
  /* @__PURE__ */ jsx("span", { style: { fontWeight: 600 }, children: letter }),
5275
5434
  /* @__PURE__ */ jsx("span", { style: dateStyle, children: date.getDate() }),
5276
5435
  feriado && /* @__PURE__ */ jsx(
@@ -5291,7 +5450,7 @@ function CalendarView() {
5291
5450
  const weekday = date.toLocaleDateString(dateLocale, { weekday: "short" });
5292
5451
  if (noMes) return /* @__PURE__ */ jsx("div", { style: wrapStyle, children: /* @__PURE__ */ jsx("span", { children: arg.text }) });
5293
5452
  const diaMes = date.toLocaleDateString(dateLocale, { day: "numeric", month: "numeric" });
5294
- return /* @__PURE__ */ jsxs("div", { style: wrapStyle, children: [
5453
+ return /* @__PURE__ */ jsxs("div", { style: wrapStyle, ...dayLink, children: [
5295
5454
  /* @__PURE__ */ jsx("span", { children: weekday }),
5296
5455
  /* @__PURE__ */ jsx("span", { style: dateStyle, children: diaMes }),
5297
5456
  feriado && /* @__PURE__ */ jsxs(
@@ -5316,7 +5475,7 @@ function CalendarView() {
5316
5475
  }
5317
5476
  )
5318
5477
  ] });
5319
- }, [dayHeaderCompact, currentLocale, holidayByDate]);
5478
+ }, [dayHeaderCompact, currentLocale, holidayByDate, openDay]);
5320
5479
  const dayCellContent = useCallback((arg) => {
5321
5480
  const date = arg.date;
5322
5481
  const pad = (n) => String(n).padStart(2, "0");
@@ -5389,32 +5548,11 @@ function CalendarView() {
5389
5548
  /* @__PURE__ */ jsx(AgendaRailColumn, { children: /* @__PURE__ */ jsx(
5390
5549
  AgendaFilterRail,
5391
5550
  {
5392
- actions: /* @__PURE__ */ jsxs(Fragment, { children: [
5393
- /* @__PURE__ */ jsxs(
5394
- "button",
5395
- {
5396
- type: "button",
5397
- onClick: () => setBlockFor({ professionalId: null, date: null }),
5398
- className: "flex w-full items-center gap-2 rounded-button border border-border bg-card px-2.5 py-2 text-sm font-medium text-foreground shadow-sm transition-colors hover:bg-muted hover:border-foreground/20",
5399
- children: [
5400
- /* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
5401
- t2("agenda.sidebar.block")
5402
- ]
5403
- }
5404
- ),
5405
- config.capabilities.professional && /* @__PURE__ */ jsxs(
5406
- "button",
5407
- {
5408
- type: "button",
5409
- onClick: () => setSchedulesOpen(true),
5410
- className: "flex w-full items-center gap-2 rounded-button border border-border bg-card px-2.5 py-2 text-sm font-medium text-foreground shadow-sm transition-colors hover:bg-muted hover:border-foreground/20",
5411
- children: [
5412
- /* @__PURE__ */ jsx(Clock3, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
5413
- t2("agenda.sidebar.workingHours")
5414
- ]
5415
- }
5416
- )
5417
- ] }),
5551
+ quickActions: [
5552
+ { id: "block", label: t2("agenda.sidebar.block"), icon: Ban, onClick: () => setBlockFor({ professionalId: null, date: null }) },
5553
+ ...config.capabilities.professional ? [{ id: "working-hours", label: t2("agenda.sidebar.workingHours"), icon: Clock3, onClick: () => setSchedulesOpen(true) }] : []
5554
+ ],
5555
+ beforeMark: { label: "Calend\xE1rio" },
5418
5556
  before: /* @__PURE__ */ jsxs(Fragment, { children: [
5419
5557
  /* @__PURE__ */ jsx(MiniCalendar, { selectedDate, onDateSelect: handleMiniDateSelect }),
5420
5558
  /* @__PURE__ */ jsx(
@@ -5545,6 +5683,43 @@ function CalendarView() {
5545
5683
  }
5546
5684
  )
5547
5685
  },
5686
+ {
5687
+ id: "week-days",
5688
+ title: "Dias da semana",
5689
+ // A choice away from the automatic one is something in force.
5690
+ count: Array.isArray(weekDaysPref) ? 1 : 0,
5691
+ hidden: !currentView.endsWith("Week") || currentView.startsWith("list"),
5692
+ defaultOpen: true,
5693
+ content: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5694
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", role: "group", "aria-label": "Dias que a semana mostra", children: [1, 2, 3, 4, 5, 6, 0].map((dow2) => {
5695
+ const on = shownWeekDays.includes(dow2);
5696
+ const name = new Date(2024, 0, 7 + dow2).toLocaleDateString(currentLocale === "pt-BR" ? "pt-BR" : "en", { weekday: "long" });
5697
+ return /* @__PURE__ */ jsx(
5698
+ "button",
5699
+ {
5700
+ type: "button",
5701
+ "aria-pressed": on,
5702
+ title: name,
5703
+ "aria-label": name,
5704
+ "data-testid": `week-day-${dow2}`,
5705
+ onClick: () => toggleWeekDay(dow2),
5706
+ className: `flex h-7 w-7 items-center justify-center rounded-full text-xs font-medium uppercase transition-colors ${on ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`,
5707
+ children: name.charAt(0)
5708
+ },
5709
+ dow2
5710
+ );
5711
+ }) }),
5712
+ Array.isArray(weekDaysPref) && /* @__PURE__ */ jsx(
5713
+ "button",
5714
+ {
5715
+ type: "button",
5716
+ onClick: () => prefs.set("weekDays", "auto"),
5717
+ className: "text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
5718
+ children: "Voltar ao autom\xE1tico"
5719
+ }
5720
+ )
5721
+ ] })
5722
+ },
5548
5723
  {
5549
5724
  id: "hide-empty",
5550
5725
  title: t2("agenda.sidebar.hideEmptyGrids"),
@@ -5686,13 +5861,15 @@ function CalendarView() {
5686
5861
  }
5687
5862
  )
5688
5863
  ] }),
5689
- /* @__PURE__ */ jsx("div", { ref: wrapperRef, className: "min-h-0 min-w-0 flex-1 overflow-x-auto overflow-y-hidden agenda-calendar", children: /* @__PURE__ */ jsx("div", { className: "h-full", style: gridMinWidth ? { minWidth: gridMinWidth } : void 0, children: /* @__PURE__ */ jsx(
5864
+ /* @__PURE__ */ jsx("div", { ref: wrapperRef, className: `min-h-0 min-w-0 flex-1 overflow-x-auto overflow-y-hidden agenda-calendar${semGradeNoPeriodo ? " agenda-unscheduled" : ""}`, children: /* @__PURE__ */ jsx("div", { className: "h-full", style: gridMinWidth ? { minWidth: gridMinWidth } : void 0, children: /* @__PURE__ */ jsx(
5690
5865
  FullCalendar,
5691
5866
  {
5692
5867
  ref: calendarRef,
5693
5868
  plugins: [dayGridPlugin, timeGridPlugin, resourceTimeGridPlugin, interactionPlugin, listPlugin],
5694
5869
  initialView: mobileGridActive ? mobileView : effectiveInitialView,
5870
+ initialDate: urlTarget?.date,
5695
5871
  schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
5872
+ views: weekViewOptions,
5696
5873
  resources: resourceCapable ? columnsSettled ? resources : [] : void 0,
5697
5874
  events,
5698
5875
  headerToolbar: false,
@@ -22553,6 +22730,15 @@ function createAgendaPlugin(options) {
22553
22730
  }
22554
22731
  ],
22555
22732
  routes: [
22733
+ // The calendar's own links (/agenda/day/2026/10/15). Same screen and,
22734
+ // with the wildcard stripped, the same page key as /agenda — so moving
22735
+ // between a week and a day never remounts it.
22736
+ {
22737
+ path: "/agenda/*",
22738
+ component: PageComponent,
22739
+ fullBleed: true,
22740
+ permission: { feature: "agenda", action: "read" }
22741
+ },
22556
22742
  {
22557
22743
  path: "/agenda",
22558
22744
  component: PageComponent,