@aglyn/plugins-crm 1.0.0-beta.181 → 1.0.0-beta.183
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/companies-section.js +2 -6
- package/src/lib/components/companies-section.js.map +1 -1
- package/src/lib/components/crm-list-toolbar.d.ts +27 -0
- package/src/lib/components/crm-list-toolbar.js +63 -0
- package/src/lib/components/crm-list-toolbar.js.map +1 -0
- package/src/lib/components/deals-section.js +48 -49
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/leads-section.js +166 -162
- package/src/lib/components/leads-section.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deals-section.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 {\n type ConsolePluginPageProps,\n type CrmDealStatus,\n dealStageById,\n filterByNextActivity,\n findOrgMember,\n isNoNextActivityClause,\n ORG_SCOPE_TOKEN,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n mdiCogOutline,\n mdiPlus,\n mdiTableLarge,\n mdiViewColumnOutline,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { useCrmSavedView } from '../hooks/use-crm-saved-view'\nimport { useCrmViewGrid } from '../hooks/use-crm-view-grid'\nimport { CRM_LIST_SLOTS, CrmColumnOrderProvider } from './crm-column-menu'\nimport { NoNextActivityToggle, nextActivityColumn } from './crm-next-activity-column'\nimport CrmViewsControl from './crm-views-control'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n CircularProgress,\n MenuItem,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useContactFieldDefinitions } from '../hooks/use-contact-field-definitions'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useDealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n BOARD_CLOSED_LIMIT,\n BOARD_OPEN_LIMIT,\n useDealsByStatus,\n usePagedDeals,\n} from '../hooks/use-deals'\nimport { useOrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { usePipeline } from '../hooks/use-pipeline'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmRoutes } from '../model/crm-routes'\nimport { type DealCsvOptions, dealsCsv } from '../model/deals-csv'\nimport {\n boardSummary,\n DEAL_STATUS_LABELS,\n type DealDoc,\n formatAmountByCurrency,\n formatMoney,\n} from '../model/deal-board-model'\nimport { customFieldColumns } from './contact-custom-columns'\nimport { DealBoard } from './deal-board'\nimport { DealEditDrawer } from './deal-edit-drawer'\nimport { DealImportButton } from './deal-import-drawer'\nimport DealsBulkBar from './deals-bulk-bar'\nimport { LostReasonDialog } from './lost-reason-dialog'\nimport { OwnerAvatar } from './owner-avatar'\nimport { PipelinesDialog } from './pipelines-dialog'\n\ntype View = 'board' | 'table'\ntype StatusFilter = CrmDealStatus | 'all'\n\n\n/** What a pipeline made at the organization level is stamped with — the org's own token. */\nconst ORG_PIPELINE_TOKENS: readonly string[] = [ORG_SCOPE_TOKEN]\n\n/**\n * `/crm/deals` — the pipeline (AGL-2598).\n *\n * ## Two views of one pipeline\n *\n * The BOARD is one pipeline's open deals, one column per open stage, read\n * in one bounded listener over `(visibleTo, pipelineId, status, updatedAt)`\n * and sorted into columns here — a listener per column would be five\n * subscriptions for one board. The TABLE is the same pipeline's deals,\n * paged by the query, with a status toggle; it is where the closed history\n * is found. The three figures above both come from the board's read, so\n * they describe what is in play whichever view is showing.\n *\n * ## Which pipeline\n *\n * The switcher in the header (AGL-2620) picks among the ACTIVE pipelines\n * and opens on the default; the board, the table, the figures and the New\n * deal drawer all follow it. It is section state rather than a route: a\n * pipeline is a lens on the section, and a saved view that names one\n * composes with it. **Pipelines** opens the dialog that creates, renames,\n * defaults, archives and — through it — edits the stages of each.\n *\n * ## Every move goes through the route\n *\n * A drag, a card's \"Move to\", \"Mark won\" and \"Mark lost\" all end in\n * `useDealStageApi`, never a Firestore write: the stage is the fact the\n * automations listen for, and only the server emits the event. The section\n * marks the card pending while the request runs and lets the listener land\n * it — see `DealBoard` for why a move is not applied optimistically.\n *\n * ## Creation is a button\n *\n * \"New deal\" opens a drawer; there is no form above the list. The drawer\n * writes client-direct against the rules, stamped with this console's scope.\n *\n * ## The table selects and exports (AGL-2621)\n *\n * The table's rows are selectable, and a selection raises `DealsBulkBar`\n * over it — whose stage moves go through the same `useDealStageApi` as a\n * drag. Export CSV beside the status toggle writes the page through\n * `dealsCsv()`, naming the pipeline, the stage and the owner; the bar\n * writes the same file over the selection.\n */\nexport function DealsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const routes = crmRoutes(basePath ?? '')\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n const scope = useCrmScope({ hostId, org })\n const pipelineState = usePipeline(scope.orgId, {\n hostId,\n org: (org ?? null) as Record<string, unknown> | null,\n })\n const { activePipelines } = pipelineState\n const [pipelineId, setPipelineId] = useState<string | null>(null)\n // The chosen pipeline while it is still active; else the default. A\n // pipeline archived from another tab falls back rather than leaving the\n // board on a pipeline the picker no longer lists.\n const pipeline =\n (pipelineId ? activePipelines.find((entry) => entry.$id === pipelineId) : null) ??\n pipelineState.pipeline\n const roster = useOrgMemberDirectory(scope.orgId)\n const api = useDealStageApi(hostId)\n const nowMs = useMemo(() => Date.now(), [])\n // The org's deal fields, for the table's optional columns (AGL-2661).\n const dealFields = useContactFieldDefinitions(scope.orgId, 'deal')\n\n const [view, setView] = useState<View>('board')\n const [closedExpanded, setClosedExpanded] = useState(false)\n /*\n * The table's status filter is the saved VIEW'S (AGL-2617): a view of\n * deals is a view of the table, and it holds the status beside the\n * columns and the sort. Opening one shows the table, because the board\n * has no columns to arrange and answers to the pipeline's stages alone.\n */\n const views = useCrmSavedView({\n section: 'deals',\n hostId,\n org,\n basePath: basePath ?? '',\n })\n const statusFilter: StatusFilter = useMemo(() => {\n const value = views.state.filters.find((clause) => clause.field === 'status')?.value\n return value === 'open' || value === 'won' || value === 'lost' ? value : 'all'\n }, [views.state.filters])\n // The status is the query's clause; the \"No next activity\" clause beside\n // it (AGL-2661) narrows the loaded page and survives a status change.\n const viewFilters = views.state.filters\n const setStatusFilter = useCallback(\n (next: StatusFilter) =>\n views.setFilters([\n ...(next === 'all' ? [] : [{ field: 'status', op: 'equals', value: next }]),\n ...viewFilters.filter(isNoNextActivityClause),\n ]),\n [views.setFilters, viewFilters],\n )\n useEffect(() => {\n if (views.currentId) setView('table')\n }, [views.currentId])\n\n // The board's read stays on in the table view too: it is what the summary\n // above both views is computed from, and it is already bounded.\n const open = useDealsByStatus(\n scope.orgId,\n scope.visibleTo,\n pipeline?.$id ?? null,\n 'open',\n BOARD_OPEN_LIMIT,\n )\n const closedPipelineId =\n view === 'board' && closedExpanded ? (pipeline?.$id ?? null) : null\n const won = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'won', BOARD_CLOSED_LIMIT)\n const lost = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'lost', BOARD_CLOSED_LIMIT)\n const paged = usePagedDeals(\n view === 'table' ? scope.orgId : null,\n scope.visibleTo,\n statusFilter,\n pipeline?.$id ?? null,\n )\n\n const summary = useMemo(() => boardSummary(open.data, pipeline), [open.data, pipeline])\n\n /*\n * MOVES IN FLIGHT. A card in this set is dimmed and cannot be dragged\n * again until the route answers; the listener is what moves it.\n */\n const [moving, setMoving] = useState<ReadonlySet<string>>(() => new Set())\n const track = useCallback(\n async (deal: DealDoc, request: () => Promise<unknown>, done: string) => {\n setMoving((current) => new Set(current).add(deal.$id))\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 setMoving((current) => {\n const next = new Set(current)\n next.delete(deal.$id)\n return next\n })\n }\n },\n [enqueueSnackbar],\n )\n const handleMove = useCallback(\n (deal: DealDoc, stageId: string) => {\n const stage = dealStageById(pipeline, stageId)\n void track(\n deal,\n () => api.moveToStage(deal, stageId),\n `Moved to ${stage?.name ?? 'stage'}`,\n )\n },\n [api, pipeline, track],\n )\n const handleWon = useCallback(\n (deal: DealDoc) => void track(deal, () => api.markWon(deal), 'Deal won'),\n [api, track],\n )\n const [losing, setLosing] = useState<DealDoc | null>(null)\n const handleLost = useCallback((deal: DealDoc) => setLosing(deal), [])\n const confirmLost = useCallback(\n (reason: string) => {\n const deal = losing\n if (!deal) return\n setLosing(null)\n void track(deal, () => api.markLost(deal, reason), 'Deal marked lost')\n },\n [api, losing, track],\n )\n\n const openDeal = useCallback(\n (deal: DealDoc) => router.push(routes.deal(deal.$id)),\n [router, routes],\n )\n\n const [creating, setCreating] = useState(false)\n const [managingPipelines, setManagingPipelines] = useState(false)\n\n const [selectedIds, setSelectedIds] = useState<string[]>([])\n // A page or a status is a different set of rows; a selection made on\n // the last one would be a count over rows no longer on screen.\n useEffect(() => setSelectedIds([]), [statusFilter, paged.page])\n const csvOptions: DealCsvOptions = useMemo(\n () => ({\n pipelineName: (id) => pipelineState.pipelineById(id)?.name,\n stageName: (pipelineId, stageId) =>\n dealStageById(pipelineState.pipelineById(pipelineId), stageId)?.name,\n ownerEmail: (uid) => {\n const member = findOrgMember(roster.members, uid)\n return member?.email || member?.label || uid\n },\n }),\n [pipelineState, roster.members],\n )\n const handleExport = useCallback(() => {\n downloadTextFile('deals.csv', 'text/csv', dealsCsv(paged.rows, csvOptions))\n }, [paged.rows, csvOptions])\n\n const columns: GridColDef[] = useMemo(\n () => [\n {\n field: 'title',\n headerName: 'Deal',\n flex: 1.6,\n minWidth: 220,\n renderCell: ({ row }: { row: DealDoc }) => {\n const withWhom = [row.contactName, row.companyName].filter(Boolean).join(' · ')\n return (\n <Stack sx={{ justifyContent: 'center', lineHeight: 1.25 }}>\n <Typography variant=\"body2\" sx={{ lineHeight: 1.25 }}>\n {row.title || 'Untitled deal'}\n </Typography>\n {withWhom ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap sx={{ lineHeight: 1.25 }}>\n {withWhom}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'stageId',\n headerName: 'Stage',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value: unknown, row: DealDoc) =>\n dealStageById(pipelineState.pipelineById(row.pipelineId), row.stageId)?.name ??\n row.stageId,\n },\n {\n field: 'amountCents',\n headerName: 'Amount',\n flex: 0.8,\n minWidth: 120,\n align: 'right',\n headerAlign: 'right',\n valueGetter: (_value: unknown, row: DealDoc) => row.amountCents ?? null,\n renderCell: ({ row }: { row: DealDoc }) =>\n typeof row.amountCents === 'number' ? formatMoney(row.amountCents, row.currency) : '—',\n },\n {\n field: 'ownerUid',\n headerName: 'Owner',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value: unknown, row: DealDoc) => roster.nameOf(row.ownerUid),\n renderCell: ({ row }: { row: DealDoc }) => {\n const label = roster.nameOf(row.ownerUid)\n return label ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <OwnerAvatar label={label} size={22} />\n <Typography variant=\"body2\" noWrap>\n {label}\n </Typography>\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )\n },\n },\n {\n field: 'expectedCloseAtMs',\n headerName: 'Expected close',\n flex: 0.8,\n minWidth: 130,\n type: 'date',\n valueGetter: (_value: unknown, row: DealDoc) =>\n typeof row.expectedCloseAtMs === 'number' ? new Date(row.expectedCloseAtMs) : null,\n renderCell: ({ row }: { row: DealDoc }) =>\n typeof row.expectedCloseAtMs === 'number'\n ? new Date(row.expectedCloseAtMs).toLocaleDateString()\n : '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 0.6,\n minWidth: 100,\n renderCell: ({ row }: { row: DealDoc }) => (\n <Chip\n size=\"small\"\n label={DEAL_STATUS_LABELS[row.status] ?? row.status}\n color={row.status === 'won' ? 'success' : row.status === 'lost' ? 'default' : 'primary'}\n variant={row.status === 'open' ? 'outlined' : 'filled'}\n />\n ),\n },\n // When the earliest open task against the deal is due (AGL-2661).\n nextActivityColumn(nowMs),\n // The org's deal fields as optional columns (AGL-2661).\n ...customFieldColumns(dealFields.active),\n ],\n [pipelineState, roster, dealFields.active, nowMs],\n )\n const tableRows = useMemo(\n () => filterByNextActivity(paged.rows, viewFilters),\n [paged.rows, viewFilters],\n )\n /* The table's column and sort models are the view's (AGL-2617). */\n const grid = useCrmViewGrid(views, columns)\n\n const noOrg = scope.ready && !scope.orgId\n const pipelineLoading =\n !noOrg && (pipelineState.status === 'loading' || pipelineState.seeding || (!pipeline && pipelineState.status !== 'error'))\n\n return (\n <>\n <CardDisplay\n header={'Deals'}\n help={pluginDocsHelp('deals', { anchor: '#the-board-and-the-table' })}\n actions={\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n {activePipelines.length > 1 ? (\n <TextField\n select\n size=\"small\"\n label=\"Pipeline\"\n value={pipeline?.$id ?? ''}\n onChange={(event) => setPipelineId(event.target.value)}\n sx={{ minWidth: 160 }}\n slotProps={{ select: { 'aria-label': 'Pipeline' } }}\n >\n {activePipelines.map((entry) => (\n <MenuItem key={entry.$id} value={entry.$id}>\n {entry.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiCogOutline.path} size={0.8} />}\n disabled={!scope.orgId || pipelineState.status === 'loading'}\n onClick={() => setManagingPipelines(true)}\n >\n {'Pipelines'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n </Stack>\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ alignItems: 'flex-end', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Figure label=\"Open deals\" value={summary.openCount.toLocaleString()} />\n <Figure label=\"Pipeline value\" value={formatAmountByCurrency(summary.valueByCurrency)} />\n <Figure label=\"Weighted value\" value={formatAmountByCurrency(summary.weightedByCurrency)} />\n <Stack sx={{ flex: 1 }} />\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n color=\"primary\"\n value={view}\n onChange={(_event, next) => {\n if (next) setView(next as View)\n }}\n aria-label=\"View\"\n >\n <ToggleButton value=\"board\" aria-label=\"Board\">\n <MdiIcon path={mdiViewColumnOutline.path} size={0.8} />\n <Typography variant=\"button\" sx={{ ml: 0.5 }}>\n {'Board'}\n </Typography>\n </ToggleButton>\n <ToggleButton value=\"table\" aria-label=\"Table\">\n <MdiIcon path={mdiTableLarge.path} size={0.8} />\n <Typography variant=\"button\" sx={{ ml: 0.5 }}>\n {'Table'}\n </Typography>\n </ToggleButton>\n </ToggleButtonGroup>\n </Stack>\n\n {noOrg ? (\n <Alert severity=\"info\">\n {'This site has no organization, so it has no pipeline.'}\n </Alert>\n ) : pipelineState.status === 'error' ? (\n <Alert severity=\"warning\">\n {'The pipeline could not be read. Deals need the \"Manage data\" permission.'}\n </Alert>\n ) : pipelineLoading ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={18} />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pipelineState.seeding ? 'Setting up your Sales pipeline…' : 'Loading the pipeline…'}\n </Typography>\n </Stack>\n ) : view === 'board' && pipeline ? (\n <>\n {open.status === 'success' && open.data.length === 0 ? (\n <EmptyStateComponent\n compact\n label={'No open deals yet'}\n description={'A deal is a sale in progress, moved across the stages below as it advances.'}\n action={\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n }\n />\n ) : null}\n <DealBoard\n pipeline={pipeline}\n deals={open.data}\n won={closedExpanded && won.status !== 'loading' ? won.data : null}\n lost={closedExpanded && lost.status !== 'loading' ? lost.data : null}\n closedExpanded={closedExpanded}\n onToggleClosed={() => setClosedExpanded((current) => !current)}\n labelFor={roster.nameOf}\n moving={moving}\n onOpen={openDeal}\n onMove={handleMove}\n onWon={handleWon}\n onLost={handleLost}\n nowMs={nowMs}\n />\n </>\n ) : (\n <Stack spacing={1.5}>\n {/* The view this table is showing, beside the status it narrows by (AGL-2617). */}\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <CrmViewsControl controller={views} allLabel=\"All deals\" />\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n value={statusFilter}\n onChange={(_event, next) => {\n if (next) setStatusFilter(next as StatusFilter)\n }}\n aria-label=\"Status\"\n >\n <ToggleButton value=\"all\">{'All'}</ToggleButton>\n <ToggleButton value=\"open\">{'Open'}</ToggleButton>\n <ToggleButton value=\"won\">{'Won'}</ToggleButton>\n <ToggleButton value=\"lost\">{'Lost'}</ToggleButton>\n </ToggleButtonGroup>\n <NoNextActivityToggle filters={viewFilters} onChange={views.setFilters} />\n <Stack sx={{ flex: 1 }} />\n <DealImportButton hostId={hostId} />\n <Button size=\"small\" onClick={handleExport} disabled={!paged.rows.length}>\n {'Export CSV'}\n </Button>\n </Stack>\n {paged.status === 'success' && paged.rows.length === 0 && paged.page === 0 ? (\n <EmptyStateComponent\n label={statusFilter === 'all' ? 'No deals yet' : `No ${statusFilter} deals`}\n description={\n statusFilter === 'all'\n ? 'A deal is a sale in progress, moved across the pipeline as it advances.'\n : undefined\n }\n action={\n statusFilter === 'all' ? (\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <>\n <DealsBulkBar\n hostId={hostId}\n scope={scope.scope}\n rows={tableRows}\n selected={selectedIds}\n onSelectedChange={setSelectedIds}\n pipelineById={pipelineState.pipelineById}\n roster={roster}\n api={api}\n csv={csvOptions}\n />\n <CrmColumnOrderProvider value={grid.columnOrder}>\n <ListTable\n rows={tableRows}\n columns={grid.columns}\n slots={CRM_LIST_SLOTS}\n selectable={{ selected: selectedIds, onChange: setSelectedIds }}\n onOpen={(_id, row) => openDeal(row as DealDoc)}\n // Columns and sort are the view's, controlled (AGL-2617).\n columnVisibilityModel={grid.columnVisibilityModel}\n onColumnVisibilityModelChange={grid.onColumnVisibilityModelChange}\n sortModel={grid.sortModel}\n onSortModelChange={grid.onSortModelChange}\n hideFooter\n />\n </CrmColumnOrderProvider>\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </>\n )}\n </Stack>\n )}\n </Stack>\n </CardDisplay>\n <DealEditDrawer\n open={creating}\n onClose={() => setCreating(false)}\n hostId={hostId}\n org={org}\n pipelines={activePipelines}\n defaultPipeline={pipeline}\n />\n <PipelinesDialog\n open={managingPipelines}\n onClose={() => setManagingPipelines(false)}\n orgId={scope.orgId ?? ''}\n // A pipeline made here is stamped the way the seeded default is\n // (`usePipeline`): the site's own tokens under a site; at the\n // organization level the org token, with the picked site as its\n // provenance (AGL-2630).\n hostId={scope.createHostId}\n pipelines={pipelineState.pipelines}\n fromCache={pipelineState.fromCache}\n unreadable={pipelineState.status === 'error'}\n visibleToTokens={scope.visibleTo}\n createTokens={scope.level === 'site' ? scope.createTokens : ORG_PIPELINE_TOKENS}\n />\n <LostReasonDialog\n open={Boolean(losing)}\n dealTitle={losing?.title ?? ''}\n onClose={() => setLosing(null)}\n onConfirm={confirmLost}\n />\n </>\n )\n}\nDealsSection.displayName = 'DealsSection'\n\nfunction Figure(props: { label: string; value: string }) {\n return (\n <Stack sx={{ lineHeight: 1.2 }}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {props.label}\n </Typography>\n <Typography variant=\"h6\" component=\"span\">\n {props.value}\n </Typography>\n </Stack>\n )\n}\nFigure.displayName = 'Figure'\n\nexport default DealsSection\n"],"names":["dealStageById","filterByNextActivity","findOrgMember","isNoNextActivityClause","ORG_SCOPE_TOKEN","pluginDocsHelp","mdiCogOutline","mdiPlus","mdiTableLarge","mdiViewColumnOutline","CardDisplay","MdiIcon","ListPagination","ListTable","useCrmSavedView","useCrmViewGrid","CRM_LIST_SLOTS","CrmColumnOrderProvider","NoNextActivityToggle","nextActivityColumn","CrmViewsControl","EmptyStateComponent","useSnackbar","Alert","Button","Chip","CircularProgress","MenuItem","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","useRouter","useCallback","useEffect","useMemo","useState","useContactFieldDefinitions","useCrmScope","useDealStageApi","BOARD_CLOSED_LIMIT","BOARD_OPEN_LIMIT","useDealsByStatus","usePagedDeals","useOrgMemberDirectory","usePipeline","downloadTextFile","crmRoutes","dealsCsv","boardSummary","DEAL_STATUS_LABELS","formatAmountByCurrency","formatMoney","customFieldColumns","DealBoard","DealEditDrawer","DealImportButton","DealsBulkBar","LostReasonDialog","OwnerAvatar","PipelinesDialog","ORG_PIPELINE_TOKENS","DealsSection","props","pipelineId","scope","hostId","org","basePath","routes","router","enqueueSnackbar","pipelineState","orgId","activePipelines","setPipelineId","pipeline","find","entry","$id","roster","api","nowMs","Date","now","dealFields","view","setView","closedExpanded","setClosedExpanded","views","section","statusFilter","value","state","filters","clause","field","viewFilters","setStatusFilter","next","setFilters","op","filter","currentId","open","visibleTo","closedPipelineId","won","lost","paged","summary","data","moving","setMoving","Set","track","deal","request","done","current","add","variant","persist","error","Error","message","allowDuplicate","delete","handleMove","stageId","stage","moveToStage","name","handleWon","markWon","losing","setLosing","handleLost","confirmLost","reason","markLost","openDeal","push","creating","setCreating","managingPipelines","setManagingPipelines","selectedIds","setSelectedIds","page","csvOptions","pipelineName","id","pipelineById","stageName","ownerEmail","uid","member","members","email","label","handleExport","rows","columns","headerName","flex","minWidth","renderCell","row","withWhom","contactName","companyName","Boolean","join","sx","justifyContent","lineHeight","title","color","noWrap","valueGetter","_value","align","headerAlign","amountCents","currency","nameOf","ownerUid","direction","spacing","alignItems","size","type","expectedCloseAtMs","toLocaleDateString","status","active","tableRows","grid","noOrg","ready","pipelineLoading","seeding","header","help","anchor","actions","length","select","onChange","event","target","slotProps","map","startIcon","path","disabled","onClick","contentGutterX","contentGutterY","flexWrap","rowGap","Figure","openCount","toLocaleString","valueByCurrency","weightedByCurrency","exclusive","_event","aria-label","ml","severity","compact","description","action","deals","onToggleClosed","labelFor","onOpen","onMove","onWon","onLost","controller","allLabel","undefined","selected","onSelectedChange","csv","columnOrder","slots","selectable","_id","columnVisibilityModel","onColumnVisibilityModelChange","sortModel","onSortModelChange","hideFooter","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","onClose","pipelines","defaultPipeline","createHostId","fromCache","unreadable","visibleToTokens","createTokens","level","dealTitle","onConfirm","displayName","component"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deals-section.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 {\n type ConsolePluginPageProps,\n type CrmDealStatus,\n dealStageById,\n filterByNextActivity,\n findOrgMember,\n isNoNextActivityClause,\n ORG_SCOPE_TOKEN,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n mdiCogOutline,\n mdiPlus,\n mdiTableLarge,\n mdiViewColumnOutline,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { useCrmSavedView } from '../hooks/use-crm-saved-view'\nimport { useCrmViewGrid } from '../hooks/use-crm-view-grid'\nimport { CrmListActions } from './crm-list-toolbar'\nimport { CRM_LIST_SLOTS, CrmColumnOrderProvider } from './crm-column-menu'\nimport { NoNextActivityToggle, nextActivityColumn } from './crm-next-activity-column'\nimport CrmViewsControl from './crm-views-control'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n CircularProgress,\n MenuItem,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useContactFieldDefinitions } from '../hooks/use-contact-field-definitions'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useDealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n BOARD_CLOSED_LIMIT,\n BOARD_OPEN_LIMIT,\n useDealsByStatus,\n usePagedDeals,\n} from '../hooks/use-deals'\nimport { useOrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { usePipeline } from '../hooks/use-pipeline'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmRoutes } from '../model/crm-routes'\nimport { type DealCsvOptions, dealsCsv } from '../model/deals-csv'\nimport {\n boardSummary,\n DEAL_STATUS_LABELS,\n type DealDoc,\n formatAmountByCurrency,\n formatMoney,\n} from '../model/deal-board-model'\nimport { customFieldColumns } from './contact-custom-columns'\nimport { DealBoard } from './deal-board'\nimport { DealEditDrawer } from './deal-edit-drawer'\nimport { DealImportButton } from './deal-import-drawer'\nimport DealsBulkBar from './deals-bulk-bar'\nimport { LostReasonDialog } from './lost-reason-dialog'\nimport { OwnerAvatar } from './owner-avatar'\nimport { PipelinesDialog } from './pipelines-dialog'\n\ntype View = 'board' | 'table'\ntype StatusFilter = CrmDealStatus | 'all'\n\n\n/** What a pipeline made at the organization level is stamped with — the org's own token. */\nconst ORG_PIPELINE_TOKENS: readonly string[] = [ORG_SCOPE_TOKEN]\n\n/**\n * `/crm/deals` — the pipeline (AGL-2598).\n *\n * ## Two views of one pipeline\n *\n * The BOARD is one pipeline's open deals, one column per open stage, read\n * in one bounded listener over `(visibleTo, pipelineId, status, updatedAt)`\n * and sorted into columns here — a listener per column would be five\n * subscriptions for one board. The TABLE is the same pipeline's deals,\n * paged by the query, with a status toggle; it is where the closed history\n * is found. The three figures above both come from the board's read, so\n * they describe what is in play whichever view is showing.\n *\n * ## Which pipeline\n *\n * The switcher in the header (AGL-2620) picks among the ACTIVE pipelines\n * and opens on the default; the board, the table, the figures and the New\n * deal drawer all follow it. It is section state rather than a route: a\n * pipeline is a lens on the section, and a saved view that names one\n * composes with it. **Pipelines** opens the dialog that creates, renames,\n * defaults, archives and — through it — edits the stages of each.\n *\n * ## Every move goes through the route\n *\n * A drag, a card's \"Move to\", \"Mark won\" and \"Mark lost\" all end in\n * `useDealStageApi`, never a Firestore write: the stage is the fact the\n * automations listen for, and only the server emits the event. The section\n * marks the card pending while the request runs and lets the listener land\n * it — see `DealBoard` for why a move is not applied optimistically.\n *\n * ## Creation is a button\n *\n * \"New deal\" opens a drawer; there is no form above the list. The drawer\n * writes client-direct against the rules, stamped with this console's scope.\n *\n * ## The table selects and exports (AGL-2621)\n *\n * The table's rows are selectable, and a selection raises `DealsBulkBar`\n * over it — whose stage moves go through the same `useDealStageApi` as a\n * drag. Export CSV beside the status toggle writes the page through\n * `dealsCsv()`, naming the pipeline, the stage and the owner; the bar\n * writes the same file over the selection.\n */\nexport function DealsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const routes = crmRoutes(basePath ?? '')\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n const scope = useCrmScope({ hostId, org })\n const pipelineState = usePipeline(scope.orgId, {\n hostId,\n org: (org ?? null) as Record<string, unknown> | null,\n })\n const { activePipelines } = pipelineState\n const [pipelineId, setPipelineId] = useState<string | null>(null)\n // The chosen pipeline while it is still active; else the default. A\n // pipeline archived from another tab falls back rather than leaving the\n // board on a pipeline the picker no longer lists.\n const pipeline =\n (pipelineId ? activePipelines.find((entry) => entry.$id === pipelineId) : null) ??\n pipelineState.pipeline\n const roster = useOrgMemberDirectory(scope.orgId)\n const api = useDealStageApi(hostId)\n const nowMs = useMemo(() => Date.now(), [])\n // The org's deal fields, for the table's optional columns (AGL-2661).\n const dealFields = useContactFieldDefinitions(scope.orgId, 'deal')\n\n const [view, setView] = useState<View>('board')\n const [closedExpanded, setClosedExpanded] = useState(false)\n /*\n * The table's status filter is the saved VIEW'S (AGL-2617): a view of\n * deals is a view of the table, and it holds the status beside the\n * columns and the sort. Opening one shows the table, because the board\n * has no columns to arrange and answers to the pipeline's stages alone.\n */\n const views = useCrmSavedView({\n section: 'deals',\n hostId,\n org,\n basePath: basePath ?? '',\n })\n const statusFilter: StatusFilter = useMemo(() => {\n const value = views.state.filters.find((clause) => clause.field === 'status')?.value\n return value === 'open' || value === 'won' || value === 'lost' ? value : 'all'\n }, [views.state.filters])\n // The status is the query's clause; the \"No next activity\" clause beside\n // it (AGL-2661) narrows the loaded page and survives a status change.\n const viewFilters = views.state.filters\n const setStatusFilter = useCallback(\n (next: StatusFilter) =>\n views.setFilters([\n ...(next === 'all' ? [] : [{ field: 'status', op: 'equals', value: next }]),\n ...viewFilters.filter(isNoNextActivityClause),\n ]),\n [views.setFilters, viewFilters],\n )\n useEffect(() => {\n if (views.currentId) setView('table')\n }, [views.currentId])\n\n // The board's read stays on in the table view too: it is what the summary\n // above both views is computed from, and it is already bounded.\n const open = useDealsByStatus(\n scope.orgId,\n scope.visibleTo,\n pipeline?.$id ?? null,\n 'open',\n BOARD_OPEN_LIMIT,\n )\n const closedPipelineId =\n view === 'board' && closedExpanded ? (pipeline?.$id ?? null) : null\n const won = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'won', BOARD_CLOSED_LIMIT)\n const lost = useDealsByStatus(scope.orgId, scope.visibleTo, closedPipelineId, 'lost', BOARD_CLOSED_LIMIT)\n const paged = usePagedDeals(\n view === 'table' ? scope.orgId : null,\n scope.visibleTo,\n statusFilter,\n pipeline?.$id ?? null,\n )\n\n const summary = useMemo(() => boardSummary(open.data, pipeline), [open.data, pipeline])\n\n /*\n * MOVES IN FLIGHT. A card in this set is dimmed and cannot be dragged\n * again until the route answers; the listener is what moves it.\n */\n const [moving, setMoving] = useState<ReadonlySet<string>>(() => new Set())\n const track = useCallback(\n async (deal: DealDoc, request: () => Promise<unknown>, done: string) => {\n setMoving((current) => new Set(current).add(deal.$id))\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 setMoving((current) => {\n const next = new Set(current)\n next.delete(deal.$id)\n return next\n })\n }\n },\n [enqueueSnackbar],\n )\n const handleMove = useCallback(\n (deal: DealDoc, stageId: string) => {\n const stage = dealStageById(pipeline, stageId)\n void track(\n deal,\n () => api.moveToStage(deal, stageId),\n `Moved to ${stage?.name ?? 'stage'}`,\n )\n },\n [api, pipeline, track],\n )\n const handleWon = useCallback(\n (deal: DealDoc) => void track(deal, () => api.markWon(deal), 'Deal won'),\n [api, track],\n )\n const [losing, setLosing] = useState<DealDoc | null>(null)\n const handleLost = useCallback((deal: DealDoc) => setLosing(deal), [])\n const confirmLost = useCallback(\n (reason: string) => {\n const deal = losing\n if (!deal) return\n setLosing(null)\n void track(deal, () => api.markLost(deal, reason), 'Deal marked lost')\n },\n [api, losing, track],\n )\n\n const openDeal = useCallback(\n (deal: DealDoc) => router.push(routes.deal(deal.$id)),\n [router, routes],\n )\n\n const [creating, setCreating] = useState(false)\n const [managingPipelines, setManagingPipelines] = useState(false)\n\n const [selectedIds, setSelectedIds] = useState<string[]>([])\n // A page or a status is a different set of rows; a selection made on\n // the last one would be a count over rows no longer on screen.\n useEffect(() => setSelectedIds([]), [statusFilter, paged.page])\n const csvOptions: DealCsvOptions = useMemo(\n () => ({\n pipelineName: (id) => pipelineState.pipelineById(id)?.name,\n stageName: (pipelineId, stageId) =>\n dealStageById(pipelineState.pipelineById(pipelineId), stageId)?.name,\n ownerEmail: (uid) => {\n const member = findOrgMember(roster.members, uid)\n return member?.email || member?.label || uid\n },\n }),\n [pipelineState, roster.members],\n )\n const handleExport = useCallback(() => {\n downloadTextFile('deals.csv', 'text/csv', dealsCsv(paged.rows, csvOptions))\n }, [paged.rows, csvOptions])\n\n const columns: GridColDef[] = useMemo(\n () => [\n {\n field: 'title',\n headerName: 'Deal',\n flex: 1.6,\n minWidth: 220,\n renderCell: ({ row }: { row: DealDoc }) => {\n const withWhom = [row.contactName, row.companyName].filter(Boolean).join(' · ')\n return (\n <Stack sx={{ justifyContent: 'center', lineHeight: 1.25 }}>\n <Typography variant=\"body2\" sx={{ lineHeight: 1.25 }}>\n {row.title || 'Untitled deal'}\n </Typography>\n {withWhom ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap sx={{ lineHeight: 1.25 }}>\n {withWhom}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'stageId',\n headerName: 'Stage',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value: unknown, row: DealDoc) =>\n dealStageById(pipelineState.pipelineById(row.pipelineId), row.stageId)?.name ??\n row.stageId,\n },\n {\n field: 'amountCents',\n headerName: 'Amount',\n flex: 0.8,\n minWidth: 120,\n align: 'right',\n headerAlign: 'right',\n valueGetter: (_value: unknown, row: DealDoc) => row.amountCents ?? null,\n renderCell: ({ row }: { row: DealDoc }) =>\n typeof row.amountCents === 'number' ? formatMoney(row.amountCents, row.currency) : '—',\n },\n {\n field: 'ownerUid',\n headerName: 'Owner',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value: unknown, row: DealDoc) => roster.nameOf(row.ownerUid),\n renderCell: ({ row }: { row: DealDoc }) => {\n const label = roster.nameOf(row.ownerUid)\n return label ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <OwnerAvatar label={label} size={22} />\n <Typography variant=\"body2\" noWrap>\n {label}\n </Typography>\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )\n },\n },\n {\n field: 'expectedCloseAtMs',\n headerName: 'Expected close',\n flex: 0.8,\n minWidth: 130,\n type: 'date',\n valueGetter: (_value: unknown, row: DealDoc) =>\n typeof row.expectedCloseAtMs === 'number' ? new Date(row.expectedCloseAtMs) : null,\n renderCell: ({ row }: { row: DealDoc }) =>\n typeof row.expectedCloseAtMs === 'number'\n ? new Date(row.expectedCloseAtMs).toLocaleDateString()\n : '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 0.6,\n minWidth: 100,\n renderCell: ({ row }: { row: DealDoc }) => (\n <Chip\n size=\"small\"\n label={DEAL_STATUS_LABELS[row.status] ?? row.status}\n color={row.status === 'won' ? 'success' : row.status === 'lost' ? 'default' : 'primary'}\n variant={row.status === 'open' ? 'outlined' : 'filled'}\n />\n ),\n },\n // When the earliest open task against the deal is due (AGL-2661).\n nextActivityColumn(nowMs),\n // The org's deal fields as optional columns (AGL-2661).\n ...customFieldColumns(dealFields.active),\n ],\n [pipelineState, roster, dealFields.active, nowMs],\n )\n const tableRows = useMemo(\n () => filterByNextActivity(paged.rows, viewFilters),\n [paged.rows, viewFilters],\n )\n /* The table's column and sort models are the view's (AGL-2617). */\n const grid = useCrmViewGrid(views, columns)\n\n const noOrg = scope.ready && !scope.orgId\n const pipelineLoading =\n !noOrg && (pipelineState.status === 'loading' || pipelineState.seeding || (!pipeline && pipelineState.status !== 'error'))\n\n return (\n <>\n <CardDisplay\n header={'Deals'}\n help={pluginDocsHelp('deals', { anchor: '#the-board-and-the-table' })}\n contentGutterX\n contentGutterY\n HeaderProps={{\n // The record actions, top right and never clipped (AGL-3311).\n action: (\n <CrmListActions>\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiCogOutline.path} size={0.8} />}\n disabled={!scope.orgId || pipelineState.status === 'loading'}\n onClick={() => setManagingPipelines(true)}\n >\n {'Pipelines'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n </CrmListActions>\n ),\n }}\n >\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ alignItems: 'flex-end', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Figure label=\"Open deals\" value={summary.openCount.toLocaleString()} />\n <Figure label=\"Pipeline value\" value={formatAmountByCurrency(summary.valueByCurrency)} />\n <Figure label=\"Weighted value\" value={formatAmountByCurrency(summary.weightedByCurrency)} />\n <Stack sx={{ flex: 1 }} />\n {/* Which pipeline the board and table show, above them rather than under (AGL-3311). */}\n {activePipelines.length > 1 ? (\n <TextField\n select\n size=\"small\"\n label=\"Pipeline\"\n value={pipeline?.$id ?? ''}\n onChange={(event) => setPipelineId(event.target.value)}\n sx={{ minWidth: 160 }}\n slotProps={{ select: { 'aria-label': 'Pipeline' } }}\n >\n {activePipelines.map((entry) => (\n <MenuItem key={entry.$id} value={entry.$id}>\n {entry.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n color=\"primary\"\n value={view}\n onChange={(_event, next) => {\n if (next) setView(next as View)\n }}\n aria-label=\"View\"\n >\n <ToggleButton value=\"board\" aria-label=\"Board\">\n <MdiIcon path={mdiViewColumnOutline.path} size={0.8} />\n <Typography variant=\"button\" sx={{ ml: 0.5 }}>\n {'Board'}\n </Typography>\n </ToggleButton>\n <ToggleButton value=\"table\" aria-label=\"Table\">\n <MdiIcon path={mdiTableLarge.path} size={0.8} />\n <Typography variant=\"button\" sx={{ ml: 0.5 }}>\n {'Table'}\n </Typography>\n </ToggleButton>\n </ToggleButtonGroup>\n </Stack>\n\n {noOrg ? (\n <Alert severity=\"info\">\n {'This site has no organization, so it has no pipeline.'}\n </Alert>\n ) : pipelineState.status === 'error' ? (\n <Alert severity=\"warning\">\n {'The pipeline could not be read. Deals need the \"Manage data\" permission.'}\n </Alert>\n ) : pipelineLoading ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={18} />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pipelineState.seeding ? 'Setting up your Sales pipeline…' : 'Loading the pipeline…'}\n </Typography>\n </Stack>\n ) : view === 'board' && pipeline ? (\n <>\n {open.status === 'success' && open.data.length === 0 ? (\n <EmptyStateComponent\n compact\n label={'No open deals yet'}\n description={'A deal is a sale in progress, moved across the stages below as it advances.'}\n action={\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n }\n />\n ) : null}\n <DealBoard\n pipeline={pipeline}\n deals={open.data}\n won={closedExpanded && won.status !== 'loading' ? won.data : null}\n lost={closedExpanded && lost.status !== 'loading' ? lost.data : null}\n closedExpanded={closedExpanded}\n onToggleClosed={() => setClosedExpanded((current) => !current)}\n labelFor={roster.nameOf}\n moving={moving}\n onOpen={openDeal}\n onMove={handleMove}\n onWon={handleWon}\n onLost={handleLost}\n nowMs={nowMs}\n />\n </>\n ) : (\n <Stack spacing={1.5}>\n {/* The view this table is showing, beside the status it narrows by (AGL-2617). */}\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <CrmViewsControl controller={views} allLabel=\"All deals\" />\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n value={statusFilter}\n onChange={(_event, next) => {\n if (next) setStatusFilter(next as StatusFilter)\n }}\n aria-label=\"Status\"\n >\n <ToggleButton value=\"all\">{'All'}</ToggleButton>\n <ToggleButton value=\"open\">{'Open'}</ToggleButton>\n <ToggleButton value=\"won\">{'Won'}</ToggleButton>\n <ToggleButton value=\"lost\">{'Lost'}</ToggleButton>\n </ToggleButtonGroup>\n <NoNextActivityToggle filters={viewFilters} onChange={views.setFilters} />\n <Stack sx={{ flex: 1 }} />\n <DealImportButton hostId={hostId} />\n <Button size=\"small\" onClick={handleExport} disabled={!paged.rows.length}>\n {'Export CSV'}\n </Button>\n </Stack>\n {paged.status === 'success' && paged.rows.length === 0 && paged.page === 0 ? (\n <EmptyStateComponent\n label={statusFilter === 'all' ? 'No deals yet' : `No ${statusFilter} deals`}\n description={\n statusFilter === 'all'\n ? 'A deal is a sale in progress, moved across the pipeline as it advances.'\n : undefined\n }\n action={\n statusFilter === 'all' ? (\n <Button\n size=\"small\"\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} size={0.8} />}\n disabled={!pipeline || !scope.orgId}\n onClick={() => setCreating(true)}\n >\n {'New deal'}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <>\n <DealsBulkBar\n hostId={hostId}\n scope={scope.scope}\n rows={tableRows}\n selected={selectedIds}\n onSelectedChange={setSelectedIds}\n pipelineById={pipelineState.pipelineById}\n roster={roster}\n api={api}\n csv={csvOptions}\n />\n <CrmColumnOrderProvider value={grid.columnOrder}>\n <ListTable\n rows={tableRows}\n columns={grid.columns}\n slots={CRM_LIST_SLOTS}\n selectable={{ selected: selectedIds, onChange: setSelectedIds }}\n onOpen={(_id, row) => openDeal(row as DealDoc)}\n // Columns and sort are the view's, controlled (AGL-2617).\n columnVisibilityModel={grid.columnVisibilityModel}\n onColumnVisibilityModelChange={grid.onColumnVisibilityModelChange}\n sortModel={grid.sortModel}\n onSortModelChange={grid.onSortModelChange}\n hideFooter\n />\n </CrmColumnOrderProvider>\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </>\n )}\n </Stack>\n )}\n </Stack>\n </CardDisplay>\n <DealEditDrawer\n open={creating}\n onClose={() => setCreating(false)}\n hostId={hostId}\n org={org}\n pipelines={activePipelines}\n defaultPipeline={pipeline}\n />\n <PipelinesDialog\n open={managingPipelines}\n onClose={() => setManagingPipelines(false)}\n orgId={scope.orgId ?? ''}\n // A pipeline made here is stamped the way the seeded default is\n // (`usePipeline`): the site's own tokens under a site; at the\n // organization level the org token, with the picked site as its\n // provenance (AGL-2630).\n hostId={scope.createHostId}\n pipelines={pipelineState.pipelines}\n fromCache={pipelineState.fromCache}\n unreadable={pipelineState.status === 'error'}\n visibleToTokens={scope.visibleTo}\n createTokens={scope.level === 'site' ? scope.createTokens : ORG_PIPELINE_TOKENS}\n />\n <LostReasonDialog\n open={Boolean(losing)}\n dealTitle={losing?.title ?? ''}\n onClose={() => setLosing(null)}\n onConfirm={confirmLost}\n />\n </>\n )\n}\nDealsSection.displayName = 'DealsSection'\n\nfunction Figure(props: { label: string; value: string }) {\n return (\n <Stack sx={{ lineHeight: 1.2 }}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {props.label}\n </Typography>\n <Typography variant=\"h6\" component=\"span\">\n {props.value}\n </Typography>\n </Stack>\n )\n}\nFigure.displayName = 'Figure'\n\nexport default DealsSection\n"],"names":["dealStageById","filterByNextActivity","findOrgMember","isNoNextActivityClause","ORG_SCOPE_TOKEN","pluginDocsHelp","mdiCogOutline","mdiPlus","mdiTableLarge","mdiViewColumnOutline","CardDisplay","MdiIcon","ListPagination","ListTable","useCrmSavedView","useCrmViewGrid","CrmListActions","CRM_LIST_SLOTS","CrmColumnOrderProvider","NoNextActivityToggle","nextActivityColumn","CrmViewsControl","EmptyStateComponent","useSnackbar","Alert","Button","Chip","CircularProgress","MenuItem","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","useRouter","useCallback","useEffect","useMemo","useState","useContactFieldDefinitions","useCrmScope","useDealStageApi","BOARD_CLOSED_LIMIT","BOARD_OPEN_LIMIT","useDealsByStatus","usePagedDeals","useOrgMemberDirectory","usePipeline","downloadTextFile","crmRoutes","dealsCsv","boardSummary","DEAL_STATUS_LABELS","formatAmountByCurrency","formatMoney","customFieldColumns","DealBoard","DealEditDrawer","DealImportButton","DealsBulkBar","LostReasonDialog","OwnerAvatar","PipelinesDialog","ORG_PIPELINE_TOKENS","DealsSection","props","pipelineId","scope","hostId","org","basePath","routes","router","enqueueSnackbar","pipelineState","orgId","activePipelines","setPipelineId","pipeline","find","entry","$id","roster","api","nowMs","Date","now","dealFields","view","setView","closedExpanded","setClosedExpanded","views","section","statusFilter","value","state","filters","clause","field","viewFilters","setStatusFilter","next","setFilters","op","filter","currentId","open","visibleTo","closedPipelineId","won","lost","paged","summary","data","moving","setMoving","Set","track","deal","request","done","current","add","variant","persist","error","Error","message","allowDuplicate","delete","handleMove","stageId","stage","moveToStage","name","handleWon","markWon","losing","setLosing","handleLost","confirmLost","reason","markLost","openDeal","push","creating","setCreating","managingPipelines","setManagingPipelines","selectedIds","setSelectedIds","page","csvOptions","pipelineName","id","pipelineById","stageName","ownerEmail","uid","member","members","email","label","handleExport","rows","columns","headerName","flex","minWidth","renderCell","row","withWhom","contactName","companyName","Boolean","join","sx","justifyContent","lineHeight","title","color","noWrap","valueGetter","_value","align","headerAlign","amountCents","currency","nameOf","ownerUid","direction","spacing","alignItems","size","type","expectedCloseAtMs","toLocaleDateString","status","active","tableRows","grid","noOrg","ready","pipelineLoading","seeding","header","help","anchor","contentGutterX","contentGutterY","HeaderProps","action","startIcon","path","disabled","onClick","flexWrap","rowGap","Figure","openCount","toLocaleString","valueByCurrency","weightedByCurrency","length","select","onChange","event","target","slotProps","map","exclusive","_event","aria-label","ml","severity","compact","description","deals","onToggleClosed","labelFor","onOpen","onMove","onWon","onLost","controller","allLabel","undefined","selected","onSelectedChange","csv","columnOrder","slots","selectable","_id","columnVisibilityModel","onColumnVisibilityModelChange","sortModel","onSortModelChange","hideFooter","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","onClose","pipelines","defaultPipeline","createHostId","fromCache","unreadable","visibleToTokens","createTokens","level","dealTitle","onConfirm","displayName","component"],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@ import { useCrmViewGrid } from "../hooks/use-crm-view-grid.js";
|
|
|
33
33
|
import { CRM_LIST_SLOTS, CrmColumnOrderProvider } from "./crm-column-menu.js";
|
|
34
34
|
import { useOrgLeads } from "../hooks/use-org-leads.js";
|
|
35
35
|
import CrmViewsControl from "./crm-views-control.js";
|
|
36
|
+
import { CrmListActions, CrmListToolbar } from "./crm-list-toolbar.js";
|
|
36
37
|
import RowActionsMenu from "@aglyn/shared-ui-jsx/components/row-actions-menu.component";
|
|
37
38
|
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
38
39
|
import EmptyStateComponent from "@aglyn/shared-ui-jsx/components/empty-state.component";
|
|
@@ -726,185 +727,188 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
|
|
|
726
727
|
help: Aglyn.pluginDocsHelp('contacts', {
|
|
727
728
|
anchor: '#whats-in-the-crm-area'
|
|
728
729
|
}),
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
730
|
+
contentGutterX: true,
|
|
731
|
+
contentGutterY: true,
|
|
732
|
+
HeaderProps: {
|
|
733
|
+
// The record actions, top right and never clipped (AGL-3311).
|
|
734
|
+
action: /*#__PURE__*/ _jsxs(CrmListActions, {
|
|
735
|
+
children: [
|
|
736
|
+
/*#__PURE__*/ _jsx(LeadImportButton, {
|
|
737
|
+
hostId: hostId,
|
|
738
|
+
orgId: orgId
|
|
739
|
+
}),
|
|
740
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
741
|
+
size: "small",
|
|
742
|
+
onClick: handleExport,
|
|
743
|
+
disabled: !rows.length,
|
|
744
|
+
children: 'Export CSV'
|
|
745
|
+
}),
|
|
746
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
747
|
+
size: "small",
|
|
748
|
+
variant: "contained",
|
|
749
|
+
onClick: ()=>setCreateOpen(true),
|
|
750
|
+
children: 'New lead'
|
|
751
|
+
})
|
|
752
|
+
]
|
|
753
|
+
})
|
|
754
|
+
},
|
|
755
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
756
|
+
spacing: 2,
|
|
735
757
|
children: [
|
|
736
|
-
/*#__PURE__*/ _jsx(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
size: "small",
|
|
742
|
-
sx: {
|
|
743
|
-
minWidth: 160
|
|
744
|
-
},
|
|
758
|
+
hostId ? /*#__PURE__*/ _jsx(LeadSurfacesNote, {
|
|
759
|
+
hostId: hostId
|
|
760
|
+
}) : /*#__PURE__*/ _jsx(OrgLeadSurfacesNote, {}),
|
|
761
|
+
/*#__PURE__*/ _jsxs(CrmListToolbar, {
|
|
762
|
+
label: "Lead filters",
|
|
745
763
|
children: [
|
|
746
|
-
/*#__PURE__*/ _jsx(
|
|
747
|
-
|
|
748
|
-
|
|
764
|
+
/*#__PURE__*/ _jsx(CrmViewsControl, {
|
|
765
|
+
controller: views,
|
|
766
|
+
allLabel: "All leads"
|
|
749
767
|
}),
|
|
750
|
-
/*#__PURE__*/
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
children:
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
768
|
+
/*#__PURE__*/ _jsxs(FormControl, {
|
|
769
|
+
size: "small",
|
|
770
|
+
sx: {
|
|
771
|
+
minWidth: 160
|
|
772
|
+
},
|
|
773
|
+
children: [
|
|
774
|
+
/*#__PURE__*/ _jsx(InputLabel, {
|
|
775
|
+
id: filterLabelId,
|
|
776
|
+
children: 'Show'
|
|
777
|
+
}),
|
|
778
|
+
/*#__PURE__*/ _jsx(Select, {
|
|
779
|
+
labelId: filterLabelId,
|
|
780
|
+
label: "Show",
|
|
781
|
+
value: filter,
|
|
782
|
+
onChange: (event)=>setFilter(event.target.value),
|
|
783
|
+
children: LEAD_FILTERS.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
784
|
+
value: option,
|
|
785
|
+
children: LEAD_FILTER_LABELS[option]
|
|
786
|
+
}, option))
|
|
787
|
+
})
|
|
788
|
+
]
|
|
771
789
|
}),
|
|
772
|
-
/*#__PURE__*/
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
children:
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
children: 'Campaign'
|
|
790
|
+
/*#__PURE__*/ _jsxs(FormControl, {
|
|
791
|
+
size: "small",
|
|
792
|
+
sx: {
|
|
793
|
+
minWidth: 160
|
|
794
|
+
},
|
|
795
|
+
children: [
|
|
796
|
+
/*#__PURE__*/ _jsx(InputLabel, {
|
|
797
|
+
id: emailFilterLabelId,
|
|
798
|
+
children: 'Email'
|
|
799
|
+
}),
|
|
800
|
+
/*#__PURE__*/ _jsx(Select, {
|
|
801
|
+
labelId: emailFilterLabelId,
|
|
802
|
+
label: "Email",
|
|
803
|
+
value: emailFilter,
|
|
804
|
+
onChange: (event)=>setEmailFilter(event.target.value),
|
|
805
|
+
children: LEAD_EMAIL_FILTERS.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
806
|
+
value: option,
|
|
807
|
+
children: LEAD_EMAIL_FILTER_LABELS[option]
|
|
808
|
+
}, option))
|
|
809
|
+
})
|
|
810
|
+
]
|
|
794
811
|
}),
|
|
795
|
-
/*#__PURE__*/ _jsxs(
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
onChange: (event)=>setCampaignFilter(String(event.target.value)),
|
|
801
|
-
displayEmpty: true,
|
|
812
|
+
/*#__PURE__*/ _jsxs(FormControl, {
|
|
813
|
+
size: "small",
|
|
814
|
+
sx: {
|
|
815
|
+
minWidth: 180
|
|
816
|
+
},
|
|
802
817
|
children: [
|
|
803
|
-
/*#__PURE__*/ _jsx(
|
|
804
|
-
|
|
805
|
-
|
|
818
|
+
/*#__PURE__*/ _jsx(InputLabel, {
|
|
819
|
+
id: campaignFilterLabelId,
|
|
820
|
+
shrink: true,
|
|
821
|
+
children: 'Campaign'
|
|
806
822
|
}),
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
campaignFilter && !campaigns.options.some((option)=>option.value === campaignFilter) ? /*#__PURE__*/ _jsx(MenuItem, {
|
|
823
|
+
/*#__PURE__*/ _jsxs(Select, {
|
|
824
|
+
labelId: campaignFilterLabelId,
|
|
825
|
+
label: "Campaign",
|
|
826
|
+
notched: true,
|
|
812
827
|
value: campaignFilter,
|
|
813
|
-
|
|
814
|
-
|
|
828
|
+
onChange: (event)=>setCampaignFilter(String(event.target.value)),
|
|
829
|
+
displayEmpty: true,
|
|
830
|
+
children: [
|
|
831
|
+
/*#__PURE__*/ _jsx(MenuItem, {
|
|
832
|
+
value: "",
|
|
833
|
+
children: 'Any campaign'
|
|
834
|
+
}),
|
|
835
|
+
campaigns.options.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
836
|
+
value: option.value,
|
|
837
|
+
children: option.label
|
|
838
|
+
}, option.value)),
|
|
839
|
+
campaignFilter && !campaigns.options.some((option)=>option.value === campaignFilter) ? /*#__PURE__*/ _jsx(MenuItem, {
|
|
840
|
+
value: campaignFilter,
|
|
841
|
+
children: campaignFilter
|
|
842
|
+
}) : null
|
|
843
|
+
]
|
|
844
|
+
})
|
|
815
845
|
]
|
|
816
|
-
})
|
|
817
|
-
]
|
|
818
|
-
}),
|
|
819
|
-
/*#__PURE__*/ _jsxs(FormControl, {
|
|
820
|
-
size: "small",
|
|
821
|
-
sx: {
|
|
822
|
-
minWidth: 180
|
|
823
|
-
},
|
|
824
|
-
children: [
|
|
825
|
-
/*#__PURE__*/ _jsx(InputLabel, {
|
|
826
|
-
id: leadSourceFilterLabelId,
|
|
827
|
-
shrink: true,
|
|
828
|
-
children: 'Lead source'
|
|
829
846
|
}),
|
|
830
|
-
/*#__PURE__*/ _jsxs(
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
onChange: (event)=>setLeadSourceFilter(String(event.target.value)),
|
|
836
|
-
displayEmpty: true,
|
|
847
|
+
/*#__PURE__*/ _jsxs(FormControl, {
|
|
848
|
+
size: "small",
|
|
849
|
+
sx: {
|
|
850
|
+
minWidth: 180
|
|
851
|
+
},
|
|
837
852
|
children: [
|
|
838
|
-
/*#__PURE__*/ _jsx(
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
leadSourceList.picklist.values.map((value)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
843
|
-
value: value.label,
|
|
844
|
-
children: value.active ? value.label : `${value.label} (inactive)`
|
|
845
|
-
}, value.id)),
|
|
846
|
-
/*#__PURE__*/ _jsx(MenuItem, {
|
|
847
|
-
value: LEAD_SOURCE_FILTER_NONE,
|
|
848
|
-
children: 'No lead source'
|
|
853
|
+
/*#__PURE__*/ _jsx(InputLabel, {
|
|
854
|
+
id: leadSourceFilterLabelId,
|
|
855
|
+
shrink: true,
|
|
856
|
+
children: 'Lead source'
|
|
849
857
|
}),
|
|
850
|
-
|
|
858
|
+
/*#__PURE__*/ _jsxs(Select, {
|
|
859
|
+
labelId: leadSourceFilterLabelId,
|
|
860
|
+
label: "Lead source",
|
|
861
|
+
notched: true,
|
|
851
862
|
value: leadSourceFilter,
|
|
852
|
-
|
|
853
|
-
|
|
863
|
+
onChange: (event)=>setLeadSourceFilter(String(event.target.value)),
|
|
864
|
+
displayEmpty: true,
|
|
865
|
+
children: [
|
|
866
|
+
/*#__PURE__*/ _jsx(MenuItem, {
|
|
867
|
+
value: "",
|
|
868
|
+
children: 'Any lead source'
|
|
869
|
+
}),
|
|
870
|
+
leadSourceList.picklist.values.map((value)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
871
|
+
value: value.label,
|
|
872
|
+
children: value.active ? value.label : `${value.label} (inactive)`
|
|
873
|
+
}, value.id)),
|
|
874
|
+
/*#__PURE__*/ _jsx(MenuItem, {
|
|
875
|
+
value: LEAD_SOURCE_FILTER_NONE,
|
|
876
|
+
children: 'No lead source'
|
|
877
|
+
}),
|
|
878
|
+
leadSourceFilter && leadSourceFilter !== LEAD_SOURCE_FILTER_NONE && !Aglyn.crmPicklistValueByLabel(leadSourceList.picklist, leadSourceFilter) ? /*#__PURE__*/ _jsx(MenuItem, {
|
|
879
|
+
value: leadSourceFilter,
|
|
880
|
+
children: leadSourceFilter
|
|
881
|
+
}) : null
|
|
882
|
+
]
|
|
883
|
+
})
|
|
854
884
|
]
|
|
885
|
+
}),
|
|
886
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
887
|
+
size: "small",
|
|
888
|
+
value: search,
|
|
889
|
+
onChange: (event)=>setSearch(event.target.value),
|
|
890
|
+
placeholder: "Search leads",
|
|
891
|
+
slotProps: {
|
|
892
|
+
input: {
|
|
893
|
+
startAdornment: /*#__PURE__*/ _jsx(InputAdornment, {
|
|
894
|
+
position: "start",
|
|
895
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
896
|
+
path: mdiMagnify.path,
|
|
897
|
+
size: 0.8
|
|
898
|
+
})
|
|
899
|
+
})
|
|
900
|
+
},
|
|
901
|
+
htmlInput: {
|
|
902
|
+
'aria-label': 'Search leads',
|
|
903
|
+
type: 'search'
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
sx: {
|
|
907
|
+
minWidth: 200
|
|
908
|
+
}
|
|
855
909
|
})
|
|
856
910
|
]
|
|
857
911
|
}),
|
|
858
|
-
/*#__PURE__*/ _jsx(TextField, {
|
|
859
|
-
size: "small",
|
|
860
|
-
value: search,
|
|
861
|
-
onChange: (event)=>setSearch(event.target.value),
|
|
862
|
-
placeholder: "Search leads",
|
|
863
|
-
slotProps: {
|
|
864
|
-
input: {
|
|
865
|
-
startAdornment: /*#__PURE__*/ _jsx(InputAdornment, {
|
|
866
|
-
position: "start",
|
|
867
|
-
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
868
|
-
path: mdiMagnify.path,
|
|
869
|
-
size: 0.8
|
|
870
|
-
})
|
|
871
|
-
})
|
|
872
|
-
},
|
|
873
|
-
htmlInput: {
|
|
874
|
-
'aria-label': 'Search leads',
|
|
875
|
-
type: 'search'
|
|
876
|
-
}
|
|
877
|
-
},
|
|
878
|
-
sx: {
|
|
879
|
-
minWidth: 200
|
|
880
|
-
}
|
|
881
|
-
}),
|
|
882
|
-
/*#__PURE__*/ _jsx(LeadImportButton, {
|
|
883
|
-
hostId: hostId,
|
|
884
|
-
orgId: orgId
|
|
885
|
-
}),
|
|
886
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
887
|
-
size: "small",
|
|
888
|
-
onClick: handleExport,
|
|
889
|
-
disabled: !rows.length,
|
|
890
|
-
children: 'Export CSV'
|
|
891
|
-
}),
|
|
892
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
893
|
-
size: "small",
|
|
894
|
-
variant: "contained",
|
|
895
|
-
onClick: ()=>setCreateOpen(true),
|
|
896
|
-
children: 'New lead'
|
|
897
|
-
})
|
|
898
|
-
]
|
|
899
|
-
}),
|
|
900
|
-
contentGutterX: true,
|
|
901
|
-
contentGutterY: true,
|
|
902
|
-
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
903
|
-
spacing: 2,
|
|
904
|
-
children: [
|
|
905
|
-
hostId ? /*#__PURE__*/ _jsx(LeadSurfacesNote, {
|
|
906
|
-
hostId: hostId
|
|
907
|
-
}) : /*#__PURE__*/ _jsx(OrgLeadSurfacesNote, {}),
|
|
908
912
|
status === 'success' && window.length === 0 ? /*#__PURE__*/ _jsx(EmptyStateComponent, {
|
|
909
913
|
label: 'No leads yet',
|
|
910
914
|
description: 'Bookings and lead-routed forms on your site become leads on their own — or add one with New lead, or bring a list in with Import CSV.'
|