@burdenoff/microfe-planmagnet 2026.928.2 → 2026.928.4

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 (85) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +102 -84
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +103 -55
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/controls/CompetitorControls.d.ts +20 -0
  8. package/dist/planmagnet/controls/CompetitorControls.d.ts.map +1 -0
  9. package/dist/planmagnet/controls/CompetitorControls.js +172 -0
  10. package/dist/planmagnet/controls/CompetitorControls.js.map +1 -0
  11. package/dist/planmagnet/controls/FeatureControls.js +29 -29
  12. package/dist/planmagnet/controls/FeatureTriage.js +11 -11
  13. package/dist/planmagnet/controls/ObjectiveControls.js +24 -24
  14. package/dist/planmagnet/controls/Pickers.d.ts +17 -2
  15. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  16. package/dist/planmagnet/controls/Pickers.js +100 -9
  17. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  18. package/dist/planmagnet/controls/PortfolioControls.d.ts +13 -0
  19. package/dist/planmagnet/controls/PortfolioControls.d.ts.map +1 -0
  20. package/dist/planmagnet/controls/PortfolioControls.js +114 -0
  21. package/dist/planmagnet/controls/PortfolioControls.js.map +1 -0
  22. package/dist/planmagnet/controls/ProgramControls.d.ts +25 -0
  23. package/dist/planmagnet/controls/ProgramControls.d.ts.map +1 -0
  24. package/dist/planmagnet/controls/ProgramControls.js +187 -0
  25. package/dist/planmagnet/controls/ProgramControls.js.map +1 -0
  26. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts +2 -0
  27. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts.map +1 -0
  28. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js +127 -0
  29. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js.map +1 -0
  30. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts +2 -0
  31. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts.map +1 -0
  32. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js +129 -0
  33. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js.map +1 -0
  34. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts +2 -0
  35. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts.map +1 -0
  36. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js +132 -0
  37. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js.map +1 -0
  38. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts +2 -0
  39. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts.map +1 -0
  40. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js +181 -0
  41. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js.map +1 -0
  42. package/dist/planmagnet/pages/PortfoliosCreatePortfolioPagePage.d.ts +2 -0
  43. package/dist/planmagnet/pages/PortfoliosCreatePortfolioPagePage.d.ts.map +1 -0
  44. package/dist/planmagnet/pages/PortfoliosCreatePortfolioPagePage.js +242 -0
  45. package/dist/planmagnet/pages/PortfoliosCreatePortfolioPagePage.js.map +1 -0
  46. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.d.ts +2 -0
  47. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.d.ts.map +1 -0
  48. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.js +302 -0
  49. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.js.map +1 -0
  50. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts +2 -0
  51. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts.map +1 -0
  52. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +335 -0
  53. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js.map +1 -0
  54. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.d.ts +2 -0
  55. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.d.ts.map +1 -0
  56. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.js +154 -0
  57. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.js.map +1 -0
  58. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +124 -138
  60. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.d.ts +2 -0
  62. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.d.ts.map +1 -0
  63. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.js +221 -0
  64. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.js.map +1 -0
  65. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.d.ts +2 -0
  66. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.d.ts.map +1 -0
  67. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.js +279 -0
  68. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.js.map +1 -0
  69. package/dist/planmagnet/pages/ProgramsProgramDetailPagePage.d.ts +2 -0
  70. package/dist/planmagnet/pages/ProgramsProgramDetailPagePage.d.ts.map +1 -0
  71. package/dist/planmagnet/pages/ProgramsProgramDetailPagePage.js +178 -0
  72. package/dist/planmagnet/pages/ProgramsProgramDetailPagePage.js.map +1 -0
  73. package/dist/planmagnet/pages/ProgramsProgramsPagePage.d.ts +2 -0
  74. package/dist/planmagnet/pages/ProgramsProgramsPagePage.d.ts.map +1 -0
  75. package/dist/planmagnet/pages/ProgramsProgramsPagePage.js +157 -0
  76. package/dist/planmagnet/pages/ProgramsProgramsPagePage.js.map +1 -0
  77. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +130 -103
  79. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +156 -136
  82. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/TasksImportPagePage.js +5 -5
  84. package/dist/planmagnet/types/index.d.ts.map +1 -1
  85. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { detailPath as t } from "../utils/rowNavigation.js";
