@aglyn/plugins-crm 1.0.0-beta.231 → 1.0.0-beta.232

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.
Files changed (53) hide show
  1. package/package.json +14 -14
  2. package/src/lib/components/activity-timeline.js +1 -0
  3. package/src/lib/components/activity-timeline.js.map +1 -1
  4. package/src/lib/components/assignment-rule-drawer.js +1 -0
  5. package/src/lib/components/assignment-rule-drawer.js.map +1 -1
  6. package/src/lib/components/contact-associations-card.js +1 -0
  7. package/src/lib/components/contact-associations-card.js.map +1 -1
  8. package/src/lib/components/contact-duplicates-card.js +1 -0
  9. package/src/lib/components/contact-duplicates-card.js.map +1 -1
  10. package/src/lib/components/contact-field-drawer.js +1 -0
  11. package/src/lib/components/contact-field-drawer.js.map +1 -1
  12. package/src/lib/components/contact-known-by-card.js +1 -0
  13. package/src/lib/components/contact-known-by-card.js.map +1 -1
  14. package/src/lib/components/contact-timeline-card.js +1 -0
  15. package/src/lib/components/contact-timeline-card.js.map +1 -1
  16. package/src/lib/components/crm-glance-card.js +1 -0
  17. package/src/lib/components/crm-glance-card.js.map +1 -1
  18. package/src/lib/components/crm-record-header.js +2 -0
  19. package/src/lib/components/crm-record-header.js.map +1 -1
  20. package/src/lib/components/deals-section.js +1 -0
  21. package/src/lib/components/deals-section.js.map +1 -1
  22. package/src/lib/components/fields-section.js +4 -0
  23. package/src/lib/components/fields-section.js.map +1 -1
  24. package/src/lib/components/lead-history-card.js +1 -0
  25. package/src/lib/components/lead-history-card.js.map +1 -1
  26. package/src/lib/components/lead-surfaces-note.js +1 -0
  27. package/src/lib/components/lead-surfaces-note.js.map +1 -1
  28. package/src/lib/components/linked-deals-card.js +1 -0
  29. package/src/lib/components/linked-deals-card.js.map +1 -1
  30. package/src/lib/components/picklist-values-card.js +1 -0
  31. package/src/lib/components/picklist-values-card.js.map +1 -1
  32. package/src/lib/components/recipes-card.js +1 -0
  33. package/src/lib/components/recipes-card.js.map +1 -1
  34. package/src/lib/components/reports/activity-card.js +1 -0
  35. package/src/lib/components/reports/activity-card.js.map +1 -1
  36. package/src/lib/components/reports/closed-deals-card.js +1 -0
  37. package/src/lib/components/reports/closed-deals-card.js.map +1 -1
  38. package/src/lib/components/reports/contacts-trend-card.js +1 -0
  39. package/src/lib/components/reports/contacts-trend-card.js.map +1 -1
  40. package/src/lib/components/reports/lead-funnel-card.js +1 -0
  41. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  42. package/src/lib/components/reports/lead-source-card.js +1 -0
  43. package/src/lib/components/reports/lead-source-card.js.map +1 -1
  44. package/src/lib/components/reports/pipeline-card.js +1 -0
  45. package/src/lib/components/reports/pipeline-card.js.map +1 -1
  46. package/src/lib/components/reports/source-conversion-card.js +1 -0
  47. package/src/lib/components/reports/source-conversion-card.js.map +1 -1
  48. package/src/lib/components/reports/tasks-card.js +1 -0
  49. package/src/lib/components/reports/tasks-card.js.map +1 -1
  50. package/src/lib/components/reports-section.js +2 -0
  51. package/src/lib/components/reports-section.js.map +1 -1
  52. package/src/lib/components/tasks-calendar.js +1 -0
  53. package/src/lib/components/tasks-calendar.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/tasks-calendar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { type CrmTaskPicklists, crmTaskPicklistLabels } from '@aglyn/aglyn'\nimport { mdiChevronLeft, mdiChevronRight } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { Box, Button, ButtonBase, IconButton, Stack, Typography } from '@mui/material'\nimport { useMemo, useState } from 'react'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport {\n bucketTasksByCalendarDay,\n CALENDAR_DAY_LABELS,\n CALENDAR_WEEK_DAYS,\n shiftCalendarMonth,\n taskCalendarMonth,\n} from '../model/task-calendar'\n\n/** How many tasks a square lists before folding the rest into a count. */\nconst TASKS_PER_DAY = 3\n\nexport interface TasksCalendarProps {\n /** The window the list is holding — the calendar reads no more than it. */\n tasks: readonly CrmTaskRow[]\n /** Open one task's drawer, the same drawer a row opens. */\n onOpen: (task: CrmTaskRow) => void\n /** The list's window filled, so neither surface is showing everything. */\n truncated?: boolean\n /** The org's task lists, which name each entry's type and status on hover (AGL-3517). */\n picklists?: CrmTaskPicklists\n}\n\n/**\n * A MONTH OF TASKS, BY DUE DATE (AGL-2662).\n *\n * The Tasks list answers \"what is on my plate\"; this answers \"what does\n * next week look like\", which a list ordered by due date can only imply.\n *\n * ## Drawn over the list's own window, and never over a second read\n *\n * The rows are the ones the section already holds. A calendar that fetched\n * its own month would be a second listener over the same collection, would\n * disagree with the list whenever the two windows differed, and would put\n * a read behind every page of the arrows. What it costs instead is honesty:\n * the grid can only place what the window carries, so the tasks due in\n * other months are counted under it rather than implied to be absent.\n *\n * ## Hand-built rather than a calendar dependency\n *\n * Seven columns of MUI boxes and the local-day arithmetic in\n * `task-calendar.ts`. The one calendar package already installed\n * (`@mui/x-date-pickers`) draws a date PICKER — a grid whose squares are\n * choices, not content — so it answers a different question, and a\n * scheduling library for one surface is a dependency the whole console\n * would carry.\n */\nexport function TasksCalendar(props: TasksCalendarProps) {\n const { tasks, onOpen, truncated, picklists } = props\n /** \"Call Ada · Call · In Progress\" — the entry's type and status on hover. */\n const hoverText = (task: CrmTaskRow) => {\n if (!picklists) return task.title\n const labels = crmTaskPicklistLabels(task, picklists)\n return `${task.title} · ${labels.type} · ${labels.status}`\n }\n /*\n * The clock is read ONCE, at mount. A component that read `Date.now()`\n * per render would move the today square under a reader who left the tab\n * open across midnight, without any state having changed — and the grid\n * is a pure function of this, so what is drawn can be pinned.\n */\n const [nowMs] = useState(() => Date.now())\n const [anchorMs, setAnchorMs] = useState(() => nowMs)\n\n const month = useMemo(\n () => taskCalendarMonth(anchorMs, nowMs),\n [anchorMs, nowMs],\n )\n const buckets = useMemo(\n () => bucketTasksByCalendarDay(tasks, (task) => task.dueAtMs, month),\n [tasks, month],\n )\n const label = useMemo(\n () =>\n month.monthDate.toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n }),\n [month],\n )\n\n return (\n <Stack spacing={1.5}>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <IconButton\n size=\"small\"\n aria-label=\"Previous month\"\n onClick={() => setAnchorMs((current) => shiftCalendarMonth(current, -1))}\n >\n <MdiIcon path={mdiChevronLeft.path} size={0.9} />\n </IconButton>\n <Typography variant=\"subtitle1\" sx={{ minWidth: 160, textAlign: 'center' }}>\n {label}\n </Typography>\n <IconButton\n size=\"small\"\n aria-label=\"Next month\"\n onClick={() => setAnchorMs((current) => shiftCalendarMonth(current, 1))}\n >\n <MdiIcon path={mdiChevronRight.path} size={0.9} />\n </IconButton>\n <Button size=\"small\" onClick={() => setAnchorMs(nowMs)}>\n {'Today'}\n </Button>\n </Stack>\n\n <Box sx={{ overflowX: 'auto' }}>\n <Box sx={{ minWidth: 640 }}>\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(${CALENDAR_WEEK_DAYS}, 1fr)`,\n gap: 0.5,\n mb: 0.5,\n }}\n >\n {CALENDAR_DAY_LABELS.map((day) => (\n <Typography\n key={day}\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ textAlign: 'center' }}\n >\n {day}\n </Typography>\n ))}\n </Box>\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(${CALENDAR_WEEK_DAYS}, 1fr)`,\n gap: 0.5,\n }}\n >\n {month.weeks.flat().map((day) => {\n const held = buckets.byDay.get(day.startMs) ?? []\n const shown = held.slice(0, TASKS_PER_DAY)\n const folded = held.length - shown.length\n return (\n <Box\n key={day.startMs}\n sx={{\n minHeight: 96,\n p: 0.75,\n borderRadius: 1,\n border: 1,\n borderColor: day.isToday ? 'primary.main' : 'divider',\n bgcolor: day.inMonth ? 'background.paper' : 'action.hover',\n }}\n >\n <Typography\n variant=\"caption\"\n sx={{\n display: 'block',\n mb: 0.5,\n fontWeight: day.isToday ? 'fontWeightBold' : undefined,\n color: day.inMonth ? 'text.primary' : 'text.disabled',\n }}\n >\n {day.date}\n </Typography>\n <Stack spacing={0.25}>\n {shown.map((task) => (\n <ButtonBase\n key={task.$id}\n onClick={() => onOpen(task)}\n title={hoverText(task)}\n sx={{\n justifyContent: 'flex-start',\n width: '100%',\n px: 0.5,\n py: 0.25,\n borderRadius: 0.5,\n bgcolor: 'action.selected',\n '&:hover': { bgcolor: 'action.focus' },\n }}\n >\n <Typography\n variant=\"caption\"\n noWrap\n sx={{\n width: '100%',\n textAlign: 'left',\n textDecoration:\n task.status === 'done' ? 'line-through' : undefined,\n color:\n task.status === 'done'\n ? 'text.secondary'\n : day.endMs <= nowMs\n ? 'error.main'\n : 'text.primary',\n }}\n >\n {task.title}\n </Typography>\n </ButtonBase>\n ))}\n {folded > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`+${folded} more`}\n </Typography>\n ) : null}\n </Stack>\n </Box>\n )\n })}\n </Box>\n </Box>\n </Box>\n\n {/*\n What the grid could not place. Said plainly, because a month with\n nothing on it reads as \"nothing is due\" and the honest reading here\n is often \"the window's tasks are somewhere else\".\n */}\n {buckets.undated.length || buckets.elsewhere.length || truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n buckets.elsewhere.length\n ? `${buckets.elsewhere.length} due outside this month`\n : '',\n buckets.undated.length\n ? `${buckets.undated.length} with no due date`\n : '',\n truncated ? 'and the view is showing only its first page' : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n ) : null}\n </Stack>\n )\n}\nTasksCalendar.displayName = 'TasksCalendar'\n\nexport default TasksCalendar\n"],"names":["crmTaskPicklistLabels","mdiChevronLeft","mdiChevronRight","MdiIcon","Box","Button","ButtonBase","IconButton","Stack","Typography","useMemo","useState","bucketTasksByCalendarDay","CALENDAR_DAY_LABELS","CALENDAR_WEEK_DAYS","shiftCalendarMonth","taskCalendarMonth","TASKS_PER_DAY","TasksCalendar","props","tasks","onOpen","truncated","picklists","hoverText","task","title","labels","type","status","nowMs","Date","now","anchorMs","setAnchorMs","month","buckets","dueAtMs","label","monthDate","toLocaleDateString","undefined","year","spacing","direction","sx","alignItems","flexWrap","rowGap","size","aria-label","onClick","current","path","variant","minWidth","textAlign","overflowX","display","gridTemplateColumns","gap","mb","map","day","color","weeks","flat","held","byDay","get","startMs","shown","slice","folded","length","minHeight","p","borderRadius","border","borderColor","isToday","bgcolor","inMonth","fontWeight","date","justifyContent","width","px","py","noWrap","textDecoration","endMs","$id","undated","elsewhere","filter","Boolean","join","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/tasks-calendar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { type CrmTaskPicklists, crmTaskPicklistLabels } from '@aglyn/aglyn'\nimport { mdiChevronLeft, mdiChevronRight } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { Box, Button, ButtonBase, IconButton, Stack, Typography } from '@mui/material'\nimport { useMemo, useState } from 'react'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport {\n bucketTasksByCalendarDay,\n CALENDAR_DAY_LABELS,\n CALENDAR_WEEK_DAYS,\n shiftCalendarMonth,\n taskCalendarMonth,\n} from '../model/task-calendar'\n\n/** How many tasks a square lists before folding the rest into a count. */\nconst TASKS_PER_DAY = 3\n\nexport interface TasksCalendarProps {\n /** The window the list is holding — the calendar reads no more than it. */\n tasks: readonly CrmTaskRow[]\n /** Open one task's drawer, the same drawer a row opens. */\n onOpen: (task: CrmTaskRow) => void\n /** The list's window filled, so neither surface is showing everything. */\n truncated?: boolean\n /** The org's task lists, which name each entry's type and status on hover (AGL-3517). */\n picklists?: CrmTaskPicklists\n}\n\n/**\n * A MONTH OF TASKS, BY DUE DATE (AGL-2662).\n *\n * The Tasks list answers \"what is on my plate\"; this answers \"what does\n * next week look like\", which a list ordered by due date can only imply.\n *\n * ## Drawn over the list's own window, and never over a second read\n *\n * The rows are the ones the section already holds. A calendar that fetched\n * its own month would be a second listener over the same collection, would\n * disagree with the list whenever the two windows differed, and would put\n * a read behind every page of the arrows. What it costs instead is honesty:\n * the grid can only place what the window carries, so the tasks due in\n * other months are counted under it rather than implied to be absent.\n *\n * ## Hand-built rather than a calendar dependency\n *\n * Seven columns of MUI boxes and the local-day arithmetic in\n * `task-calendar.ts`. The one calendar package already installed\n * (`@mui/x-date-pickers`) draws a date PICKER — a grid whose squares are\n * choices, not content — so it answers a different question, and a\n * scheduling library for one surface is a dependency the whole console\n * would carry.\n */\nexport function TasksCalendar(props: TasksCalendarProps) {\n const { tasks, onOpen, truncated, picklists } = props\n /** \"Call Ada · Call · In Progress\" — the entry's type and status on hover. */\n const hoverText = (task: CrmTaskRow) => {\n if (!picklists) return task.title\n const labels = crmTaskPicklistLabels(task, picklists)\n return `${task.title} · ${labels.type} · ${labels.status}`\n }\n /*\n * The clock is read ONCE, at mount. A component that read `Date.now()`\n * per render would move the today square under a reader who left the tab\n * open across midnight, without any state having changed — and the grid\n * is a pure function of this, so what is drawn can be pinned.\n */\n const [nowMs] = useState(() => Date.now())\n const [anchorMs, setAnchorMs] = useState(() => nowMs)\n\n const month = useMemo(\n () => taskCalendarMonth(anchorMs, nowMs),\n [anchorMs, nowMs],\n )\n const buckets = useMemo(\n () => bucketTasksByCalendarDay(tasks, (task) => task.dueAtMs, month),\n [tasks, month],\n )\n const label = useMemo(\n () =>\n month.monthDate.toLocaleDateString(undefined, {\n month: 'long',\n year: 'numeric',\n }),\n [month],\n )\n\n return (\n <Stack spacing={1.5}>\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <IconButton\n size=\"small\"\n aria-label=\"Previous month\"\n onClick={() => setAnchorMs((current) => shiftCalendarMonth(current, -1))}\n >\n <MdiIcon path={mdiChevronLeft.path} size={0.9} />\n </IconButton>\n <Typography variant=\"subtitle1\" sx={{ minWidth: 160, textAlign: 'center' }}>\n {label}\n </Typography>\n <IconButton\n size=\"small\"\n aria-label=\"Next month\"\n onClick={() => setAnchorMs((current) => shiftCalendarMonth(current, 1))}\n >\n <MdiIcon path={mdiChevronRight.path} size={0.9} />\n </IconButton>\n <Button size=\"small\" onClick={() => setAnchorMs(nowMs)}>\n {'Today'}\n </Button>\n </Stack>\n\n <Box sx={{ overflowX: 'auto' }}>\n <Box sx={{ minWidth: 640 }}>\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(${CALENDAR_WEEK_DAYS}, 1fr)`,\n gap: 0.5,\n mb: 0.5,\n }}\n >\n {CALENDAR_DAY_LABELS.map((day) => (\n <Typography\n key={day}\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ textAlign: 'center' }}\n >\n {day}\n </Typography>\n ))}\n </Box>\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(${CALENDAR_WEEK_DAYS}, 1fr)`,\n gap: 0.5,\n }}\n >\n {month.weeks.flat().map((day) => {\n const held = buckets.byDay.get(day.startMs) ?? []\n const shown = held.slice(0, TASKS_PER_DAY)\n const folded = held.length - shown.length\n return (\n <Box\n key={day.startMs}\n sx={{\n minHeight: 96,\n p: 0.75,\n borderRadius: 1,\n border: 1,\n borderColor: day.isToday ? 'primary.main' : 'divider',\n bgcolor: day.inMonth ? 'background.paper' : 'action.hover',\n }}\n >\n <Typography\n variant=\"caption\"\n sx={{\n display: 'block',\n mb: 0.5,\n fontWeight: day.isToday ? 'fontWeightBold' : undefined,\n color: day.inMonth ? 'text.primary' : 'text.disabled',\n }}\n >\n {day.date}\n </Typography>\n <Stack spacing={0.25}>\n {shown.map((task) => (\n <ButtonBase\n key={task.$id}\n onClick={() => onOpen(task)}\n title={hoverText(task)}\n sx={{\n justifyContent: 'flex-start',\n width: '100%',\n px: 0.5,\n py: 0.25,\n borderRadius: 0.5,\n bgcolor: 'action.selected',\n '&:hover': { bgcolor: 'action.focus' },\n }}\n >\n <Typography\n variant=\"caption\"\n noWrap\n sx={{\n width: '100%',\n textAlign: 'left',\n textDecoration:\n task.status === 'done' ? 'line-through' : undefined,\n color:\n task.status === 'done'\n ? 'text.secondary'\n : day.endMs <= nowMs\n ? 'error.main'\n : 'text.primary',\n }}\n >\n {task.title}\n </Typography>\n </ButtonBase>\n ))}\n {folded > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`+${folded} more`}\n </Typography>\n ) : null}\n </Stack>\n </Box>\n )\n })}\n </Box>\n </Box>\n </Box>\n\n {/*\n What the grid could not place. Said plainly, because a month with\n nothing on it reads as \"nothing is due\" and the honest reading here\n is often \"the window's tasks are somewhere else\".\n */}\n {buckets.undated.length || buckets.elsewhere.length || truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n buckets.elsewhere.length\n ? `${buckets.elsewhere.length} due outside this month`\n : '',\n buckets.undated.length\n ? `${buckets.undated.length} with no due date`\n : '',\n truncated ? 'and the view is showing only its first page' : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n ) : null}\n </Stack>\n )\n}\nTasksCalendar.displayName = 'TasksCalendar'\n\nexport default TasksCalendar\n"],"names":["crmTaskPicklistLabels","mdiChevronLeft","mdiChevronRight","MdiIcon","Box","Button","ButtonBase","IconButton","Stack","Typography","useMemo","useState","bucketTasksByCalendarDay","CALENDAR_DAY_LABELS","CALENDAR_WEEK_DAYS","shiftCalendarMonth","taskCalendarMonth","TASKS_PER_DAY","TasksCalendar","props","tasks","onOpen","truncated","picklists","hoverText","task","title","labels","type","status","nowMs","Date","now","anchorMs","setAnchorMs","month","buckets","dueAtMs","label","monthDate","toLocaleDateString","undefined","year","spacing","useFlexGap","direction","sx","alignItems","flexWrap","rowGap","size","aria-label","onClick","current","path","variant","minWidth","textAlign","overflowX","display","gridTemplateColumns","gap","mb","map","day","color","weeks","flat","held","byDay","get","startMs","shown","slice","folded","length","minHeight","p","borderRadius","border","borderColor","isToday","bgcolor","inMonth","fontWeight","date","justifyContent","width","px","py","noWrap","textDecoration","endMs","$id","undated","elsewhere","filter","Boolean","join","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAAgCA,qBAAqB,QAAQ,eAAc;AAC3E,SAASC,cAAc,EAAEC,eAAe,QAAQ,yBAAwB;AACxE,SAASC,OAAO,QAAQ,uBAAsB;AAC9C,SAASC,GAAG,EAAEC,MAAM,EAAEC,UAAU,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AACtF,SAASC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAEzC,SACEC,wBAAwB,EACxBC,mBAAmB,EACnBC,kBAAkB,EAClBC,kBAAkB,EAClBC,iBAAiB,QACZ,4BAAwB;AAE/B,wEAAwE,GACxE,MAAMC,gBAAgB;AAatB;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GACD,OAAO,SAASC,cAAcC,KAAyB;IACrD,MAAM,EAAEC,KAAK,EAAEC,MAAM,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGJ;IAChD,4EAA4E,GAC5E,MAAMK,YAAY,CAACC;QACjB,IAAI,CAACF,WAAW,OAAOE,KAAKC,KAAK;QACjC,MAAMC,SAAS3B,sBAAsByB,MAAMF;QAC3C,OAAO,GAAGE,KAAKC,KAAK,CAAC,GAAG,EAAEC,OAAOC,IAAI,CAAC,GAAG,EAAED,OAAOE,MAAM,EAAE;IAC5D;IACA;;;;;GAKC,GACD,MAAM,CAACC,MAAM,GAAGnB,SAAS,IAAMoB,KAAKC,GAAG;IACvC,MAAM,CAACC,UAAUC,YAAY,GAAGvB,SAAS,IAAMmB;IAE/C,MAAMK,QAAQzB,QACZ,IAAMM,kBAAkBiB,UAAUH,QAClC;QAACG;QAAUH;KAAM;IAEnB,MAAMM,UAAU1B,QACd,IAAME,yBAAyBQ,OAAO,CAACK,OAASA,KAAKY,OAAO,EAAEF,QAC9D;QAACf;QAAOe;KAAM;IAEhB,MAAMG,QAAQ5B,QACZ,IACEyB,MAAMI,SAAS,CAACC,kBAAkB,CAACC,WAAW;YAC5CN,OAAO;YACPO,MAAM;QACR,IACF;QAACP;KAAM;IAGT,qBACE,MAAC3B;QAAMmC,SAAS;;0BACd,MAACnC;gBACCoC,UAAU;gBACVC,WAAU;gBACVF,SAAS;gBACTG,IAAI;oBAAEC,YAAY;oBAAUC,UAAU;oBAAQC,QAAQ;gBAAE;;kCAExD,KAAC1C;wBACC2C,MAAK;wBACLC,cAAW;wBACXC,SAAS,IAAMlB,YAAY,CAACmB,UAAYtC,mBAAmBsC,SAAS,CAAC;kCAErE,cAAA,KAAClD;4BAAQmD,MAAMrD,eAAeqD,IAAI;4BAAEJ,MAAM;;;kCAE5C,KAACzC;wBAAW8C,SAAQ;wBAAYT,IAAI;4BAAEU,UAAU;4BAAKC,WAAW;wBAAS;kCACtEnB;;kCAEH,KAAC/B;wBACC2C,MAAK;wBACLC,cAAW;wBACXC,SAAS,IAAMlB,YAAY,CAACmB,UAAYtC,mBAAmBsC,SAAS;kCAEpE,cAAA,KAAClD;4BAAQmD,MAAMpD,gBAAgBoD,IAAI;4BAAEJ,MAAM;;;kCAE7C,KAAC7C;wBAAO6C,MAAK;wBAAQE,SAAS,IAAMlB,YAAYJ;kCAC7C;;;;0BAIL,KAAC1B;gBAAI0C,IAAI;oBAAEY,WAAW;gBAAO;0BAC3B,cAAA,MAACtD;oBAAI0C,IAAI;wBAAEU,UAAU;oBAAI;;sCACvB,KAACpD;4BACC0C,IAAI;gCACFa,SAAS;gCACTC,qBAAqB,CAAC,OAAO,EAAE9C,mBAAmB,MAAM,CAAC;gCACzD+C,KAAK;gCACLC,IAAI;4BACN;sCAECjD,oBAAoBkD,GAAG,CAAC,CAACC,oBACxB,KAACvD;oCAEC8C,SAAQ;oCACRU,OAAM;oCACNnB,IAAI;wCAAEW,WAAW;oCAAS;8CAEzBO;mCALIA;;sCASX,KAAC5D;4BACC0C,IAAI;gCACFa,SAAS;gCACTC,qBAAqB,CAAC,OAAO,EAAE9C,mBAAmB,MAAM,CAAC;gCACzD+C,KAAK;4BACP;sCAEC1B,MAAM+B,KAAK,CAACC,IAAI,GAAGJ,GAAG,CAAC,CAACC;oCACV5B;gCAAb,MAAMgC,QAAOhC,qBAAAA,QAAQiC,KAAK,CAACC,GAAG,CAACN,IAAIO,OAAO,aAA7BnC,qBAAkC,EAAE;gCACjD,MAAMoC,QAAQJ,KAAKK,KAAK,CAAC,GAAGxD;gCAC5B,MAAMyD,SAASN,KAAKO,MAAM,GAAGH,MAAMG,MAAM;gCACzC,qBACE,MAACvE;oCAEC0C,IAAI;wCACF8B,WAAW;wCACXC,GAAG;wCACHC,cAAc;wCACdC,QAAQ;wCACRC,aAAahB,IAAIiB,OAAO,GAAG,iBAAiB;wCAC5CC,SAASlB,IAAImB,OAAO,GAAG,qBAAqB;oCAC9C;;sDAEA,KAAC1E;4CACC8C,SAAQ;4CACRT,IAAI;gDACFa,SAAS;gDACTG,IAAI;gDACJsB,YAAYpB,IAAIiB,OAAO,GAAG,mBAAmBxC;gDAC7CwB,OAAOD,IAAImB,OAAO,GAAG,iBAAiB;4CACxC;sDAECnB,IAAIqB,IAAI;;sDAEX,MAAC7E;4CAAMmC,SAAS;;gDACb6B,MAAMT,GAAG,CAAC,CAACtC,qBACV,KAACnB;wDAEC8C,SAAS,IAAM/B,OAAOI;wDACtBC,OAAOF,UAAUC;wDACjBqB,IAAI;4DACFwC,gBAAgB;4DAChBC,OAAO;4DACPC,IAAI;4DACJC,IAAI;4DACJX,cAAc;4DACdI,SAAS;4DACT,WAAW;gEAAEA,SAAS;4DAAe;wDACvC;kEAEA,cAAA,KAACzE;4DACC8C,SAAQ;4DACRmC,MAAM;4DACN5C,IAAI;gEACFyC,OAAO;gEACP9B,WAAW;gEACXkC,gBACElE,KAAKI,MAAM,KAAK,SAAS,iBAAiBY;gEAC5CwB,OACExC,KAAKI,MAAM,KAAK,SACZ,mBACAmC,IAAI4B,KAAK,IAAI9D,QACX,eACA;4DACV;sEAECL,KAAKC,KAAK;;uDA7BRD,KAAKoE,GAAG;gDAiChBnB,SAAS,kBACR,KAACjE;oDAAW8C,SAAQ;oDAAUU,OAAM;8DACjC,CAAC,CAAC,EAAES,OAAO,KAAK,CAAC;qDAElB;;;;mCA7DDV,IAAIO,OAAO;4BAiEtB;;;;;YAULnC,QAAQ0D,OAAO,CAACnB,MAAM,IAAIvC,QAAQ2D,SAAS,CAACpB,MAAM,IAAIrD,0BACrD,KAACb;gBAAW8C,SAAQ;gBAAUU,OAAM;0BACjC;oBACC7B,QAAQ2D,SAAS,CAACpB,MAAM,GACpB,GAAGvC,QAAQ2D,SAAS,CAACpB,MAAM,CAAC,uBAAuB,CAAC,GACpD;oBACJvC,QAAQ0D,OAAO,CAACnB,MAAM,GAClB,GAAGvC,QAAQ0D,OAAO,CAACnB,MAAM,CAAC,iBAAiB,CAAC,GAC5C;oBACJrD,YAAY,gDAAgD;iBAC7D,CACE0E,MAAM,CAACC,SACPC,IAAI,CAAC;iBAER;;;AAGV;AACAhF,cAAciF,WAAW,GAAG;AAE5B,eAAejF,cAAa"}