@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.
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
- package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
- package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
- package/dist/planmagnet/controls/CompetitorControls.d.ts +71 -0
- package/dist/planmagnet/controls/CompetitorControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/CompetitorControls.js +346 -12
- package/dist/planmagnet/controls/CompetitorControls.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +23 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +431 -229
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
- package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeedbackControls.js +153 -0
- package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
- package/dist/planmagnet/controls/Pickers.d.ts +15 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
- package/dist/planmagnet/controls/Pickers.js +61 -1
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.d.ts +36 -0
- package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.js +118 -1
- package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -1
- package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/SyncControls.js +1 -0
- package/dist/planmagnet/controls/SyncControls.js.map +1 -1
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
- package/dist/planmagnet/dashboards/DashboardUI.js +8 -8
- package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +67 -59
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
- package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
- package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
- package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
- package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
- package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.js +19 -9
- package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +290 -283
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js +126 -63
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js +14 -0
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js +93 -57
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js +125 -83
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +226 -126
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +169 -122
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +165 -109
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
- package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +15 -3
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
- package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
- package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +59 -53
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
- package/dist/planmagnet/pages/analytics/MetricsCard.d.ts +8 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.d.ts.map +1 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.js +164 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.js.map +1 -0
- package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
- package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
- package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
- package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
- package/dist/planmagnet/people/People.d.ts +14 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -1
- package/dist/planmagnet/people/People.js +36 -22
- package/dist/planmagnet/people/People.js.map +1 -1
- package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts +2 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts.map +1 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.js +155 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.js.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
- package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
- package/package.json +1 -1
|
@@ -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
|
|
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":"
|
|
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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
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
|
|
19
|
-
let
|
|
20
|
+
function T() {
|
|
21
|
+
let m = e(), [T, E] = l({
|
|
20
22
|
name: "",
|
|
21
23
|
website: "",
|
|
22
24
|
description: "",
|
|
23
25
|
tags: ""
|
|
24
|
-
}), [
|
|
25
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
30
|
+
/* @__PURE__ */ _("div", {
|
|
29
31
|
className: "flex items-center justify-between",
|
|
30
|
-
children: [/* @__PURE__ */
|
|
32
|
+
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
|
|
31
33
|
className: "text-2xl font-semibold",
|
|
32
34
|
children: "Add Competitor"
|
|
33
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
38
|
+
})] }), /* @__PURE__ */ _("div", {
|
|
37
39
|
className: "flex gap-2",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ _(v, {
|
|
39
41
|
variant: "outline",
|
|
40
42
|
size: "sm",
|
|
41
|
-
onClick: () =>
|
|
42
|
-
children: [/* @__PURE__ */
|
|
43
|
-
}), /* @__PURE__ */
|
|
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 (
|
|
47
|
-
|
|
48
|
+
if (!(!T.name.trim() || !z)) {
|
|
49
|
+
I(null);
|
|
48
50
|
try {
|
|
49
|
-
await
|
|
50
|
-
name:
|
|
51
|
-
website:
|
|
52
|
-
description:
|
|
53
|
-
tags:
|
|
54
|
-
|
|
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
|
-
|
|
62
|
+
I(t(e, "The competitor could not be created."));
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
},
|
|
60
|
-
disabled:
|
|
61
|
-
children: [
|
|
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
|
-
|
|
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: ",
|
|
74
|
+
children: ["Failed to add competitor: ", F]
|
|
69
75
|
}),
|
|
70
|
-
/* @__PURE__ */
|
|
76
|
+
/* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ _(S, {
|
|
71
77
|
className: "flex items-center gap-2",
|
|
72
|
-
children: [/* @__PURE__ */
|
|
73
|
-
}) }), /* @__PURE__ */
|
|
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__ */
|
|
82
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
77
83
|
className: "text-sm font-medium",
|
|
78
84
|
children: "Name"
|
|
79
|
-
}), /* @__PURE__ */
|
|
80
|
-
value:
|
|
81
|
-
onChange: (e) =>
|
|
82
|
-
...
|
|
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__ */
|
|
94
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
89
95
|
className: "text-sm font-medium",
|
|
90
96
|
children: "Website"
|
|
91
|
-
}), /* @__PURE__ */
|
|
92
|
-
value:
|
|
93
|
-
onChange: (e) =>
|
|
94
|
-
...
|
|
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__ */
|
|
106
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
101
107
|
className: "text-sm font-medium",
|
|
102
108
|
children: "Description / Notes"
|
|
103
|
-
}), /* @__PURE__ */
|
|
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:
|
|
106
|
-
onChange: (e) =>
|
|
107
|
-
...
|
|
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__ */
|
|
118
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
113
119
|
className: "text-sm font-medium",
|
|
114
120
|
children: "Tags (comma-separated)"
|
|
115
|
-
}), /* @__PURE__ */
|
|
116
|
-
value:
|
|
117
|
-
onChange: (e) =>
|
|
118
|
-
...
|
|
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 {
|
|
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":";;;;;;;;;AAQA,IAAM,IAAoB,CAAG;;;;;;;;AAS7B,SAAgB,IAAsC;CACpD,IAAM,IAAW,GAAuB,EAElC,CAAC,GAAM,KAAW,EAAS;EAC/B,MAAM;EACN,SAAS;EACT,aAAa;EACb,MAAM;EACP,CAAC,EACI,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,GAAkB,EAAE,SAAS,OAAY,EAAY,GAAmB,EAC7E,gBAAgB,CAAC,mBAAmB,oBAAoB,EACzD,CAAC;AA2BF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAmB,CAAA,EAC1D,kBAAC,KAAD;KAAG,WAAU;eAAgC;KAEzC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,eAAe,EAAS,6BAA6B;gBAHvD,CAKE,kBAAC,GAAD,EAAG,WAAU,gBAAiB,CAAA,EAAA,SAEvB;SACT,kBAAC,GAAD;MAAQ,MAAK;MAAK,SA3CL,YAAY;AAC1B,aAAK,KAAK,MAAM,EACrB;UAAa,KAAK;AAClB,YAAI;AAgBF,SAfA,MAAM,EACJ,EAAiB,EACf,WAAW,EACT,OAAO;UACL,MAAM,EAAK,KAAK,MAAM;UACtB,SAAS,EAAK,QAAQ,MAAM,IAAI,KAAA;UAChC,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;UACxC,MAAM,EAAK,KACR,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ;UACnB,EACF,EACF,CAAC,CACH,EACD,EAAS,6BAA6B;iBAC/B,GAAG;AACV,WAAa,aAAa,QAAQ,EAAE,UAAU,uCAAuC;;;;MAsBxC,UAAU,KAAU,CAAC,EAAK,KAAK,MAAM;gBAA9E,CACG,IACC,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,GAEjD,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EACjC,iBAEK;QACL;OACF;;GAEL,KACC,kBAAC,OAAD;IAAK,MAAK;IAAQ,WAAU;cAA5B,CAAwF,8BAC3D,EACvB;;GAGR,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAW,WAAU;cAArB,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,qBAEpB;OACD,CAAA,EACb,kBAAC,GAAD;IAAa,WAAU;cAAvB;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAsB;MAAY,CAAA,EACnD,kBAAC,GAAD;MACE,OAAO,EAAK;MACZ,WAAW,MAAM,EAAQ;OAAE,GAAG;OAAM,MAAM,EAAE,OAAO;OAAO,CAAC;MAC3D,WAAU;MACV,aAAY;MACZ,CAAA,CACE,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAsB;MAAe,CAAA,EACtD,kBAAC,GAAD;MACE,OAAO,EAAK;MACZ,WAAW,MAAM,EAAQ;OAAE,GAAG;OAAM,SAAS,EAAE,OAAO;OAAO,CAAC;MAC9D,WAAU;MACV,aAAY;MACZ,CAAA,CACE,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAsB;MAA2B,CAAA,EAClE,kBAAC,YAAD;MACE,WAAU;MACV,OAAO,EAAK;MACZ,WAAW,MAAM,EAAQ;OAAE,GAAG;OAAM,aAAa,EAAE,OAAO;OAAO,CAAC;MAClE,aAAY;MACZ,CAAA,CACE,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAsB;MAA8B,CAAA,EACrE,kBAAC,GAAD;MACE,OAAO,EAAK;MACZ,WAAW,MAAM,EAAQ;OAAE,GAAG;OAAM,MAAM,EAAE,OAAO;OAAO,CAAC;MAC3D,WAAU;MACV,aAAY;MACZ,CAAA,CACE,EAAA,CAAA;KACM;MACT,EAAA,CAAA;GACH"}
|
|
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 & 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 & 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":"
|
|
1
|
+
{"version":3,"file":"CompetitorsEditCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx"],"names":[],"mappings":"AAsDA,wBAAgB,iCAAiC,4CA0QhD"}
|