@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
@@ -20,6 +20,8 @@ var b = l`
20
20
  website
21
21
  description
22
22
  tags
23
+ strengths
24
+ weaknesses
23
25
  updatedAt
24
26
  featureRequests {
25
27
  id
@@ -97,6 +99,18 @@ function x() {
97
99
  className: "text-xs text-primary",
98
100
  children: e.website
99
101
  }),
102
+ (e.strengths.length > 0 || e.weaknesses.length > 0) && /* @__PURE__ */ f("div", {
103
+ className: "flex gap-3 text-xs text-muted-foreground",
104
+ children: [e.strengths.length > 0 && /* @__PURE__ */ f("span", { children: [
105
+ "+",
106
+ e.strengths.length,
107
+ " strengths"
108
+ ] }), e.weaknesses.length > 0 && /* @__PURE__ */ f("span", { children: [
109
+ "−",
110
+ e.weaknesses.length,
111
+ " weaknesses"
112
+ ] })]
113
+ }),
100
114
  /* @__PURE__ */ f("div", {
101
115
  className: "flex items-center justify-between text-xs text-muted-foreground border-t pt-2",
102
116
  children: [/* @__PURE__ */ f("span", { children: [
@@ -1 +1 @@
1
- {"version":3,"file":"CompetitorsCompetitorsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsCompetitorsPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport { Swords, Plus, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\n\nconst LIST_COMPETITORS = gql`\n query ListCompetitors(\n $workspaceId: ID!\n $filter: PlanmagnetCompetitorFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetCompetitors(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n website\n description\n tags\n updatedAt\n featureRequests {\n id\n }\n }\n totalCount\n }\n }\n`;\n\ninterface CompetitorNode {\n id: string;\n name: string;\n website: string | null;\n description: string | null;\n tags: string[];\n featureRequests: { id: string }[];\n}\n\nexport function CompetitorsCompetitorsPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [search, setSearch] = useState('');\n const deferred = useDeferredValue(search.trim());\n\n const { data, loading, error } = useQuery<{\n planmagnetCompetitors: { items: CompetitorNode[]; totalCount: number };\n }>(LIST_COMPETITORS, {\n variables: {\n workspaceId,\n pagination: { first: 50 },\n filter: deferred ? { search: deferred } : undefined,\n },\n skip: !workspaceId,\n });\n\n // Only before the first load: a refetch must not blank the page (it unmounts every component on it and\n // loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load competitors: {error.message}</div>;\n }\n\n const competitors = data?.planmagnetCompetitors.items ?? [];\n const totalCount = data?.planmagnetCompetitors.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Competitors</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} competitor{totalCount === 1 ? '' : 's'} tracked across feature requests\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/create-competitor-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Competitor\n </Button>\n </div>\n\n <Input\n aria-label=\"Search competitors\"\n placeholder=\"Search by name…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"max-w-xs\"\n />\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {competitors.map((comp) => (\n <Card\n key={comp.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/competitor-detail-page', comp.id)),\n comp.name\n )}\n >\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Swords className=\"h-4 w-4 text-primary\" />\n {comp.name}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {comp.description ?? 'No description'}\n </p>\n {comp.website && <p className=\"text-xs text-primary\">{comp.website}</p>}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground border-t pt-2\">\n <span>\n {comp.featureRequests.length} linked feature\n {comp.featureRequests.length === 1 ? '' : 's'}\n </span>\n {comp.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {comp.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {competitors.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n {search\n ? 'No competitors match your search.'\n : 'No competitors yet. Add one to start tracking it against your feature requests.'}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAgBA,IAAM,IAAmB,CAAG;;;;;;;;;;;;;;;;;;;;;;AAgC5B,SAAgB,IAAiC;CAC/C,IAAM,IAAW,GAAuB,EAClC,EAAE,mBAAgB,GAAe,EACjC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,IAAW,EAAiB,EAAO,MAAM,CAAC,EAE1C,EAAE,SAAM,YAAS,aAAU,EAE9B,GAAkB;EACnB,WAAW;GACT;GACA,YAAY,EAAE,OAAO,IAAI;GACzB,QAAQ,IAAW,EAAE,QAAQ,GAAU,GAAG,KAAA;GAC3C;EACD,MAAM,CAAC;EACR,CAAC;AAIF,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,gCAA6B,EAAM,QAAc;;CAGhG,IAAM,IAAc,GAAM,sBAAsB,SAAS,EAAE,EACrD,IAAa,GAAM,sBAAsB,cAAc;AAE7D,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAgB,CAAA,EACvD,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG;MAAW;MAAY,MAAe,IAAI,KAAK;MAAI;MAClD;OACA,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAS,mCAAmC;eAA7E,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,iBAE1B;OACL;;GAEN,kBAAC,GAAD;IACE,cAAW;IACX,aAAY;IACZ,OAAO;IACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;IAC1C,WAAU;IACV,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,KAAK,MAChB,kBAAC,GAAD;KAEE,GAAI,QACI,EAAS,EAAW,oCAAoC,EAAK,GAAG,CAAC,EACvE,EAAK,KACN;eALH,CAOE,kBAAC,GAAD;MAAY,WAAU;gBACpB,kBAAC,GAAD;OAAW,WAAU;iBAArB,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC1C,EAAK,KACI;;MACD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAK,eAAe;QACnB,CAAA;OACH,EAAK,WAAW,kBAAC,KAAD;QAAG,WAAU;kBAAwB,EAAK;QAAY,CAAA;OACvE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAA;SACG,EAAK,gBAAgB;SAAO;SAC5B,EAAK,gBAAgB,WAAW,IAAI,KAAK;SACrC,EAAA,CAAA,EACN,EAAK,KAAK,SAAS,KAClB,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAK,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC1B,kBAAC,GAAD;UAAiB,SAAQ;UAAU,WAAU;oBAC1C;UACK,EAFI,EAEJ,CACR;SACE,CAAA,CAEJ;;OACM;QACT;OAjCA,EAAK,GAiCL,CACP;IACE,CAAA;GACL,EAAY,WAAW,KACtB,kBAAC,KAAD;IAAG,WAAU;cACV,IACG,sCACA;IACF,CAAA;GAEF"}
1
+ {"version":3,"file":"CompetitorsCompetitorsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsCompetitorsPagePage.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport { Swords, Plus, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\n\nconst LIST_COMPETITORS = gql`\n query ListCompetitors(\n $workspaceId: ID!\n $filter: PlanmagnetCompetitorFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetCompetitors(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n website\n description\n tags\n strengths\n weaknesses\n updatedAt\n featureRequests {\n id\n }\n }\n totalCount\n }\n }\n`;\n\ninterface CompetitorNode {\n id: string;\n name: string;\n website: string | null;\n description: string | null;\n tags: string[];\n strengths: string[];\n weaknesses: string[];\n featureRequests: { id: string }[];\n}\n\nexport function CompetitorsCompetitorsPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [search, setSearch] = useState('');\n const deferred = useDeferredValue(search.trim());\n\n const { data, loading, error } = useQuery<{\n planmagnetCompetitors: { items: CompetitorNode[]; totalCount: number };\n }>(LIST_COMPETITORS, {\n variables: {\n workspaceId,\n pagination: { first: 50 },\n filter: deferred ? { search: deferred } : undefined,\n },\n skip: !workspaceId,\n });\n\n // Only before the first load: a refetch must not blank the page (it unmounts every component on it and\n // loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load competitors: {error.message}</div>;\n }\n\n const competitors = data?.planmagnetCompetitors.items ?? [];\n const totalCount = data?.planmagnetCompetitors.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Competitors</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} competitor{totalCount === 1 ? '' : 's'} tracked across feature requests\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/create-competitor-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Competitor\n </Button>\n </div>\n\n <Input\n aria-label=\"Search competitors\"\n placeholder=\"Search by name…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"max-w-xs\"\n />\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {competitors.map((comp) => (\n <Card\n key={comp.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/competitor-detail-page', comp.id)),\n comp.name\n )}\n >\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Swords className=\"h-4 w-4 text-primary\" />\n {comp.name}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {comp.description ?? 'No description'}\n </p>\n {comp.website && <p className=\"text-xs text-primary\">{comp.website}</p>}\n {/* PRM-31 */}\n {(comp.strengths.length > 0 || comp.weaknesses.length > 0) && (\n <div className=\"flex gap-3 text-xs text-muted-foreground\">\n {comp.strengths.length > 0 && <span>+{comp.strengths.length} strengths</span>}\n {comp.weaknesses.length > 0 && <span>−{comp.weaknesses.length} weaknesses</span>}\n </div>\n )}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground border-t pt-2\">\n <span>\n {comp.featureRequests.length} linked feature\n {comp.featureRequests.length === 1 ? '' : 's'}\n </span>\n {comp.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {comp.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {competitors.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n {search\n ? 'No competitors match your search.'\n : 'No competitors yet. Add one to start tracking it against your feature requests.'}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CompetitorsCreateCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,mCAAmC,4CA0HlD"}
1
+ {"version":3,"file":"CompetitorsCreateCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx"],"names":[],"mappings":"AA2BA,wBAAgB,mCAAmC,4CAwLlD"}
@@ -1,13 +1,15 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
- import { throwOnError as t } from "../utils/mutationResult.js";
3
- import { useState as n } from "react";
4
- import { Loader2 as r, Save as i, Swords as a, X as o } from "lucide-react";
5
- import { gql as s } from "@apollo/client";
6
- import { useMutation as c } from "@apollo/client/react";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
- import { Button as d, Card as f, CardContent as p, CardHeader as m, CardTitle as h, Input as g } from "@burdenoff/fe-libs/ui";
2
+ import { mutationErrorMessage as t } from "../utils/graphqlErrors.js";
3
+ import { throwOnError as n } from "../utils/mutationResult.js";
4
+ import { JsonKeyValueEditor as r, StringListEditor as i, cleanStringList as a, competitorListValid as o, jsonRowsToObject as s, jsonRowsValid as c } from "../controls/CompetitorControls.js";
5
+ import { useState as l } from "react";
6
+ import { Loader2 as u, Save as d, Swords as f, X as p } from "lucide-react";
7
+ import { gql as m } from "@apollo/client";
8
+ import { useMutation as h } from "@apollo/client/react";
9
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
10
+ import { Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S, Input as C } from "@burdenoff/fe-libs/ui";
9
11
  //#region src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx
10
- var _ = s`
12
+ var w = m`
11
13
  mutation CreateCompetitor($input: PlanmagnetCreateCompetitorInput!) {
12
14
  planmagnetCreateCompetitor(input: $input) {
13
15
  id
@@ -15,118 +17,152 @@ var _ = s`
15
17
  }
