@open-mercato/core 0.6.8-develop.6962.1.bd029a2bb2 → 0.6.8-develop.6964.1.36b364cfd8
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.
- package/dist/modules/customers/components/calendar/AgendaList.js +8 -7
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +11 -8
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/components/calendar/AgendaList.tsx +8 -7
- package/src/modules/customers/components/calendar/MonthGrid.tsx +11 -8
|
@@ -35,11 +35,11 @@ function formatUrlHost(location) {
|
|
|
35
35
|
return location;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
function formatTime(date) {
|
|
39
|
-
return date.toLocaleTimeString(
|
|
38
|
+
function formatTime(locale, date) {
|
|
39
|
+
return date.toLocaleTimeString(locale, { hour: "numeric", minute: "2-digit" });
|
|
40
40
|
}
|
|
41
|
-
function groupLabelOf(date) {
|
|
42
|
-
return date.toLocaleDateString(
|
|
41
|
+
function groupLabelOf(locale, date) {
|
|
42
|
+
return date.toLocaleDateString(locale, { weekday: "long", month: "short", day: "numeric" });
|
|
43
43
|
}
|
|
44
44
|
function deriveTypeLabel(interactionType) {
|
|
45
45
|
const normalized = interactionType.replace(/[-_]+/g, " ").trim();
|
|
@@ -76,7 +76,7 @@ function AgendaDayHeader({ date, count }) {
|
|
|
76
76
|
const resolvedCount = t(countKey, { count });
|
|
77
77
|
const countLabel = resolvedCount === countKey ? t("customers.calendar.agenda.eventsCount.other", "{count} events", { count }) : resolvedCount;
|
|
78
78
|
return /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5", children: [
|
|
79
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: groupLabelOf(date) }),
|
|
79
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: groupLabelOf(locale, date) }),
|
|
80
80
|
todayMarker || tomorrowMarker ? /* @__PURE__ */ jsx("span", { className: cn("text-xs font-medium", todayMarker ? "text-foreground" : "text-muted-foreground"), children: `\xB7 ${todayMarker ? t("customers.calendar.agenda.today", "Today") : t("customers.calendar.agenda.tomorrow", "Tomorrow")}` }) : null,
|
|
81
81
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "w-2.5 shrink-0" }),
|
|
82
82
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: countLabel })
|
|
@@ -88,6 +88,7 @@ function AgendaRow({
|
|
|
88
88
|
onItemClick
|
|
89
89
|
}) {
|
|
90
90
|
const t = useT();
|
|
91
|
+
const locale = useLocale();
|
|
91
92
|
const canceled = item.status === "canceled";
|
|
92
93
|
const done = item.status === "done";
|
|
93
94
|
const title = eventDisplayTitle(item.title, t("customers.calendar.grid.untitled", "Untitled"));
|
|
@@ -99,8 +100,8 @@ function AgendaRow({
|
|
|
99
100
|
teams: t("customers.calendar.platformFull.teams", "Microsoft Teams")
|
|
100
101
|
};
|
|
101
102
|
const locationLabel = item.platform ? platformLabels[item.platform] : item.locationKind === "url" && item.location ? formatUrlHost(item.location) : item.location;
|
|
102
|
-
const startLabel = item.allDay ? t("customers.calendar.grid.allDay", "All day") : formatTime(item.start);
|
|
103
|
-
const endLabel = item.allDay ? null : formatTime(item.end);
|
|
103
|
+
const startLabel = item.allDay ? t("customers.calendar.grid.allDay", "All day") : formatTime(locale, item.start);
|
|
104
|
+
const endLabel = item.allDay ? null : formatTime(locale, item.end);
|
|
104
105
|
const ariaTime = item.allDay ? startLabel : `${startLabel} \u2013 ${endLabel}`;
|
|
105
106
|
return /* @__PURE__ */ jsxs(
|
|
106
107
|
Button,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/calendar/AgendaList.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isToday } from 'date-fns/isToday'\nimport { isTomorrow } from 'date-fns/isTomorrow'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'\nimport type { AgendaListProps, CalendarCategory, CalendarItem } from './types'\n\nconst MAX_AVATARS_PER_ROW = 2\n\nconst CATEGORY_BADGE_CLASS: Record<CalendarCategory, string> = {\n meeting: 'bg-status-info-bg text-status-info-text',\n event: 'bg-status-warning-bg text-status-warning-text',\n task: 'bg-primary text-primary-foreground',\n other: 'bg-status-neutral-bg text-status-neutral-text',\n}\n\nconst CATEGORY_LABEL: Record<CalendarCategory, { key: string; fallback: string }> = {\n meeting: { key: 'customers.calendar.category.meeting', fallback: 'Meeting' },\n event: { key: 'customers.calendar.category.event', fallback: 'Event' },\n task: { key: 'customers.calendar.category.task', fallback: 'Task' },\n other: { key: 'customers.calendar.category.other', fallback: 'Other' },\n}\n\ntype AgendaDayGroup = { date: Date; items: CalendarItem[] }\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatUrlHost(location: string): string {\n try {\n const url = new URL(location.startsWith('http') ? location : `https://${location}`)\n return url.hostname\n } catch {\n return location\n }\n}\n\nfunction formatTime(date: Date): string {\n return date.toLocaleTimeString(
|
|
5
|
-
"mappings": ";AA+FI,SACE,KADF;AA7FJ,YAAY,WAAW;AACvB,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,WAAW,YAAY;AAChC,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,mBAAmB,sBAAsB;AAGlD,MAAM,sBAAsB;AAE5B,MAAM,uBAAyD;AAAA,EAC7D,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AACT;AAEA,MAAM,iBAA8E;AAAA,EAClF,SAAS,EAAE,KAAK,uCAAuC,UAAU,UAAU;AAAA,EAC3E,OAAO,EAAE,KAAK,qCAAqC,UAAU,QAAQ;AAAA,EACrE,MAAM,EAAE,KAAK,oCAAoC,UAAU,OAAO;AAAA,EAClE,OAAO,EAAE,KAAK,qCAAqC,UAAU,QAAQ;AACvE;AAIA,SAAS,SAAS,MAAoB;AACpC,SAAO,OAAO,MAAM,YAAY;AAClC;AAEA,SAAS,cAAc,UAA0B;AAC/C,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,SAAS,WAAW,MAAM,IAAI,WAAW,WAAW,QAAQ,EAAE;AAClF,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,MAAoB;
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isToday } from 'date-fns/isToday'\nimport { isTomorrow } from 'date-fns/isTomorrow'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'\nimport type { AgendaListProps, CalendarCategory, CalendarItem } from './types'\n\nconst MAX_AVATARS_PER_ROW = 2\n\nconst CATEGORY_BADGE_CLASS: Record<CalendarCategory, string> = {\n meeting: 'bg-status-info-bg text-status-info-text',\n event: 'bg-status-warning-bg text-status-warning-text',\n task: 'bg-primary text-primary-foreground',\n other: 'bg-status-neutral-bg text-status-neutral-text',\n}\n\nconst CATEGORY_LABEL: Record<CalendarCategory, { key: string; fallback: string }> = {\n meeting: { key: 'customers.calendar.category.meeting', fallback: 'Meeting' },\n event: { key: 'customers.calendar.category.event', fallback: 'Event' },\n task: { key: 'customers.calendar.category.task', fallback: 'Task' },\n other: { key: 'customers.calendar.category.other', fallback: 'Other' },\n}\n\ntype AgendaDayGroup = { date: Date; items: CalendarItem[] }\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatUrlHost(location: string): string {\n try {\n const url = new URL(location.startsWith('http') ? location : `https://${location}`)\n return url.hostname\n } catch {\n return location\n }\n}\n\nfunction formatTime(locale: string, date: Date): string {\n return date.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' })\n}\n\nfunction groupLabelOf(locale: string, date: Date): string {\n return date.toLocaleDateString(locale, { weekday: 'long', month: 'short', day: 'numeric' })\n}\n\nfunction deriveTypeLabel(interactionType: string): string {\n const normalized = interactionType.replace(/[-_]+/g, ' ').trim()\n if (!normalized) return interactionType\n return normalized.charAt(0).toUpperCase() + normalized.slice(1)\n}\n\nfunction participantLabel(participant: CalendarItem['participants'][number]): string {\n return participant.name ?? participant.email ?? participant.userId\n}\n\nfunction buildDayGroups(anchor: Date, horizonDays: number, items: CalendarItem[]): AgendaDayGroup[] {\n const sorted = [...items].sort((first, second) => first.start.getTime() - second.start.getTime())\n const byDay = new Map<string, CalendarItem[]>()\n for (const item of sorted) {\n const key = dayKeyOf(item.start)\n const bucket = byDay.get(key)\n if (bucket) bucket.push(item)\n else byDay.set(key, [item])\n }\n const firstDay = startOfDay(anchor)\n const groups: AgendaDayGroup[] = []\n for (let offset = 0; offset <= horizonDays; offset += 1) {\n const day = addDays(firstDay, offset)\n const dayItems = byDay.get(dayKeyOf(day))\n if (dayItems && dayItems.length > 0) groups.push({ date: day, items: dayItems })\n }\n return groups\n}\n\nfunction AgendaDayHeader({ date, count }: { date: Date; count: number }) {\n const t = useT()\n const locale = useLocale()\n const todayMarker = isToday(date)\n const tomorrowMarker = !todayMarker && isTomorrow(date)\n const countKey = `customers.calendar.agenda.eventsCount.${pluralCategory(locale, count)}`\n const resolvedCount = t(countKey, { count })\n const countLabel =\n resolvedCount === countKey\n ? t('customers.calendar.agenda.eventsCount.other', '{count} events', { count })\n : resolvedCount\n return (\n <div className=\"flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5\">\n <span className=\"text-sm font-semibold text-foreground\">{groupLabelOf(locale, date)}</span>\n {todayMarker || tomorrowMarker ? (\n <span className={cn('text-xs font-medium', todayMarker ? 'text-foreground' : 'text-muted-foreground')}>\n {`\u00B7 ${todayMarker ? t('customers.calendar.agenda.today', 'Today') : t('customers.calendar.agenda.tomorrow', 'Tomorrow')}`}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"w-2.5 shrink-0\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{countLabel}</span>\n </div>\n )\n}\n\nfunction AgendaRow({\n item,\n typeLabels,\n onItemClick,\n}: {\n item: CalendarItem\n typeLabels?: Record<string, string>\n onItemClick: (item: CalendarItem) => void\n}) {\n const t = useT()\n const locale = useLocale()\n const canceled = item.status === 'canceled'\n const done = item.status === 'done'\n const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))\n const typeLabel = typeLabels?.[item.interactionType] ?? deriveTypeLabel(item.interactionType)\n const platformLabels: Record<string, string> = {\n zoom: t('customers.calendar.platformFull.zoom', 'Zoom'),\n meet: t('customers.calendar.platformFull.meet', 'Google Meet'),\n slack: t('customers.calendar.platformFull.slack', 'Slack'),\n teams: t('customers.calendar.platformFull.teams', 'Microsoft Teams'),\n }\n const locationLabel = item.platform\n ? platformLabels[item.platform]\n : item.locationKind === 'url' && item.location\n ? formatUrlHost(item.location)\n : item.location\n const startLabel = item.allDay ? t('customers.calendar.grid.allDay', 'All day') : formatTime(locale, item.start)\n const endLabel = item.allDay ? null : formatTime(locale, item.end)\n const ariaTime = item.allDay ? startLabel : `${startLabel} \u2013 ${endLabel}`\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-label={`${title} \u00B7 ${ariaTime}`}\n onClick={() => onItemClick(item)}\n className={cn(\n 'h-auto w-full justify-start whitespace-normal rounded-none bg-background px-3 py-3 text-left transition-colors hover:bg-muted/30 sm:gap-3.5 sm:px-5',\n canceled && 'opacity-60',\n )}\n >\n <div className=\"flex w-16 shrink-0 flex-col gap-0.5 sm:w-[86px]\">\n <span className={cn('truncate text-sm font-semibold leading-4', done || canceled ? 'text-muted-foreground' : 'text-foreground')}>\n {startLabel}\n </span>\n {endLabel ? <span className=\"truncate text-xs text-muted-foreground\">{endLabel}</span> : null}\n </div>\n <span\n aria-hidden=\"true\"\n className={cn('h-9 w-1 shrink-0 rounded-full', !item.color && 'bg-muted-foreground/40')}\n style={item.color ? { backgroundColor: item.color } : undefined}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span\n className={cn(\n 'truncate text-sm font-semibold',\n done || canceled ? 'text-muted-foreground' : 'text-foreground',\n canceled && 'line-through',\n )}\n >\n {title}\n </span>\n <span className=\"truncate text-xs text-muted-foreground\">\n {locationLabel ? `${typeLabel} \u00B7 ${locationLabel}` : typeLabel}\n </span>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {item.participants.length > 0 ? (\n <span className=\"hidden items-center gap-0.5 sm:flex\">\n {item.participants.slice(0, MAX_AVATARS_PER_ROW).map((participant) => (\n <Avatar key={participant.userId} size=\"xs\" label={participantLabel(participant)} />\n ))}\n {item.participants.length > MAX_AVATARS_PER_ROW ? (\n <span className=\"ps-0.5 text-xs font-medium text-muted-foreground\">\n +{item.participants.length - MAX_AVATARS_PER_ROW}\n </span>\n ) : null}\n </span>\n ) : null}\n <span\n className={cn(\n 'inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-overline font-medium uppercase tracking-wide',\n CATEGORY_BADGE_CLASS[item.category],\n )}\n >\n {t(CATEGORY_LABEL[item.category].key, CATEGORY_LABEL[item.category].fallback)}\n </span>\n </div>\n </Button>\n )\n}\n\nexport function AgendaList({ anchor, horizonDays, items, typeLabels, onItemClick }: AgendaListProps) {\n const t = useT()\n const groups = React.useMemo(() => buildDayGroups(anchor, horizonDays, items), [anchor, horizonDays, items])\n return (\n <div className=\"flex w-full flex-col divide-y divide-border overflow-hidden border border-border bg-background\">\n {groups.map((group) => (\n <React.Fragment key={dayKeyOf(group.date)}>\n <AgendaDayHeader date={group.date} count={group.items.length} />\n {group.items.map((item) => (\n <AgendaRow key={item.id} item={item} typeLabels={typeLabels} onItemClick={onItemClick} />\n ))}\n </React.Fragment>\n ))}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,7 +5,7 @@ import { addDays } from "date-fns/addDays";
|
|
|
5
5
|
import { format } from "date-fns/format";
|
|
6
6
|
import { isSameMonth } from "date-fns/isSameMonth";
|
|
7
7
|
import { isToday } from "date-fns/isToday";
|
|
8
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
|
+
import { useLocale, useT } from "@open-mercato/shared/lib/i18n/context";
|
|
9
9
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
10
10
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
11
11
|
import { getVisibleRange } from "../../lib/calendar/range.js";
|
|
@@ -15,11 +15,11 @@ const SOFT_TINT_ALPHA = "1A";
|
|
|
15
15
|
function dayKeyOf(date) {
|
|
16
16
|
return format(date, "yyyy-MM-dd");
|
|
17
17
|
}
|
|
18
|
-
function formatTime(date) {
|
|
19
|
-
return date.toLocaleTimeString(
|
|
18
|
+
function formatTime(locale, date) {
|
|
19
|
+
return date.toLocaleTimeString(locale, { hour: "numeric", minute: "2-digit" });
|
|
20
20
|
}
|
|
21
|
-
function fullDateLabel(date) {
|
|
22
|
-
return date.toLocaleDateString(
|
|
21
|
+
function fullDateLabel(locale, date) {
|
|
22
|
+
return date.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
|
|
23
23
|
}
|
|
24
24
|
function isWeekend(date) {
|
|
25
25
|
const weekday = date.getDay();
|
|
@@ -52,9 +52,10 @@ function groupItemsByDay(items) {
|
|
|
52
52
|
}
|
|
53
53
|
function MonthPill({ item, onItemClick }) {
|
|
54
54
|
const t = useT();
|
|
55
|
+
const locale = useLocale();
|
|
55
56
|
const canceled = item.status === "canceled";
|
|
56
57
|
const title = eventDisplayTitle(item.title, t("customers.calendar.grid.untitled", "Untitled"));
|
|
57
|
-
const timeLabel = item.allDay ? "" : ` \xB7 ${formatTime(item.start)} \u2013 ${formatTime(item.end)}`;
|
|
58
|
+
const timeLabel = item.allDay ? "" : ` \xB7 ${formatTime(locale, item.start)} \u2013 ${formatTime(locale, item.end)}`;
|
|
58
59
|
const tintStyle = item.color ? { backgroundColor: `${item.color}${SOFT_TINT_ALPHA}`, color: item.color } : void 0;
|
|
59
60
|
return /* @__PURE__ */ jsxs(
|
|
60
61
|
Button,
|
|
@@ -102,11 +103,12 @@ function MonthDayCell({
|
|
|
102
103
|
onDayOpen
|
|
103
104
|
}) {
|
|
104
105
|
const t = useT();
|
|
106
|
+
const locale = useLocale();
|
|
105
107
|
const inMonth = isSameMonth(day, anchor);
|
|
106
108
|
const today = isToday(day);
|
|
107
109
|
const visibleItems = items.slice(0, MAX_PILLS_PER_DAY);
|
|
108
110
|
const hiddenCount = items.length - visibleItems.length;
|
|
109
|
-
const dayLabel = fullDateLabel(day);
|
|
111
|
+
const dayLabel = fullDateLabel(locale, day);
|
|
110
112
|
return /* @__PURE__ */ jsxs(
|
|
111
113
|
"div",
|
|
112
114
|
{
|
|
@@ -164,11 +166,12 @@ function MonthDayCell({
|
|
|
164
166
|
}
|
|
165
167
|
function MonthGrid({ anchor, items, onItemClick, onDayOpen }) {
|
|
166
168
|
const t = useT();
|
|
169
|
+
const locale = useLocale();
|
|
167
170
|
const weeks = React.useMemo(() => buildWeeks(anchor), [anchor]);
|
|
168
171
|
const itemsByDay = React.useMemo(() => groupItemsByDay(items), [items]);
|
|
169
172
|
return /* @__PURE__ */ jsxs("div", { className: "relative flex h-full w-full flex-col overflow-hidden border border-border bg-background", children: [
|
|
170
173
|
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-full shrink-0 border-b border-border", children: weeks[0]?.map((day) => {
|
|
171
|
-
const label = day.toLocaleDateString(
|
|
174
|
+
const label = day.toLocaleDateString(locale, { weekday: "short" }).toLocaleUpperCase(locale);
|
|
172
175
|
return /* @__PURE__ */ jsx(
|
|
173
176
|
"div",
|
|
174
177
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/calendar/MonthGrid.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isSameMonth } from 'date-fns/isSameMonth'\nimport { isToday } from 'date-fns/isToday'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { getVisibleRange } from '../../lib/calendar/range'\nimport { eventDisplayTitle } from '../../lib/calendar/labels'\nimport type { CalendarItem, MonthGridProps } from './types'\n\nconst MAX_PILLS_PER_DAY = 2\nconst SOFT_TINT_ALPHA = '1A'\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatTime(date: Date): string {\n return date.toLocaleTimeString(
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isSameMonth } from 'date-fns/isSameMonth'\nimport { isToday } from 'date-fns/isToday'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { getVisibleRange } from '../../lib/calendar/range'\nimport { eventDisplayTitle } from '../../lib/calendar/labels'\nimport type { CalendarItem, MonthGridProps } from './types'\n\nconst MAX_PILLS_PER_DAY = 2\nconst SOFT_TINT_ALPHA = '1A'\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatTime(locale: string, date: Date): string {\n return date.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' })\n}\n\nfunction fullDateLabel(locale: string, date: Date): string {\n return date.toLocaleDateString(locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })\n}\n\nfunction isWeekend(date: Date): boolean {\n const weekday = date.getDay()\n return weekday === 0 || weekday === 6\n}\n\nfunction buildWeeks(anchor: Date): Date[][] {\n const range = getVisibleRange('month', anchor, 0)\n const weeks: Date[][] = []\n let cursor = range.from\n while (cursor.getTime() <= range.to.getTime()) {\n const week: Date[] = []\n for (let dayIndex = 0; dayIndex < 7; dayIndex += 1) {\n week.push(cursor)\n cursor = addDays(cursor, 1)\n }\n weeks.push(week)\n }\n return weeks\n}\n\nfunction groupItemsByDay(items: CalendarItem[]): Map<string, CalendarItem[]> {\n const sorted = [...items].sort((first, second) => first.start.getTime() - second.start.getTime())\n const byDay = new Map<string, CalendarItem[]>()\n for (const item of sorted) {\n const key = dayKeyOf(item.start)\n const bucket = byDay.get(key)\n if (bucket) bucket.push(item)\n else byDay.set(key, [item])\n }\n return byDay\n}\n\nfunction MonthPill({ item, onItemClick }: { item: CalendarItem; onItemClick: (item: CalendarItem) => void }) {\n const t = useT()\n const locale = useLocale()\n const canceled = item.status === 'canceled'\n const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))\n const timeLabel = item.allDay ? '' : ` \u00B7 ${formatTime(locale, item.start)} \u2013 ${formatTime(locale, item.end)}`\n const tintStyle = item.color\n ? { backgroundColor: `${item.color}${SOFT_TINT_ALPHA}`, color: item.color }\n : undefined\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-label={`${title}${timeLabel}`}\n onClick={(event) => {\n event.stopPropagation()\n onItemClick(item)\n }}\n className={cn(\n 'h-4 w-full min-w-0 justify-center gap-0 rounded-full p-0 pr-2',\n !item.color && 'bg-muted text-muted-foreground',\n canceled && 'opacity-60',\n )}\n style={tintStyle}\n >\n <span aria-hidden=\"true\" className=\"flex size-4 shrink-0 items-center justify-center\">\n <span\n className={cn('size-1.5 rounded-full', !item.color && 'bg-muted-foreground')}\n style={item.color ? { backgroundColor: item.color } : undefined}\n />\n </span>\n <span\n className={cn(\n 'min-w-0 truncate text-overline font-medium uppercase tracking-wide',\n canceled && 'line-through',\n )}\n >\n {title}\n </span>\n </Button>\n )\n}\n\nfunction MonthDayCell({\n day,\n anchor,\n items,\n onItemClick,\n onDayOpen,\n}: {\n day: Date\n anchor: Date\n items: CalendarItem[]\n onItemClick: (item: CalendarItem) => void\n onDayOpen: (date: Date) => void\n}) {\n const t = useT()\n const locale = useLocale()\n const inMonth = isSameMonth(day, anchor)\n const today = isToday(day)\n const visibleItems = items.slice(0, MAX_PILLS_PER_DAY)\n const hiddenCount = items.length - visibleItems.length\n const dayLabel = fullDateLabel(locale, day)\n return (\n <div\n className={cn(\n 'relative flex h-full min-w-0 flex-1 flex-col items-start gap-1 overflow-hidden border-r border-border p-2 text-left last:border-r-0',\n isWeekend(day) ? 'bg-muted/40' : 'bg-background',\n )}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-label={dayLabel}\n onClick={() => onDayOpen(day)}\n className=\"absolute inset-0 z-0 h-full w-full rounded-none p-0 hover:bg-muted/50\"\n >\n <span className=\"sr-only\">{dayLabel}</span>\n </Button>\n {today ? (\n <span className=\"pointer-events-none relative z-10 flex items-center justify-center rounded-full bg-primary px-1.5 py-0.5 text-xs font-semibold text-primary-foreground\">\n {day.getDate()}\n </span>\n ) : (\n <span className={cn('pointer-events-none relative z-10 text-xs font-medium', inMonth ? 'text-foreground' : 'text-muted-foreground')}>\n {day.getDate()}\n </span>\n )}\n <span className=\"relative z-20 hidden w-full flex-col gap-1 sm:flex\">\n {visibleItems.map((item) => (\n <MonthPill key={item.id} item={item} onItemClick={onItemClick} />\n ))}\n {hiddenCount > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto self-start p-0 text-overline font-medium text-muted-foreground hover:bg-transparent hover:underline\"\n onClick={(event) => {\n event.stopPropagation()\n onDayOpen(day)\n }}\n >\n {t('customers.calendar.grid.more', '+{count} more', { count: hiddenCount })}\n </Button>\n ) : null}\n </span>\n {items.length > 0 ? (\n <span aria-hidden=\"true\" className=\"pointer-events-none relative z-10 flex w-full flex-wrap items-center gap-0.5 sm:hidden\">\n {items.slice(0, 4).map((item) => (\n <span\n key={item.id}\n className={cn('size-1.5 rounded-full', !item.color && 'bg-muted-foreground')}\n style={item.color ? { backgroundColor: item.color } : undefined}\n />\n ))}\n {items.length > 4 ? (\n <span className=\"text-overline font-medium leading-none text-muted-foreground\">\n +{items.length - 4}\n </span>\n ) : null}\n </span>\n ) : null}\n </div>\n )\n}\n\nexport function MonthGrid({ anchor, items, onItemClick, onDayOpen }: MonthGridProps) {\n const t = useT()\n const locale = useLocale()\n const weeks = React.useMemo(() => buildWeeks(anchor), [anchor])\n const itemsByDay = React.useMemo(() => groupItemsByDay(items), [items])\n return (\n <div className=\"relative flex h-full w-full flex-col overflow-hidden border border-border bg-background\">\n <div className=\"flex h-9 w-full shrink-0 border-b border-border\">\n {weeks[0]?.map((day) => {\n const label = day.toLocaleDateString(locale, { weekday: 'short' }).toLocaleUpperCase(locale)\n return (\n <div\n key={dayKeyOf(day)}\n className=\"flex min-w-0 flex-1 items-center justify-center border-r border-border last:border-r-0 sm:justify-start sm:pl-2.5\"\n >\n <span className=\"truncate text-overline font-medium uppercase tracking-widest text-muted-foreground\">\n <span className=\"sm:hidden\">{label.charAt(0)}</span>\n <span className=\"hidden sm:inline\">{label}</span>\n </span>\n </div>\n )\n })}\n </div>\n {weeks.map((week) => (\n <div\n key={dayKeyOf(week[0])}\n className=\"flex w-full flex-1 border-b border-border last:border-b-0 min-h-14 sm:min-h-[86px]\"\n >\n {week.map((day) => (\n <MonthDayCell\n key={dayKeyOf(day)}\n day={day}\n anchor={anchor}\n items={itemsByDay.get(dayKeyOf(day)) ?? []}\n onItemClick={onItemClick}\n onDayOpen={onDayOpen}\n />\n ))}\n </div>\n ))}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.6964.1.36b364cfd8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -254,16 +254,16 @@
|
|
|
254
254
|
"zod": "^4.4.3"
|
|
255
255
|
},
|
|
256
256
|
"peerDependencies": {
|
|
257
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
258
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
259
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
257
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.6964.1.36b364cfd8",
|
|
258
|
+
"@open-mercato/shared": "0.6.8-develop.6964.1.36b364cfd8",
|
|
259
|
+
"@open-mercato/ui": "0.6.8-develop.6964.1.36b364cfd8",
|
|
260
260
|
"react": "^19.0.0",
|
|
261
261
|
"react-dom": "^19.0.0"
|
|
262
262
|
},
|
|
263
263
|
"devDependencies": {
|
|
264
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
265
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
266
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
264
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.6964.1.36b364cfd8",
|
|
265
|
+
"@open-mercato/shared": "0.6.8-develop.6964.1.36b364cfd8",
|
|
266
|
+
"@open-mercato/ui": "0.6.8-develop.6964.1.36b364cfd8",
|
|
267
267
|
"@testing-library/dom": "^10.4.1",
|
|
268
268
|
"@testing-library/jest-dom": "^7.0.0",
|
|
269
269
|
"@testing-library/react": "^16.3.1",
|
|
@@ -44,12 +44,12 @@ function formatUrlHost(location: string): string {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function formatTime(date: Date): string {
|
|
48
|
-
return date.toLocaleTimeString(
|
|
47
|
+
function formatTime(locale: string, date: Date): string {
|
|
48
|
+
return date.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' })
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function groupLabelOf(date: Date): string {
|
|
52
|
-
return date.toLocaleDateString(
|
|
51
|
+
function groupLabelOf(locale: string, date: Date): string {
|
|
52
|
+
return date.toLocaleDateString(locale, { weekday: 'long', month: 'short', day: 'numeric' })
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function deriveTypeLabel(interactionType: string): string {
|
|
@@ -94,7 +94,7 @@ function AgendaDayHeader({ date, count }: { date: Date; count: number }) {
|
|
|
94
94
|
: resolvedCount
|
|
95
95
|
return (
|
|
96
96
|
<div className="flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5">
|
|
97
|
-
<span className="text-sm font-semibold text-foreground">{groupLabelOf(date)}</span>
|
|
97
|
+
<span className="text-sm font-semibold text-foreground">{groupLabelOf(locale, date)}</span>
|
|
98
98
|
{todayMarker || tomorrowMarker ? (
|
|
99
99
|
<span className={cn('text-xs font-medium', todayMarker ? 'text-foreground' : 'text-muted-foreground')}>
|
|
100
100
|
{`· ${todayMarker ? t('customers.calendar.agenda.today', 'Today') : t('customers.calendar.agenda.tomorrow', 'Tomorrow')}`}
|
|
@@ -116,6 +116,7 @@ function AgendaRow({
|
|
|
116
116
|
onItemClick: (item: CalendarItem) => void
|
|
117
117
|
}) {
|
|
118
118
|
const t = useT()
|
|
119
|
+
const locale = useLocale()
|
|
119
120
|
const canceled = item.status === 'canceled'
|
|
120
121
|
const done = item.status === 'done'
|
|
121
122
|
const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))
|
|
@@ -131,8 +132,8 @@ function AgendaRow({
|
|
|
131
132
|
: item.locationKind === 'url' && item.location
|
|
132
133
|
? formatUrlHost(item.location)
|
|
133
134
|
: item.location
|
|
134
|
-
const startLabel = item.allDay ? t('customers.calendar.grid.allDay', 'All day') : formatTime(item.start)
|
|
135
|
-
const endLabel = item.allDay ? null : formatTime(item.end)
|
|
135
|
+
const startLabel = item.allDay ? t('customers.calendar.grid.allDay', 'All day') : formatTime(locale, item.start)
|
|
136
|
+
const endLabel = item.allDay ? null : formatTime(locale, item.end)
|
|
136
137
|
const ariaTime = item.allDay ? startLabel : `${startLabel} – ${endLabel}`
|
|
137
138
|
return (
|
|
138
139
|
<Button
|
|
@@ -5,7 +5,7 @@ import { addDays } from 'date-fns/addDays'
|
|
|
5
5
|
import { format } from 'date-fns/format'
|
|
6
6
|
import { isSameMonth } from 'date-fns/isSameMonth'
|
|
7
7
|
import { isToday } from 'date-fns/isToday'
|
|
8
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
8
|
+
import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
|
|
9
9
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
10
10
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
11
11
|
import { getVisibleRange } from '../../lib/calendar/range'
|
|
@@ -19,12 +19,12 @@ function dayKeyOf(date: Date): string {
|
|
|
19
19
|
return format(date, 'yyyy-MM-dd')
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function formatTime(date: Date): string {
|
|
23
|
-
return date.toLocaleTimeString(
|
|
22
|
+
function formatTime(locale: string, date: Date): string {
|
|
23
|
+
return date.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' })
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function fullDateLabel(date: Date): string {
|
|
27
|
-
return date.toLocaleDateString(
|
|
26
|
+
function fullDateLabel(locale: string, date: Date): string {
|
|
27
|
+
return date.toLocaleDateString(locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function isWeekend(date: Date): boolean {
|
|
@@ -61,9 +61,10 @@ function groupItemsByDay(items: CalendarItem[]): Map<string, CalendarItem[]> {
|
|
|
61
61
|
|
|
62
62
|
function MonthPill({ item, onItemClick }: { item: CalendarItem; onItemClick: (item: CalendarItem) => void }) {
|
|
63
63
|
const t = useT()
|
|
64
|
+
const locale = useLocale()
|
|
64
65
|
const canceled = item.status === 'canceled'
|
|
65
66
|
const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))
|
|
66
|
-
const timeLabel = item.allDay ? '' : ` · ${formatTime(item.start)} – ${formatTime(item.end)}`
|
|
67
|
+
const timeLabel = item.allDay ? '' : ` · ${formatTime(locale, item.start)} – ${formatTime(locale, item.end)}`
|
|
67
68
|
const tintStyle = item.color
|
|
68
69
|
? { backgroundColor: `${item.color}${SOFT_TINT_ALPHA}`, color: item.color }
|
|
69
70
|
: undefined
|
|
@@ -115,11 +116,12 @@ function MonthDayCell({
|
|
|
115
116
|
onDayOpen: (date: Date) => void
|
|
116
117
|
}) {
|
|
117
118
|
const t = useT()
|
|
119
|
+
const locale = useLocale()
|
|
118
120
|
const inMonth = isSameMonth(day, anchor)
|
|
119
121
|
const today = isToday(day)
|
|
120
122
|
const visibleItems = items.slice(0, MAX_PILLS_PER_DAY)
|
|
121
123
|
const hiddenCount = items.length - visibleItems.length
|
|
122
|
-
const dayLabel = fullDateLabel(day)
|
|
124
|
+
const dayLabel = fullDateLabel(locale, day)
|
|
123
125
|
return (
|
|
124
126
|
<div
|
|
125
127
|
className={cn(
|
|
@@ -186,13 +188,14 @@ function MonthDayCell({
|
|
|
186
188
|
|
|
187
189
|
export function MonthGrid({ anchor, items, onItemClick, onDayOpen }: MonthGridProps) {
|
|
188
190
|
const t = useT()
|
|
191
|
+
const locale = useLocale()
|
|
189
192
|
const weeks = React.useMemo(() => buildWeeks(anchor), [anchor])
|
|
190
193
|
const itemsByDay = React.useMemo(() => groupItemsByDay(items), [items])
|
|
191
194
|
return (
|
|
192
195
|
<div className="relative flex h-full w-full flex-col overflow-hidden border border-border bg-background">
|
|
193
196
|
<div className="flex h-9 w-full shrink-0 border-b border-border">
|
|
194
197
|
{weeks[0]?.map((day) => {
|
|
195
|
-
const label = day.toLocaleDateString(
|
|
198
|
+
const label = day.toLocaleDateString(locale, { weekday: 'short' }).toLocaleUpperCase(locale)
|
|
196
199
|
return (
|
|
197
200
|
<div
|
|
198
201
|
key={dayKeyOf(day)}
|