@burdenoff/microfe-planmagnet 2026.1001.5 → 2026.1001.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
  3. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  4. package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
  5. package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
  6. package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
  7. package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
  8. package/dist/planmagnet/controls/CompetitorControls.d.ts +71 -0
  9. package/dist/planmagnet/controls/CompetitorControls.d.ts.map +1 -1
  10. package/dist/planmagnet/controls/CompetitorControls.js +346 -12
  11. package/dist/planmagnet/controls/CompetitorControls.js.map +1 -1
  12. package/dist/planmagnet/controls/FeatureControls.d.ts +23 -1
  13. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  14. package/dist/planmagnet/controls/FeatureControls.js +431 -229
  15. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  16. package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
  17. package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
  18. package/dist/planmagnet/controls/FeedbackControls.js +153 -0
  19. package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
  20. package/dist/planmagnet/controls/Pickers.d.ts +15 -0
  21. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  22. package/dist/planmagnet/controls/Pickers.js +61 -1
  23. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  24. package/dist/planmagnet/controls/PrioritizationControls.d.ts +36 -0
  25. package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -1
  26. package/dist/planmagnet/controls/PrioritizationControls.js +118 -1
  27. package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -1
  28. package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
  29. package/dist/planmagnet/controls/SyncControls.js +1 -0
  30. package/dist/planmagnet/controls/SyncControls.js.map +1 -1
  31. package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
  32. package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
  33. package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
  34. package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
  35. package/dist/planmagnet/dashboards/DashboardUI.js +8 -8
  36. package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
  37. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
  38. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +67 -59
  39. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
  40. package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
  41. package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
  42. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
  43. package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
  44. package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
  45. package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
  46. package/dist/planmagnet/integrations/syncTypes.js +19 -9
  47. package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
  48. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  49. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +290 -283
  50. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  51. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts.map +1 -1
  52. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js +126 -63
  53. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js.map +1 -1
  54. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts.map +1 -1
  55. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js +14 -0
  56. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js.map +1 -1
  57. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts.map +1 -1
  58. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js +93 -57
  59. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js.map +1 -1
  60. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts.map +1 -1
  61. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js +125 -83
  62. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js.map +1 -1
  63. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
  64. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
  66. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
  69. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
  72. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
  73. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
  74. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
  75. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
  76. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
  77. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +226 -126
  79. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
  82. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  84. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +169 -122
  85. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  86. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  87. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +165 -109
  88. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  89. package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
  90. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
  91. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
  92. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  93. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +15 -3
  94. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  95. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
  96. package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
  97. package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
  98. package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
  99. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
  100. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  101. package/dist/planmagnet/pages/TeamTeamPagePage.js +59 -53
  102. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  103. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
  104. package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
  105. package/dist/planmagnet/pages/analytics/MetricsCard.d.ts +8 -0
  106. package/dist/planmagnet/pages/analytics/MetricsCard.d.ts.map +1 -0
  107. package/dist/planmagnet/pages/analytics/MetricsCard.js +164 -0
  108. package/dist/planmagnet/pages/analytics/MetricsCard.js.map +1 -0
  109. package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
  110. package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
  111. package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
  112. package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
  113. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
  114. package/dist/planmagnet/people/People.d.ts +14 -0
  115. package/dist/planmagnet/people/People.d.ts.map +1 -1
  116. package/dist/planmagnet/people/People.js +36 -22
  117. package/dist/planmagnet/people/People.js.map +1 -1
  118. package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts +2 -0
  119. package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts.map +1 -0
  120. package/dist/planmagnet/team/UnmatchedCrewIdentities.js +155 -0
  121. package/dist/planmagnet/team/UnmatchedCrewIdentities.js.map +1 -0
  122. package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
  123. package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
  124. package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
  125. package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
  126. 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 &quot;Run now&quot; 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 &quot;Run now&quot; 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,4CAgPxC"}
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 S } from "@apollo/client";
10
- import { useQuery as C } from "@apollo/client/react";
11
- import { jsx as w, jsxs as T } from "react/jsx-runtime";
12
- import { Button as E, EmptyState as D, Input as O } from "@burdenoff/fe-libs/ui";
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 k = 20, A = ["ALL", ...s], j = S`
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 M() {
56
- let { workspaceId: s } = e(), S = t(), [M, N] = _(""), [P, F] = _(""), [I, L] = _("ALL"), [R, z] = _(!1), [B, V] = _({}), H = g(M.trim()), U = {
57
- ...H ? { search: H } : {},
58
- ...P ? { projectId: P } : {},
59
- ...I === "ALL" ? {} : { status: I }
60
- }, { data: W, loading: G, error: K, fetchMore: q } = C(j, {
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: U,
64
- pagination: { first: k }
63
+ filter: W,
64
+ pagination: { first: A }
65
65
  },
66
66
  skip: !s,
67
67
  fetchPolicy: "cache-and-network"
68
- }), J = W?.planmagnetExternalSyncs, Y = J?.items ?? [], X = H !== "" || P !== "" || I !== "ALL", Z = !!J && Y.length === 0 && !X, Q = async () => {
69
- let e = J?.pageInfo.endCursor;
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
- z(!0);
71
+ B(!0);
72
72
  try {
73
- await q({
73
+ await J({
74
74
  variables: { pagination: {
75
- first: k,
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
- z(!1);
84
+ B(!1);
85
85
  }
86
86
  }
87
87
  };
88
- return /* @__PURE__ */ T("div", {
88
+ return /* @__PURE__ */ E("div", {
89
89
  className: "space-y-6 p-6",
90
90
  "data-testid": "sync-list",
91
91
  children: [
92
- /* @__PURE__ */ T("div", {
92
+ /* @__PURE__ */ E("div", {
93
93
  className: "flex flex-wrap items-center justify-between gap-3",
94
- children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ T("h1", {
94
+ children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ E("h1", {
95
95
  className: "text-2xl font-semibold flex items-center gap-2",
96
- children: [/* @__PURE__ */ w(y, { className: "h-5 w-5 text-primary" }), "Integrations"]
97
- }), /* @__PURE__ */ w("p", {
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: J ? `${J.totalCount} sync${J.totalCount === 1 ? "" : "s"}` : "Keep a project in step with an external issue tracker"
100
- })] }), !Z && /* @__PURE__ */ T(E, {
101
- size: "sm",
102
- onClick: () => S("/integrations/new-sync-page"),
103
- "data-testid": "sync-setup",
104
- children: [/* @__PURE__ */ w(b, { className: "mr-1 h-4 w-4" }), "Set up a sync"]
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__ */ T("div", {
116
+ /* @__PURE__ */ E("div", {
108
117
  className: "flex flex-wrap items-center gap-3",
109
- children: [/* @__PURE__ */ T("div", {
118
+ children: [/* @__PURE__ */ E("div", {
110
119
  className: "relative w-full max-w-sm",
111
- children: [/* @__PURE__ */ w(x, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ w(O, {
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: M,
124
+ value: N,
116
125
  "data-testid": "sync-list-search",
117
- onChange: (e) => N(e.target.value)
126
+ onChange: (e) => P(e.target.value)
118
127
  })]
119
- }), /* @__PURE__ */ w("div", {
128
+ }), /* @__PURE__ */ T("div", {
120
129
  className: "w-48",
121
130
  "data-testid": "sync-list-project-filter",
122
- children: /* @__PURE__ */ w(o, {
131
+ children: /* @__PURE__ */ T(o, {
123
132
  id: "sync-list-filter-project",
124
- value: P,
125
- onChange: F,
133
+ value: F,
134
+ onChange: I,
126
135
  noneLabel: "Any project"
127
136
  })
128
137
  })]
129
138
  }),
130
- /* @__PURE__ */ w("div", {
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: A.map((e) => /* @__PURE__ */ w("button", {
143
+ children: j.map((e) => /* @__PURE__ */ T("button", {
135
144
  type: "button",
136
- "aria-pressed": I === e,
145
+ "aria-pressed": L === e,
137
146
  "data-testid": `sync-list-status-${e}`,
138
- onClick: () => L(e),
139
- className: `px-3 py-1.5 text-sm rounded-md ${I === e ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
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
- K && /* @__PURE__ */ T("p", {
152
+ q && /* @__PURE__ */ E("p", {
144
153
  role: "alert",
145
154
  className: "text-sm text-destructive",
146
- children: ["Syncs could not be loaded: ", K.message]
155
+ children: ["Syncs could not be loaded: ", q.message]
147
156
  }),
148
- G && !W && /* @__PURE__ */ w("p", {
157
+ K && !G && /* @__PURE__ */ T("p", {
149
158
  className: "text-sm text-muted-foreground",
150
159
  children: "Loading syncs…"
151
160
  }),
152
- J && Y.length === 0 && /* @__PURE__ */ w(D, {
153
- icon: /* @__PURE__ */ w(y, { className: "h-8 w-8 text-muted-foreground" }),
154
- title: X ? "No syncs match" : "No syncs yet",
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: Z ? /* @__PURE__ */ T(E, {
165
+ action: Q ? /* @__PURE__ */ E(D, {
157
166
  size: "sm",
158
- onClick: () => S("/integrations/new-sync-page"),
167
+ onClick: () => C("/integrations/new-sync-page"),
159
168
  "data-testid": "sync-setup-empty",
160
- children: [/* @__PURE__ */ w(b, { className: "mr-1 h-4 w-4" }), "Set up a sync"]
169
+ children: [/* @__PURE__ */ T(b, { className: "mr-1 h-4 w-4" }), "Set up a sync"]
161
170
  }) : void 0
162
171
  }),
163
- /* @__PURE__ */ w("ul", {
172
+ /* @__PURE__ */ T("ul", {
164
173
  className: "space-y-3",
165
- children: Y.map((e) => {
166
- let t = i(() => S(r("/integrations/sync-detail-page", e.id)), `${l(e.provider)} sync for ${e.project?.name ?? "unknown project"}`);
167
- return /* @__PURE__ */ T("li", {
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__ */ T("div", {
180
+ children: [/* @__PURE__ */ E("div", {
172
181
  className: "flex flex-wrap items-start justify-between gap-3",
173
- children: [/* @__PURE__ */ T("div", {
182
+ children: [/* @__PURE__ */ E("div", {
174
183
  className: "min-w-0 space-y-1",
175
184
  children: [
176
- /* @__PURE__ */ T("div", {
185
+ /* @__PURE__ */ E("div", {
177
186
  className: "flex flex-wrap items-center gap-2",
178
187
  children: [
179
- /* @__PURE__ */ w("span", {
188
+ /* @__PURE__ */ T("span", {
180
189
  className: "text-sm font-medium",
181
190
  children: l(e.provider)
182
191
  }),
183
- /* @__PURE__ */ w(m, { status: e.status }),
184
- /* @__PURE__ */ w("span", {
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__ */ T("p", {
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__ */ T("a", {
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__ */ w(v, { className: "h-3 w-3" })]
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__ */ w(f, { health: e.runHealth }),
211
- e.lastSyncError && /* @__PURE__ */ w("p", {
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__ */ w(h, {
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__ */ T("div", {
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__ */ w(d, {
235
+ /* @__PURE__ */ T(d, {
227
236
  syncId: e.id,
228
- onResult: (t) => V((n) => ({
237
+ onResult: (t) => H((n) => ({
229
238
  ...n,
230
239
  [e.id]: t
231
240
  }))
232
241
  }),
233
- /* @__PURE__ */ w(u, {
242
+ /* @__PURE__ */ T(u, {
234
243
  syncId: e.id,
235
244
  status: e.status
236
245
  }),
237
- /* @__PURE__ */ w(E, {
246
+ /* @__PURE__ */ T(D, {
238
247
  size: "sm",
239
248
  variant: "ghost",
240
- onClick: () => S(r("/integrations/sync-detail-page", e.id)),
249
+ onClick: () => C(r("/integrations/sync-detail-page", e.id)),
241
250
  children: "Open"
242
251
  })
243
252
  ]
244
253
  })]
245
- }), B[e.id] && /* @__PURE__ */ w("div", {
254
+ }), V[e.id] && /* @__PURE__ */ T("div", {
246
255
  onClick: a,
247
256
  onKeyDown: (e) => e.stopPropagation(),
248
- children: /* @__PURE__ */ w(p, { result: B[e.id] })
257
+ children: /* @__PURE__ */ T(p, { result: V[e.id] })
249
258
  })]
250
259
  }, e.id);
251
260
  })
252
261
  }),
253
- J?.pageInfo.hasNextPage && /* @__PURE__ */ w(E, {
262
+ Y?.pageInfo.hasNextPage && /* @__PURE__ */ T(D, {
254
263
  size: "sm",
255
264
  variant: "outline",
256
- onClick: () => void Q(),
257
- disabled: R,
265
+ onClick: () => void $(),
266
+ disabled: z,
258
267
  "data-testid": "sync-list-more",
259
- children: R ? "Loading…" : `Show more (${Y.length} of ${J.totalCount})`
268
+ children: z ? "Loading…" : `Show more (${X.length} of ${Y.totalCount})`
260
269
  })
261
270
  ]
262
271
  });
263
272
  }
264
273
  //#endregion
265
- export { M as IntegrationsSyncsPagePage };
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":";;;;;;;;;;;;;AAgCA,IAAM,IAAO,IACP,IAAiB,CAAC,OAAO,GAAG,EAAc,EAE1C,IAAa,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CtB,SAAgB,IAA4B;CAC1C,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAClC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAQ,KAAa,EAA0C,MAAM,EACtE,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAY,KAAiB,EAAwC,EAAE,CAAC,EACzE,IAAO,EAAiB,EAAO,MAAM,CAAC,EAEtC,IAAS;EACb,GAAI,IAAO,EAAE,QAAQ,GAAM,GAAG,EAAE;EAChC,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE;EAClC,GAAI,MAAW,QAAqB,EAAE,GAAf,EAAE,WAAQ;EAClC,EAEK,EAAE,SAAM,YAAS,UAAO,iBAAc,EAMzC,GAAY;EACb,WAAW;GAAE,aAAa,KAAe;GAAI;GAAQ,YAAY,EAAE,OAAO,GAAM;GAAE;EAClF,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,IAAO,GAAM,yBACb,IAAQ,GAAM,SAAS,EAAE,EACzB,IAAgB,MAAS,MAAM,MAAc,MAAM,MAAW,OAG9D,IAAwB,CAAC,CAAC,KAAQ,EAAM,WAAW,KAAK,CAAC,GAEzD,IAAW,YAAY;EAC3B,IAAM,IAAQ,GAAM,SAAS;AACxB,SACL;KAAe,GAAK;AACpB,OAAI;AACF,UAAM,EAAU;KACd,WAAW,EAAE,YAAY;MAAE,OAAO;MAAM;MAAO,EAAE;KACjD,cAAc,GAAM,EAAE,0BAAuB,EAC3C,yBAAyB;MACvB,GAAG,EAAgB;MACnB,OAAO,CACL,GAAG,EAAK,wBAAwB,OAChC,GAAG,EAAgB,wBAAwB,MAC5C;MACF,EACF;KACF,CAAC;aACM;AACR,MAAe,GAAM;;;;AAIzB,QACE,kBAAC,OAAD;EAAK,WAAU;EAAgB,eAAY;YAA3C;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd,CACE,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA,EAAA,eAEtC;QACL,kBAAC,KAAD;KAAG,WAAU;eACV,IACG,GAAG,EAAK,WAAW,OAAO,EAAK,eAAe,IAAI,KAAK,QACvD;KACF,CAAA,CACA,EAAA,CAAA,EACL,CAAC,KACA,kBAAC,GAAD;KACE,MAAK;KACL,eAAe,EAAS,8BAA8B;KACtD,eAAY;eAHd,CAKE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAE1B;OAEP;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,2DAA4D,CAAA,EAC9E,kBAAC,GAAD;MACE,cAAW;MACX,aAAY;MACZ,WAAU;MACV,OAAO;MACP,eAAY;MACZ,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,CAAA,CACE;QACN,kBAAC,OAAD;KAAK,WAAU;KAAO,eAAY;eAChC,kBAAC,GAAD;MACE,IAAG;MACH,OAAO;MACP,UAAU;MACV,WAAU;MACV,CAAA;KACE,CAAA,CACF;;GACN,kBAAC,OAAD;IAAK,WAAU;IAAuB,MAAK;IAAQ,cAAW;cAC3D,EAAe,KAAK,MACnB,kBAAC,UAAD;KAEE,MAAK;KACL,gBAAc,MAAW;KACzB,eAAa,oBAAoB;KACjC,eAAe,EAAU,EAAE;KAC3B,WAAW,kCAAkC,MAAW,IAAI,uCAAuC;eAElG,MAAM,QAAQ,QAAQ,EAAU,EAAE;KAC5B,EARF,EAQE,CACT;IACE,CAAA;GAEL,KACC,kBAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;cAA1B,CAAqD,+BACvB,EAAM,QAChC;;GAGL,KAAW,CAAC,KAAQ,kBAAC,KAAD;IAAG,WAAU;cAAgC;IAAkB,CAAA;GACnF,KAAQ,EAAM,WAAW,KACxB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAM,WAAU,iCAAkC,CAAA;IACxD,OAAO,IAAgB,mBAAmB;IAC1C,aAAY;IACZ,QACE,IACE,kBAAC,GAAD;KACE,MAAK;KACL,eAAe,EAAS,8BAA8B;KACtD,eAAY;eAHd,CAKE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAE1B;SACP,KAAA;IAEN,CAAA;GAGJ,kBAAC,MAAD;IAAI,WAAU;cACX,EAAM,KAAK,MAAS;KACnB,IAAM,IAAW,QACT,EAAS,EAAW,kCAAkC,EAAK,GAAG,CAAC,EACrE,GAAG,EAAc,EAAK,SAAS,CAAC,YAAY,EAAK,SAAS,QAAQ,oBACnE;AACD,YACE,kBAAC,MAAD;MAEE,eAAY;MACZ,GAAI;MACJ,WAAW,mCAAmC,EAAS;gBAJzD,CAME,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,QAAD;YAAM,WAAU;sBAAuB,EAAc,EAAK,SAAS;YAAQ,CAAA;WAC3E,kBAAC,GAAD,EAAiB,QAAQ,EAAK,QAAU,CAAA;WACxC,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAe,EAAK,UAAU;YAC1B,CAAA;WACH;;SACN,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,EAAK,SAAS,QAAQ;WACtB;WACA,EAAK,qBACJ,kBAAC,KAAD;YACE,MAAM,EAAK;YACX,QAAO;YACP,KAAI;YACJ,WAAU;YACV,SAAS;sBALX,CAOG,EAAK,mBACN,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CAClC;gBAEJ,EAAK;WAEN;WACA,EAAK;WAAiB;WAAM,EAAK,qBAAqB,IAAI,KAAK;WAAI;WAClE;;SACJ,kBAAC,GAAD,EAAmB,QAAQ,EAAK,WAAa,CAAA;SAC5C,EAAK,iBACJ,kBAAC,KAAD;UAAG,WAAU;UAAiC,eAAY;oBACxD,kBAAC,GAAD;WACE,MAAM,EAAK;WACX,KAAK;WACL,WAAU;WACV,CAAA;UACA,CAAA;SAEF;WACN,kBAAC,OAAD;QACE,WAAU;QACV,SAAS;QACT,YAAY,MAAM,EAAE,iBAAiB;kBAHvC;SAKE,kBAAC,GAAD;UACE,QAAQ,EAAK;UACb,WAAW,MAAW,GAAe,OAAU;WAAE,GAAG;YAAO,EAAK,KAAK;WAAQ,EAAE;UAC/E,CAAA;SACF,kBAAC,GAAD;UAAmB,QAAQ,EAAK;UAAI,QAAQ,EAAK;UAAU,CAAA;SAC3D,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,eAAe,EAAS,EAAW,kCAAkC,EAAK,GAAG,CAAC;oBAC/E;UAEQ,CAAA;SACL;UACF;UACL,EAAW,EAAK,OACf,kBAAC,OAAD;OAAK,SAAS;OAAmB,YAAY,MAAM,EAAE,iBAAiB;iBACpE,kBAAC,GAAD,EAAsB,QAAQ,EAAW,EAAK,KAAO,CAAA;OACjD,CAAA,CAEL;QArEE,EAAK,GAqEP;MAEP;IACC,CAAA;GACJ,GAAM,SAAS,eACd,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,eAAe,KAAK,GAAU;IAC9B,UAAU;IACV,eAAY;cAEX,IAAc,aAAa,cAAc,EAAM,OAAO,MAAM,EAAK,WAAW;IACtE,CAAA;GAEP"}
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,2 @@
1
+ export declare function IntegrationsWebhooksPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=IntegrationsWebhooksPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegrationsWebhooksPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/IntegrationsWebhooksPagePage.tsx"],"names":[],"mappings":"AAsHA,wBAAgB,4BAA4B,4CAoG3C"}