3
+ import { CompetitorHeaderActions as n } from "../controls/CompetitorControls.js";
4
+ import { Loader2 as r, Swords as i } from "lucide-react";
5
+ import { useParams as a } from "react-router";
6
+ import { gql as o } from "@apollo/client";
7
+ import { useQuery as s } from "@apollo/client/react";
8
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
9
+ import { Badge as u, Card as d, CardContent as f, CardHeader as p, CardTitle as m } from "@burdenoff/fe-libs/ui";
10
+ //#region src/planmagnet/pages/CompetitorsCompetitorDetailPagePage.tsx
11
+ var h = o`
12
+ query GetCompetitor($id: ID!) {
13
+ planmagnetCompetitor(id: $id) {
14
+ id
15
+ workspaceId
16
+ name
17
+ website
18
+ description
19
+ tags
20
+ createdBy
21
+ createdAt
22
+ updatedAt
23
+ featureRequests {
24
+ id
25
+ title
26
+ status
27
+ }
28
+ }
29
+ }
30
+ `;
31
+ function g() {
32
+ let o = e(), g = a().id ?? "", { data: _, loading: v, error: y } = s(h, {
33
+ variables: { id: g },
34
+ skip: !g
35
+ });
36
+ if (v && !_) return /* @__PURE__ */ c("div", {
37
+ className: "flex items-center justify-center p-12",
38
+ children: /* @__PURE__ */ c(r, { className: "h-8 w-8 animate-spin text-muted-foreground" })
39
+ });
40
+ if (y) return /* @__PURE__ */ l("div", {
41
+ className: "p-6 text-destructive",
42
+ children: ["Failed to load competitor: ", y.message]
43
+ });
44
+ let b = _?.planmagnetCompetitor;
45
+ return b ? /* @__PURE__ */ l("div", {
46
+ className: "space-y-6 p-6",
47
+ children: [/* @__PURE__ */ l("div", {
48
+ className: "flex items-center justify-between",
49
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ l("div", {
50
+ className: "flex items-center gap-2",
51
+ children: [/* @__PURE__ */ c(i, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ c("h1", {
52
+ className: "text-2xl font-semibold",
53
+ children: b.name
54
+ })]
55
+ }), b.website && /* @__PURE__ */ c("p", {
56
+ className: "text-sm text-primary mt-1",
57
+ children: b.website
58
+ })] }), /* @__PURE__ */ c(n, {
59
+ competitorId: b.id,
60
+ name: b.name
61
+ })]
62
+ }), /* @__PURE__ */ l("div", {
63
+ className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
64
+ children: [/* @__PURE__ */ l("div", {
65
+ className: "lg:col-span-2 space-y-6",
66
+ children: [/* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ c(m, { children: "Notes" }) }), /* @__PURE__ */ c(f, { children: /* @__PURE__ */ c("p", {
67
+ className: "text-sm",
68
+ children: b.description ?? "No notes yet."
69
+ }) })] }), /* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ l(m, { children: [
70
+ "Linked Feature Requests (",
71
+ b.featureRequests.length,
72
+ ")"
73
+ ] }) }), /* @__PURE__ */ l(f, {
74
+ className: "space-y-2",
75
+ children: [b.featureRequests.length === 0 && /* @__PURE__ */ c("p", {
76
+ className: "text-sm text-muted-foreground",
77
+ children: "Not linked to any feature request yet. Add it from a feature request’s detail page."
78
+ }), b.featureRequests.map((e) => /* @__PURE__ */ l("button", {
79
+ type: "button",
80
+ className: "flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted",
81
+ onClick: () => o(t("/products/feature-detail-page", e.id)),
82
+ children: [/* @__PURE__ */ c("span", {
83
+ className: "text-sm font-medium",
84
+ children: e.title
85
+ }), /* @__PURE__ */ c(u, {
86
+ variant: "outline",
87
+ className: "text-xs capitalize",
88
+ children: e.status.toLowerCase().replace("_", " ")
89
+ })]
90
+ }, e.id))]
91
+ })] })]
92
+ }), /* @__PURE__ */ l("div", {
93
+ className: "space-y-6",
94
+ children: [/* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ c(m, { children: "Details" }) }), /* @__PURE__ */ l(f, {
95
+ className: "space-y-2 text-sm",
96
+ children: [/* @__PURE__ */ l("div", {
97
+ className: "flex justify-between",
98
+ children: [/* @__PURE__ */ c("span", {
99
+ className: "text-muted-foreground",
100
+ children: "Created"
101
+ }), /* @__PURE__ */ c("span", { children: new Date(b.createdAt).toLocaleDateString() })]
102
+ }), /* @__PURE__ */ l("div", {
103
+ className: "flex justify-between",
104
+ children: [/* @__PURE__ */ c("span", {
105
+ className: "text-muted-foreground",
106
+ children: "Updated"
107
+ }), /* @__PURE__ */ c("span", { children: new Date(b.updatedAt).toLocaleDateString() })]
108
+ })]
109
+ })] }), b.tags.length > 0 && /* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ c(m, { children: "Tags" }) }), /* @__PURE__ */ c(f, {
110
+ className: "flex flex-wrap gap-1",
111
+ children: b.tags.map((e) => /* @__PURE__ */ c(u, {
112
+ variant: "outline",
113
+ className: "text-xs",
114
+ children: e
115
+ }, e))
116
+ })] })]
117
+ })]
118
+ })]
119
+ }) : /* @__PURE__ */ c("div", {
120
+ className: "p-6 text-muted-foreground",
121
+ children: "Competitor not found."
122
+ });
123
+ }
124
+ //#endregion
125
+ export { g as CompetitorsCompetitorDetailPagePage };
126
+
127
+ //# sourceMappingURL=CompetitorsCompetitorDetailPagePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompetitorsCompetitorDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsCompetitorDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Swords, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { CompetitorHeaderActions } from '../controls/CompetitorControls';\n\nconst GET_COMPETITOR = gql`\n query GetCompetitor($id: ID!) {\n planmagnetCompetitor(id: $id) {\n id\n workspaceId\n name\n website\n description\n tags\n createdBy\n createdAt\n updatedAt\n featureRequests {\n id\n title\n status\n }\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n status: string;\n}\n\nexport function CompetitorsCompetitorDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const competitorId = params.id ?? '';\n\n const { data, loading, error } = useQuery<{\n planmagnetCompetitor: {\n id: string;\n name: string;\n website: string | null;\n description: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n featureRequests: FeatureRequestNode[];\n } | null;\n }>(GET_COMPETITOR, { variables: { id: competitorId }, skip: !competitorId });\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 competitor: {error.message}</div>;\n }\n\n const competitor = data?.planmagnetCompetitor;\n if (!competitor) return <div className=\"p-6 text-muted-foreground\">Competitor not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Swords className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{competitor.name}</h1>\n </div>\n {competitor.website && <p className=\"text-sm text-primary mt-1\">{competitor.website}</p>}\n </div>\n <CompetitorHeaderActions competitorId={competitor.id} name={competitor.name} />\n </div>\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>Notes</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{competitor.description ?? 'No notes yet.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Linked Feature Requests ({competitor.featureRequests.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {competitor.featureRequests.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n Not linked to any feature request yet. Add it from a feature request&rsquo;s\n detail page.\n </p>\n )}\n {competitor.featureRequests.map((fr) => (\n <button\n key={fr.id}\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted\"\n onClick={() => navigate(detailPath('/products/feature-detail-page', fr.id))}\n >\n <span className=\"text-sm font-medium\">{fr.title}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {fr.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </button>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(competitor.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(competitor.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {competitor.tags.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {competitor.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function CompetitorsCompetitorsPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=CompetitorsCompetitorsPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompetitorsCompetitorsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsCompetitorsPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,8BAA8B,4CAyG7C"}
@@ -0,0 +1,129 @@
1
+ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
+ import { detailPath as n, rowLinkProps as r } from "../utils/rowNavigation.js";
3
+ import { useDeferredValue as i, useState as a } from "react";
4
+ import { Loader2 as o, Plus as s, Swords as c } from "lucide-react";
5
+ import { gql as l } from "@apollo/client";
6
+ import { useQuery as u } from "@apollo/client/react";
7
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
8
+ import { Badge as p, Button as m, Card as h, CardContent as g, CardHeader as _, CardTitle as v, Input as y } from "@burdenoff/fe-libs/ui";
9
+ //#region src/planmagnet/pages/CompetitorsCompetitorsPagePage.tsx
10
+ var b = l`
11
+ query ListCompetitors(
12
+ $workspaceId: ID!
13
+ $filter: PlanmagnetCompetitorFilter
14
+ $pagination: PlanmagnetPaginationInput
15
+ ) {
16
+ planmagnetCompetitors(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
17
+ items {
18
+ id
19
+ name
20
+ website
21
+ description
22
+ tags
23
+ updatedAt
24
+ featureRequests {
25
+ id
26
+ }
27
+ }
28
+ totalCount
29
+ }
30
+ }
31
+ `;
32
+ function x() {
33
+ let l = t(), { workspaceId: x } = e(), [S, C] = a(""), w = i(S.trim()), { data: T, loading: E, error: D } = u(b, {
34
+ variables: {
35
+ workspaceId: x,
36
+ pagination: { first: 50 },
37
+ filter: w ? { search: w } : void 0
38
+ },
39
+ skip: !x
40
+ });
41
+ if (E && !T) return /* @__PURE__ */ d("div", {
42
+ className: "flex items-center justify-center p-12",
43
+ children: /* @__PURE__ */ d(o, { className: "h-8 w-8 animate-spin text-muted-foreground" })
44
+ });
45
+ if (D) return /* @__PURE__ */ f("div", {
46
+ className: "p-6 text-destructive",
47
+ children: ["Failed to load competitors: ", D.message]
48
+ });
49
+ let O = T?.planmagnetCompetitors.items ?? [], k = T?.planmagnetCompetitors.totalCount ?? 0;
50
+ return /* @__PURE__ */ f("div", {
51
+ className: "space-y-6 p-6",
52
+ children: [
53
+ /* @__PURE__ */ f("div", {
54
+ className: "flex items-center justify-between",
55
+ children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
56
+ className: "text-2xl font-semibold",
57
+ children: "Competitors"
58
+ }), /* @__PURE__ */ f("p", {
59
+ className: "text-sm text-muted-foreground",
60
+ children: [
61
+ k,
62
+ " competitor",
63
+ k === 1 ? "" : "s",
64
+ " tracked across feature requests"
65
+ ]
66
+ })] }), /* @__PURE__ */ f(m, {
67
+ size: "sm",
68
+ onClick: () => l("/products/create-competitor-page"),
69
+ children: [/* @__PURE__ */ d(s, { className: "mr-1 h-4 w-4" }), "New Competitor"]
70
+ })]
71
+ }),
72
+ /* @__PURE__ */ d(y, {
73
+ "aria-label": "Search competitors",
74
+ placeholder: "Search by name…",
75
+ value: S,
76
+ onChange: (e) => C(e.target.value),
77
+ className: "max-w-xs"
78
+ }),
79
+ /* @__PURE__ */ d("div", {
80
+ className: "grid grid-cols-1 md:grid-cols-2 gap-4",
81
+ children: O.map((e) => /* @__PURE__ */ f(h, {
82
+ ...r(() => l(n("/products/competitor-detail-page", e.id)), e.name),
83
+ children: [/* @__PURE__ */ d(_, {
84
+ className: "pb-2",
85
+ children: /* @__PURE__ */ f(v, {
86
+ className: "text-base flex items-center gap-2",
87
+ children: [/* @__PURE__ */ d(c, { className: "h-4 w-4 text-primary" }), e.name]
88
+ })
89
+ }), /* @__PURE__ */ f(g, {
90
+ className: "space-y-3",
91
+ children: [
92
+ /* @__PURE__ */ d("p", {
93
+ className: "text-sm text-muted-foreground",
94
+ children: e.description ?? "No description"
95
+ }),
96
+ e.website && /* @__PURE__ */ d("p", {
97
+ className: "text-xs text-primary",
98
+ children: e.website
99
+ }),
100
+ /* @__PURE__ */ f("div", {
101
+ className: "flex items-center justify-between text-xs text-muted-foreground border-t pt-2",
102
+ children: [/* @__PURE__ */ f("span", { children: [
103
+ e.featureRequests.length,
104
+ " linked feature",
105
+ e.featureRequests.length === 1 ? "" : "s"
106
+ ] }), e.tags.length > 0 && /* @__PURE__ */ d("div", {
107
+ className: "flex gap-1",
108
+ children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ d(p, {
109
+ variant: "outline",
110
+ className: "text-[10px]",
111
+ children: e
112
+ }, e))
113
+ })]
114
+ })
115
+ ]
116
+ })]
117
+ }, e.id))
118
+ }),
119
+ O.length === 0 && /* @__PURE__ */ d("p", {
120
+ className: "text-center text-muted-foreground py-8",
121
+ children: S ? "No competitors match your search." : "No competitors yet. Add one to start tracking it against your feature requests."
122
+ })
123
+ ]
124
+ });
125
+ }
126
+ //#endregion
127
+ export { x as CompetitorsCompetitorsPagePage };
128
+
129
+ //# sourceMappingURL=CompetitorsCompetitorsPagePage.js.map
@@ -0,0 +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"}
@@ -0,0 +1,2 @@
1
+ export declare function CompetitorsCreateCompetitorPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=CompetitorsCreateCompetitorPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompetitorsCreateCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,mCAAmC,4CA0HlD"}
@@ -0,0 +1,132 @@
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";
9
+ //#region src/planmagnet/pages/CompetitorsCreateCompetitorPagePage.tsx
10
+ var _ = s`
11
+ mutation CreateCompetitor($input: PlanmagnetCreateCompetitorInput!) {
12
+ planmagnetCreateCompetitor(input: $input) {
13
+ id
14
+ name
15
+ }
16
+ }
17
+ `;
18
+ function v() {
19
+ let s = e(), [v, y] = n({
20
+ name: "",
21
+ website: "",
22
+ description: "",
23
+ tags: ""
24
+ }), [b, x] = n(null), [S, { loading: C }] = c(_, { refetchQueries: ["ListCompetitors", "PickerCompetitors"] });
25
+ return /* @__PURE__ */ u("div", {
26
+ className: "space-y-6 p-6 max-w-4xl",
27
+ children: [
28
+ /* @__PURE__ */ u("div", {
29
+ className: "flex items-center justify-between",
30
+ children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
31
+ className: "text-2xl font-semibold",
32
+ children: "Add Competitor"
33
+ }), /* @__PURE__ */ l("p", {
34
+ className: "text-sm text-muted-foreground",
35
+ children: "Track a competitor so you can link it to feature requests it offers."
36
+ })] }), /* @__PURE__ */ u("div", {
37
+ className: "flex gap-2",
38
+ children: [/* @__PURE__ */ u(d, {
39
+ variant: "outline",
40
+ 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, {
44
+ size: "sm",
45
+ onClick: async () => {
46
+ if (v.name.trim()) {
47
+ x(null);
48
+ 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");
55
+ } catch (e) {
56
+ x(e instanceof Error ? e.message : "The competitor could not be created.");
57
+ }
58
+ }
59
+ },
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"]
62
+ })]
63
+ })]
64
+ }),
65
+ b && /* @__PURE__ */ u("div", {
66
+ role: "alert",
67
+ className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
68
+ children: ["Failed to add competitor: ", b]
69
+ }),
70
+ /* @__PURE__ */ u(f, { children: [/* @__PURE__ */ l(m, { children: /* @__PURE__ */ u(h, {
71
+ className: "flex items-center gap-2",
72
+ children: [/* @__PURE__ */ l(a, { className: "h-4 w-4" }), "Competitor Details"]
73
+ }) }), /* @__PURE__ */ u(p, {
74
+ className: "space-y-4",
75
+ children: [
76
+ /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
77
+ className: "text-sm font-medium",
78
+ children: "Name"
79
+ }), /* @__PURE__ */ l(g, {
80
+ value: v.name,
81
+ onChange: (e) => y({
82
+ ...v,
83
+ name: e.target.value
84
+ }),
85
+ className: "mt-1",
86
+ placeholder: "e.g. Linear"
87
+ })] }),
88
+ /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
89
+ className: "text-sm font-medium",
90
+ children: "Website"
91
+ }), /* @__PURE__ */ l(g, {
92
+ value: v.website,
93
+ onChange: (e) => y({
94
+ ...v,
95
+ website: e.target.value
96
+ }),
97
+ className: "mt-1",
98
+ placeholder: "https://…"
99
+ })] }),
100
+ /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
101
+ className: "text-sm font-medium",
102
+ children: "Description / Notes"
103
+ }), /* @__PURE__ */ l("textarea", {
104
+ 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,
108
+ description: e.target.value
109
+ }),
110
+ placeholder: "What they offer, how they compare…"
111
+ })] }),
112
+ /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("label", {
113
+ className: "text-sm font-medium",
114
+ children: "Tags (comma-separated)"
115
+ }), /* @__PURE__ */ l(g, {
116
+ value: v.tags,
117
+ onChange: (e) => y({
118
+ ...v,
119
+ tags: e.target.value
120
+ }),
121
+ className: "mt-1",
122
+ placeholder: "e.g. enterprise, pm-tool"
123
+ })] })
124
+ ]
125
+ })] })
126
+ ]
127
+ });
128
+ }
129
+ //#endregion
130
+ export { v as CompetitorsCreateCompetitorPagePage };
131
+
132
+ //# sourceMappingURL=CompetitorsCreateCompetitorPagePage.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,2 @@
1
+ export declare function CompetitorsEditCompetitorPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=CompetitorsEditCompetitorPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompetitorsEditCompetitorPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx"],"names":[],"mappings":"AAmCA,wBAAgB,iCAAiC,4CA8KhD"}
@@ -0,0 +1,181 @@
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { throwOnError as t } from "../utils/mutationResult.js";
3
+ import { detailPath as n } from "../utils/rowNavigation.js";
4
+ import { useMemo as r, useState as i } from "react";
5
+ import { ArrowLeft as a, Loader2 as o, Save as s } from "lucide-react";
6
+ import { useParams as c } from "react-router";
7
+ import { gql as l } from "@apollo/client";
8
+ import { useMutation as u, useQuery as d } from "@apollo/client/react";
9
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { Button as m, Card as h, CardContent as g, CardHeader as _, CardTitle as v, Input as y } from "@burdenoff/fe-libs/ui";
11
+ //#region src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx
12
+ var b = l`
13
+ query GetCompetitorForEdit($id: ID!) {
14
+ planmagnetCompetitor(id: $id) {
15
+ id
16
+ name
17
+ website
18
+ description
19
+ tags
20
+ }
21
+ }
22
+ `, x = l`
23
+ mutation UpdateCompetitor($id: ID!, $input: PlanmagnetUpdateCompetitorInput!) {
24
+ planmagnetUpdateCompetitor(id: $id, input: $input) {
25
+ id
26
+ name
27
+ website
28
+ description
29
+ tags
30
+ updatedAt
31
+ }
32
+ }
33
+ `;
34
+ function S() {
35
+ let l = e(), S = c().id ?? "", { data: C, loading: w, error: T } = d(b, {
36
+ variables: { id: S },
37
+ skip: !S
38
+ }), [E, { loading: D }] = u(x, {
39
+ refetchQueries: [
40
+ "ListCompetitors",
41
+ "GetCompetitorForEdit",
42
+ "GetCompetitor",
43
+ "PickerCompetitors"
44
+ ],
45
+ awaitRefetchQueries: !0
46
+ }), [O, k] = i(null), [A, j] = i(!1), M = C?.planmagnetCompetitor, N = r(() => ({
47
+ name: M?.name ?? "",
48
+ website: M?.website ?? "",
49
+ description: M?.description ?? "",
50
+ tags: M?.tags?.join(", ") ?? ""
51
+ }), [M]), [P, F] = i({}), I = {
52
+ ...N,
53
+ ...P
54
+ }, L = (e) => F(e);
55
+ return w && !C ? /* @__PURE__ */ f("div", {
56
+ className: "flex items-center justify-center p-12",
57
+ children: /* @__PURE__ */ f(o, { className: "h-8 w-8 animate-spin text-muted-foreground" })
58
+ }) : T ? /* @__PURE__ */ p("div", {
59
+ className: "p-6 text-destructive",
60
+ children: ["Failed to load competitor: ", T.message]
61
+ }) : M ? /* @__PURE__ */ p("div", {
62
+ className: "space-y-6 p-6 max-w-4xl",
63
+ children: [
64
+ /* @__PURE__ */ p("div", {
65
+ className: "flex items-center justify-between",
66
+ children: [/* @__PURE__ */ p("div", {
67
+ className: "flex items-center gap-3",
68
+ children: [/* @__PURE__ */ f(m, {
69
+ variant: "ghost",
70
+ size: "sm",
71
+ "aria-label": "Back to the competitor",
72
+ onClick: () => l(n("/products/competitor-detail-page", S)),
73
+ children: /* @__PURE__ */ f(a, { className: "h-4 w-4" })
74
+ }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
75
+ className: "text-2xl font-semibold",
76
+ children: "Edit Competitor"
77
+ }), /* @__PURE__ */ f("p", {
78
+ className: "text-sm text-muted-foreground",
79
+ children: M.name
80
+ })] })]
81
+ }), /* @__PURE__ */ p(m, {
82
+ size: "sm",
83
+ onClick: async () => {
84
+ if (!M || !I.name.trim()) return;
85
+ let e = (e) => e.split(",").map((e) => e.trim()).filter(Boolean), n = {
86
+ name: I.name.trim(),
87
+ website: I.website.trim() || null,
88
+ description: I.description.trim() || null,
89
+ tags: e(I.tags)
90
+ }, r = {
91
+ name: N.name,
92
+ website: N.website.trim() || null,
93
+ description: N.description.trim() || null,
94
+ tags: e(N.tags)
95
+ }, i = Object.fromEntries(Object.entries(n).filter(([e, t]) => JSON.stringify(t) !== JSON.stringify(r[e])));
96
+ if (k(null), j(!1), Object.keys(i).length === 0) {
97
+ j(!0);
98
+ return;
99
+ }
100
+ try {
101
+ await t(E({ variables: {
102
+ id: S,
103
+ input: i
104
+ } })), j(!0);
105
+ } catch (e) {
106
+ k(e instanceof Error ? e.message : "The competitor could not be saved.");
107
+ }
108
+ },
109
+ disabled: D || !I.name.trim(),
110
+ children: [D ? /* @__PURE__ */ f(o, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ f(s, { className: "mr-1 h-4 w-4" }), "Save Changes"]
111
+ })]
112
+ }),
113
+ A && /* @__PURE__ */ f("div", {
114
+ role: "status",
115
+ className: "p-3 bg-muted text-status-success-text rounded-md text-sm",
116
+ children: "Changes saved."
117
+ }),
118
+ O && /* @__PURE__ */ f("div", {
119
+ role: "alert",
120
+ className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
121
+ children: O
122
+ }),
123
+ /* @__PURE__ */ p(h, { children: [/* @__PURE__ */ f(_, { children: /* @__PURE__ */ f(v, { children: "Competitor Details" }) }), /* @__PURE__ */ p(g, {
124
+ className: "space-y-4",
125
+ children: [
126
+ /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
127
+ className: "text-sm font-medium",
128
+ children: "Name"
129
+ }), /* @__PURE__ */ f(y, {
130
+ value: I.name,
131
+ onChange: (e) => L({
132
+ ...I,
133
+ name: e.target.value
134
+ }),
135
+ className: "mt-1"
136
+ })] }),
137
+ /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
138
+ className: "text-sm font-medium",
139
+ children: "Website"
140
+ }), /* @__PURE__ */ f(y, {
141
+ value: I.website,
142
+ onChange: (e) => L({
143
+ ...I,
144
+ website: e.target.value
145
+ }),
146
+ className: "mt-1"
147
+ })] }),
148
+ /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
149
+ className: "text-sm font-medium",
150
+ children: "Description / Notes"
151
+ }), /* @__PURE__ */ f("textarea", {
152
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
153
+ value: I.description,
154
+ onChange: (e) => L({
155
+ ...I,
156
+ description: e.target.value
157
+ })
158
+ })] }),
159
+ /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
160
+ className: "text-sm font-medium",
161
+ children: "Tags (comma-separated)"
162
+ }), /* @__PURE__ */ f(y, {
163
+ value: I.tags,
164
+ onChange: (e) => L({
165
+ ...I,
166
+ tags: e.target.value
167
+ }),
168
+ className: "mt-1"
169
+ })] })
170
+ ]
171
+ })] })
172
+ ]
173
+ }) : /* @__PURE__ */ f("div", {
174
+ className: "p-6 text-muted-foreground",
175
+ children: "Competitor not found."
176
+ });
177
+ }
178
+ //#endregion
179
+ export { S as CompetitorsEditCompetitorPagePage };
180
+
181
+ //# sourceMappingURL=CompetitorsEditCompetitorPagePage.js.map