@open-mercato/core 0.6.6-develop.6519.1.bdc91fb755 → 0.6.6-develop.6520.1.55df6df792
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/customers/backend/calendar/page.js +9 -1
- package/dist/modules/customers/backend/calendar/page.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +0 -11
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -235
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +5 -5
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +1 -11
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/TimeGrid.js +162 -176
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/LocationField.js +61 -14
- package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +10 -5
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PriorityField.js +85 -0
- package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +8 -3
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js +225 -0
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +12 -7
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/inputs.js +68 -57
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/lookups.js +82 -2
- package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
- package/dist/modules/customers/components/calendar/useCalendarItems.js +6 -1
- package/dist/modules/customers/components/calendar/useCalendarItems.js.map +2 -2
- package/dist/modules/customers/data/validators.js +3 -1
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +53 -22
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +1 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/backend/calendar/page.tsx +8 -1
- package/src/modules/customers/components/calendar/AgendaList.tsx +0 -10
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +444 -261
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +12 -5
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +6 -6
- package/src/modules/customers/components/calendar/MonthGrid.tsx +0 -11
- package/src/modules/customers/components/calendar/TimeGrid.tsx +1 -24
- package/src/modules/customers/components/calendar/editor/LocationField.tsx +64 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +11 -4
- package/src/modules/customers/components/calendar/editor/PriorityField.tsx +90 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +8 -3
- package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +264 -0
- package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +14 -6
- package/src/modules/customers/components/calendar/editor/inputs.tsx +91 -50
- package/src/modules/customers/components/calendar/editor/lookups.ts +115 -2
- package/src/modules/customers/components/calendar/useCalendarItems.ts +8 -1
- package/src/modules/customers/data/validators.ts +3 -1
- package/src/modules/customers/i18n/de.json +12 -2
- package/src/modules/customers/i18n/en.json +12 -2
- package/src/modules/customers/i18n/es.json +12 -2
- package/src/modules/customers/i18n/pl.json +12 -2
- package/src/modules/customers/lib/calendar/editorPayload.ts +94 -37
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +1 -1
- package/src/modules/entities/i18n/de.json +120 -0
- package/src/modules/entities/i18n/en.json +120 -0
- package/src/modules/entities/i18n/es.json +120 -0
- package/src/modules/entities/i18n/pl.json +120 -0
- package/dist/modules/customers/components/calendar/editor/CategoryField.js +0 -75
- package/dist/modules/customers/components/calendar/editor/CategoryField.js.map +0 -7
- package/src/modules/customers/components/calendar/editor/CategoryField.tsx +0 -79
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { MapPin, Phone, Plus } from "lucide-react";
|
|
4
5
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
6
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
6
7
|
import { Field } from "./inputs.js";
|
|
8
|
+
import { fetchPeoplePhones } from "./lookups.js";
|
|
9
|
+
function PhoneSuggestions({
|
|
10
|
+
contactIds,
|
|
11
|
+
onPick
|
|
12
|
+
}) {
|
|
13
|
+
const t = useT();
|
|
14
|
+
const [contacts, setContacts] = React.useState([]);
|
|
15
|
+
const idsKey = contactIds.join(",");
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
if (contactIds.length === 0) {
|
|
18
|
+
setContacts([]);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
fetchPeoplePhones(contactIds, controller.signal).then((found) => {
|
|
24
|
+
if (!cancelled) setContacts(found);
|
|
25
|
+
});
|
|
26
|
+
return () => {
|
|
27
|
+
cancelled = true;
|
|
28
|
+
controller.abort();
|
|
29
|
+
};
|
|
30
|
+
}, [idsKey]);
|
|
31
|
+
if (contacts.length === 0) return null;
|
|
32
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 pt-1.5", children: contacts.map((contact) => /* @__PURE__ */ jsxs(
|
|
33
|
+
"button",
|
|
34
|
+
{
|
|
35
|
+
type: "button",
|
|
36
|
+
onClick: () => onPick(contact.phone),
|
|
37
|
+
title: t("customers.calendar.editor.phoneSuggestion.use", "Use {name}\u2019s number", { name: contact.name }),
|
|
38
|
+
className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ jsx(Plus, { "aria-hidden": true, className: "size-3 shrink-0" }),
|
|
41
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0", children: t("customers.calendar.editor.phoneSuggestion.add", "Add") }),
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: contact.name }),
|
|
43
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-foreground", children: contact.phone })
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
contact.id
|
|
47
|
+
)) });
|
|
48
|
+
}
|
|
7
49
|
function LocationField({
|
|
8
50
|
variant,
|
|
9
51
|
value,
|
|
10
|
-
onChange
|
|
52
|
+
onChange,
|
|
53
|
+
phoneContactIds
|
|
11
54
|
}) {
|
|
12
55
|
const t = useT();
|
|
13
56
|
const label = variant === "phoneLink" ? t("customers.calendar.editor.phoneLink", "Phone / link") : t("customers.calendar.editor.location", "Location");
|
|
14
57
|
const placeholder = variant === "phoneLink" ? t("customers.calendar.editor.phoneLinkPlaceholder", "Add a phone number or link\u2026") : t("customers.calendar.editor.locationPlaceholder", "Add a location or link\u2026");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
58
|
+
const showSuggestions = variant === "phoneLink" && value.trim().length === 0;
|
|
59
|
+
return /* @__PURE__ */ jsxs(Field, { label, children: [
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
Input,
|
|
62
|
+
{
|
|
63
|
+
type: "text",
|
|
64
|
+
value,
|
|
65
|
+
onChange: (event) => onChange(event.target.value),
|
|
66
|
+
placeholder,
|
|
67
|
+
"aria-label": label,
|
|
68
|
+
leftIcon: variant === "phoneLink" ? /* @__PURE__ */ jsx(Phone, {}) : /* @__PURE__ */ jsx(MapPin, {})
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
showSuggestions ? /* @__PURE__ */ jsx(PhoneSuggestions, { contactIds: phoneContactIds ?? [], onPick: (phone) => onChange(phone) }) : null
|
|
72
|
+
] });
|
|
26
73
|
}
|
|
27
74
|
export {
|
|
28
75
|
LocationField
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/LocationField.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { MapPin } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Field } from './inputs'\n\nexport function LocationField({\n variant,\n value,\n onChange,\n}: {\n variant: 'location' | 'phoneLink'\n value: string\n onChange(next: string): void\n}) {\n const t = useT()\n const label = variant === 'phoneLink'\n ? t('customers.calendar.editor.phoneLink', 'Phone / link')\n : t('customers.calendar.editor.location', 'Location')\n const placeholder = variant === 'phoneLink'\n ? t('customers.calendar.editor.phoneLinkPlaceholder', 'Add a phone number or link\u2026')\n : t('customers.calendar.editor.locationPlaceholder', 'Add a location or link\u2026')\n return (\n <Field label={label}>\n <Input\n type=\"text\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n placeholder={placeholder}\n aria-label={label}\n leftIcon={<MapPin />}\n />\n </Field>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { MapPin, Phone, Plus } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Field } from './inputs'\nimport { fetchPeoplePhones, type ContactPhone } from './lookups'\n\n// Below the Call \"Phone / link\" field: when a linked contact has a primary\n// phone and the field is still empty, offer it as a one-click suggestion chip\n// showing the actual number (#3552). Companies/staff have no phone and never\n// appear. The chips vanish once the field is filled.\nfunction PhoneSuggestions({\n contactIds,\n onPick,\n}: {\n contactIds: string[]\n onPick(phone: string): void\n}) {\n const t = useT()\n const [contacts, setContacts] = React.useState<ContactPhone[]>([])\n const idsKey = contactIds.join(',')\n\n React.useEffect(() => {\n if (contactIds.length === 0) {\n setContacts([])\n return\n }\n const controller = new AbortController()\n let cancelled = false\n fetchPeoplePhones(contactIds, controller.signal).then((found) => {\n if (!cancelled) setContacts(found)\n })\n return () => {\n cancelled = true\n controller.abort()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- idsKey captures the ids\n }, [idsKey])\n\n if (contacts.length === 0) return null\n return (\n <div className=\"flex flex-wrap items-center gap-1.5 pt-1.5\">\n {contacts.map((contact) => (\n <button\n key={contact.id}\n type=\"button\"\n onClick={() => onPick(contact.phone)}\n title={t('customers.calendar.editor.phoneSuggestion.use', 'Use {name}\u2019s number', { name: contact.name })}\n className=\"inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Plus aria-hidden className=\"size-3 shrink-0\" />\n <span className=\"shrink-0\">{t('customers.calendar.editor.phoneSuggestion.add', 'Add')}</span>\n <span className=\"truncate\">{contact.name}</span>\n <span className=\"shrink-0 tabular-nums text-foreground\">{contact.phone}</span>\n </button>\n ))}\n </div>\n )\n}\n\nexport function LocationField({\n variant,\n value,\n onChange,\n phoneContactIds,\n}: {\n variant: 'location' | 'phoneLink'\n value: string\n onChange(next: string): void\n /** Person ids whose phone can be suggested (Call variant only). */\n phoneContactIds?: string[]\n}) {\n const t = useT()\n const label = variant === 'phoneLink'\n ? t('customers.calendar.editor.phoneLink', 'Phone / link')\n : t('customers.calendar.editor.location', 'Location')\n const placeholder = variant === 'phoneLink'\n ? t('customers.calendar.editor.phoneLinkPlaceholder', 'Add a phone number or link\u2026')\n : t('customers.calendar.editor.locationPlaceholder', 'Add a location or link\u2026')\n // Suggestions only help while the field is empty \u2014 once filled they'd be noise.\n const showSuggestions = variant === 'phoneLink' && value.trim().length === 0\n return (\n <Field label={label}>\n <Input\n type=\"text\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n placeholder={placeholder}\n aria-label={label}\n leftIcon={variant === 'phoneLink' ? <Phone /> : <MapPin />}\n />\n {showSuggestions ? (\n <PhoneSuggestions contactIds={phoneContactIds ?? []} onPick={(phone) => onChange(phone)} />\n ) : null}\n </Field>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA6CQ,SAOE,KAPF;AA3CR,YAAY,WAAW;AACvB,SAAS,QAAQ,OAAO,YAAY;AACpC,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,yBAA4C;AAMrD,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AACF,GAGG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAyB,CAAC,CAAC;AACjE,QAAM,SAAS,WAAW,KAAK,GAAG;AAElC,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW,WAAW,GAAG;AAC3B,kBAAY,CAAC,CAAC;AACd;AAAA,IACF;AACA,UAAM,aAAa,IAAI,gBAAgB;AACvC,QAAI,YAAY;AAChB,sBAAkB,YAAY,WAAW,MAAM,EAAE,KAAK,CAAC,UAAU;AAC/D,UAAI,CAAC,UAAW,aAAY,KAAK;AAAA,IACnC,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AACZ,iBAAW,MAAM;AAAA,IACnB;AAAA,EAEF,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,SACE,oBAAC,SAAI,WAAU,8CACZ,mBAAS,IAAI,CAAC,YACb;AAAA,IAAC;AAAA;AAAA,MAEC,MAAK;AAAA,MACL,SAAS,MAAM,OAAO,QAAQ,KAAK;AAAA,MACnC,OAAO,EAAE,iDAAiD,4BAAuB,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,MACvG,WAAU;AAAA,MAEV;AAAA,4BAAC,QAAK,eAAW,MAAC,WAAU,mBAAkB;AAAA,QAC9C,oBAAC,UAAK,WAAU,YAAY,YAAE,iDAAiD,KAAK,GAAE;AAAA,QACtF,oBAAC,UAAK,WAAU,YAAY,kBAAQ,MAAK;AAAA,QACzC,oBAAC,UAAK,WAAU,yCAAyC,kBAAQ,OAAM;AAAA;AAAA;AAAA,IATlE,QAAQ;AAAA,EAUf,CACD,GACH;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,QAAQ,YAAY,cACtB,EAAE,uCAAuC,cAAc,IACvD,EAAE,sCAAsC,UAAU;AACtD,QAAM,cAAc,YAAY,cAC5B,EAAE,kDAAkD,kCAA6B,IACjF,EAAE,iDAAiD,8BAAyB;AAEhF,QAAM,kBAAkB,YAAY,eAAe,MAAM,KAAK,EAAE,WAAW;AAC3E,SACE,qBAAC,SAAM,OACL;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD;AAAA,QACA,cAAY;AAAA,QACZ,UAAU,YAAY,cAAc,oBAAC,SAAM,IAAK,oBAAC,UAAO;AAAA;AAAA,IAC1D;AAAA,IACC,kBACC,oBAAC,oBAAiB,YAAY,mBAAmB,CAAC,GAAG,QAAQ,CAAC,UAAU,SAAS,KAAK,GAAG,IACvF;AAAA,KACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,20 +8,23 @@ import { Button } from "@open-mercato/ui/primitives/button";
|
|
|
8
8
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
9
9
|
import { composeAccessibleName } from "../../../lib/calendar/labels.js";
|
|
10
10
|
import { searchPeopleOptions } from "./lookups.js";
|
|
11
|
-
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge } from "./inputs.js";
|
|
11
|
+
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from "./inputs.js";
|
|
12
12
|
function PeopleField({
|
|
13
13
|
mode,
|
|
14
14
|
placeholder,
|
|
15
15
|
ariaLabel,
|
|
16
16
|
value,
|
|
17
17
|
onChange,
|
|
18
|
-
includeCustomers
|
|
18
|
+
includeCustomers,
|
|
19
|
+
includeStaff = true
|
|
19
20
|
}) {
|
|
20
21
|
const t = useT();
|
|
21
22
|
const [query, setQuery] = React.useState("");
|
|
22
23
|
const [open, setOpen] = React.useState(false);
|
|
23
24
|
const [options, setOptions] = React.useState([]);
|
|
24
25
|
const [loading, setLoading] = React.useState(false);
|
|
26
|
+
const close = React.useCallback(() => setOpen(false), []);
|
|
27
|
+
const rootRef = useDropdownDismiss(open, close);
|
|
25
28
|
React.useEffect(() => {
|
|
26
29
|
if (!open) return;
|
|
27
30
|
const controller = new AbortController();
|
|
@@ -29,7 +32,7 @@ function PeopleField({
|
|
|
29
32
|
const timer = window.setTimeout(async () => {
|
|
30
33
|
setLoading(true);
|
|
31
34
|
try {
|
|
32
|
-
const results = await searchPeopleOptions(query.trim(), { includeCustomers, signal: controller.signal });
|
|
35
|
+
const results = await searchPeopleOptions(query.trim(), { includeCustomers, includeStaff, signal: controller.signal });
|
|
33
36
|
if (cancelled) return;
|
|
34
37
|
setOptions(results);
|
|
35
38
|
} catch {
|
|
@@ -43,13 +46,14 @@ function PeopleField({
|
|
|
43
46
|
controller.abort();
|
|
44
47
|
window.clearTimeout(timer);
|
|
45
48
|
};
|
|
46
|
-
}, [open, query, includeCustomers]);
|
|
49
|
+
}, [open, query, includeCustomers, includeStaff]);
|
|
47
50
|
const selectedIds = new Set(value.map((participant) => participant.userId));
|
|
48
51
|
const visibleOptions = options.filter((option) => !selectedIds.has(option.userId));
|
|
49
52
|
const customerBadge = /* @__PURE__ */ jsx(UppercaseBadge, { className: "bg-status-info-bg text-status-info-text", children: t("customers.calendar.editor.customerBadge", "Customer") });
|
|
50
53
|
return /* @__PURE__ */ jsxs(
|
|
51
54
|
"div",
|
|
52
55
|
{
|
|
56
|
+
ref: rootRef,
|
|
53
57
|
className: "relative w-full",
|
|
54
58
|
onBlur: (event) => {
|
|
55
59
|
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
@@ -60,7 +64,7 @@ function PeopleField({
|
|
|
60
64
|
{
|
|
61
65
|
className: cn(
|
|
62
66
|
"flex w-full flex-wrap content-center items-center gap-2 rounded-md bg-background px-2.5 py-2",
|
|
63
|
-
|
|
67
|
+
"min-h-14",
|
|
64
68
|
CONTROL_BORDER
|
|
65
69
|
),
|
|
66
70
|
children: [
|
|
@@ -107,6 +111,7 @@ function PeopleField({
|
|
|
107
111
|
option.email,
|
|
108
112
|
option.isCustomer ? t("customers.calendar.editor.customerBadge", "Customer") : null
|
|
109
113
|
]),
|
|
114
|
+
title: composeAccessibleName([option.name, option.email]),
|
|
110
115
|
onClick: () => {
|
|
111
116
|
const participant = {
|
|
112
117
|
userId: option.userId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/PeopleField.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorParticipant } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport { searchPeopleOptions, type PersonOption } from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge } from './inputs'\n\nexport function PeopleField({\n mode,\n placeholder,\n ariaLabel,\n value,\n onChange,\n includeCustomers,\n}: {\n mode: 'multi' | 'single'\n placeholder: string\n ariaLabel: string\n value: EditorParticipant[]\n onChange(next: EditorParticipant[]): void\n includeCustomers: boolean\n}) {\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [options, setOptions] = React.useState<PersonOption[]>([])\n const [loading, setLoading] = React.useState(false)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await searchPeopleOptions(query.trim(), { includeCustomers, signal: controller.signal })\n if (cancelled) return\n setOptions(results)\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 250)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query, includeCustomers])\n\n const selectedIds = new Set(value.map((participant) => participant.userId))\n const visibleOptions = options.filter((option) => !selectedIds.has(option.userId))\n\n const customerBadge = (\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.customerBadge', 'Customer')}\n </UppercaseBadge>\n )\n\n return (\n <div\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex w-full flex-wrap content-center items-center gap-2 rounded-md bg-background px-2.5 py-2',\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorParticipant } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport { searchPeopleOptions, type PersonOption } from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from './inputs'\n\nexport function PeopleField({\n mode,\n placeholder,\n ariaLabel,\n value,\n onChange,\n includeCustomers,\n includeStaff = true,\n}: {\n mode: 'multi' | 'single'\n placeholder: string\n ariaLabel: string\n value: EditorParticipant[]\n onChange(next: EditorParticipant[]): void\n includeCustomers: boolean\n /** Pass false when the staff module is not loaded \u2014 customer-only options. */\n includeStaff?: boolean\n}) {\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [open, setOpen] = React.useState(false)\n const [options, setOptions] = React.useState<PersonOption[]>([])\n const [loading, setLoading] = React.useState(false)\n const close = React.useCallback(() => setOpen(false), [])\n const rootRef = useDropdownDismiss(open, close)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const results = await searchPeopleOptions(query.trim(), { includeCustomers, includeStaff, signal: controller.signal })\n if (cancelled) return\n setOptions(results)\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 250)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query, includeCustomers, includeStaff])\n\n const selectedIds = new Set(value.map((participant) => participant.userId))\n const visibleOptions = options.filter((option) => !selectedIds.has(option.userId))\n\n const customerBadge = (\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.customerBadge', 'Customer')}\n </UppercaseBadge>\n )\n\n return (\n <div\n ref={rootRef}\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex w-full flex-wrap content-center items-center gap-2 rounded-md bg-background px-2.5 py-2',\n 'min-h-14',\n CONTROL_BORDER,\n )}\n >\n {value.map((participant) => (\n <PersonChip\n key={participant.userId}\n name={participant.name}\n badge={participant.isCustomer ? customerBadge : undefined}\n onRemove={() => onChange(value.filter((entry) => entry.userId !== participant.userId))}\n removeLabel={t('customers.calendar.editor.removePerson', 'Remove {name}', { name: participant.name })}\n />\n ))}\n <Input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onFocus={() => setOpen(true)}\n placeholder={placeholder}\n aria-label={ariaLabel}\n role=\"combobox\"\n aria-expanded={open}\n className=\"min-w-36 flex-1 border-0 bg-transparent px-0 shadow-none hover:bg-transparent focus-within:border-transparent focus-within:shadow-none\"\n inputClassName=\"text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n {open ? (\n <div role=\"listbox\" aria-label={ariaLabel} className={DROPDOWN_PANEL_CLASS}>\n {loading ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.searching', 'Searching\u2026')}\n </p>\n ) : null}\n {!loading && visibleOptions.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.noResults', 'No results')}\n </p>\n ) : null}\n {!loading\n ? visibleOptions.map((option) => (\n <Button\n key={`${option.isCustomer ? 'customer' : 'staff'}:${option.userId}`}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={false}\n aria-label={composeAccessibleName([\n option.name,\n option.email,\n option.isCustomer ? t('customers.calendar.editor.customerBadge', 'Customer') : null,\n ])}\n title={composeAccessibleName([option.name, option.email])}\n onClick={() => {\n const participant: EditorParticipant = {\n userId: option.userId,\n name: option.name,\n email: option.email ?? undefined,\n isCustomer: option.isCustomer,\n }\n onChange(mode === 'single' ? [participant] : [...value, participant])\n setQuery('')\n if (mode === 'single') setOpen(false)\n }}\n className=\"h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground\"\n >\n <Avatar size=\"xs\" label={option.name} />\n <span className=\"min-w-0 flex-1 truncate\">\n {option.name}\n {option.email ? <span className=\"ml-1.5 text-xs text-muted-foreground\">{option.email}</span> : null}\n </span>\n {option.isCustomer ? customerBadge : null}\n </Button>\n ))\n : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Check, ChevronDown, ChevronsDown, ChevronsUp, Equal } from "lucide-react";
|
|
5
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
6
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
7
|
+
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, useDropdownDismiss } from "./inputs.js";
|
|
8
|
+
const PRIORITY_META = {
|
|
9
|
+
low: { Icon: ChevronsDown, color: "text-status-info-text" },
|
|
10
|
+
medium: { Icon: Equal, color: "text-muted-foreground" },
|
|
11
|
+
high: { Icon: ChevronsUp, color: "text-status-error-text" }
|
|
12
|
+
};
|
|
13
|
+
const PRIORITY_ORDER = ["high", "medium", "low"];
|
|
14
|
+
function PriorityField({
|
|
15
|
+
value,
|
|
16
|
+
labels,
|
|
17
|
+
ariaLabel,
|
|
18
|
+
onChange
|
|
19
|
+
}) {
|
|
20
|
+
const [open, setOpen] = React.useState(false);
|
|
21
|
+
const close = React.useCallback(() => setOpen(false), []);
|
|
22
|
+
const rootRef = useDropdownDismiss(open, close);
|
|
23
|
+
const selected = PRIORITY_META[value];
|
|
24
|
+
const SelectedIcon = selected.Icon;
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
"div",
|
|
27
|
+
{
|
|
28
|
+
ref: rootRef,
|
|
29
|
+
className: "relative w-full",
|
|
30
|
+
onBlur: (event) => {
|
|
31
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
32
|
+
},
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsxs(
|
|
35
|
+
Button,
|
|
36
|
+
{
|
|
37
|
+
type: "button",
|
|
38
|
+
variant: "outline",
|
|
39
|
+
"aria-haspopup": "listbox",
|
|
40
|
+
"aria-expanded": open,
|
|
41
|
+
"aria-label": ariaLabel,
|
|
42
|
+
onClick: () => setOpen((previous) => !previous),
|
|
43
|
+
className: cn("h-9 w-full justify-between bg-background px-3 font-normal shadow-none", CONTROL_BORDER),
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
46
|
+
/* @__PURE__ */ jsx(SelectedIcon, { "aria-hidden": true, className: cn("size-4 shrink-0", selected.color) }),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm text-foreground", children: labels[value] })
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-4 shrink-0 opacity-60" })
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
open ? /* @__PURE__ */ jsx("div", { role: "listbox", "aria-label": ariaLabel, className: DROPDOWN_PANEL_CLASS, children: PRIORITY_ORDER.map((priority) => {
|
|
54
|
+
const meta = PRIORITY_META[priority];
|
|
55
|
+
const Icon = meta.Icon;
|
|
56
|
+
const active = priority === value;
|
|
57
|
+
return /* @__PURE__ */ jsxs(
|
|
58
|
+
Button,
|
|
59
|
+
{
|
|
60
|
+
type: "button",
|
|
61
|
+
variant: "ghost",
|
|
62
|
+
role: "option",
|
|
63
|
+
"aria-selected": active,
|
|
64
|
+
onClick: () => {
|
|
65
|
+
onChange(priority);
|
|
66
|
+
setOpen(false);
|
|
67
|
+
},
|
|
68
|
+
className: cn("h-auto w-full justify-start gap-2 px-2 py-1.5 text-left text-sm font-normal", active && "bg-muted"),
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: cn("size-4 shrink-0", meta.color) }),
|
|
71
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-foreground", children: labels[priority] }),
|
|
72
|
+
/* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: cn("size-4 shrink-0", active ? "opacity-100" : "opacity-0") })
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
priority
|
|
76
|
+
);
|
|
77
|
+
}) }) : null
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
PriorityField
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=PriorityField.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/PriorityField.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Check, ChevronDown, ChevronsDown, ChevronsUp, Equal } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport type { EditorPriority } from '../../../lib/calendar/editorPayload'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, useDropdownDismiss } from './inputs'\n\n// Jira/Linear-style priority glyphs: filled double chevrons for the extremes,\n// an equals bar for the middle. Colours stay on DS status tokens (no amber):\n// blue = low urgency, muted = medium, red = high.\nconst PRIORITY_META: Record<EditorPriority, { Icon: React.ComponentType<{ className?: string }>; color: string }> = {\n low: { Icon: ChevronsDown, color: 'text-status-info-text' },\n medium: { Icon: Equal, color: 'text-muted-foreground' },\n high: { Icon: ChevronsUp, color: 'text-status-error-text' },\n}\n\nconst PRIORITY_ORDER: EditorPriority[] = ['high', 'medium', 'low']\n\nexport function PriorityField({\n value,\n labels,\n ariaLabel,\n onChange,\n}: {\n value: EditorPriority\n labels: Record<EditorPriority, string>\n ariaLabel: string\n onChange(next: EditorPriority): void\n}) {\n const [open, setOpen] = React.useState(false)\n const close = React.useCallback(() => setOpen(false), [])\n const rootRef = useDropdownDismiss(open, close)\n const selected = PRIORITY_META[value]\n const SelectedIcon = selected.Icon\n\n return (\n <div\n ref={rootRef}\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => setOpen((previous) => !previous)}\n className={cn('h-9 w-full justify-between bg-background px-3 font-normal shadow-none', CONTROL_BORDER)}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n <SelectedIcon aria-hidden className={cn('size-4 shrink-0', selected.color)} />\n <span className=\"truncate text-sm text-foreground\">{labels[value]}</span>\n </span>\n <ChevronDown aria-hidden className=\"size-4 shrink-0 opacity-60\" />\n </Button>\n {open ? (\n <div role=\"listbox\" aria-label={ariaLabel} className={DROPDOWN_PANEL_CLASS}>\n {PRIORITY_ORDER.map((priority) => {\n const meta = PRIORITY_META[priority]\n const Icon = meta.Icon\n const active = priority === value\n return (\n <Button\n key={priority}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={active}\n onClick={() => {\n onChange(priority)\n setOpen(false)\n }}\n className={cn('h-auto w-full justify-start gap-2 px-2 py-1.5 text-left text-sm font-normal', active && 'bg-muted')}\n >\n <Icon aria-hidden className={cn('size-4 shrink-0', meta.color)} />\n <span className=\"min-w-0 flex-1 truncate text-foreground\">{labels[priority]}</span>\n <Check aria-hidden className={cn('size-4 shrink-0', active ? 'opacity-100' : 'opacity-0')} />\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAsDQ,SACE,KADF;AApDR,YAAY,WAAW;AACvB,SAAS,OAAO,aAAa,cAAc,YAAY,aAAa;AACpE,SAAS,UAAU;AACnB,SAAS,cAAc;AAEvB,SAAS,gBAAgB,sBAAsB,0BAA0B;AAKzE,MAAM,gBAA8G;AAAA,EAClH,KAAK,EAAE,MAAM,cAAc,OAAO,wBAAwB;AAAA,EAC1D,QAAQ,EAAE,MAAM,OAAO,OAAO,wBAAwB;AAAA,EACtD,MAAM,EAAE,MAAM,YAAY,OAAO,yBAAyB;AAC5D;AAEA,MAAM,iBAAmC,CAAC,QAAQ,UAAU,KAAK;AAE1D,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,QAAQ,MAAM,YAAY,MAAM,QAAQ,KAAK,GAAG,CAAC,CAAC;AACxD,QAAM,UAAU,mBAAmB,MAAM,KAAK;AAC9C,QAAM,WAAW,cAAc,KAAK;AACpC,QAAM,eAAe,SAAS;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,QAAQ,CAAC,UAAU;AACjB,YAAI,CAAC,MAAM,cAAc,SAAS,MAAM,aAA4B,EAAG,SAAQ,KAAK;AAAA,MACtF;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,iBAAc;AAAA,YACd,iBAAe;AAAA,YACf,cAAY;AAAA,YACZ,SAAS,MAAM,QAAQ,CAAC,aAAa,CAAC,QAAQ;AAAA,YAC9C,WAAW,GAAG,yEAAyE,cAAc;AAAA,YAErG;AAAA,mCAAC,UAAK,WAAU,mCACd;AAAA,oCAAC,gBAAa,eAAW,MAAC,WAAW,GAAG,mBAAmB,SAAS,KAAK,GAAG;AAAA,gBAC5E,oBAAC,UAAK,WAAU,oCAAoC,iBAAO,KAAK,GAAE;AAAA,iBACpE;AAAA,cACA,oBAAC,eAAY,eAAW,MAAC,WAAU,8BAA6B;AAAA;AAAA;AAAA,QAClE;AAAA,QACC,OACC,oBAAC,SAAI,MAAK,WAAU,cAAY,WAAW,WAAW,sBACnD,yBAAe,IAAI,CAAC,aAAa;AAChC,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM,OAAO,KAAK;AAClB,gBAAM,SAAS,aAAa;AAC5B,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,iBAAe;AAAA,cACf,SAAS,MAAM;AACb,yBAAS,QAAQ;AACjB,wBAAQ,KAAK;AAAA,cACf;AAAA,cACA,WAAW,GAAG,+EAA+E,UAAU,UAAU;AAAA,cAEjH;AAAA,oCAAC,QAAK,eAAW,MAAC,WAAW,GAAG,mBAAmB,KAAK,KAAK,GAAG;AAAA,gBAChE,oBAAC,UAAK,WAAU,2CAA2C,iBAAO,QAAQ,GAAE;AAAA,gBAC5E,oBAAC,SAAM,eAAW,MAAC,WAAW,GAAG,mBAAmB,SAAS,gBAAgB,WAAW,GAAG;AAAA;AAAA;AAAA,YAbtF;AAAA,UAcP;AAAA,QAEJ,CAAC,GACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
fetchDealsForEntity,
|
|
13
13
|
searchRelatedEntities
|
|
14
14
|
} from "./lookups.js";
|
|
15
|
-
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge } from "./inputs.js";
|
|
15
|
+
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from "./inputs.js";
|
|
16
16
|
const OPTION_ROW_CLASS = "h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground";
|
|
17
17
|
function RelatedToField({
|
|
18
18
|
label,
|
|
@@ -24,6 +24,8 @@ function RelatedToField({
|
|
|
24
24
|
}) {
|
|
25
25
|
const t = useT();
|
|
26
26
|
const [open, setOpen] = React.useState(false);
|
|
27
|
+
const closeDropdown = React.useCallback(() => setOpen(false), []);
|
|
28
|
+
const rootRef = useDropdownDismiss(open, closeDropdown);
|
|
27
29
|
const [query, setQuery] = React.useState("");
|
|
28
30
|
const [options, setOptions] = React.useState([]);
|
|
29
31
|
const [deals, setDeals] = React.useState([]);
|
|
@@ -75,6 +77,7 @@ function RelatedToField({
|
|
|
75
77
|
return /* @__PURE__ */ jsxs(
|
|
76
78
|
"div",
|
|
77
79
|
{
|
|
80
|
+
ref: rootRef,
|
|
78
81
|
className: "relative w-full",
|
|
79
82
|
onBlur: (event) => {
|
|
80
83
|
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
@@ -84,7 +87,7 @@ function RelatedToField({
|
|
|
84
87
|
"div",
|
|
85
88
|
{
|
|
86
89
|
className: cn(
|
|
87
|
-
"flex h-10 w-full items-center rounded-md bg-background pl-2.5 pr-3",
|
|
90
|
+
"flex h-10 w-full items-center rounded-md bg-background pl-2.5 pr-3 transition-colors hover:bg-accent/50",
|
|
88
91
|
error ? "border border-status-error-border" : CONTROL_BORDER
|
|
89
92
|
),
|
|
90
93
|
children: [
|
|
@@ -97,7 +100,7 @@ function RelatedToField({
|
|
|
97
100
|
"aria-expanded": open,
|
|
98
101
|
"aria-label": label,
|
|
99
102
|
onClick: () => setOpen((previous) => !previous),
|
|
100
|
-
className: "h-full min-w-0 flex-1 justify-between rounded-none bg-transparent p-0 text-left shadow-none hover:bg-transparent",
|
|
103
|
+
className: "h-full min-w-0 flex-1 justify-between rounded-none bg-transparent p-0 text-left shadow-none hover:bg-transparent dark:hover:bg-transparent",
|
|
101
104
|
children: [
|
|
102
105
|
/* @__PURE__ */ jsx("span", { className: "flex min-w-0 items-center gap-2", children: value ? /* @__PURE__ */ jsx(
|
|
103
106
|
PersonChip,
|
|
@@ -155,6 +158,7 @@ function RelatedToField({
|
|
|
155
158
|
role: "option",
|
|
156
159
|
"aria-selected": value?.id === option.id,
|
|
157
160
|
"aria-label": composeAccessibleName([option.label, option.subtitle, kindLabel(option.kind)]),
|
|
161
|
+
title: composeAccessibleName([option.label, option.subtitle]),
|
|
158
162
|
onClick: () => {
|
|
159
163
|
onChange({ id: option.id, kind: option.kind, label: option.label });
|
|
160
164
|
if (value?.id !== option.id) onDealChange(null);
|
|
@@ -196,6 +200,7 @@ function RelatedToField({
|
|
|
196
200
|
variant: "ghost",
|
|
197
201
|
role: "option",
|
|
198
202
|
"aria-selected": deal?.id === option.id,
|
|
203
|
+
title: option.label,
|
|
199
204
|
onClick: () => {
|
|
200
205
|
onDealChange(option);
|
|
201
206
|
setOpen(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/components/calendar/editor/RelatedToField.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, User, X } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorRelatedTo } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport {\n fetchDealsForEntity,\n searchRelatedEntities,\n type DealOption,\n type RelatedEntityOption,\n} from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge } from './inputs'\n\nconst OPTION_ROW_CLASS =\n 'h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground'\n\nexport function RelatedToField({\n label,\n value,\n deal,\n onChange,\n onDealChange,\n error,\n}: {\n label: string\n value: EditorRelatedTo | null\n deal: DealOption | null\n onChange(next: EditorRelatedTo | null): void\n onDealChange(next: DealOption | null): void\n error?: string | null\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [query, setQuery] = React.useState('')\n const [options, setOptions] = React.useState<RelatedEntityOption[]>([])\n const [deals, setDeals] = React.useState<DealOption[]>([])\n const [loading, setLoading] = React.useState(false)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const trimmed = query.trim()\n const [people, companies] = await Promise.all([\n searchRelatedEntities('person', trimmed, controller.signal),\n searchRelatedEntities('company', trimmed, controller.signal),\n ])\n if (cancelled) return\n setOptions([...people, ...companies])\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query])\n\n React.useEffect(() => {\n if (!open || !value || value.kind === 'unknown') {\n setDeals([])\n return\n }\n const controller = new AbortController()\n let cancelled = false\n fetchDealsForEntity({ id: value.id, kind: value.kind }, controller.signal)\n .then((items) => { if (!cancelled) setDeals(items) })\n .catch(() => { if (!cancelled) setDeals([]) })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [open, value])\n\n const kindLabel = (kind: RelatedEntityOption['kind']) =>\n kind === 'person'\n ? t('customers.deals.detail.tabs.peopleSingular', 'Person')\n : t('customers.deals.detail.tabs.companySingular', 'Company')\n\n return (\n <div\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex h-10 w-full items-center rounded-md bg-background pl-2.5 pr-3',\n error ? 'border border-status-error-border' : CONTROL_BORDER,\n )}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={label}\n onClick={() => setOpen((previous) => !previous)}\n className=\"h-full min-w-0 flex-1 justify-between rounded-none bg-transparent p-0 text-left shadow-none hover:bg-transparent\"\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {value ? (\n <PersonChip\n compact\n name={value.label || value.id}\n />\n ) : (\n <span className=\"truncate text-sm text-muted-foreground\">\n {t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n </span>\n )}\n </span>\n <span className=\"flex shrink-0 items-center\">\n {deal ? (\n <UppercaseBadge className=\"h-7 bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.dealBadge', '{name} \u00B7 Deal', { name: deal.label })}\n </UppercaseBadge>\n ) : null}\n <span aria-hidden className=\"h-px w-2\" />\n <ChevronDown aria-hidden className=\"size-4 opacity-60\" />\n </span>\n </Button>\n {value ? (\n <IconButton\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => {\n onChange(null)\n onDealChange(null)\n }}\n aria-label={t('customers.calendar.editor.removeRelated', 'Clear related record')}\n className=\"ms-1 shrink-0 text-muted-foreground\"\n >\n <X aria-hidden className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {open ? (\n <div role=\"listbox\" aria-label={label} className={DROPDOWN_PANEL_CLASS}>\n <Input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n aria-label={t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n autoFocus\n size=\"sm\"\n className=\"mb-1\"\n />\n {loading ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.searching', 'Searching\u2026')}\n </p>\n ) : null}\n {!loading && options.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.noResults', 'No results')}\n </p>\n ) : null}\n {!loading\n ? options.map((option) => (\n <Button\n key={`${option.kind}:${option.id}`}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={value?.id === option.id}\n aria-label={composeAccessibleName([option.label, option.subtitle, kindLabel(option.kind)])}\n onClick={() => {\n onChange({ id: option.id, kind: option.kind, label: option.label })\n if (value?.id !== option.id) onDealChange(null)\n setQuery('')\n }}\n className={OPTION_ROW_CLASS}\n >\n {option.kind === 'company'\n ? <Building2 aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n : <User aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />}\n <span className=\"min-w-0 flex-1 truncate\">\n {option.label}\n {option.subtitle ? <span className=\"ml-1.5 text-xs text-muted-foreground\">{option.subtitle}</span> : null}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">{kindLabel(option.kind)}</span>\n </Button>\n ))\n : null}\n {value && deals.length > 0 ? (\n <>\n <p className=\"px-2 pb-1 pt-2 text-overline font-medium uppercase text-muted-foreground\">\n {t('customers.calendar.editor.dealsSection', 'Deals')}\n </p>\n <Button\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={deal === null}\n onClick={() => {\n onDealChange(null)\n setOpen(false)\n }}\n className={OPTION_ROW_CLASS}\n >\n {t('customers.calendar.editor.dealNone', 'No deal')}\n </Button>\n {deals.map((option) => (\n <Button\n key={option.id}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={deal?.id === option.id}\n onClick={() => {\n onDealChange(option)\n setOpen(false)\n }}\n className={OPTION_ROW_CLASS}\n >\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.dealBadgeSuffix', 'Deal')}\n </UppercaseBadge>\n </Button>\n ))}\n </>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, User, X } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { EditorRelatedTo } from '../../../lib/calendar/editorPayload'\nimport { composeAccessibleName } from '../../../lib/calendar/labels'\nimport {\n fetchDealsForEntity,\n searchRelatedEntities,\n type DealOption,\n type RelatedEntityOption,\n} from './lookups'\nimport { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from './inputs'\n\nconst OPTION_ROW_CLASS =\n 'h-auto w-full justify-start gap-2 whitespace-normal px-2 py-1.5 text-left text-sm font-normal text-foreground'\n\nexport function RelatedToField({\n label,\n value,\n deal,\n onChange,\n onDealChange,\n error,\n}: {\n label: string\n value: EditorRelatedTo | null\n deal: DealOption | null\n onChange(next: EditorRelatedTo | null): void\n onDealChange(next: DealOption | null): void\n error?: string | null\n}) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const closeDropdown = React.useCallback(() => setOpen(false), [])\n const rootRef = useDropdownDismiss(open, closeDropdown)\n const [query, setQuery] = React.useState('')\n const [options, setOptions] = React.useState<RelatedEntityOption[]>([])\n const [deals, setDeals] = React.useState<DealOption[]>([])\n const [loading, setLoading] = React.useState(false)\n\n React.useEffect(() => {\n if (!open) return\n const controller = new AbortController()\n let cancelled = false\n const timer = window.setTimeout(async () => {\n setLoading(true)\n try {\n const trimmed = query.trim()\n const [people, companies] = await Promise.all([\n searchRelatedEntities('person', trimmed, controller.signal),\n searchRelatedEntities('company', trimmed, controller.signal),\n ])\n if (cancelled) return\n setOptions([...people, ...companies])\n } catch {\n if (!cancelled) setOptions([])\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, 200)\n return () => {\n cancelled = true\n controller.abort()\n window.clearTimeout(timer)\n }\n }, [open, query])\n\n React.useEffect(() => {\n if (!open || !value || value.kind === 'unknown') {\n setDeals([])\n return\n }\n const controller = new AbortController()\n let cancelled = false\n fetchDealsForEntity({ id: value.id, kind: value.kind }, controller.signal)\n .then((items) => { if (!cancelled) setDeals(items) })\n .catch(() => { if (!cancelled) setDeals([]) })\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [open, value])\n\n const kindLabel = (kind: RelatedEntityOption['kind']) =>\n kind === 'person'\n ? t('customers.deals.detail.tabs.peopleSingular', 'Person')\n : t('customers.deals.detail.tabs.companySingular', 'Company')\n\n return (\n <div\n ref={rootRef}\n className=\"relative w-full\"\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <div\n className={cn(\n 'flex h-10 w-full items-center rounded-md bg-background pl-2.5 pr-3 transition-colors hover:bg-accent/50',\n error ? 'border border-status-error-border' : CONTROL_BORDER,\n )}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={label}\n onClick={() => setOpen((previous) => !previous)}\n className=\"h-full min-w-0 flex-1 justify-between rounded-none bg-transparent p-0 text-left shadow-none hover:bg-transparent dark:hover:bg-transparent\"\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {value ? (\n <PersonChip\n compact\n name={value.label || value.id}\n />\n ) : (\n <span className=\"truncate text-sm text-muted-foreground\">\n {t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n </span>\n )}\n </span>\n <span className=\"flex shrink-0 items-center\">\n {deal ? (\n <UppercaseBadge className=\"h-7 bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.dealBadge', '{name} \u00B7 Deal', { name: deal.label })}\n </UppercaseBadge>\n ) : null}\n <span aria-hidden className=\"h-px w-2\" />\n <ChevronDown aria-hidden className=\"size-4 opacity-60\" />\n </span>\n </Button>\n {value ? (\n <IconButton\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => {\n onChange(null)\n onDealChange(null)\n }}\n aria-label={t('customers.calendar.editor.removeRelated', 'Clear related record')}\n className=\"ms-1 shrink-0 text-muted-foreground\"\n >\n <X aria-hidden className=\"size-3.5\" />\n </IconButton>\n ) : null}\n </div>\n {open ? (\n <div role=\"listbox\" aria-label={label} className={DROPDOWN_PANEL_CLASS}>\n <Input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n aria-label={t('customers.calendar.editor.relatedToPlaceholder', 'Search people or companies\u2026')}\n autoFocus\n size=\"sm\"\n className=\"mb-1\"\n />\n {loading ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.searching', 'Searching\u2026')}\n </p>\n ) : null}\n {!loading && options.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {t('customers.calendar.editor.noResults', 'No results')}\n </p>\n ) : null}\n {!loading\n ? options.map((option) => (\n <Button\n key={`${option.kind}:${option.id}`}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={value?.id === option.id}\n aria-label={composeAccessibleName([option.label, option.subtitle, kindLabel(option.kind)])}\n title={composeAccessibleName([option.label, option.subtitle])}\n onClick={() => {\n onChange({ id: option.id, kind: option.kind, label: option.label })\n if (value?.id !== option.id) onDealChange(null)\n setQuery('')\n }}\n className={OPTION_ROW_CLASS}\n >\n {option.kind === 'company'\n ? <Building2 aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n : <User aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />}\n <span className=\"min-w-0 flex-1 truncate\">\n {option.label}\n {option.subtitle ? <span className=\"ml-1.5 text-xs text-muted-foreground\">{option.subtitle}</span> : null}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">{kindLabel(option.kind)}</span>\n </Button>\n ))\n : null}\n {value && deals.length > 0 ? (\n <>\n <p className=\"px-2 pb-1 pt-2 text-overline font-medium uppercase text-muted-foreground\">\n {t('customers.calendar.editor.dealsSection', 'Deals')}\n </p>\n <Button\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={deal === null}\n onClick={() => {\n onDealChange(null)\n setOpen(false)\n }}\n className={OPTION_ROW_CLASS}\n >\n {t('customers.calendar.editor.dealNone', 'No deal')}\n </Button>\n {deals.map((option) => (\n <Button\n key={option.id}\n type=\"button\"\n variant=\"ghost\"\n role=\"option\"\n aria-selected={deal?.id === option.id}\n title={option.label}\n onClick={() => {\n onDealChange(option)\n setOpen(false)\n }}\n className={OPTION_ROW_CLASS}\n >\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n <UppercaseBadge className=\"bg-status-info-bg text-status-info-text\">\n {t('customers.calendar.editor.dealBadgeSuffix', 'Deal')}\n </UppercaseBadge>\n </Button>\n ))}\n </>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|