16
18
  }
17
19
  `;
18
- function v() {
19
- let s = e(), [v, y] = n({
20
+ function T() {
21
+ let m = e(), [T, E] = l({
20
22
  name: "",
21
23
  website: "",
22
24
  description: "",
23
25
  tags: ""
24
- }), [b, x] = n(null), [S, { loading: C }] = c(_, { refetchQueries: ["ListCompetitors", "PickerCompetitors"] });
25
- return /* @__PURE__ */ u("div", {
26
+ }), [D, O] = l([]), [k, A] = l([]), [j, M] = l([]), [N, P] = l([]), [F, I] = l(null), [L, { loading: R }] = h(w, { refetchQueries: ["ListCompetitors", "PickerCompetitors"] }), z = o(D) && o(k) && c(j) && c(N);
27
+ return /* @__PURE__ */ _("div", {
26
28
  className: "space-y-6 p-6 max-w-4xl",
27
29
  children: [
28
- /* @__PURE__ */ u("div", {
30
+ /* @__PURE__ */ _("div", {
29
31
  className: "flex items-center justify-between",
30
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
32
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
31
33
  className: "text-2xl font-semibold",
32
34
  children: "Add Competitor"
33
- }), /* @__PURE__ */ l("p", {
35
+ }), /* @__PURE__ */ g("p", {
34
36
  className: "text-sm text-muted-foreground",
35
37
  children: "Track a competitor so you can link it to feature requests it offers."
36
- })] }), /* @__PURE__ */ u("div", {
38
+ })] }), /* @__PURE__ */ _("div", {
37
39
  className: "flex gap-2",
38
- children: [/* @__PURE__ */ u(d, {
40
+ children: [/* @__PURE__ */ _(v, {
39
41
  variant: "outline",
40
42
  size: "sm",
41
- onClick: () => s("/products/competitors-page"),
42
- children: [/* @__PURE__ */ l(o, { className: "mr-1 h-4 w-4" }), "Cancel"]
43
- }), /* @__PURE__ */ u(d, {
43
+ onClick: () => m("/products/competitors-page"),
44
+ children: [/* @__PURE__ */ g(p, { className: "mr-1 h-4 w-4" }), "Cancel"]
45
+ }), /* @__PURE__ */ _(v, {
44
46
  size: "sm",
45
47
  onClick: async () => {
46
- if (v.name.trim()) {
47
- x(null);
48
+ if (!(!T.name.trim() || !z)) {
49
+ I(null);
48
50
  try {
49
- await t(S({ variables: { input: {
50
- name: v.name.trim(),
51
- website: v.website.trim() || void 0,
52
- description: v.description.trim() || void 0,
53
- tags: v.tags.split(",").map((e) => e.trim()).filter(Boolean)
54
- } } })), s("/products/competitors-page");
51
+ await n(L({ variables: { input: {
52
+ name: T.name.trim(),
53
+ website: T.website.trim() || void 0,
54
+ description: T.description.trim() || void 0,
55
+ tags: T.tags.split(",").map((e) => e.trim()).filter(Boolean),
56
+ strengths: a(D),
57
+ weaknesses: a(k),
58
+ pricing: s(j),
59
+ features: s(N)
60
+ } } })), m("/products/competitors-page");
55
61
  } catch (e) {
56
- x(e instanceof Error ? e.message : "The competitor could not be created.");
62
+ I(t(e, "The competitor could not be created."));
57
63
  }
58
64
  }
59
65
  },
60
- disabled: C || !v.name.trim(),
61
- children: [C ? /* @__PURE__ */ l(r, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ l(i, { className: "mr-1 h-4 w-4" }), "Add Competitor"]
66
+ disabled: R || !T.name.trim() || !z,
67
+ children: [R ? /* @__PURE__ */ g(u, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "mr-1 h-4 w-4" }), "Add Competitor"]
62
68
  })]
63
69
  })]
64
70
  }),
65
- b && /* @__PURE__ */ u("div", {
71
+ F && /* @__PURE__ */ _("div", {
66
72
  role: "alert",
67
73
  className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
68
- children: ["Failed to add competitor: ", b]
74
+ children: ["Failed to add competitor: ", F]
69
75
  }),
70
- /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ u(h, {
76
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ _(S, {
71
77
  className: "flex items-center gap-2",
72
- children: [/* @__PURE__ */ l(a, { className: "h-4 w-4" }), "Competitor Details"]
73
- }) }), /* @__PURE__ */ u(p, {
78
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), "Competitor Details"]
79
+ }) }), /* @__PURE__ */ _(b, {
74
80
  className: "space-y-4",
75
81
  children: [
76
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
82
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
77
83
  className: "text-sm font-medium",
78
84
  children: "Name"
79
- }), /* @__PURE__ */ l(g, {
80
- value: v.name,
81
- onChange: (e) => y({
82
- ...v,
85
+ }), /* @__PURE__ */ g(C, {
86
+ value: T.name,
87
+ onChange: (e) => E({
88
+ ...T,
83
89
  name: e.target.value
84
90
  }),
85
91
  className: "mt-1",
86
92
  placeholder: "e.g. Linear"
87
93
  })] }),
88
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
94
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
89
95
  className: "text-sm font-medium",
90
96
  children: "Website"
91
- }), /* @__PURE__ */ l(g, {
92
- value: v.website,
93
- onChange: (e) => y({
94
- ...v,
97
+ }), /* @__PURE__ */ g(C, {
98
+ value: T.website,
99
+ onChange: (e) => E({
100
+ ...T,
95
101
  website: e.target.value
96
102
  }),
97
103
  className: "mt-1",
98
104
  placeholder: "https://…"
99
105
  })] }),
100
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
106
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
101
107
  className: "text-sm font-medium",
102
108
  children: "Description / Notes"
103
- }), /* @__PURE__ */ l("textarea", {
109
+ }), /* @__PURE__ */ g("textarea", {
104
110
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
105
- value: v.description,
106
- onChange: (e) => y({
107
- ...v,
111
+ value: T.description,
112
+ onChange: (e) => E({
113
+ ...T,
108
114
  description: e.target.value
109
115
  }),
110
116
  placeholder: "What they offer, how they compare…"
111
117
  })] }),
112
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
118
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
113
119
  className: "text-sm font-medium",
114
120
  children: "Tags (comma-separated)"
115
- }), /* @__PURE__ */ l(g, {
116
- value: v.tags,
117
- onChange: (e) => y({
118
- ...v,
121
+ }), /* @__PURE__ */ g(C, {
122
+ value: T.tags,
123
+ onChange: (e) => E({
124
+ ...T,
119
125
  tags: e.target.value
120
126
  }),
121
127
  className: "mt-1",
122
128
  placeholder: "e.g. enterprise, pm-tool"
123
129
  })] })
124
130
  ]
131
+ })] }),
132
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Strengths & Weaknesses" }) }), /* @__PURE__ */ _(b, {
133
+ className: "space-y-4",
134
+ children: [/* @__PURE__ */ g(i, {
135
+ label: "Strengths",
136
+ values: D,
137
+ onChange: O,
138
+ placeholder: "e.g. Strong enterprise SSO",
139
+ testId: "competitor-strengths"
140
+ }), /* @__PURE__ */ g(i, {
141
+ label: "Weaknesses",
142
+ values: k,
143
+ onChange: A,
144
+ placeholder: "e.g. No mobile app",
145
+ testId: "competitor-weaknesses"
146
+ })]
147
+ })] }),
148
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Pricing & Feature Comparison" }) }), /* @__PURE__ */ _(b, {
149
+ className: "space-y-4",
150
+ children: [/* @__PURE__ */ g(r, {
151
+ label: "Pricing",
152
+ rows: j,
153
+ onChange: M,
154
+ testId: "competitor-pricing"
155
+ }), /* @__PURE__ */ g(r, {
156
+ label: "Features",
157
+ rows: N,
158
+ onChange: P,
159
+ testId: "competitor-features"
160
+ })]
125
161
  })] })
126
162
  ]
127
163
  });
128
164
  }
129
165
  //#endregion
130
- export { v as CompetitorsCreateCompetitorPagePage };
166
+ export { T as CompetitorsCreateCompetitorPagePage };
131
167
 
132
168
  //# sourceMappingURL=CompetitorsCreateCompetitorPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CompetitorsCreateCompetitorPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Swords, Loader2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\n\nconst CREATE_COMPETITOR = gql`\n mutation CreateCompetitor($input: PlanmagnetCreateCompetitorInput!) {\n planmagnetCreateCompetitor(input: $input) {\n id\n name\n }\n }\n`;\n\nexport function CompetitorsCreateCompetitorPagePage() {\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n name: '',\n website: '',\n description: '',\n tags: '',\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n const [createCompetitor, { loading: saving }] = useMutation(CREATE_COMPETITOR, {\n refetchQueries: ['ListCompetitors', 'PickerCompetitors'],\n });\n\n const handleCreate = async () => {\n if (!form.name.trim()) return;\n setSaveError(null);\n try {\n await throwOnError(\n createCompetitor({\n variables: {\n input: {\n name: form.name.trim(),\n website: form.website.trim() || undefined,\n description: form.description.trim() || undefined,\n tags: form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n },\n },\n })\n );\n navigate('/products/competitors-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The competitor could not be created.');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Add Competitor</h1>\n <p className=\"text-sm text-muted-foreground\">\n Track a competitor so you can link it to feature requests it offers.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate('/products/competitors-page')}\n >\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleCreate} disabled={saving || !form.name.trim()}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Add Competitor\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to add competitor: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Swords className=\"h-4 w-4\" />\n Competitor Details\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Linear\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Website</label>\n <Input\n value={form.website}\n onChange={(e) => setForm({ ...form, website: e.target.value })}\n className=\"mt-1\"\n placeholder=\"https://…\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description / Notes</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"What they offer, how they compare…\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. enterprise, pm-tool\"\n />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CompetitorsCreateCompetitorPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Swords, Loader2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport {\n JsonKeyValueEditor,\n StringListEditor,\n cleanStringList,\n competitorListValid,\n jsonRowsToObject,\n jsonRowsValid,\n type JsonKeyValueRow,\n} from '../controls/CompetitorControls';\n\nconst CREATE_COMPETITOR = gql`\n mutation CreateCompetitor($input: PlanmagnetCreateCompetitorInput!) {\n planmagnetCreateCompetitor(input: $input) {\n id\n name\n }\n }\n`;\n\nexport function CompetitorsCreateCompetitorPagePage() {\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n name: '',\n website: '',\n description: '',\n tags: '',\n });\n // PRM-31\n const [strengths, setStrengths] = useState<string[]>([]);\n const [weaknesses, setWeaknesses] = useState<string[]>([]);\n const [pricingRows, setPricingRows] = useState<JsonKeyValueRow[]>([]);\n const [featuresRows, setFeaturesRows] = useState<JsonKeyValueRow[]>([]);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [createCompetitor, { loading: saving }] = useMutation(CREATE_COMPETITOR, {\n refetchQueries: ['ListCompetitors', 'PickerCompetitors'],\n });\n\n const fieldsValid =\n competitorListValid(strengths) &&\n competitorListValid(weaknesses) &&\n jsonRowsValid(pricingRows) &&\n jsonRowsValid(featuresRows);\n\n const handleCreate = async () => {\n if (!form.name.trim() || !fieldsValid) return;\n setSaveError(null);\n try {\n await throwOnError(\n createCompetitor({\n variables: {\n input: {\n name: form.name.trim(),\n website: form.website.trim() || undefined,\n description: form.description.trim() || undefined,\n tags: form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n strengths: cleanStringList(strengths),\n weaknesses: cleanStringList(weaknesses),\n pricing: jsonRowsToObject(pricingRows),\n features: jsonRowsToObject(featuresRows),\n },\n },\n })\n );\n navigate('/products/competitors-page');\n } catch (e) {\n setSaveError(mutationErrorMessage(e, 'The competitor could not be created.'));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Add Competitor</h1>\n <p className=\"text-sm text-muted-foreground\">\n Track a competitor so you can link it to feature requests it offers.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate('/products/competitors-page')}\n >\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={saving || !form.name.trim() || !fieldsValid}\n >\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Add Competitor\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to add competitor: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Swords className=\"h-4 w-4\" />\n Competitor Details\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Linear\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Website</label>\n <Input\n value={form.website}\n onChange={(e) => setForm({ ...form, website: e.target.value })}\n className=\"mt-1\"\n placeholder=\"https://…\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description / Notes</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"What they offer, how they compare…\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. enterprise, pm-tool\"\n />\n </div>\n </CardContent>\n </Card>\n\n {/* PRM-31 */}\n <Card>\n <CardHeader>\n <CardTitle>Strengths &amp; Weaknesses</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <StringListEditor\n label=\"Strengths\"\n values={strengths}\n onChange={setStrengths}\n placeholder=\"e.g. Strong enterprise SSO\"\n testId=\"competitor-strengths\"\n />\n <StringListEditor\n label=\"Weaknesses\"\n values={weaknesses}\n onChange={setWeaknesses}\n placeholder=\"e.g. No mobile app\"\n testId=\"competitor-weaknesses\"\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Pricing &amp; Feature Comparison</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <JsonKeyValueEditor\n label=\"Pricing\"\n rows={pricingRows}\n onChange={setPricingRows}\n testId=\"competitor-pricing\"\n />\n <JsonKeyValueEditor\n label=\"Features\"\n rows={featuresRows}\n onChange={setFeaturesRows}\n testId=\"competitor-features\"\n />\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CompetitorsEditCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx"],"names":[],"mappings":"AAmCA,wBAAgB,iCAAiC,4CA8KhD"}
1
+ {"version":3,"file":"CompetitorsEditCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx"],"names":[],"mappings":"AAsDA,wBAAgB,iCAAiC,4CA0QhD"}