@aglyn/plugins-crm 1.0.0-beta.199 → 1.0.0-beta.201
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/package.json +13 -13
- package/src/lib/components/activity-list.d.ts +36 -5
- package/src/lib/components/activity-list.js +159 -137
- package/src/lib/components/activity-list.js.map +1 -1
- package/src/lib/components/activity-timeline.d.ts +84 -0
- package/src/lib/components/activity-timeline.js +224 -0
- package/src/lib/components/activity-timeline.js.map +1 -0
- package/src/lib/components/companies-section.d.ts +6 -6
- package/src/lib/components/companies-section.js +54 -84
- package/src/lib/components/companies-section.js.map +1 -1
- package/src/lib/components/company-edit-drawer.js +17 -4
- package/src/lib/components/company-edit-drawer.js.map +1 -1
- package/src/lib/components/company-picker.js +6 -2
- package/src/lib/components/company-picker.js.map +1 -1
- package/src/lib/components/company-properties-card.js +53 -36
- package/src/lib/components/company-properties-card.js.map +1 -1
- package/src/lib/components/contact-associations-card.d.ts +3 -2
- package/src/lib/components/contact-associations-card.js +12 -12
- package/src/lib/components/contact-associations-card.js.map +1 -1
- package/src/lib/components/contact-detail-page.d.ts +4 -3
- package/src/lib/components/contact-detail-page.js +18 -12
- package/src/lib/components/contact-detail-page.js.map +1 -1
- package/src/lib/components/contact-list-columns.d.ts +3 -5
- package/src/lib/components/contact-list-columns.js +6 -8
- package/src/lib/components/contact-list-columns.js.map +1 -1
- package/src/lib/components/contact-properties-card.d.ts +21 -4
- package/src/lib/components/contact-properties-card.js +129 -29
- package/src/lib/components/contact-properties-card.js.map +1 -1
- package/src/lib/components/contact-timeline-card.d.ts +12 -6
- package/src/lib/components/contact-timeline-card.js +132 -174
- package/src/lib/components/contact-timeline-card.js.map +1 -1
- package/src/lib/components/contacts-bulk-bar.js +10 -1
- package/src/lib/components/contacts-bulk-bar.js.map +1 -1
- package/src/lib/components/contacts-section.js +282 -331
- package/src/lib/components/contacts-section.js.map +1 -1
- package/src/lib/components/crm-email-state-chip.js +4 -1
- package/src/lib/components/crm-email-state-chip.js.map +1 -1
- package/src/lib/components/crm-next-activity-column.d.ts +4 -2
- package/src/lib/components/crm-next-activity-column.js +6 -3
- package/src/lib/components/crm-next-activity-column.js.map +1 -1
- package/src/lib/components/deal-edit-drawer.js +8 -4
- package/src/lib/components/deal-edit-drawer.js.map +1 -1
- package/src/lib/components/deal-products-card.js +15 -13
- package/src/lib/components/deal-products-card.js.map +1 -1
- package/src/lib/components/deal-properties-card.js +7 -0
- package/src/lib/components/deal-properties-card.js.map +1 -1
- package/src/lib/components/deal-stage-card.js +31 -29
- package/src/lib/components/deal-stage-card.js.map +1 -1
- package/src/lib/components/deals-section.d.ts +2 -2
- package/src/lib/components/deals-section.js +67 -97
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/fields-section.d.ts +4 -0
- package/src/lib/components/fields-section.js +368 -154
- package/src/lib/components/fields-section.js.map +1 -1
- package/src/lib/components/form-contact-fields-card.js +24 -20
- package/src/lib/components/form-contact-fields-card.js.map +1 -1
- package/src/lib/components/lead-campaigns-card.d.ts +1 -1
- package/src/lib/components/lead-campaigns-card.js +43 -38
- package/src/lib/components/lead-campaigns-card.js.map +1 -1
- package/src/lib/components/lead-properties-card.d.ts +14 -6
- package/src/lib/components/lead-properties-card.js +323 -311
- package/src/lib/components/lead-properties-card.js.map +1 -1
- package/src/lib/components/lead-surfaces-note.js +13 -2
- package/src/lib/components/lead-surfaces-note.js.map +1 -1
- package/src/lib/components/leads-bulk-bar.js +16 -3
- package/src/lib/components/leads-bulk-bar.js.map +1 -1
- package/src/lib/components/leads-section.d.ts +2 -1
- package/src/lib/components/leads-section.js +110 -148
- package/src/lib/components/leads-section.js.map +1 -1
- package/src/lib/components/linked-deals-card.js +12 -10
- package/src/lib/components/linked-deals-card.js.map +1 -1
- package/src/lib/components/recent-activity-feed.d.ts +12 -8
- package/src/lib/components/recent-activity-feed.js +54 -29
- package/src/lib/components/recent-activity-feed.js.map +1 -1
- package/src/lib/components/record-activity-card.d.ts +4 -2
- package/src/lib/components/record-activity-card.js +29 -11
- package/src/lib/components/record-activity-card.js.map +1 -1
- package/src/lib/components/reports/activity-card.js +18 -12
- package/src/lib/components/reports/activity-card.js.map +1 -1
- package/src/lib/components/reports/lead-funnel-card.js +47 -37
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
- package/src/lib/components/reports/pipeline-card.js +22 -16
- package/src/lib/components/reports/pipeline-card.js.map +1 -1
- package/src/lib/components/reports/report-export.d.ts +4 -11
- package/src/lib/components/reports/report-export.js +16 -33
- package/src/lib/components/reports/report-export.js.map +1 -1
- package/src/lib/components/reports/source-conversion-card.js +18 -12
- package/src/lib/components/reports/source-conversion-card.js.map +1 -1
- package/src/lib/components/reports/tasks-card.js +35 -25
- package/src/lib/components/reports/tasks-card.js.map +1 -1
- package/src/lib/components/tasks-section.js +151 -144
- package/src/lib/components/tasks-section.js.map +1 -1
- package/src/lib/constants/company-filters.d.ts +13 -0
- package/src/lib/constants/company-filters.js +47 -17
- package/src/lib/constants/company-filters.js.map +1 -1
- package/src/lib/constants/contact-filters.d.ts +61 -10
- package/src/lib/constants/contact-filters.js +373 -141
- package/src/lib/constants/contact-filters.js.map +1 -1
- package/src/lib/constants/deal-filters.d.ts +44 -0
- package/src/lib/constants/deal-filters.js +78 -0
- package/src/lib/constants/deal-filters.js.map +1 -0
- package/src/lib/constants/field-list-query.d.ts +23 -0
- package/src/lib/constants/field-list-query.js +80 -0
- package/src/lib/constants/field-list-query.js.map +1 -0
- package/src/lib/hooks/use-crm-list-query.d.ts +68 -0
- package/src/lib/hooks/use-crm-list-query.js +182 -0
- package/src/lib/hooks/use-crm-list-query.js.map +1 -0
- package/src/lib/hooks/use-crm-tasks.d.ts +23 -6
- package/src/lib/hooks/use-crm-tasks.js +44 -42
- package/src/lib/hooks/use-crm-tasks.js.map +1 -1
- package/src/lib/hooks/use-deals.d.ts +2 -34
- package/src/lib/hooks/use-deals.js +3 -62
- package/src/lib/hooks/use-deals.js.map +1 -1
- package/src/lib/model/crm-list-query.d.ts +91 -0
- package/src/lib/model/crm-list-query.js +182 -0
- package/src/lib/model/crm-list-query.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +50 -44
- package/src/lib/model/lead-filters.js +194 -88
- package/src/lib/model/lead-filters.js.map +1 -1
- package/src/lib/model/list-fields-api.d.ts +26 -0
- package/src/lib/model/list-fields-api.js +47 -0
- package/src/lib/model/list-fields-api.js.map +1 -0
- package/src/lib/model/list-fields.d.ts +47 -0
- package/src/lib/model/list-fields.js +39 -0
- package/src/lib/model/list-fields.js.map +1 -0
- package/src/lib/model/task-views.d.ts +15 -10
- package/src/lib/model/task-views.js +68 -9
- package/src/lib/model/task-views.js.map +1 -1
- package/src/lib/server/capture-contact.js +7 -5
- package/src/lib/server/capture-contact.js.map +1 -1
- package/src/lib/server/companies-import.js +3 -3
- package/src/lib/server/companies-import.js.map +1 -1
- package/src/lib/server/company-delete.js +3 -1
- package/src/lib/server/company-delete.js.map +1 -1
- package/src/lib/server/consent-group-participant.js +4 -0
- package/src/lib/server/consent-group-participant.js.map +1 -1
- package/src/lib/server/contact-update.js +14 -8
- package/src/lib/server/contact-update.js.map +1 -1
- package/src/lib/server/contacts-import.js +4 -2
- package/src/lib/server/contacts-import.js.map +1 -1
- package/src/lib/server/deals-import.js +4 -2
- package/src/lib/server/deals-import.js.map +1 -1
- package/src/lib/server/erase-person.js +2 -1
- package/src/lib/server/erase-person.js.map +1 -1
- package/src/lib/server/lead-create.js +4 -1
- package/src/lib/server/lead-create.js.map +1 -1
- package/src/lib/server/lead-source-values.js +9 -5
- package/src/lib/server/lead-source-values.js.map +1 -1
- package/src/lib/server/leads-import.js +4 -1
- package/src/lib/server/leads-import.js.map +1 -1
- package/src/lib/server/list-fields-routes.d.ts +29 -0
- package/src/lib/server/list-fields-routes.js +85 -0
- package/src/lib/server/list-fields-routes.js.map +1 -0
- package/src/lib/server/record-email-state.js +5 -2
- package/src/lib/server/record-email-state.js.map +1 -1
- package/src/lib/server/record-timeline.js +3 -2
- package/src/lib/server/record-timeline.js.map +1 -1
- package/src/lib/server/task-routes.js +6 -3
- package/src/lib/server/task-routes.js.map +1 -1
- package/src/lib/server/tasks-import.js +5 -2
- package/src/lib/server/tasks-import.js.map +1 -1
- package/src/lib/server.js +10 -1
- package/src/lib/server.js.map +1 -1
- package/src/lib/components/contact-custom-fields-card.d.ts +0 -39
- package/src/lib/components/contact-custom-fields-card.js +0 -204
- package/src/lib/components/contact-custom-fields-card.js.map +0 -1
- package/src/lib/hooks/use-org-leads.d.ts +0 -50
- package/src/lib/hooks/use-org-leads.js +0 -104
- package/src/lib/hooks/use-org-leads.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-stage-card.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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiThumbDownOutline, mdiTrophyOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Stack,\n Step,\n StepButton,\n Stepper,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState } from 'react'\nimport { type DealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n daysInStage,\n DEAL_STATUS_LABELS,\n type DealDoc,\n openStages,\n type PipelineDoc,\n} from '../model/deal-board-model'\nimport { LostReasonDialog } from './lost-reason-dialog'\n\nexport interface DealStageCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n api: DealStageApi\n nowMs: number\n}\n\n/**\n * Where the deal is, and the controls that move it (AGL-2598).\n *\n * A stepper across the open stages — click one to move there — and the two\n * closing buttons. Every one of them calls the stage route, never Firestore:\n * the move is what an automation listens for, and the route is the only\n * writer that emits it. On a closed deal the stepper gives way to the\n * verdict — Won or Lost, when, and why — and a way back: reopening is a\n * move into an open stage, which the route emits as `dealStageChanged` like\n * any other move.\n */\nexport function DealStageCard(props: DealStageCardProps) {\n const { deal, pipeline, api, nowMs } = props\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n const [losing, setLosing] = useState(false)\n const stages = openStages(pipeline)\n const activeIndex = stages.findIndex((stage) => stage.id === deal.stageId)\n\n const run = useCallback(\n async (request: () => Promise<unknown>, done: string) => {\n setBusy(true)\n try {\n await request()\n enqueueSnackbar(done, { variant: 'success', persist: false })\n return true\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The deal could not be moved.',\n { variant: 'warning', allowDuplicate: true },\n )\n return false\n } finally {\n setBusy(false)\n }\n },\n [enqueueSnackbar],\n )\n\n const closed = deal.status !== 'open'\n const days = daysInStage(deal, nowMs)\n\n return (\n <CardDisplay\n header={'Stage'}\n help={pluginDocsHelp('deals', { anchor: '#moving-winning-and-losing' })}\n actions={\n !closed ? (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"success\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiTrophyOutline.path} size={0.8} />}\n onClick={() => void run(() => api.markWon(deal), 'Deal won')}\n >\n {'Won'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiThumbDownOutline.path} size={0.8} />}\n onClick={() => setLosing(true)}\n >\n {'Lost'}\n </Button>\n </Stack>\n ) : null\n }\n contentGutterX\n contentGutterY\n >\n {!pipeline ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\"This deal's pipeline no longer exists, so it has no stages to move through.\"}\n </Typography>\n ) : closed ? (\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={DEAL_STATUS_LABELS[deal.status]}\n color={deal.status === 'won' ? 'success' : 'default'}\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {typeof deal.closedAtMs === 'number'\n ? `Closed ${new Date(deal.closedAtMs).toLocaleDateString()}`\n : 'Closed'}\n </Typography>\n </Stack>\n {deal.status === 'lost' && deal.lostReason ? (\n <Typography variant=\"body2\">{deal.lostReason}</Typography>\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Reopen into\"\n value=\"\"\n disabled={busy}\n onChange={(event) => {\n const stageId = event.target.value\n if (!stageId) return\n void run(() => api.moveToStage(deal, stageId), 'Deal reopened')\n }}\n helperText=\"Reopening puts the deal back in an open stage and tells the automations.\"\n sx={{ maxWidth: 320 }}\n >\n <MenuItem value=\"\">{'Pick a stage'}</MenuItem>\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <Stepper nonLinear activeStep={activeIndex} alternativeLabel>\n {stages.map((stage, index) => (\n <Step key={stage.id} completed={index < activeIndex}>\n <StepButton\n disabled={busy || stage.id === deal.stageId}\n onClick={() =>\n void run(\n () => api.moveToStage(deal, stage.id),\n `Moved to ${stage.name}`,\n )\n }\n >\n {stage.name}\n </StepButton>\n </Step>\n ))}\n </Stepper>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ textAlign: 'center' }}>\n {activeIndex === -1\n ? 'This deal sits in a stage the pipeline no longer has. Pick one above.'\n : `${days} ${days === 1 ? 'day' : 'days'} in ${stages[activeIndex].name} · ` +\n `${stages[activeIndex].probability}% likely to close`}\n </Typography>\n </Stack>\n )}\n <LostReasonDialog\n open={losing}\n dealTitle={deal.title}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) =>\n void run(() => api.markLost(deal, reason), 'Deal marked lost').then(\n (ok) => ok && setLosing(false),\n )\n }\n />\n </CardDisplay>\n )\n}\nDealStageCard.displayName = 'DealStageCard'\n\nexport default DealStageCard\n"],"names":["pluginDocsHelp","mdiThumbDownOutline","mdiTrophyOutline","CardDisplay","MdiIcon","useSnackbar","Button","Chip","MenuItem","Stack","Step","StepButton","Stepper","TextField","Typography","useCallback","useState","daysInStage","DEAL_STATUS_LABELS","openStages","LostReasonDialog","DealStageCard","props","deal","pipeline","api","nowMs","enqueueSnackbar","busy","setBusy","losing","setLosing","stages","activeIndex","findIndex","stage","id","stageId","run","request","done","variant","persist","error","Error","message","allowDuplicate","closed","status","days","header","help","anchor","actions","direction","spacing","size","color","disabled","startIcon","path","onClick","markWon","contentGutterX","contentGutterY","sx","alignItems","label","closedAtMs","Date","toLocaleDateString","lostReason","select","value","onChange","event","target","moveToStage","helperText","maxWidth","map","name","nonLinear","activeStep","alternativeLabel","index","completed","textAlign","probability","open","dealTitle","title","onClose","onConfirm","reason","markLost","then","ok","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-stage-card.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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiThumbDownOutline, mdiTrophyOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Stack,\n Step,\n StepButton,\n Stepper,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState } from 'react'\nimport { type DealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n daysInStage,\n DEAL_STATUS_LABELS,\n type DealDoc,\n openStages,\n type PipelineDoc,\n} from '../model/deal-board-model'\nimport { LostReasonDialog } from './lost-reason-dialog'\n\nexport interface DealStageCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n api: DealStageApi\n nowMs: number\n}\n\n/**\n * Where the deal is, and the controls that move it (AGL-2598).\n *\n * A stepper across the open stages — click one to move there — and the two\n * closing buttons. Every one of them calls the stage route, never Firestore:\n * the move is what an automation listens for, and the route is the only\n * writer that emits it. On a closed deal the stepper gives way to the\n * verdict — Won or Lost, when, and why — and a way back: reopening is a\n * move into an open stage, which the route emits as `dealStageChanged` like\n * any other move.\n */\nexport function DealStageCard(props: DealStageCardProps) {\n const { deal, pipeline, api, nowMs } = props\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n const [losing, setLosing] = useState(false)\n const stages = openStages(pipeline)\n const activeIndex = stages.findIndex((stage) => stage.id === deal.stageId)\n\n const run = useCallback(\n async (request: () => Promise<unknown>, done: string) => {\n setBusy(true)\n try {\n await request()\n enqueueSnackbar(done, { variant: 'success', persist: false })\n return true\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The deal could not be moved.',\n { variant: 'warning', allowDuplicate: true },\n )\n return false\n } finally {\n setBusy(false)\n }\n },\n [enqueueSnackbar],\n )\n\n const closed = deal.status !== 'open'\n const days = daysInStage(deal, nowMs)\n\n return (\n <CardDisplay\n header={'Stage'}\n help={pluginDocsHelp('deals', { anchor: '#moving-winning-and-losing' })}\n HeaderProps={{\n action: !closed ? (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"success\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiTrophyOutline.path} size={0.8} />}\n onClick={() => void run(() => api.markWon(deal), 'Deal won')}\n >\n {'Won'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiThumbDownOutline.path} size={0.8} />}\n onClick={() => setLosing(true)}\n >\n {'Lost'}\n </Button>\n </Stack>\n ) : null,\n }}\n contentGutterX\n contentGutterY\n >\n {!pipeline ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\"This deal's pipeline no longer exists, so it has no stages to move through.\"}\n </Typography>\n ) : closed ? (\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={DEAL_STATUS_LABELS[deal.status]}\n color={deal.status === 'won' ? 'success' : 'default'}\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {typeof deal.closedAtMs === 'number'\n ? `Closed ${new Date(deal.closedAtMs).toLocaleDateString()}`\n : 'Closed'}\n </Typography>\n </Stack>\n {deal.status === 'lost' && deal.lostReason ? (\n <Typography variant=\"body2\">{deal.lostReason}</Typography>\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Reopen into\"\n value=\"\"\n disabled={busy}\n onChange={(event) => {\n const stageId = event.target.value\n if (!stageId) return\n void run(() => api.moveToStage(deal, stageId), 'Deal reopened')\n }}\n helperText=\"Reopening puts the deal back in an open stage and tells the automations.\"\n sx={{ maxWidth: 320 }}\n >\n <MenuItem value=\"\">{'Pick a stage'}</MenuItem>\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <Stepper nonLinear activeStep={activeIndex} alternativeLabel>\n {stages.map((stage, index) => (\n <Step key={stage.id} completed={index < activeIndex}>\n <StepButton\n disabled={busy || stage.id === deal.stageId}\n onClick={() =>\n void run(\n () => api.moveToStage(deal, stage.id),\n `Moved to ${stage.name}`,\n )\n }\n >\n {stage.name}\n </StepButton>\n </Step>\n ))}\n </Stepper>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ textAlign: 'center' }}>\n {activeIndex === -1\n ? 'This deal sits in a stage the pipeline no longer has. Pick one above.'\n : `${days} ${days === 1 ? 'day' : 'days'} in ${stages[activeIndex].name} · ` +\n `${stages[activeIndex].probability}% likely to close`}\n </Typography>\n </Stack>\n )}\n <LostReasonDialog\n open={losing}\n dealTitle={deal.title}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) =>\n void run(() => api.markLost(deal, reason), 'Deal marked lost').then(\n (ok) => ok && setLosing(false),\n )\n }\n />\n </CardDisplay>\n )\n}\nDealStageCard.displayName = 'DealStageCard'\n\nexport default DealStageCard\n"],"names":["pluginDocsHelp","mdiThumbDownOutline","mdiTrophyOutline","CardDisplay","MdiIcon","useSnackbar","Button","Chip","MenuItem","Stack","Step","StepButton","Stepper","TextField","Typography","useCallback","useState","daysInStage","DEAL_STATUS_LABELS","openStages","LostReasonDialog","DealStageCard","props","deal","pipeline","api","nowMs","enqueueSnackbar","busy","setBusy","losing","setLosing","stages","activeIndex","findIndex","stage","id","stageId","run","request","done","variant","persist","error","Error","message","allowDuplicate","closed","status","days","header","help","anchor","HeaderProps","action","direction","spacing","size","color","disabled","startIcon","path","onClick","markWon","contentGutterX","contentGutterY","sx","alignItems","label","closedAtMs","Date","toLocaleDateString","lostReason","select","value","onChange","event","target","moveToStage","helperText","maxWidth","map","name","nonLinear","activeStep","alternativeLabel","index","completed","textAlign","probability","open","dealTitle","title","onClose","onConfirm","reason","markLost","then","ok","displayName"],"mappings":"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"}
|
|
@@ -8,8 +8,8 @@ import { type ConsolePluginPageProps } from '@aglyn/aglyn';
|
|
|
8
8
|
* in one bounded listener over `(visibleTo, pipelineId, status, updatedAt)`
|
|
9
9
|
* and sorted into columns here — a listener per column would be five
|
|
10
10
|
* subscriptions for one board. The TABLE is the same pipeline's deals,
|
|
11
|
-
* paged by
|
|
12
|
-
* is found. The three figures above both come from the board's read, so
|
|
11
|
+
* paged by a query that carries its status, "No next activity" and search
|
|
12
|
+
* word (AGL-3321); it is where the closed history is found. The three figures above both come from the board's read, so
|
|
13
13
|
* they describe what is in play whichever view is showing.
|
|
14
14
|
*
|
|
15
15
|
* ## Which pipeline
|
|
@@ -15,19 +15,21 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
-
import {
|
|
18
|
+
import { CRM_COLLECTIONS, dealStageById, findOrgMember, ORG_SCOPE_TOKEN, pluginDocsHelp } from "@aglyn/aglyn";
|
|
19
19
|
import { mdiCogOutline, mdiPlus, mdiTableLarge, mdiViewColumnOutline } from "@aglyn/shared-data-mdi";
|
|
20
20
|
import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
22
22
|
import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
|
|
23
|
+
import ListQueryNotices, { listQueryRefusals } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
|
|
23
24
|
import { useCrmSavedView } from "../hooks/use-crm-saved-view.js";
|
|
24
25
|
import { useCrmViewGrid } from "../hooks/use-crm-view-grid.js";
|
|
25
26
|
import { CrmListActions, CrmListToolbar } from "./crm-list-toolbar.js";
|
|
26
27
|
import { CRM_LIST_SLOTS, CrmColumnOrderProvider } from "./crm-column-menu.js";
|
|
27
28
|
import { CRM_NEXT_ACTIVITY_FILTER_FIELD, CRM_NEXT_ACTIVITY_FILTER_HEADER, nextActivityColumn } from "./crm-next-activity-column.js";
|
|
29
|
+
import { DEAL_LIST_DECLARATION, DEAL_LIST_FILTER_FIELDS, DEAL_PREFIX_SEARCH, dealPipelineBase } from "../constants/deal-filters.js";
|
|
30
|
+
import { useCrmFoldsScope, useCrmListQuery } from "../hooks/use-crm-list-query.js";
|
|
28
31
|
import ListFilterChips from "@aglyn/shared-ui-jsx/components/list-filter-chips.component";
|
|
29
|
-
import { listFilterGridColumns
|
|
30
|
-
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
32
|
+
import { listFilterGridColumns } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
|
|
31
33
|
import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
|
|
32
34
|
import CrmViewsControl from "./crm-views-control.js";
|
|
33
35
|
import EmptyStateComponent from "@aglyn/shared-ui-jsx/components/empty-state.component";
|
|
@@ -38,7 +40,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
38
40
|
import { useContactFieldDefinitions } from "../hooks/use-contact-field-definitions.js";
|
|
39
41
|
import { useCrmScope } from "../hooks/use-crm-scope.js";
|
|
40
42
|
import { useDealStageApi } from "../hooks/use-deal-stage-api.js";
|
|
41
|
-
import { BOARD_CLOSED_LIMIT, BOARD_OPEN_LIMIT,
|
|
43
|
+
import { BOARD_CLOSED_LIMIT, BOARD_OPEN_LIMIT, useDealsByStatus } from "../hooks/use-deals.js";
|
|
42
44
|
import { useOrgMemberDirectory } from "../hooks/use-org-member-directory.js";
|
|
43
45
|
import { usePipeline } from "../hooks/use-pipeline.js";
|
|
44
46
|
import { downloadTextFile } from "../model/contacts-csv.js";
|
|
@@ -53,24 +55,11 @@ import DealsBulkBar from "./deals-bulk-bar.js";
|
|
|
53
55
|
import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
54
56
|
import { OwnerAvatar } from "./owner-avatar.js";
|
|
55
57
|
import { PipelinesDialog } from "./pipelines-dialog.js";
|
|
56
|
-
/** What the deals table's panel offers: the
|
|
57
|
-
{
|
|
58
|
-
column: 'status',
|
|
59
|
-
kind: 'exact',
|
|
60
|
-
path: 'status',
|
|
61
|
-
operators: [
|
|
62
|
-
'equals'
|
|
63
|
-
]
|
|
64
|
-
},
|
|
65
|
-
CRM_NEXT_ACTIVITY_FILTER_FIELD
|
|
66
|
-
];
|
|
58
|
+
/** What the deals table's panel offers: the status, and "No next activity". */ const DEAL_GRID_FILTER_FIELDS = DEAL_LIST_FILTER_FIELDS;
|
|
67
59
|
const DEAL_GRID_FILTER_HEADERS = {
|
|
68
60
|
status: 'Status',
|
|
69
61
|
[CRM_NEXT_ACTIVITY_FILTER_FIELD.column]: CRM_NEXT_ACTIVITY_FILTER_HEADER
|
|
70
62
|
};
|
|
71
|
-
/** What the table's search reads on a deal. */ const DEAL_SEARCH_FIELDS = [
|
|
72
|
-
'title'
|
|
73
|
-
];
|
|
74
63
|
const DEAL_FILTER_OPTIONS = {
|
|
75
64
|
status: [
|
|
76
65
|
'open',
|
|
@@ -93,8 +82,8 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
93
82
|
* in one bounded listener over `(visibleTo, pipelineId, status, updatedAt)`
|
|
94
83
|
* and sorted into columns here — a listener per column would be five
|
|
95
84
|
* subscriptions for one board. The TABLE is the same pipeline's deals,
|
|
96
|
-
* paged by
|
|
97
|
-
* is found. The three figures above both come from the board's read, so
|
|
85
|
+
* paged by a query that carries its status, "No next activity" and search
|
|
86
|
+
* word (AGL-3321); it is where the closed history is found. The three figures above both come from the board's read, so
|
|
98
87
|
* they describe what is in play whichever view is showing.
|
|
99
88
|
*
|
|
100
89
|
* ## Which pipeline
|
|
@@ -127,7 +116,7 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
127
116
|
* `dealsCsv()`, naming the pipeline, the stage and the owner; the bar
|
|
128
117
|
* writes the same file over the selection.
|
|
129
118
|
*/ export function DealsSection(props) {
|
|
130
|
-
var _ref, _ref1, _ref2, _ref3,
|
|
119
|
+
var _ref, _ref1, _ref2, _ref3, _scope_orgId, _ref4;
|
|
131
120
|
const { hostId, org, basePath } = props;
|
|
132
121
|
const routes = crmRoutes(basePath != null ? basePath : '');
|
|
133
122
|
const router = useRouter();
|
|
@@ -171,8 +160,7 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
171
160
|
}, [
|
|
172
161
|
views.state.filters
|
|
173
162
|
]);
|
|
174
|
-
//
|
|
175
|
-
// it (AGL-2661) narrows the loaded page and survives a status change.
|
|
163
|
+
// Every clause is the query's, the status and "No next activity" alike.
|
|
176
164
|
const viewFilters = views.state.filters;
|
|
177
165
|
useEffect(()=>{
|
|
178
166
|
if (views.currentId) setView('table');
|
|
@@ -185,29 +173,33 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
185
173
|
const closedPipelineId = view === 'board' && closedExpanded ? (_ref2 = pipeline == null ? void 0 : pipeline.$id) != null ? _ref2 : null : null;
|
|
186
174
|
const won = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'won', BOARD_CLOSED_LIMIT);
|
|
187
175
|
const lost = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'lost', BOARD_CLOSED_LIMIT);
|
|
188
|
-
const paged = usePagedDeals(view === 'table' ? scope.orgId : null, scope.visibleTo, statusFilter, (_ref3 = pipeline == null ? void 0 : pipeline.$id) != null ? _ref3 : null);
|
|
189
176
|
/*
|
|
190
|
-
* THE
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
177
|
+
* THE TABLE (AGL-3321): the pipeline's deals, every clause and the search
|
|
178
|
+
* word on one query — the search reads the title's word prefixes every
|
|
179
|
+
* deal writer stamps, so a deal on page four is found by any word of its
|
|
180
|
+
* title — paged by that query, and read only while the table is showing.
|
|
194
181
|
*/ const [searchWords, setSearchWords] = useState([]);
|
|
195
182
|
const searching = searchWords.some((word)=>word.trim());
|
|
196
|
-
const searchWindow = useDealSearchWindow(view === 'table' && searching ? scope.orgId : null, scope.visibleTo, statusFilter, (_ref4 = pipeline == null ? void 0 : pipeline.$id) != null ? _ref4 : null);
|
|
197
183
|
const searchKey = searchWords.join(' ');
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const searchTruncated = searchWindow.data.length > DEAL_SEARCH_WINDOW;
|
|
205
|
-
const [searchPage, setSearchPage] = useState(0);
|
|
206
|
-
const [searchPageSize, setSearchPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT);
|
|
207
|
-
useEffect(()=>setSearchPage(0), [
|
|
208
|
-
searchKey,
|
|
209
|
-
statusFilter
|
|
184
|
+
const foldsScope = useCrmFoldsScope(scope.orgId, scope.visibleTo);
|
|
185
|
+
const pipelineBase = useMemo(()=>{
|
|
186
|
+
var _ref;
|
|
187
|
+
return dealPipelineBase((_ref = pipeline == null ? void 0 : pipeline.$id) != null ? _ref : null);
|
|
188
|
+
}, [
|
|
189
|
+
pipeline == null ? void 0 : pipeline.$id
|
|
210
190
|
]);
|
|
191
|
+
const paged = useCrmListQuery({
|
|
192
|
+
scope: scope.scope,
|
|
193
|
+
collection: CRM_COLLECTIONS.deals,
|
|
194
|
+
visibleTo: scope.visibleTo,
|
|
195
|
+
foldsScope,
|
|
196
|
+
declaration: DEAL_LIST_DECLARATION,
|
|
197
|
+
clauses: viewFilters,
|
|
198
|
+
search: searchWords,
|
|
199
|
+
base: pipelineBase,
|
|
200
|
+
prefixSearch: DEAL_PREFIX_SEARCH,
|
|
201
|
+
enabled: view === 'table' && Boolean(pipeline)
|
|
202
|
+
});
|
|
211
203
|
const summary = useMemo(()=>boardSummary(open.data, pipeline), [
|
|
212
204
|
open.data,
|
|
213
205
|
pipeline
|
|
@@ -276,7 +268,7 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
276
268
|
// A page or a status is a different set of rows; a selection made on
|
|
277
269
|
// the last one would be a count over rows no longer on screen.
|
|
278
270
|
useEffect(()=>setSelectedIds([]), [
|
|
279
|
-
|
|
271
|
+
paged.plan,
|
|
280
272
|
paged.page
|
|
281
273
|
]);
|
|
282
274
|
const csvOptions = useMemo(()=>({
|
|
@@ -296,12 +288,10 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
296
288
|
pipelineState,
|
|
297
289
|
roster.members
|
|
298
290
|
]);
|
|
299
|
-
// The page on screen
|
|
291
|
+
// The page on screen; Export all on the bulk bar takes the whole list.
|
|
300
292
|
const handleExport = useCallback(()=>{
|
|
301
|
-
downloadTextFile('deals.csv', 'text/csv', dealsCsv(
|
|
293
|
+
downloadTextFile('deals.csv', 'text/csv', dealsCsv(paged.rows, csvOptions));
|
|
302
294
|
}, [
|
|
303
|
-
searching,
|
|
304
|
-
found,
|
|
305
295
|
paged.rows,
|
|
306
296
|
csvOptions
|
|
307
297
|
]);
|
|
@@ -432,23 +422,11 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
432
422
|
dealFields.active,
|
|
433
423
|
nowMs
|
|
434
424
|
]);
|
|
435
|
-
/** The table's rows: the query's page
|
|
436
|
-
found,
|
|
437
|
-
viewFilters
|
|
438
|
-
]);
|
|
439
|
-
const tableRows = useMemo(()=>searching ? searchRows.slice(searchPage * searchPageSize, (searchPage + 1) * searchPageSize) : filterByNextActivity(paged.rows, viewFilters), [
|
|
440
|
-
searching,
|
|
441
|
-
searchRows,
|
|
442
|
-
searchPage,
|
|
443
|
-
searchPageSize,
|
|
444
|
-
paged.rows,
|
|
445
|
-
viewFilters
|
|
446
|
-
]);
|
|
425
|
+
/** The table's rows: the query's page. */ const tableRows = paged.rows;
|
|
447
426
|
/*
|
|
448
427
|
* The table's column and sort models are the view's (AGL-2617); its
|
|
449
|
-
* filters are the grid's own panel over the view's clauses (AGL-3313)
|
|
450
|
-
*
|
|
451
|
-
* "No next activity" stands beside it over the loaded page.
|
|
428
|
+
* filters are the grid's own panel over the view's clauses (AGL-3313),
|
|
429
|
+
* every one of them asked of the query (AGL-3321).
|
|
452
430
|
*/ const filterColumns = useMemo(()=>listFilterGridColumns(columns, DEAL_GRID_FILTER_FIELDS, DEAL_FILTER_OPTIONS, DEAL_GRID_FILTER_HEADERS), [
|
|
453
431
|
columns
|
|
454
432
|
]);
|
|
@@ -459,13 +437,19 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
459
437
|
selectFields: [
|
|
460
438
|
'status'
|
|
461
439
|
],
|
|
462
|
-
single: true,
|
|
463
|
-
keepAlongside: isNoNextActivityClause,
|
|
464
440
|
search: {
|
|
465
441
|
words: searchWords,
|
|
466
442
|
onChange: setSearchWords
|
|
467
443
|
}
|
|
468
444
|
});
|
|
445
|
+
// What the query could not hold, named by the clause the reader set.
|
|
446
|
+
const refused = useMemo(()=>listQueryRefusals(paged.plan.refused, {
|
|
447
|
+
fields: DEAL_GRID_FILTER_FIELDS,
|
|
448
|
+
headers: DEAL_GRID_FILTER_HEADERS,
|
|
449
|
+
options: DEAL_FILTER_OPTIONS
|
|
450
|
+
}), [
|
|
451
|
+
paged.plan.refused
|
|
452
|
+
]);
|
|
469
453
|
const noOrg = scope.ready && !scope.orgId;
|
|
470
454
|
const pipelineLoading = !noOrg && (pipelineState.status === 'loading' || pipelineState.seeding || !pipeline && pipelineState.status !== 'error');
|
|
471
455
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
@@ -481,6 +465,15 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
481
465
|
// The record actions, top right and never clipped (AGL-3311).
|
|
482
466
|
action: /*#__PURE__*/ _jsxs(CrmListActions, {
|
|
483
467
|
children: [
|
|
468
|
+
/*#__PURE__*/ _jsx(DealImportButton, {
|
|
469
|
+
hostId: hostId
|
|
470
|
+
}),
|
|
471
|
+
view === 'table' ? /*#__PURE__*/ _jsx(Button, {
|
|
472
|
+
size: "small",
|
|
473
|
+
onClick: handleExport,
|
|
474
|
+
disabled: !paged.rows.length,
|
|
475
|
+
children: 'Export CSV'
|
|
476
|
+
}) : null,
|
|
484
477
|
/*#__PURE__*/ _jsx(Button, {
|
|
485
478
|
size: "small",
|
|
486
479
|
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
@@ -538,7 +531,7 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
538
531
|
select: true,
|
|
539
532
|
size: "small",
|
|
540
533
|
label: "Pipeline",
|
|
541
|
-
value: (
|
|
534
|
+
value: (_ref3 = pipeline == null ? void 0 : pipeline.$id) != null ? _ref3 : '',
|
|
542
535
|
onChange: (event)=>setPipelineId(event.target.value),
|
|
543
536
|
sx: {
|
|
544
537
|
minWidth: 160
|
|
@@ -673,24 +666,14 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
673
666
|
clauses: viewFilters,
|
|
674
667
|
onChange: views.setFilters,
|
|
675
668
|
options: DEAL_FILTER_OPTIONS,
|
|
676
|
-
|
|
677
|
-
}),
|
|
678
|
-
/*#__PURE__*/ _jsx(Stack, {
|
|
679
|
-
sx: {
|
|
680
|
-
flex: 1
|
|
681
|
-
}
|
|
682
|
-
}),
|
|
683
|
-
/*#__PURE__*/ _jsx(DealImportButton, {
|
|
684
|
-
hostId: hostId
|
|
685
|
-
}),
|
|
686
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
687
|
-
size: "small",
|
|
688
|
-
onClick: handleExport,
|
|
689
|
-
disabled: !(searching ? found : paged.rows).length,
|
|
690
|
-
children: 'Export CSV'
|
|
669
|
+
marksServed: false
|
|
691
670
|
})
|
|
692
671
|
]
|
|
693
672
|
}),
|
|
673
|
+
/*#__PURE__*/ _jsx(ListQueryNotices, {
|
|
674
|
+
refused: refused,
|
|
675
|
+
notices: paged.plan.notices
|
|
676
|
+
}),
|
|
694
677
|
/*#__PURE__*/ _jsxs(_Fragment, {
|
|
695
678
|
children: [
|
|
696
679
|
/*#__PURE__*/ _jsx(DealsBulkBar, {
|
|
@@ -715,14 +698,12 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
715
698
|
onChange: setSelectedIds
|
|
716
699
|
},
|
|
717
700
|
onOpen: (_id, row)=>openDeal(row),
|
|
718
|
-
// The panel
|
|
719
|
-
// narrows the loaded rows (AGL-3313); the search runs
|
|
720
|
-
// over every deal in the window (AGL-3315).
|
|
701
|
+
// The panel and the search are the query's (AGL-3321).
|
|
721
702
|
filterMode: "server",
|
|
722
703
|
filterModel: gridFilter.filterModel,
|
|
723
704
|
onFilterModelChange: gridFilter.onFilterModelChange,
|
|
724
705
|
quickFilter: true,
|
|
725
|
-
loading:
|
|
706
|
+
loading: paged.status === 'loading',
|
|
726
707
|
// An empty table is said inside the grid, so its toolbar
|
|
727
708
|
// stays to change the status that emptied it.
|
|
728
709
|
noRowsLabel: searching ? `No deals match “${searchKey.trim()}”` : statusFilter === 'all' ? 'No deals yet' : `No ${statusFilter} deals`,
|
|
@@ -746,25 +727,14 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
746
727
|
hideFooter: true
|
|
747
728
|
})
|
|
748
729
|
}),
|
|
749
|
-
|
|
750
|
-
page: searchPage,
|
|
751
|
-
pageSize: searchPageSize,
|
|
752
|
-
rowCount: tableRows.length,
|
|
753
|
-
count: searchRows.length,
|
|
754
|
-
onPageChange: setSearchPage,
|
|
755
|
-
onPageSizeChange: setSearchPageSize
|
|
756
|
-
}) : /*#__PURE__*/ _jsx(ListPagination, {
|
|
730
|
+
/*#__PURE__*/ _jsx(ListPagination, {
|
|
757
731
|
page: paged.page,
|
|
758
732
|
pageSize: paged.pageSize,
|
|
759
733
|
rowCount: paged.rows.length,
|
|
760
734
|
hasMore: paged.hasMore,
|
|
761
735
|
onPageChange: paged.setPage,
|
|
762
736
|
onPageSizeChange: paged.setPageSize
|
|
763
|
-
})
|
|
764
|
-
searching && searchTruncated ? /*#__PURE__*/ _jsx(Alert, {
|
|
765
|
-
severity: "info",
|
|
766
|
-
children: `The search reads the ${DEAL_SEARCH_WINDOW.toLocaleString()} most recently ` + 'changed deals; narrow by status to reach older ones.'
|
|
767
|
-
}) : null
|
|
737
|
+
})
|
|
768
738
|
]
|
|
769
739
|
})
|
|
770
740
|
]
|
|
@@ -797,7 +767,7 @@ const DEAL_FILTER_OPTIONS = {
|
|
|
797
767
|
}),
|
|
798
768
|
/*#__PURE__*/ _jsx(LostReasonDialog, {
|
|
799
769
|
open: Boolean(losing),
|
|
800
|
-
dealTitle: (
|
|
770
|
+
dealTitle: (_ref4 = losing == null ? void 0 : losing.title) != null ? _ref4 : '',
|
|
801
771
|
onClose: ()=>setLosing(null),
|
|
802
772
|
onConfirm: confirmLost
|
|
803
773
|
})
|