@burdenoff/microfe-planmagnet 2026.1001.6 → 2026.1001.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
- package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
- package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +18 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +259 -202
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
- package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeedbackControls.js +153 -0
- package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
- package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/SyncControls.js +1 -0
- package/dist/planmagnet/controls/SyncControls.js.map +1 -1
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
- package/dist/planmagnet/dashboards/DashboardUI.js +7 -7
- package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
- package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
- package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
- package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
- package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
- package/dist/planmagnet/hooks/useLiveRefresh.d.ts +33 -2
- package/dist/planmagnet/hooks/useLiveRefresh.d.ts.map +1 -1
- package/dist/planmagnet/hooks/useLiveRefresh.js +71 -42
- package/dist/planmagnet/hooks/useLiveRefresh.js.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.js +19 -9
- package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +8 -8
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +136 -129
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +149 -119
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +153 -117
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +173 -138
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
- package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
- package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
- package/dist/planmagnet/pages/analytics/MetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
- package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
- package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
- package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
- package/dist/planmagnet/people/People.d.ts +14 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -1
- package/dist/planmagnet/people/People.js +36 -22
- package/dist/planmagnet/people/People.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js +171 -153
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js.map +1 -1
- package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
- package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntegrationsSyncDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/IntegrationsSyncDetailPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { ExternalLink, History, Plug, ShieldAlert } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport {\n ConflictResolveActions,\n ConflictStatusBadge,\n DeleteSyncButton,\n EditSyncDialog,\n PauseResumeButton,\n RunNowButton,\n SyncHealthSummary,\n SyncRunResultSummary,\n SyncRunStatusBadge,\n SyncStatusBadge,\n TestConnectionButton,\n TruncatedText,\n CONFLICT_STRATEGY_EXPLANATION,\n} from '../controls/SyncControls';\nimport {\n directionLabel,\n parseGithubFieldMapping,\n providerLabel,\n type ConflictStatus,\n type ExternalSyncRow,\n type SyncConflictRow,\n type SyncRunResult,\n type SyncRunRow,\n} from '../integrations/syncTypes';\n\n/**\n * Sync detail (AUT-09/10/24/40): one ExternalSync — its health, its settings, its run history and its open\n * conflicts. Every write action here can be refused by role (FORBIDDEN) or by the provider itself (a honest\n * NOT_IMPLEMENTED for a non-GitHub provider, a real GitHub API error) — both are surfaced inline, never masked.\n */\n\nconst RUNS_PAGE = 20;\nconst CONFLICTS_PAGE = 20;\n\nconst GET_SYNC_DETAIL = gql`\n query SyncDetail($id: ID!) {\n planmagnetExternalSync(id: $id) {\n id\n provider\n direction\n status\n projectId\n project {\n id\n name\n }\n externalProjectId\n externalProjectUrl\n conflictStrategy\n fieldMapping\n syncFilters\n lastSyncedAt\n lastSyncError\n totalItemsSynced\n createdAt\n updatedAt\n runHealth {\n lastRunAt\n lastStatus\n consecutiveFailures\n runsLast7Days\n }\n }\n }\n`;\n\nconst GET_SYNC_RUNS = gql`\n query SyncRuns($syncId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetSyncRuns(syncId: $syncId, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n direction\n status\n startedAt\n finishedAt\n itemsCreated\n itemsUpdated\n itemsSkipped\n errorCount\n conflictCount\n errorMessage\n triggeredBy\n }\n }\n }\n`;\n\nconst GET_SYNC_CONFLICTS = gql`\n query SyncConflicts(\n $syncId: ID!\n $status: PlanmagnetSyncConflictStatus\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSyncConflicts(syncId: $syncId, status: $status, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n createdAt\n fields\n localUpdatedAt\n remoteUpdatedAt\n resolution\n resolvedAt\n resolvedBy\n status\n mappingId\n mapping {\n id\n internalId\n internalType\n externalId\n externalType\n externalUrl\n }\n }\n }\n }\n`;\n\nfunction RunHistorySection({ syncId }: { syncId: string }) {\n const [loadingMore, setLoadingMore] = useState(false);\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetSyncRuns: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: SyncRunRow[];\n };\n }>(GET_SYNC_RUNS, { variables: { syncId, pagination: { first: RUNS_PAGE } } });\n\n const list = data?.planmagnetSyncRuns;\n const runs = list?.items ?? [];\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: RUNS_PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetSyncRuns: {\n ...fetchMoreResult.planmagnetSyncRuns,\n items: [...prev.planmagnetSyncRuns.items, ...fetchMoreResult.planmagnetSyncRuns.items],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-3\" data-testid=\"sync-runs\">\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Run history could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading run history…</p>}\n {list && runs.length === 0 && (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n No runs yet — use "Run now" above to start the first one.\n </p>\n )}\n <div className=\"overflow-x-auto\">\n {runs.length > 0 && (\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"text-left text-muted-foreground\">\n <th className=\"py-1 pr-3 font-medium\">Started</th>\n <th className=\"py-1 pr-3 font-medium\">Direction</th>\n <th className=\"py-1 pr-3 font-medium\">Status</th>\n <th className=\"py-1 pr-3 font-medium\">Created / updated / skipped</th>\n <th className=\"py-1 pr-3 font-medium\">Conflicts</th>\n <th className=\"py-1 pr-3 font-medium\">Triggered by</th>\n <th className=\"py-1 font-medium\">Error</th>\n </tr>\n </thead>\n <tbody>\n {runs.map((run) => (\n <tr key={run.id} className=\"border-t align-top\" data-testid=\"sync-run-row\">\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n <div>{new Date(run.startedAt).toLocaleString()}</div>\n <div className=\"text-xs text-muted-foreground\">\n finished {new Date(run.finishedAt).toLocaleString()}\n </div>\n </td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">{directionLabel(run.direction)}</td>\n <td className=\"py-1.5 pr-3\">\n <SyncRunStatusBadge status={run.status} />\n </td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n {run.itemsCreated} / {run.itemsUpdated} / {run.itemsSkipped}\n </td>\n <td className=\"py-1.5 pr-3\">{run.conflictCount}</td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n <PersonName id={run.triggeredBy} />\n </td>\n <td className=\"py-1.5 text-status-error-text\">\n {run.errorMessage ? (\n <TruncatedText\n text={run.errorMessage}\n max={100}\n className=\"text-status-error-text\"\n />\n ) : (\n <span className=\"text-muted-foreground\">—</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${runs.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n\nfunction ConflictRow({ conflict }: { conflict: SyncConflictRow }) {\n const navigate = usePlanMagnetNavigate();\n const mapping = conflict.mapping;\n const isWorkItem = mapping?.internalType === 'workitem';\n\n return (\n <li className=\"rounded-md border p-4 space-y-2\" data-testid=\"sync-conflict\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <ConflictStatusBadge status={conflict.status} />\n <span className=\"text-xs text-muted-foreground\">\n Detected {new Date(conflict.createdAt).toLocaleString()}\n </span>\n </div>\n {mapping && (\n <div className=\"flex items-center gap-2 text-xs\">\n {isWorkItem ? (\n <button\n type=\"button\"\n className=\"font-medium text-primary hover:underline\"\n onClick={() =>\n navigate(\n `/tasks/task-detail-page?taskId=${encodeURIComponent(mapping.internalId)}`\n )\n }\n >\n Open in PlanMagnet\n </button>\n ) : (\n <span className=\"text-muted-foreground\">\n {mapping.internalType} {mapping.internalId}\n </span>\n )}\n {mapping.externalUrl && (\n <a\n href={mapping.externalUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-primary hover:underline\"\n >\n View on {mapping.externalType}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n </div>\n )}\n </div>\n <p className=\"text-sm\">\n Fields in conflict:{' '}\n <span className=\"font-medium\">\n {conflict.fields.length ? conflict.fields.join(', ') : 'unknown'}\n </span>\n </p>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2 text-xs text-muted-foreground\">\n <span>PlanMagnet last updated {new Date(conflict.localUpdatedAt).toLocaleString()}</span>\n <span>Provider last updated {new Date(conflict.remoteUpdatedAt).toLocaleString()}</span>\n </div>\n {conflict.status === 'OPEN' ? (\n <ConflictResolveActions conflictId={conflict.id} />\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n Resolved {conflict.resolvedAt ? new Date(conflict.resolvedAt).toLocaleString() : ''}\n {conflict.resolution\n ? ` — ${conflict.resolution === 'SOURCE_WINS' ? 'kept PlanMagnet' : conflict.resolution === 'TARGET_WINS' ? 'took the provider' : 'dismissed manually'}`\n : ''}\n {conflict.resolvedBy ? (\n <>\n {' by '}\n <PersonName id={conflict.resolvedBy} />\n </>\n ) : null}\n </p>\n )}\n </li>\n );\n}\n\nfunction ConflictsSection({ syncId }: { syncId: string }) {\n const [status, setStatus] = useState<'ALL' | ConflictStatus>('OPEN');\n const [loadingMore, setLoadingMore] = useState(false);\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetSyncConflicts: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: SyncConflictRow[];\n };\n }>(GET_SYNC_CONFLICTS, {\n variables: {\n syncId,\n status: status === 'ALL' ? undefined : status,\n pagination: { first: CONFLICTS_PAGE },\n },\n });\n\n const list = data?.planmagnetSyncConflicts;\n const conflicts = list?.items ?? [];\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: CONFLICTS_PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetSyncConflicts: {\n ...fetchMoreResult.planmagnetSyncConflicts,\n items: [\n ...prev.planmagnetSyncConflicts.items,\n ...fetchMoreResult.planmagnetSyncConflicts.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-3\" data-testid=\"sync-conflicts\">\n <div className=\"flex flex-wrap gap-1\" role=\"group\" aria-label=\"Filter conflicts by status\">\n {(['OPEN', 'RESOLVED', 'ALL'] as const).map((s) => (\n <button\n key={s}\n type=\"button\"\n aria-pressed={status === s}\n data-testid={`sync-conflicts-status-${s}`}\n onClick={() => setStatus(s)}\n className={`px-3 py-1.5 text-sm rounded-md ${status === s ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {s === 'ALL' ? 'All' : s.charAt(0) + s.slice(1).toLowerCase()}\n </button>\n ))}\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Conflicts could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading conflicts…</p>}\n {list && conflicts.length === 0 && (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {status === 'OPEN' ? 'No open conflicts.' : 'No conflicts found.'}\n </p>\n )}\n <ul className=\"space-y-3\">\n {conflicts.map((conflict) => (\n <ConflictRow key={conflict.id} conflict={conflict} />\n ))}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${conflicts.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n\nexport function IntegrationsSyncDetailPagePage() {\n const syncId = useEntityId();\n const navigate = usePlanMagnetNavigate();\n const [runResult, setRunResult] = useState<SyncRunResult | null>(null);\n\n const { data, loading, error } = useQuery<{ planmagnetExternalSync: ExternalSyncRow | null }>(\n GET_SYNC_DETAIL,\n { variables: { id: syncId }, skip: !syncId }\n );\n\n if (!syncId)\n return (\n <div className=\"p-6 text-muted-foreground\">No sync selected. Open one from Integrations.</div>\n );\n // Only before the first load: a refetch after an action must not blank the page.\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-destructive\">Sync could not be loaded: {error.message}</div>;\n\n const sync = data?.planmagnetExternalSync;\n if (!sync) return <div className=\"p-6 text-muted-foreground\">Sync not found.</div>;\n\n const mapping = parseGithubFieldMapping(sync.fieldMapping);\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"sync-detail\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <Plug className=\"h-6 w-6 text-primary mt-0.5\" />\n <div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">{providerLabel(sync.provider)} sync</h1>\n <SyncStatusBadge status={sync.status} />\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {sync.project ? (\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() =>\n navigate(detailPath('/projects/project-detail-page', sync.project?.id))\n }\n >\n {sync.project.name}\n </button>\n ) : (\n 'Unknown project'\n )}\n {' · '}\n {directionLabel(sync.direction)}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <RunNowButton syncId={sync.id} size=\"default\" onResult={setRunResult} />\n <PauseResumeButton syncId={sync.id} status={sync.status} />\n <TestConnectionButton syncId={sync.id} />\n <EditSyncDialog\n sync={{\n id: sync.id,\n provider: sync.provider,\n direction: sync.direction,\n conflictStrategy: sync.conflictStrategy,\n fieldMapping: sync.fieldMapping,\n syncFilters: sync.syncFilters,\n }}\n />\n <DeleteSyncButton\n syncId={sync.id}\n label={`${providerLabel(sync.provider)} sync`}\n onDeleted={() => navigate('/integrations/syncs-page')}\n />\n </div>\n </div>\n\n {runResult && <SyncRunResultSummary result={runResult} />}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Health</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <SyncHealthSummary health={sync.runHealth} />\n {sync.lastSyncError && (\n <p className=\"text-sm text-status-error-text\" data-testid=\"sync-last-error\">\n <TruncatedText\n text={sync.lastSyncError}\n max={200}\n className=\"text-status-error-text\"\n />\n </p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <History className=\"h-4 w-4 text-primary\" />\n History\n </CardTitle>\n </CardHeader>\n <CardContent>\n <Tabs defaultValue=\"runs\">\n <TabsList>\n <TabsTrigger value=\"runs\" data-testid=\"sync-tab-runs\">\n Run history\n </TabsTrigger>\n <TabsTrigger value=\"conflicts\" data-testid=\"sync-tab-conflicts\">\n <ShieldAlert className=\"mr-1 h-3.5 w-3.5\" />\n Conflicts\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"runs\" className=\"mt-4\">\n <RunHistorySection syncId={sync.id} />\n </TabsContent>\n <TabsContent value=\"conflicts\" className=\"mt-4\">\n <ConflictsSection syncId={sync.id} />\n </TabsContent>\n </Tabs>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <span className=\"text-muted-foreground\">External project</span>\n {sync.externalProjectUrl ? (\n <a\n href={sync.externalProjectUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 font-medium text-primary hover:underline\"\n >\n {sync.externalProjectId}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : (\n <span className=\"font-medium\">{sync.externalProjectId}</span>\n )}\n </div>\n <div className=\"flex justify-between gap-2\">\n <span className=\"text-muted-foreground\">Conflict strategy</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {sync.conflictStrategy === 'SOURCE_WINS'\n ? 'PlanMagnet wins'\n : sync.conflictStrategy === 'TARGET_WINS'\n ? 'Provider wins'\n : 'Manual'}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {CONFLICT_STRATEGY_EXPLANATION[sync.conflictStrategy]}\n </p>\n {sync.provider === 'GITHUB' && (\n <div>\n <p className=\"text-muted-foreground\">Field mapping</p>\n <p className=\"font-medium\">\n Priority label: <code>{mapping.priority || 'priority:'}</code>\n </p>\n <p className=\"font-medium\">\n Due-date label: <code>{mapping.dueDate || 'due:'}</code>\n </p>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Items synced</span>\n <span className=\"font-medium\">{sync.totalItemsSynced}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Last synced</span>\n <span>\n {sync.lastSyncedAt ? new Date(sync.lastSyncedAt).toLocaleString() : 'Never'}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(sync.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(sync.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IntegrationsSyncDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/IntegrationsSyncDetailPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { ExternalLink, History, Plug, ShieldAlert } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport {\n ConflictResolveActions,\n ConflictStatusBadge,\n DeleteSyncButton,\n EditSyncDialog,\n PauseResumeButton,\n RunNowButton,\n SyncHealthSummary,\n SyncRunResultSummary,\n SyncRunStatusBadge,\n SyncStatusBadge,\n TestConnectionButton,\n TruncatedText,\n CONFLICT_STRATEGY_EXPLANATION,\n} from '../controls/SyncControls';\nimport {\n conflictStrategyLabel,\n directionLabel,\n parseGithubFieldMapping,\n providerLabel,\n type ConflictStatus,\n type ExternalSyncRow,\n type SyncConflictRow,\n type SyncRunResult,\n type SyncRunRow,\n} from '../integrations/syncTypes';\n\n/** Past-tense phrasing for a decided `SyncConflict.resolution` (AUT-10/24) — covers all four\n * `PlanmagnetSyncConflictResolution` values, including `MERGE`, which this line previously fell through to\n * \"dismissed manually\" for. */\nconst RESOLUTION_PHRASE: Record<string, string> = {\n SOURCE_WINS: 'kept PlanMagnet',\n TARGET_WINS: 'took the provider',\n MERGE: 'merged',\n MANUAL: 'dismissed manually',\n};\n\n/**\n * Sync detail (AUT-09/10/24/40): one ExternalSync — its health, its settings, its run history and its open\n * conflicts. Every write action here can be refused by role (FORBIDDEN) or by the provider itself (a honest\n * NOT_IMPLEMENTED for a non-GitHub provider, a real GitHub API error) — both are surfaced inline, never masked.\n */\n\nconst RUNS_PAGE = 20;\nconst CONFLICTS_PAGE = 20;\n\nconst GET_SYNC_DETAIL = gql`\n query SyncDetail($id: ID!) {\n planmagnetExternalSync(id: $id) {\n id\n provider\n direction\n status\n projectId\n project {\n id\n name\n }\n externalProjectId\n externalProjectUrl\n conflictStrategy\n fieldMapping\n syncFilters\n lastSyncedAt\n lastSyncError\n totalItemsSynced\n createdAt\n updatedAt\n runHealth {\n lastRunAt\n lastStatus\n consecutiveFailures\n runsLast7Days\n }\n }\n }\n`;\n\nconst GET_SYNC_RUNS = gql`\n query SyncRuns($syncId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetSyncRuns(syncId: $syncId, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n direction\n status\n startedAt\n finishedAt\n itemsCreated\n itemsUpdated\n itemsSkipped\n errorCount\n conflictCount\n errorMessage\n triggeredBy\n }\n }\n }\n`;\n\nconst GET_SYNC_CONFLICTS = gql`\n query SyncConflicts(\n $syncId: ID!\n $status: PlanmagnetSyncConflictStatus\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSyncConflicts(syncId: $syncId, status: $status, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n createdAt\n fields\n localUpdatedAt\n remoteUpdatedAt\n resolution\n resolvedAt\n resolvedBy\n status\n mappingId\n mapping {\n id\n internalId\n internalType\n externalId\n externalType\n externalUrl\n }\n }\n }\n }\n`;\n\nfunction RunHistorySection({ syncId }: { syncId: string }) {\n const [loadingMore, setLoadingMore] = useState(false);\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetSyncRuns: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: SyncRunRow[];\n };\n }>(GET_SYNC_RUNS, { variables: { syncId, pagination: { first: RUNS_PAGE } } });\n\n const list = data?.planmagnetSyncRuns;\n const runs = list?.items ?? [];\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: RUNS_PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetSyncRuns: {\n ...fetchMoreResult.planmagnetSyncRuns,\n items: [...prev.planmagnetSyncRuns.items, ...fetchMoreResult.planmagnetSyncRuns.items],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-3\" data-testid=\"sync-runs\">\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Run history could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading run history…</p>}\n {list && runs.length === 0 && (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n No runs yet — use "Run now" above to start the first one.\n </p>\n )}\n <div className=\"overflow-x-auto\">\n {runs.length > 0 && (\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"text-left text-muted-foreground\">\n <th className=\"py-1 pr-3 font-medium\">Started</th>\n <th className=\"py-1 pr-3 font-medium\">Direction</th>\n <th className=\"py-1 pr-3 font-medium\">Status</th>\n <th className=\"py-1 pr-3 font-medium\">Created / updated / skipped</th>\n <th className=\"py-1 pr-3 font-medium\">Conflicts</th>\n <th className=\"py-1 pr-3 font-medium\">Triggered by</th>\n <th className=\"py-1 font-medium\">Error</th>\n </tr>\n </thead>\n <tbody>\n {runs.map((run) => (\n <tr key={run.id} className=\"border-t align-top\" data-testid=\"sync-run-row\">\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n <div>{new Date(run.startedAt).toLocaleString()}</div>\n <div className=\"text-xs text-muted-foreground\">\n finished {new Date(run.finishedAt).toLocaleString()}\n </div>\n </td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">{directionLabel(run.direction)}</td>\n <td className=\"py-1.5 pr-3\">\n <SyncRunStatusBadge status={run.status} />\n </td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n {run.itemsCreated} / {run.itemsUpdated} / {run.itemsSkipped}\n </td>\n <td className=\"py-1.5 pr-3\">{run.conflictCount}</td>\n <td className=\"py-1.5 pr-3 whitespace-nowrap\">\n <PersonName id={run.triggeredBy} />\n </td>\n <td className=\"py-1.5 text-status-error-text\">\n {run.errorMessage ? (\n <TruncatedText\n text={run.errorMessage}\n max={100}\n className=\"text-status-error-text\"\n />\n ) : (\n <span className=\"text-muted-foreground\">—</span>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${runs.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n\nfunction ConflictRow({ conflict }: { conflict: SyncConflictRow }) {\n const navigate = usePlanMagnetNavigate();\n const mapping = conflict.mapping;\n const isWorkItem = mapping?.internalType === 'workitem';\n\n return (\n <li className=\"rounded-md border p-4 space-y-2\" data-testid=\"sync-conflict\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <ConflictStatusBadge status={conflict.status} />\n <span className=\"text-xs text-muted-foreground\">\n Detected {new Date(conflict.createdAt).toLocaleString()}\n </span>\n </div>\n {mapping && (\n <div className=\"flex items-center gap-2 text-xs\">\n {isWorkItem ? (\n <button\n type=\"button\"\n className=\"font-medium text-primary hover:underline\"\n onClick={() =>\n navigate(\n `/tasks/task-detail-page?taskId=${encodeURIComponent(mapping.internalId)}`\n )\n }\n >\n Open in PlanMagnet\n </button>\n ) : (\n <span className=\"text-muted-foreground\">\n {mapping.internalType} {mapping.internalId}\n </span>\n )}\n {mapping.externalUrl && (\n <a\n href={mapping.externalUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-primary hover:underline\"\n >\n View on {mapping.externalType}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n </div>\n )}\n </div>\n <p className=\"text-sm\">\n Fields in conflict:{' '}\n <span className=\"font-medium\">\n {conflict.fields.length ? conflict.fields.join(', ') : 'unknown'}\n </span>\n </p>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2 text-xs text-muted-foreground\">\n <span>PlanMagnet last updated {new Date(conflict.localUpdatedAt).toLocaleString()}</span>\n <span>Provider last updated {new Date(conflict.remoteUpdatedAt).toLocaleString()}</span>\n </div>\n {conflict.status === 'OPEN' ? (\n <ConflictResolveActions conflictId={conflict.id} />\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n Resolved {conflict.resolvedAt ? new Date(conflict.resolvedAt).toLocaleString() : ''}\n {conflict.resolution\n ? ` — ${RESOLUTION_PHRASE[conflict.resolution] ?? conflict.resolution.toLowerCase()}`\n : ''}\n {conflict.resolvedBy ? (\n <>\n {' by '}\n <PersonName id={conflict.resolvedBy} />\n </>\n ) : null}\n </p>\n )}\n </li>\n );\n}\n\nfunction ConflictsSection({ syncId }: { syncId: string }) {\n const [status, setStatus] = useState<'ALL' | ConflictStatus>('OPEN');\n const [loadingMore, setLoadingMore] = useState(false);\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetSyncConflicts: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: SyncConflictRow[];\n };\n }>(GET_SYNC_CONFLICTS, {\n variables: {\n syncId,\n status: status === 'ALL' ? undefined : status,\n pagination: { first: CONFLICTS_PAGE },\n },\n });\n\n const list = data?.planmagnetSyncConflicts;\n const conflicts = list?.items ?? [];\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: CONFLICTS_PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetSyncConflicts: {\n ...fetchMoreResult.planmagnetSyncConflicts,\n items: [\n ...prev.planmagnetSyncConflicts.items,\n ...fetchMoreResult.planmagnetSyncConflicts.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-3\" data-testid=\"sync-conflicts\">\n <div className=\"flex flex-wrap gap-1\" role=\"group\" aria-label=\"Filter conflicts by status\">\n {(['OPEN', 'RESOLVED', 'ALL'] as const).map((s) => (\n <button\n key={s}\n type=\"button\"\n aria-pressed={status === s}\n data-testid={`sync-conflicts-status-${s}`}\n onClick={() => setStatus(s)}\n className={`px-3 py-1.5 text-sm rounded-md ${status === s ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {s === 'ALL' ? 'All' : s.charAt(0) + s.slice(1).toLowerCase()}\n </button>\n ))}\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Conflicts could not be loaded: {error.message}\n </p>\n )}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading conflicts…</p>}\n {list && conflicts.length === 0 && (\n <p className=\"py-6 text-center text-sm text-muted-foreground\">\n {status === 'OPEN' ? 'No open conflicts.' : 'No conflicts found.'}\n </p>\n )}\n <ul className=\"space-y-3\">\n {conflicts.map((conflict) => (\n <ConflictRow key={conflict.id} conflict={conflict} />\n ))}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => void loadMore()} disabled={loadingMore}>\n {loadingMore ? 'Loading…' : `Show more (${conflicts.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n\nexport function IntegrationsSyncDetailPagePage() {\n const syncId = useEntityId();\n const navigate = usePlanMagnetNavigate();\n const [runResult, setRunResult] = useState<SyncRunResult | null>(null);\n\n const { data, loading, error } = useQuery<{ planmagnetExternalSync: ExternalSyncRow | null }>(\n GET_SYNC_DETAIL,\n { variables: { id: syncId }, skip: !syncId }\n );\n\n if (!syncId)\n return (\n <div className=\"p-6 text-muted-foreground\">No sync selected. Open one from Integrations.</div>\n );\n // Only before the first load: a refetch after an action must not blank the page.\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-destructive\">Sync could not be loaded: {error.message}</div>;\n\n const sync = data?.planmagnetExternalSync;\n if (!sync) return <div className=\"p-6 text-muted-foreground\">Sync not found.</div>;\n\n const mapping = parseGithubFieldMapping(sync.fieldMapping);\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"sync-detail\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <Plug className=\"h-6 w-6 text-primary mt-0.5\" />\n <div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">{providerLabel(sync.provider)} sync</h1>\n <SyncStatusBadge status={sync.status} />\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {sync.project ? (\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() =>\n navigate(detailPath('/projects/project-detail-page', sync.project?.id))\n }\n >\n {sync.project.name}\n </button>\n ) : (\n 'Unknown project'\n )}\n {' · '}\n {directionLabel(sync.direction)}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <RunNowButton syncId={sync.id} size=\"default\" onResult={setRunResult} />\n <PauseResumeButton syncId={sync.id} status={sync.status} />\n <TestConnectionButton syncId={sync.id} />\n <EditSyncDialog\n sync={{\n id: sync.id,\n provider: sync.provider,\n direction: sync.direction,\n conflictStrategy: sync.conflictStrategy,\n fieldMapping: sync.fieldMapping,\n syncFilters: sync.syncFilters,\n }}\n />\n <DeleteSyncButton\n syncId={sync.id}\n label={`${providerLabel(sync.provider)} sync`}\n onDeleted={() => navigate('/integrations/syncs-page')}\n />\n </div>\n </div>\n\n {runResult && <SyncRunResultSummary result={runResult} />}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Health</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <SyncHealthSummary health={sync.runHealth} />\n {sync.lastSyncError && (\n <p className=\"text-sm text-status-error-text\" data-testid=\"sync-last-error\">\n <TruncatedText\n text={sync.lastSyncError}\n max={200}\n className=\"text-status-error-text\"\n />\n </p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <History className=\"h-4 w-4 text-primary\" />\n History\n </CardTitle>\n </CardHeader>\n <CardContent>\n <Tabs defaultValue=\"runs\">\n <TabsList>\n <TabsTrigger value=\"runs\" data-testid=\"sync-tab-runs\">\n Run history\n </TabsTrigger>\n <TabsTrigger value=\"conflicts\" data-testid=\"sync-tab-conflicts\">\n <ShieldAlert className=\"mr-1 h-3.5 w-3.5\" />\n Conflicts\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"runs\" className=\"mt-4\">\n <RunHistorySection syncId={sync.id} />\n </TabsContent>\n <TabsContent value=\"conflicts\" className=\"mt-4\">\n <ConflictsSection syncId={sync.id} />\n </TabsContent>\n </Tabs>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <span className=\"text-muted-foreground\">External project</span>\n {sync.externalProjectUrl ? (\n <a\n href={sync.externalProjectUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 font-medium text-primary hover:underline\"\n >\n {sync.externalProjectId}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : (\n <span className=\"font-medium\">{sync.externalProjectId}</span>\n )}\n </div>\n <div className=\"flex justify-between gap-2\">\n <span className=\"text-muted-foreground\">Conflict strategy</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {conflictStrategyLabel(sync.conflictStrategy)}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {CONFLICT_STRATEGY_EXPLANATION[sync.conflictStrategy]}\n </p>\n {sync.provider === 'GITHUB' && (\n <div>\n <p className=\"text-muted-foreground\">Field mapping</p>\n <p className=\"font-medium\">\n Priority label: <code>{mapping.priority || 'priority:'}</code>\n </p>\n <p className=\"font-medium\">\n Due-date label: <code>{mapping.dueDate || 'due:'}</code>\n </p>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Items synced</span>\n <span className=\"font-medium\">{sync.totalItemsSynced}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Last synced</span>\n <span>\n {sync.lastSyncedAt ? new Date(sync.lastSyncedAt).toLocaleString() : 'Never'}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(sync.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(sync.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntegrationsSyncsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/IntegrationsSyncsPagePage.tsx"],"names":[],"mappings":"AA6EA,wBAAgB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"IntegrationsSyncsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/IntegrationsSyncsPagePage.tsx"],"names":[],"mappings":"AA6EA,wBAAgB,yBAAyB,4CA6PxC"}
|
|
@@ -5,13 +5,13 @@ import { ProjectSelect as o } from "../controls/Pickers.js";
|
|
|
5
5
|
import { SYNC_STATUSES as s, directionLabel as c, providerLabel as l } from "../integrations/syncTypes.js";
|
|
6
6
|
import { PauseResumeButton as u, RunNowButton as d, SyncHealthSummary as f, SyncRunResultSummary as p, SyncStatusBadge as m, TruncatedText as h } from "../controls/SyncControls.js";
|
|
7
7
|
import { useDeferredValue as g, useState as _ } from "react";
|
|
8
|
-
import { ExternalLink as v, Plug as y, Plus as b, Search as x } from "lucide-react";
|
|
9
|
-
import { gql as
|
|
10
|
-
import { useQuery as
|
|
11
|
-
import { jsx as
|
|
12
|
-
import { Button as
|
|
8
|
+
import { ExternalLink as v, Plug as y, Plus as b, Search as x, Webhook as S } from "lucide-react";
|
|
9
|
+
import { gql as C } from "@apollo/client";
|
|
10
|
+
import { useQuery as w } from "@apollo/client/react";
|
|
11
|
+
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
12
|
+
import { Button as D, EmptyState as O, Input as k } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
//#region src/planmagnet/pages/IntegrationsSyncsPagePage.tsx
|
|
14
|
-
var
|
|
14
|
+
var A = 20, j = ["ALL", ...s], M = C`
|
|
15
15
|
query SyncsList(
|
|
16
16
|
$workspaceId: ID!
|
|
17
17
|
$filter: PlanmagnetExternalSyncFilter
|
|
@@ -52,27 +52,27 @@ var k = 20, A = ["ALL", ...s], j = S`
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
`;
|
|
55
|
-
function
|
|
56
|
-
let { workspaceId: s } = e(),
|
|
57
|
-
...
|
|
58
|
-
...
|
|
59
|
-
...
|
|
60
|
-
}, { data:
|
|
55
|
+
function N() {
|
|
56
|
+
let { workspaceId: s } = e(), C = t(), [N, P] = _(""), [F, I] = _(""), [L, R] = _("ALL"), [z, B] = _(!1), [V, H] = _({}), U = g(N.trim()), W = {
|
|
57
|
+
...U ? { search: U } : {},
|
|
58
|
+
...F ? { projectId: F } : {},
|
|
59
|
+
...L === "ALL" ? {} : { status: L }
|
|
60
|
+
}, { data: G, loading: K, error: q, fetchMore: J } = w(M, {
|
|
61
61
|
variables: {
|
|
62
62
|
workspaceId: s ?? "",
|
|
63
|
-
filter:
|
|
64
|
-
pagination: { first:
|
|
63
|
+
filter: W,
|
|
64
|
+
pagination: { first: A }
|
|
65
65
|
},
|
|
66
66
|
skip: !s,
|
|
67
67
|
fetchPolicy: "cache-and-network"
|
|
68
|
-
}),
|
|
69
|
-
let e =
|
|
68
|
+
}), Y = G?.planmagnetExternalSyncs, X = Y?.items ?? [], Z = U !== "" || F !== "" || L !== "ALL", Q = !!Y && X.length === 0 && !Z, $ = async () => {
|
|
69
|
+
let e = Y?.pageInfo.endCursor;
|
|
70
70
|
if (e) {
|
|
71
|
-
|
|
71
|
+
B(!0);
|
|
72
72
|
try {
|
|
73
|
-
await
|
|
73
|
+
await J({
|
|
74
74
|
variables: { pagination: {
|
|
75
|
-
first:
|
|
75
|
+
first: A,
|
|
76
76
|
after: e
|
|
77
77
|
} },
|
|
78
78
|
updateQuery: (e, { fetchMoreResult: t }) => ({ planmagnetExternalSyncs: {
|
|
@@ -81,124 +81,133 @@ function M() {
|
|
|
81
81
|
} })
|
|
82
82
|
});
|
|
83
83
|
} finally {
|
|
84
|
-
|
|
84
|
+
B(!1);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
-
return /* @__PURE__ */
|
|
88
|
+
return /* @__PURE__ */ E("div", {
|
|
89
89
|
className: "space-y-6 p-6",
|
|
90
90
|
"data-testid": "sync-list",
|
|
91
91
|
children: [
|
|
92
|
-
/* @__PURE__ */
|
|
92
|
+
/* @__PURE__ */ E("div", {
|
|
93
93
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ E("h1", {
|
|
95
95
|
className: "text-2xl font-semibold flex items-center gap-2",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
97
|
-
}), /* @__PURE__ */
|
|
96
|
+
children: [/* @__PURE__ */ T(y, { className: "h-5 w-5 text-primary" }), "Integrations"]
|
|
97
|
+
}), /* @__PURE__ */ T("p", {
|
|
98
98
|
className: "text-sm text-muted-foreground",
|
|
99
|
-
children:
|
|
100
|
-
})] }),
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
children: Y ? `${Y.totalCount} sync${Y.totalCount === 1 ? "" : "s"}` : "Keep a project in step with an external issue tracker"
|
|
100
|
+
})] }), /* @__PURE__ */ E("div", {
|
|
101
|
+
className: "flex flex-wrap items-center gap-2",
|
|
102
|
+
children: [/* @__PURE__ */ E(D, {
|
|
103
|
+
size: "sm",
|
|
104
|
+
variant: "outline",
|
|
105
|
+
onClick: () => C("/integrations/webhooks-page"),
|
|
106
|
+
"data-testid": "sync-webhooks-link",
|
|
107
|
+
children: [/* @__PURE__ */ T(S, { className: "mr-1 h-4 w-4" }), "Webhooks"]
|
|
108
|
+
}), !Q && /* @__PURE__ */ E(D, {
|
|
109
|
+
size: "sm",
|
|
110
|
+
onClick: () => C("/integrations/new-sync-page"),
|
|
111
|
+
"data-testid": "sync-setup",
|
|
112
|
+
children: [/* @__PURE__ */ T(b, { className: "mr-1 h-4 w-4" }), "Set up a sync"]
|
|
113
|
+
})]
|
|
105
114
|
})]
|
|
106
115
|
}),
|
|
107
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ E("div", {
|
|
108
117
|
className: "flex flex-wrap items-center gap-3",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
118
|
+
children: [/* @__PURE__ */ E("div", {
|
|
110
119
|
className: "relative w-full max-w-sm",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ T(x, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ T(k, {
|
|
112
121
|
"aria-label": "Search syncs",
|
|
113
122
|
placeholder: "Search by external project",
|
|
114
123
|
className: "pl-8",
|
|
115
|
-
value:
|
|
124
|
+
value: N,
|
|
116
125
|
"data-testid": "sync-list-search",
|
|
117
|
-
onChange: (e) =>
|
|
126
|
+
onChange: (e) => P(e.target.value)
|
|
118
127
|
})]
|
|
119
|
-
}), /* @__PURE__ */
|
|
128
|
+
}), /* @__PURE__ */ T("div", {
|
|
120
129
|
className: "w-48",
|
|
121
130
|
"data-testid": "sync-list-project-filter",
|
|
122
|
-
children: /* @__PURE__ */
|
|
131
|
+
children: /* @__PURE__ */ T(o, {
|
|
123
132
|
id: "sync-list-filter-project",
|
|
124
|
-
value:
|
|
125
|
-
onChange:
|
|
133
|
+
value: F,
|
|
134
|
+
onChange: I,
|
|
126
135
|
noneLabel: "Any project"
|
|
127
136
|
})
|
|
128
137
|
})]
|
|
129
138
|
}),
|
|
130
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ T("div", {
|
|
131
140
|
className: "flex flex-wrap gap-1",
|
|
132
141
|
role: "group",
|
|
133
142
|
"aria-label": "Filter by status",
|
|
134
|
-
children:
|
|
143
|
+
children: j.map((e) => /* @__PURE__ */ T("button", {
|
|
135
144
|
type: "button",
|
|
136
|
-
"aria-pressed":
|
|
145
|
+
"aria-pressed": L === e,
|
|
137
146
|
"data-testid": `sync-list-status-${e}`,
|
|
138
|
-
onClick: () =>
|
|
139
|
-
className: `px-3 py-1.5 text-sm rounded-md ${
|
|
147
|
+
onClick: () => R(e),
|
|
148
|
+
className: `px-3 py-1.5 text-sm rounded-md ${L === e ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
|
|
140
149
|
children: e === "ALL" ? "All" : n(e)
|
|
141
150
|
}, e))
|
|
142
151
|
}),
|
|
143
|
-
|
|
152
|
+
q && /* @__PURE__ */ E("p", {
|
|
144
153
|
role: "alert",
|
|
145
154
|
className: "text-sm text-destructive",
|
|
146
|
-
children: ["Syncs could not be loaded: ",
|
|
155
|
+
children: ["Syncs could not be loaded: ", q.message]
|
|
147
156
|
}),
|
|
148
|
-
|
|
157
|
+
K && !G && /* @__PURE__ */ T("p", {
|
|
149
158
|
className: "text-sm text-muted-foreground",
|
|
150
159
|
children: "Loading syncs…"
|
|
151
160
|
}),
|
|
152
|
-
|
|
153
|
-
icon: /* @__PURE__ */
|
|
154
|
-
title:
|
|
161
|
+
Y && X.length === 0 && /* @__PURE__ */ T(O, {
|
|
162
|
+
icon: /* @__PURE__ */ T(y, { className: "h-8 w-8 text-muted-foreground" }),
|
|
163
|
+
title: Z ? "No syncs match" : "No syncs yet",
|
|
155
164
|
description: "A sync keeps a project's work items in step with an external tool such as GitHub Issues — created, updated, and resolved on both sides.",
|
|
156
|
-
action:
|
|
165
|
+
action: Q ? /* @__PURE__ */ E(D, {
|
|
157
166
|
size: "sm",
|
|
158
|
-
onClick: () =>
|
|
167
|
+
onClick: () => C("/integrations/new-sync-page"),
|
|
159
168
|
"data-testid": "sync-setup-empty",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
169
|
+
children: [/* @__PURE__ */ T(b, { className: "mr-1 h-4 w-4" }), "Set up a sync"]
|
|
161
170
|
}) : void 0
|
|
162
171
|
}),
|
|
163
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ T("ul", {
|
|
164
173
|
className: "space-y-3",
|
|
165
|
-
children:
|
|
166
|
-
let t = i(() =>
|
|
167
|
-
return /* @__PURE__ */
|
|
174
|
+
children: X.map((e) => {
|
|
175
|
+
let t = i(() => C(r("/integrations/sync-detail-page", e.id)), `${l(e.provider)} sync for ${e.project?.name ?? "unknown project"}`);
|
|
176
|
+
return /* @__PURE__ */ E("li", {
|
|
168
177
|
"data-testid": "sync-row",
|
|
169
178
|
...t,
|
|
170
179
|
className: `rounded-md border p-4 space-y-2 ${t.className}`,
|
|
171
|
-
children: [/* @__PURE__ */
|
|
180
|
+
children: [/* @__PURE__ */ E("div", {
|
|
172
181
|
className: "flex flex-wrap items-start justify-between gap-3",
|
|
173
|
-
children: [/* @__PURE__ */
|
|
182
|
+
children: [/* @__PURE__ */ E("div", {
|
|
174
183
|
className: "min-w-0 space-y-1",
|
|
175
184
|
children: [
|
|
176
|
-
/* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ E("div", {
|
|
177
186
|
className: "flex flex-wrap items-center gap-2",
|
|
178
187
|
children: [
|
|
179
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ T("span", {
|
|
180
189
|
className: "text-sm font-medium",
|
|
181
190
|
children: l(e.provider)
|
|
182
191
|
}),
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ T(m, { status: e.status }),
|
|
193
|
+
/* @__PURE__ */ T("span", {
|
|
185
194
|
className: "text-xs text-muted-foreground",
|
|
186
195
|
children: c(e.direction)
|
|
187
196
|
})
|
|
188
197
|
]
|
|
189
198
|
}),
|
|
190
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ E("p", {
|
|
191
200
|
className: "text-xs text-muted-foreground",
|
|
192
201
|
children: [
|
|
193
202
|
e.project?.name ?? "Unknown project",
|
|
194
203
|
" · ",
|
|
195
|
-
e.externalProjectUrl ? /* @__PURE__ */
|
|
204
|
+
e.externalProjectUrl ? /* @__PURE__ */ E("a", {
|
|
196
205
|
href: e.externalProjectUrl,
|
|
197
206
|
target: "_blank",
|
|
198
207
|
rel: "noreferrer noopener",
|
|
199
208
|
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
200
209
|
onClick: a,
|
|
201
|
-
children: [e.externalProjectId, /* @__PURE__ */
|
|
210
|
+
children: [e.externalProjectId, /* @__PURE__ */ T(v, { className: "h-3 w-3" })]
|
|
202
211
|
}) : e.externalProjectId,
|
|
203
212
|
" · ",
|
|
204
213
|
e.totalItemsSynced,
|
|
@@ -207,61 +216,61 @@ function M() {
|
|
|
207
216
|
" synced"
|
|
208
217
|
]
|
|
209
218
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
-
e.lastSyncError && /* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ T(f, { health: e.runHealth }),
|
|
220
|
+
e.lastSyncError && /* @__PURE__ */ T("p", {
|
|
212
221
|
className: "text-xs text-status-error-text",
|
|
213
222
|
"data-testid": "sync-row-last-error",
|
|
214
|
-
children: /* @__PURE__ */
|
|
223
|
+
children: /* @__PURE__ */ T(h, {
|
|
215
224
|
text: e.lastSyncError,
|
|
216
225
|
max: 120,
|
|
217
226
|
className: "text-status-error-text"
|
|
218
227
|
})
|
|
219
228
|
})
|
|
220
229
|
]
|
|
221
|
-
}), /* @__PURE__ */
|
|
230
|
+
}), /* @__PURE__ */ E("div", {
|
|
222
231
|
className: "flex shrink-0 flex-wrap items-center gap-2",
|
|
223
232
|
onClick: a,
|
|
224
233
|
onKeyDown: (e) => e.stopPropagation(),
|
|
225
234
|
children: [
|
|
226
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ T(d, {
|
|
227
236
|
syncId: e.id,
|
|
228
|
-
onResult: (t) =>
|
|
237
|
+
onResult: (t) => H((n) => ({
|
|
229
238
|
...n,
|
|
230
239
|
[e.id]: t
|
|
231
240
|
}))
|
|
232
241
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ T(u, {
|
|
234
243
|
syncId: e.id,
|
|
235
244
|
status: e.status
|
|
236
245
|
}),
|
|
237
|
-
/* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ T(D, {
|
|
238
247
|
size: "sm",
|
|
239
248
|
variant: "ghost",
|
|
240
|
-
onClick: () =>
|
|
249
|
+
onClick: () => C(r("/integrations/sync-detail-page", e.id)),
|
|
241
250
|
children: "Open"
|
|
242
251
|
})
|
|
243
252
|
]
|
|
244
253
|
})]
|
|
245
|
-
}),
|
|
254
|
+
}), V[e.id] && /* @__PURE__ */ T("div", {
|
|
246
255
|
onClick: a,
|
|
247
256
|
onKeyDown: (e) => e.stopPropagation(),
|
|
248
|
-
children: /* @__PURE__ */
|
|
257
|
+
children: /* @__PURE__ */ T(p, { result: V[e.id] })
|
|
249
258
|
})]
|
|
250
259
|
}, e.id);
|
|
251
260
|
})
|
|
252
261
|
}),
|
|
253
|
-
|
|
262
|
+
Y?.pageInfo.hasNextPage && /* @__PURE__ */ T(D, {
|
|
254
263
|
size: "sm",
|
|
255
264
|
variant: "outline",
|
|
256
|
-
onClick: () => void
|
|
257
|
-
disabled:
|
|
265
|
+
onClick: () => void $(),
|
|
266
|
+
disabled: z,
|
|
258
267
|
"data-testid": "sync-list-more",
|
|
259
|
-
children:
|
|
268
|
+
children: z ? "Loading…" : `Show more (${X.length} of ${Y.totalCount})`
|
|
260
269
|
})
|
|
261
270
|
]
|
|
262
271
|
});
|
|
263
272
|
}
|
|
264
273
|
//#endregion
|
|
265
|
-
export {
|
|
274
|
+
export { N as IntegrationsSyncsPagePage };
|
|
266
275
|
|
|
267
276
|
//# sourceMappingURL=IntegrationsSyncsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntegrationsSyncsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/IntegrationsSyncsPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Button, EmptyState, Input } from '@burdenoff/fe-libs/ui';\nimport { ExternalLink, Plug, Plus, Search } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { ProjectSelect } from '../controls/Pickers';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport {\n PauseResumeButton,\n RunNowButton,\n SyncHealthSummary,\n SyncRunResultSummary,\n SyncStatusBadge,\n TruncatedText,\n} from '../controls/SyncControls';\nimport {\n providerLabel,\n directionLabel,\n type ExternalSyncRow,\n type SyncRunResult,\n SYNC_STATUSES,\n} from '../integrations/syncTypes';\nimport { detailPath, rowLinkProps, stopRowActivation } from '../utils/rowNavigation';\n\n/**\n * Integrations (sync) list (AUT-08/40/09): every ExternalSync of the workspace, with its live health and the\n * actions that don't need the detail page — run now, pause/resume, open. Backend\n * (planmagnetExternalSyncs/CreateExternalSync/…/ResolveSyncConflict) is real and prod-verified for GITHUB\n * (BOFF-4446, AUT-04..25) — this is the page that makes it reachable.\n */\n\nconst PAGE = 20;\nconst STATUS_FILTERS = ['ALL', ...SYNC_STATUSES] as const;\n\nconst SYNCS_LIST = gql`\n query SyncsList(\n $workspaceId: ID!\n $filter: PlanmagnetExternalSyncFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetExternalSyncs(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n provider\n direction\n status\n projectId\n project {\n id\n name\n }\n externalProjectId\n externalProjectUrl\n conflictStrategy\n fieldMapping\n lastSyncedAt\n lastSyncError\n totalItemsSynced\n createdAt\n updatedAt\n runHealth {\n lastRunAt\n lastStatus\n consecutiveFailures\n runsLast7Days\n }\n }\n }\n }\n`;\n\nexport function IntegrationsSyncsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [search, setSearch] = useState('');\n const [projectId, setProjectId] = useState('');\n const [status, setStatus] = useState<(typeof STATUS_FILTERS)[number]>('ALL');\n const [loadingMore, setLoadingMore] = useState(false);\n const [runResults, setRunResults] = useState<Record<string, SyncRunResult>>({});\n const term = useDeferredValue(search.trim());\n\n const filter = {\n ...(term ? { search: term } : {}),\n ...(projectId ? { projectId } : {}),\n ...(status !== 'ALL' ? { status } : {}),\n };\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetExternalSyncs: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: ExternalSyncRow[];\n };\n }>(SYNCS_LIST, {\n variables: { workspaceId: workspaceId ?? '', filter, pagination: { first: PAGE } },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const list = data?.planmagnetExternalSyncs;\n const syncs = list?.items ?? [];\n const filtersActive = term !== '' || projectId !== '' || status !== 'ALL';\n // Mirrors QualityGates/Templates pages' own gate: the EmptyState's own \"Set up a sync\" action replaces the\n // header's button only once the list is confirmed empty AND unfiltered, so the two never render together.\n const showsEmptyStateAction = !!list && syncs.length === 0 && !filtersActive;\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetExternalSyncs: {\n ...fetchMoreResult.planmagnetExternalSyncs,\n items: [\n ...prev.planmagnetExternalSyncs.items,\n ...fetchMoreResult.planmagnetExternalSyncs.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"sync-list\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Plug className=\"h-5 w-5 text-primary\" />\n Integrations\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {list\n ? `${list.totalCount} sync${list.totalCount === 1 ? '' : 's'}`\n : 'Keep a project in step with an external issue tracker'}\n </p>\n </div>\n {!showsEmptyStateAction && (\n <Button\n size=\"sm\"\n onClick={() => navigate('/integrations/new-sync-page')}\n data-testid=\"sync-setup\"\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n Set up a sync\n </Button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n aria-label=\"Search syncs\"\n placeholder=\"Search by external project\"\n className=\"pl-8\"\n value={search}\n data-testid=\"sync-list-search\"\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <div className=\"w-48\" data-testid=\"sync-list-project-filter\">\n <ProjectSelect\n id=\"sync-list-filter-project\"\n value={projectId}\n onChange={setProjectId}\n noneLabel=\"Any project\"\n />\n </div>\n </div>\n <div className=\"flex flex-wrap gap-1\" role=\"group\" aria-label=\"Filter by status\">\n {STATUS_FILTERS.map((s) => (\n <button\n key={s}\n type=\"button\"\n aria-pressed={status === s}\n data-testid={`sync-list-status-${s}`}\n onClick={() => setStatus(s)}\n className={`px-3 py-1.5 text-sm rounded-md ${status === s ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {s === 'ALL' ? 'All' : enumLabel(s)}\n </button>\n ))}\n </div>\n\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Syncs could not be loaded: {error.message}\n </p>\n )}\n {/* Only before the first load: a refetch after an action must not blank the page. */}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading syncs…</p>}\n {list && syncs.length === 0 && (\n <EmptyState\n icon={<Plug className=\"h-8 w-8 text-muted-foreground\" />}\n title={filtersActive ? 'No syncs match' : 'No syncs yet'}\n description=\"A sync keeps a project's work items in step with an external tool such as GitHub Issues — created, updated, and resolved on both sides.\"\n action={\n showsEmptyStateAction ? (\n <Button\n size=\"sm\"\n onClick={() => navigate('/integrations/new-sync-page')}\n data-testid=\"sync-setup-empty\"\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n Set up a sync\n </Button>\n ) : undefined\n }\n />\n )}\n\n <ul className=\"space-y-3\">\n {syncs.map((sync) => {\n const rowProps = rowLinkProps(\n () => navigate(detailPath('/integrations/sync-detail-page', sync.id)),\n `${providerLabel(sync.provider)} sync for ${sync.project?.name ?? 'unknown project'}`\n );\n return (\n <li\n key={sync.id}\n data-testid=\"sync-row\"\n {...rowProps}\n className={`rounded-md border p-4 space-y-2 ${rowProps.className}`}\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{providerLabel(sync.provider)}</span>\n <SyncStatusBadge status={sync.status} />\n <span className=\"text-xs text-muted-foreground\">\n {directionLabel(sync.direction)}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {sync.project?.name ?? 'Unknown project'}\n {' · '}\n {sync.externalProjectUrl ? (\n <a\n href={sync.externalProjectUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-primary hover:underline\"\n onClick={stopRowActivation}\n >\n {sync.externalProjectId}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : (\n sync.externalProjectId\n )}\n {' · '}\n {sync.totalItemsSynced} item{sync.totalItemsSynced === 1 ? '' : 's'} synced\n </p>\n <SyncHealthSummary health={sync.runHealth} />\n {sync.lastSyncError && (\n <p className=\"text-xs text-status-error-text\" data-testid=\"sync-row-last-error\">\n <TruncatedText\n text={sync.lastSyncError}\n max={120}\n className=\"text-status-error-text\"\n />\n </p>\n )}\n </div>\n <div\n className=\"flex shrink-0 flex-wrap items-center gap-2\"\n onClick={stopRowActivation}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <RunNowButton\n syncId={sync.id}\n onResult={(result) => setRunResults((prev) => ({ ...prev, [sync.id]: result }))}\n />\n <PauseResumeButton syncId={sync.id} status={sync.status} />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => navigate(detailPath('/integrations/sync-detail-page', sync.id))}\n >\n Open\n </Button>\n </div>\n </div>\n {runResults[sync.id] && (\n <div onClick={stopRowActivation} onKeyDown={(e) => e.stopPropagation()}>\n <SyncRunResultSummary result={runResults[sync.id]} />\n </div>\n )}\n </li>\n );\n })}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void loadMore()}\n disabled={loadingMore}\n data-testid=\"sync-list-more\"\n >\n {loadingMore ? 'Loading…' : `Show more (${syncs.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IntegrationsSyncsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/IntegrationsSyncsPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Button, EmptyState, Input } from '@burdenoff/fe-libs/ui';\nimport { ExternalLink, Plug, Plus, Search, Webhook } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { ProjectSelect } from '../controls/Pickers';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport {\n PauseResumeButton,\n RunNowButton,\n SyncHealthSummary,\n SyncRunResultSummary,\n SyncStatusBadge,\n TruncatedText,\n} from '../controls/SyncControls';\nimport {\n providerLabel,\n directionLabel,\n type ExternalSyncRow,\n type SyncRunResult,\n SYNC_STATUSES,\n} from '../integrations/syncTypes';\nimport { detailPath, rowLinkProps, stopRowActivation } from '../utils/rowNavigation';\n\n/**\n * Integrations (sync) list (AUT-08/40/09): every ExternalSync of the workspace, with its live health and the\n * actions that don't need the detail page — run now, pause/resume, open. Backend\n * (planmagnetExternalSyncs/CreateExternalSync/…/ResolveSyncConflict) is real and prod-verified for GITHUB\n * (BOFF-4446, AUT-04..25) — this is the page that makes it reachable.\n */\n\nconst PAGE = 20;\nconst STATUS_FILTERS = ['ALL', ...SYNC_STATUSES] as const;\n\nconst SYNCS_LIST = gql`\n query SyncsList(\n $workspaceId: ID!\n $filter: PlanmagnetExternalSyncFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetExternalSyncs(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n provider\n direction\n status\n projectId\n project {\n id\n name\n }\n externalProjectId\n externalProjectUrl\n conflictStrategy\n fieldMapping\n lastSyncedAt\n lastSyncError\n totalItemsSynced\n createdAt\n updatedAt\n runHealth {\n lastRunAt\n lastStatus\n consecutiveFailures\n runsLast7Days\n }\n }\n }\n }\n`;\n\nexport function IntegrationsSyncsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [search, setSearch] = useState('');\n const [projectId, setProjectId] = useState('');\n const [status, setStatus] = useState<(typeof STATUS_FILTERS)[number]>('ALL');\n const [loadingMore, setLoadingMore] = useState(false);\n const [runResults, setRunResults] = useState<Record<string, SyncRunResult>>({});\n const term = useDeferredValue(search.trim());\n\n const filter = {\n ...(term ? { search: term } : {}),\n ...(projectId ? { projectId } : {}),\n ...(status !== 'ALL' ? { status } : {}),\n };\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetExternalSyncs: {\n totalCount: number;\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n items: ExternalSyncRow[];\n };\n }>(SYNCS_LIST, {\n variables: { workspaceId: workspaceId ?? '', filter, pagination: { first: PAGE } },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const list = data?.planmagnetExternalSyncs;\n const syncs = list?.items ?? [];\n const filtersActive = term !== '' || projectId !== '' || status !== 'ALL';\n // Mirrors QualityGates/Templates pages' own gate: the EmptyState's own \"Set up a sync\" action replaces the\n // header's button only once the list is confirmed empty AND unfiltered, so the two never render together.\n const showsEmptyStateAction = !!list && syncs.length === 0 && !filtersActive;\n\n const loadMore = async () => {\n const after = list?.pageInfo.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetExternalSyncs: {\n ...fetchMoreResult.planmagnetExternalSyncs,\n items: [\n ...prev.planmagnetExternalSyncs.items,\n ...fetchMoreResult.planmagnetExternalSyncs.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"sync-list\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Plug className=\"h-5 w-5 text-primary\" />\n Integrations\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {list\n ? `${list.totalCount} sync${list.totalCount === 1 ? '' : 's'}`\n : 'Keep a project in step with an external issue tracker'}\n </p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {/* AUT-33: outbound webhook subscriptions live on their own sub-route, not the host sidebar — see\n PlanMagnetRoutes.tsx's comment on that route. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate('/integrations/webhooks-page')}\n data-testid=\"sync-webhooks-link\"\n >\n <Webhook className=\"mr-1 h-4 w-4\" />\n Webhooks\n </Button>\n {!showsEmptyStateAction && (\n <Button\n size=\"sm\"\n onClick={() => navigate('/integrations/new-sync-page')}\n data-testid=\"sync-setup\"\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n Set up a sync\n </Button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n aria-label=\"Search syncs\"\n placeholder=\"Search by external project\"\n className=\"pl-8\"\n value={search}\n data-testid=\"sync-list-search\"\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <div className=\"w-48\" data-testid=\"sync-list-project-filter\">\n <ProjectSelect\n id=\"sync-list-filter-project\"\n value={projectId}\n onChange={setProjectId}\n noneLabel=\"Any project\"\n />\n </div>\n </div>\n <div className=\"flex flex-wrap gap-1\" role=\"group\" aria-label=\"Filter by status\">\n {STATUS_FILTERS.map((s) => (\n <button\n key={s}\n type=\"button\"\n aria-pressed={status === s}\n data-testid={`sync-list-status-${s}`}\n onClick={() => setStatus(s)}\n className={`px-3 py-1.5 text-sm rounded-md ${status === s ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {s === 'ALL' ? 'All' : enumLabel(s)}\n </button>\n ))}\n </div>\n\n {error && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n Syncs could not be loaded: {error.message}\n </p>\n )}\n {/* Only before the first load: a refetch after an action must not blank the page. */}\n {loading && !data && <p className=\"text-sm text-muted-foreground\">Loading syncs…</p>}\n {list && syncs.length === 0 && (\n <EmptyState\n icon={<Plug className=\"h-8 w-8 text-muted-foreground\" />}\n title={filtersActive ? 'No syncs match' : 'No syncs yet'}\n description=\"A sync keeps a project's work items in step with an external tool such as GitHub Issues — created, updated, and resolved on both sides.\"\n action={\n showsEmptyStateAction ? (\n <Button\n size=\"sm\"\n onClick={() => navigate('/integrations/new-sync-page')}\n data-testid=\"sync-setup-empty\"\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n Set up a sync\n </Button>\n ) : undefined\n }\n />\n )}\n\n <ul className=\"space-y-3\">\n {syncs.map((sync) => {\n const rowProps = rowLinkProps(\n () => navigate(detailPath('/integrations/sync-detail-page', sync.id)),\n `${providerLabel(sync.provider)} sync for ${sync.project?.name ?? 'unknown project'}`\n );\n return (\n <li\n key={sync.id}\n data-testid=\"sync-row\"\n {...rowProps}\n className={`rounded-md border p-4 space-y-2 ${rowProps.className}`}\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{providerLabel(sync.provider)}</span>\n <SyncStatusBadge status={sync.status} />\n <span className=\"text-xs text-muted-foreground\">\n {directionLabel(sync.direction)}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {sync.project?.name ?? 'Unknown project'}\n {' · '}\n {sync.externalProjectUrl ? (\n <a\n href={sync.externalProjectUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 text-primary hover:underline\"\n onClick={stopRowActivation}\n >\n {sync.externalProjectId}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : (\n sync.externalProjectId\n )}\n {' · '}\n {sync.totalItemsSynced} item{sync.totalItemsSynced === 1 ? '' : 's'} synced\n </p>\n <SyncHealthSummary health={sync.runHealth} />\n {sync.lastSyncError && (\n <p className=\"text-xs text-status-error-text\" data-testid=\"sync-row-last-error\">\n <TruncatedText\n text={sync.lastSyncError}\n max={120}\n className=\"text-status-error-text\"\n />\n </p>\n )}\n </div>\n <div\n className=\"flex shrink-0 flex-wrap items-center gap-2\"\n onClick={stopRowActivation}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <RunNowButton\n syncId={sync.id}\n onResult={(result) => setRunResults((prev) => ({ ...prev, [sync.id]: result }))}\n />\n <PauseResumeButton syncId={sync.id} status={sync.status} />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => navigate(detailPath('/integrations/sync-detail-page', sync.id))}\n >\n Open\n </Button>\n </div>\n </div>\n {runResults[sync.id] && (\n <div onClick={stopRowActivation} onKeyDown={(e) => e.stopPropagation()}>\n <SyncRunResultSummary result={runResults[sync.id]} />\n </div>\n )}\n </li>\n );\n })}\n </ul>\n {list?.pageInfo.hasNextPage && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void loadMore()}\n disabled={loadingMore}\n data-testid=\"sync-list-more\"\n >\n {loadingMore ? 'Loading…' : `Show more (${syncs.length} of ${list.totalCount})`}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IntegrationsWebhooksPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/IntegrationsWebhooksPagePage.tsx"],"names":[],"mappings":"AAsHA,wBAAgB,4BAA4B,4CAoG3C"}
|