@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 @@
1
+ {"version":3,"file":"CompetitorsEditCompetitorPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/CompetitorsEditCompetitorPagePage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError } from '../utils/mutationResult';\n\nconst GET_COMPETITOR = gql`\n query GetCompetitorForEdit($id: ID!) {\n planmagnetCompetitor(id: $id) {\n id\n name\n website\n description\n tags\n }\n }\n`;\n\nconst UPDATE_COMPETITOR = gql`\n mutation UpdateCompetitor($id: ID!, $input: PlanmagnetUpdateCompetitorInput!) {\n planmagnetUpdateCompetitor(id: $id, input: $input) {\n id\n name\n website\n description\n tags\n updatedAt\n }\n }\n`;\n\nexport function CompetitorsEditCompetitorPagePage() {\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 } | null;\n }>(GET_COMPETITOR, { variables: { id: competitorId }, skip: !competitorId });\n\n const [updateCompetitor, { loading: saving }] = useMutation(UPDATE_COMPETITOR, {\n refetchQueries: [\n 'ListCompetitors',\n 'GetCompetitorForEdit',\n 'GetCompetitor',\n 'PickerCompetitors',\n ],\n awaitRefetchQueries: true,\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saved, setSaved] = useState(false);\n\n const competitor = data?.planmagnetCompetitor;\n\n const defaultForm = useMemo(\n () => ({\n name: competitor?.name ?? '',\n website: competitor?.website ?? '',\n description: competitor?.description ?? '',\n tags: competitor?.tags?.join(', ') ?? '',\n }),\n [competitor]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n const setForm = (val: Record<string, string>) => setFormOverrides(val);\n\n const handleSave = async () => {\n if (!competitor || !form.name.trim()) return;\n const tagList = (v: string) =>\n v\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n const next: Record<string, unknown> = {\n name: form.name.trim(),\n website: form.website.trim() || null,\n description: form.description.trim() || null,\n tags: tagList(form.tags),\n };\n const before: Record<string, unknown> = {\n name: defaultForm.name,\n website: defaultForm.website.trim() || null,\n description: defaultForm.description.trim() || null,\n tags: tagList(defaultForm.tags),\n };\n const input = Object.fromEntries(\n Object.entries(next).filter(([k, v]) => JSON.stringify(v) !== JSON.stringify(before[k]))\n );\n setSaveError(null);\n setSaved(false);\n if (Object.keys(input).length === 0) {\n setSaved(true);\n return;\n }\n try {\n await throwOnError(updateCompetitor({ variables: { id: competitorId, input } }));\n setSaved(true);\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The competitor could not be saved.');\n }\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 competitor: {error.message}</div>;\n }\n\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 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Back to the competitor\"\n onClick={() => navigate(detailPath('/products/competitor-detail-page', competitorId))}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Competitor</h1>\n <p className=\"text-sm text-muted-foreground\">{competitor.name}</p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} 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 Save Changes\n </Button>\n </div>\n\n {saved && (\n <div role=\"status\" className=\"p-3 bg-muted text-status-success-text rounded-md text-sm\">\n Changes saved.\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Competitor Details</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 />\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 />\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 />\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 />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function PortfoliosCreatePortfolioPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=PortfoliosCreatePortfolioPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortfoliosCreatePortfolioPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/PortfoliosCreatePortfolioPagePage.tsx"],"names":[],"mappings":"AAmBA,wBAAgB,iCAAiC,4CAyOhD"}
@@ -0,0 +1,242 @@
1
+ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
+ import { throwOnError as n } from "../utils/mutationResult.js";
3
+ import { enumLabel as r } from "../controls/WorkItemControls.js";
4
+ import { PORTFOLIO_STATUSES as i, STRATEGIC_ALIGNMENTS as a } from "../controls/PortfolioControls.js";
5
+ import { useState as o } from "react";
6
+ import { Briefcase as s, Loader2 as c, Save as l, X as u } from "lucide-react";
7
+ import { gql as d } from "@apollo/client";
8
+ import { useMutation as f } from "@apollo/client/react";
9
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
10
+ import { Button as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y, Input as b } from "@burdenoff/fe-libs/ui";
11
+ //#region src/planmagnet/pages/PortfoliosCreatePortfolioPagePage.tsx
12
+ var x = d`
13
+ mutation CreatePortfolio($input: PlanmagnetCreatePortfolioInput!) {
14
+ planmagnetCreatePortfolio(input: $input) {
15
+ id
16
+ name
17
+ }
18
+ }
19
+ `;
20
+ function S() {
21
+ let { currentUser: d } = e(), S = t(), [C, w] = o({
22
+ name: "",
23
+ description: "",
24
+ status: "ACTIVE",
25
+ alignment: "",
26
+ alignmentLabel: "",
27
+ budgetTotal: "",
28
+ currency: "",
29
+ fiscalPeriod: "",
30
+ tags: ""
31
+ }), [T, E] = o(null), [D, { loading: O }] = f(x, { refetchQueries: ["ListPortfolios"] }), k = C.alignment === "CUSTOM" && !C.alignmentLabel.trim(), A = C.budgetTotal !== "" && Number(C.budgetTotal) < 0;
32
+ return /* @__PURE__ */ m("div", {
33
+ className: "space-y-6 p-6 max-w-4xl",
34
+ children: [
35
+ /* @__PURE__ */ m("div", {
36
+ className: "flex items-center justify-between",
37
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
38
+ className: "text-2xl font-semibold",
39
+ children: "Create Portfolio"
40
+ }), /* @__PURE__ */ p("p", {
41
+ className: "text-sm text-muted-foreground",
42
+ children: "Group programs and projects for executive-level oversight and alignment tracking."
43
+ })] }), /* @__PURE__ */ m("div", {
44
+ className: "flex gap-2",
45
+ children: [/* @__PURE__ */ m(h, {
46
+ variant: "outline",
47
+ size: "sm",
48
+ onClick: () => S("/products/portfolios-page"),
49
+ children: [/* @__PURE__ */ p(u, { className: "mr-1 h-4 w-4" }), "Cancel"]
50
+ }), /* @__PURE__ */ m(h, {
51
+ size: "sm",
52
+ onClick: async () => {
53
+ if (!(!C.name.trim() || k || A)) {
54
+ E(null);
55
+ try {
56
+ await n(D({ variables: { input: {
57
+ name: C.name.trim(),
58
+ description: C.description.trim() || void 0,
59
+ status: C.status,
60
+ ownerId: d?.id ?? "",
61
+ ...C.alignment ? { alignment: C.alignment } : {},
62
+ ...C.alignment === "CUSTOM" ? { alignmentLabel: C.alignmentLabel.trim() } : {},
63
+ ...C.budgetTotal === "" ? {} : { budgetTotal: Number(C.budgetTotal) },
64
+ currency: C.currency.trim() || void 0,
65
+ fiscalPeriod: C.fiscalPeriod.trim() || void 0,
66
+ tags: C.tags.split(",").map((e) => e.trim()).filter(Boolean)
67
+ } } })), S("/products/portfolios-page");
68
+ } catch (e) {
69
+ E(e instanceof Error ? e.message : "The portfolio could not be created.");
70
+ }
71
+ }
72
+ },
73
+ disabled: O || !C.name.trim() || k || A,
74
+ children: [O ? /* @__PURE__ */ p(c, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ p(l, { className: "mr-1 h-4 w-4" }), "Create Portfolio"]
75
+ })]
76
+ })]
77
+ }),
78
+ T && /* @__PURE__ */ m("div", {
79
+ role: "alert",
80
+ className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
81
+ children: ["Failed to create portfolio: ", T]
82
+ }),
83
+ /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, {
84
+ className: "flex items-center gap-2",
85
+ children: [/* @__PURE__ */ p(s, { className: "h-4 w-4" }), "Portfolio Details"]
86
+ }) }), /* @__PURE__ */ m(_, {
87
+ className: "space-y-4",
88
+ children: [
89
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
90
+ className: "text-sm font-medium",
91
+ children: "Name"
92
+ }), /* @__PURE__ */ p(b, {
93
+ value: C.name,
94
+ onChange: (e) => w({
95
+ ...C,
96
+ name: e.target.value
97
+ }),
98
+ className: "mt-1",
99
+ placeholder: "e.g. 2026 Platform Investments"
100
+ })] }),
101
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
102
+ className: "text-sm font-medium",
103
+ children: "Description"
104
+ }), /* @__PURE__ */ p("textarea", {
105
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
106
+ value: C.description,
107
+ onChange: (e) => w({
108
+ ...C,
109
+ description: e.target.value
110
+ }),
111
+ placeholder: "Describe what this portfolio covers…"
112
+ })] }),
113
+ /* @__PURE__ */ m("div", {
114
+ className: "grid grid-cols-2 gap-4",
115
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
116
+ htmlFor: "portfolio-status",
117
+ className: "text-sm font-medium",
118
+ children: "Status"
119
+ }), /* @__PURE__ */ p("select", {
120
+ id: "portfolio-status",
121
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
122
+ value: C.status,
123
+ onChange: (e) => w({
124
+ ...C,
125
+ status: e.target.value
126
+ }),
127
+ children: i.map((e) => /* @__PURE__ */ p("option", {
128
+ value: e,
129
+ children: r(e)
130
+ }, e))
131
+ })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
132
+ htmlFor: "portfolio-alignment",
133
+ className: "text-sm font-medium",
134
+ children: "Strategic Alignment"
135
+ }), /* @__PURE__ */ m("select", {
136
+ id: "portfolio-alignment",
137
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
138
+ value: C.alignment,
139
+ onChange: (e) => w({
140
+ ...C,
141
+ alignment: e.target.value
142
+ }),
143
+ children: [/* @__PURE__ */ p("option", {
144
+ value: "",
145
+ children: "Not set"
146
+ }), a.map((e) => /* @__PURE__ */ p("option", {
147
+ value: e,
148
+ children: e === "CUSTOM" ? "Custom…" : r(e)
149
+ }, e))]
150
+ })] })]
151
+ }),
152
+ C.alignment === "CUSTOM" && /* @__PURE__ */ m("div", { children: [
153
+ /* @__PURE__ */ p("label", {
154
+ className: "text-sm font-medium",
155
+ children: "Custom Alignment Label"
156
+ }),
157
+ /* @__PURE__ */ p(b, {
158
+ value: C.alignmentLabel,
159
+ onChange: (e) => w({
160
+ ...C,
161
+ alignmentLabel: e.target.value
162
+ }),
163
+ className: "mt-1",
164
+ placeholder: "Name this strategic theme",
165
+ "aria-invalid": k
166
+ }),
167
+ k && /* @__PURE__ */ p("p", {
168
+ role: "alert",
169
+ className: "text-sm text-destructive mt-1",
170
+ children: "A custom alignment needs a label."
171
+ })
172
+ ] }),
173
+ /* @__PURE__ */ m("div", {
174
+ className: "grid grid-cols-3 gap-4",
175
+ children: [
176
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
177
+ className: "text-sm font-medium",
178
+ children: "Total Budget"
179
+ }), /* @__PURE__ */ p(b, {
180
+ type: "number",
181
+ min: "0",
182
+ value: C.budgetTotal,
183
+ onChange: (e) => w({
184
+ ...C,
185
+ budgetTotal: e.target.value
186
+ }),
187
+ className: "mt-1",
188
+ "aria-invalid": A,
189
+ placeholder: "0"
190
+ })] }),
191
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
192
+ className: "text-sm font-medium",
193
+ children: "Currency"
194
+ }), /* @__PURE__ */ p(b, {
195
+ value: C.currency,
196
+ onChange: (e) => w({
197
+ ...C,
198
+ currency: e.target.value
199
+ }),
200
+ className: "mt-1",
201
+ placeholder: "USD"
202
+ })] }),
203
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
204
+ className: "text-sm font-medium",
205
+ children: "Fiscal Period"
206
+ }), /* @__PURE__ */ p(b, {
207
+ value: C.fiscalPeriod,
208
+ onChange: (e) => w({
209
+ ...C,
210
+ fiscalPeriod: e.target.value
211
+ }),
212
+ className: "mt-1",
213
+ placeholder: "e.g. FY2026"
214
+ })] })
215
+ ]
216
+ }),
217
+ A && /* @__PURE__ */ p("p", {
218
+ role: "alert",
219
+ className: "text-sm text-destructive",
220
+ children: "The budget cannot be negative."
221
+ }),
222
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
223
+ className: "text-sm font-medium",
224
+ children: "Tags (comma-separated)"
225
+ }), /* @__PURE__ */ p(b, {
226
+ value: C.tags,
227
+ onChange: (e) => w({
228
+ ...C,
229
+ tags: e.target.value
230
+ }),
231
+ className: "mt-1",
232
+ placeholder: "e.g. strategic, core, growth"
233
+ })] })
234
+ ]
235
+ })] })
236
+ ]
237
+ });
238
+ }
239
+ //#endregion
240
+ export { S as PortfoliosCreatePortfolioPagePage };
241
+
242
+ //# sourceMappingURL=PortfoliosCreatePortfolioPagePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortfoliosCreatePortfolioPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/PortfoliosCreatePortfolioPagePage.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, Briefcase, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PORTFOLIO_STATUSES, STRATEGIC_ALIGNMENTS } from '../controls/PortfolioControls';\n\nconst CREATE_PORTFOLIO = gql`\n mutation CreatePortfolio($input: PlanmagnetCreatePortfolioInput!) {\n planmagnetCreatePortfolio(input: $input) {\n id\n name\n }\n }\n`;\n\nexport function PortfoliosCreatePortfolioPagePage() {\n const { currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n name: '',\n description: '',\n status: 'ACTIVE' as (typeof PORTFOLIO_STATUSES)[number],\n alignment: '' as '' | (typeof STRATEGIC_ALIGNMENTS)[number],\n alignmentLabel: '',\n budgetTotal: '',\n currency: '',\n fiscalPeriod: '',\n tags: '',\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n const [createPortfolio, { loading: saving }] = useMutation(CREATE_PORTFOLIO, {\n refetchQueries: ['ListPortfolios'],\n });\n\n // The service refuses CUSTOM without a label (BOFF-7934) — checked here too so the reason shows before a\n // round trip, not only after one.\n const needsLabel = form.alignment === 'CUSTOM' && !form.alignmentLabel.trim();\n const invalidBudget = form.budgetTotal !== '' && Number(form.budgetTotal) < 0;\n\n const handleCreate = async () => {\n if (!form.name.trim() || needsLabel || invalidBudget) return;\n setSaveError(null);\n try {\n await throwOnError(\n createPortfolio({\n variables: {\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n ownerId: currentUser?.id ?? '',\n ...(form.alignment ? { alignment: form.alignment } : {}),\n ...(form.alignment === 'CUSTOM'\n ? { alignmentLabel: form.alignmentLabel.trim() }\n : {}),\n ...(form.budgetTotal !== '' ? { budgetTotal: Number(form.budgetTotal) } : {}),\n currency: form.currency.trim() || undefined,\n fiscalPeriod: form.fiscalPeriod.trim() || undefined,\n tags: form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n },\n },\n })\n );\n navigate('/products/portfolios-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The portfolio 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\">Create Portfolio</h1>\n <p className=\"text-sm text-muted-foreground\">\n Group programs and projects for executive-level oversight and alignment tracking.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/portfolios-page')}>\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() || needsLabel || invalidBudget}\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 Create Portfolio\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 create portfolio: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Briefcase className=\"h-4 w-4\" />\n Portfolio 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. 2026 Platform Investments\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</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=\"Describe what this portfolio covers…\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"portfolio-status\" className=\"text-sm font-medium\">\n Status\n </label>\n <select\n id=\"portfolio-status\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.status}\n onChange={(e) =>\n setForm({\n ...form,\n status: e.target.value as (typeof PORTFOLIO_STATUSES)[number],\n })\n }\n >\n {PORTFOLIO_STATUSES.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"portfolio-alignment\" className=\"text-sm font-medium\">\n Strategic Alignment\n </label>\n <select\n id=\"portfolio-alignment\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.alignment}\n onChange={(e) =>\n setForm({\n ...form,\n alignment: e.target.value as typeof form.alignment,\n })\n }\n >\n <option value=\"\">Not set</option>\n {STRATEGIC_ALIGNMENTS.map((a) => (\n <option key={a} value={a}>\n {a === 'CUSTOM' ? 'Custom…' : enumLabel(a)}\n </option>\n ))}\n </select>\n </div>\n </div>\n {form.alignment === 'CUSTOM' && (\n <div>\n <label className=\"text-sm font-medium\">Custom Alignment Label</label>\n <Input\n value={form.alignmentLabel}\n onChange={(e) => setForm({ ...form, alignmentLabel: e.target.value })}\n className=\"mt-1\"\n placeholder=\"Name this strategic theme\"\n aria-invalid={needsLabel}\n />\n {needsLabel && (\n <p role=\"alert\" className=\"text-sm text-destructive mt-1\">\n A custom alignment needs a label.\n </p>\n )}\n </div>\n )}\n <div className=\"grid grid-cols-3 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Total Budget</label>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.budgetTotal}\n onChange={(e) => setForm({ ...form, budgetTotal: e.target.value })}\n className=\"mt-1\"\n aria-invalid={invalidBudget}\n placeholder=\"0\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Currency</label>\n <Input\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n className=\"mt-1\"\n placeholder=\"USD\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Fiscal Period</label>\n <Input\n value={form.fiscalPeriod}\n onChange={(e) => setForm({ ...form, fiscalPeriod: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. FY2026\"\n />\n </div>\n </div>\n {invalidBudget && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n The budget cannot be negative.\n </p>\n )}\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. strategic, core, growth\"\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 PortfoliosEditPortfolioPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=PortfoliosEditPortfolioPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortfoliosEditPortfolioPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/PortfoliosEditPortfolioPagePage.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,+BAA+B,4CAuR9C"}
@@ -0,0 +1,302 @@
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 { enumLabel as r } from "../controls/WorkItemControls.js";
5
+ import { PORTFOLIO_STATUSES as i, STRATEGIC_ALIGNMENTS as a } from "../controls/PortfolioControls.js";
6
+ import { useMemo as o, useState as s } from "react";
7
+ import { ArrowLeft as c, Loader2 as l, Save as u } from "lucide-react";
8
+ import { useParams as d } from "react-router";
9
+ import { gql as f } from "@apollo/client";
10
+ import { useMutation as p, useQuery as m } from "@apollo/client/react";
11
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
12
+ import { Button as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x, Input as S } from "@burdenoff/fe-libs/ui";
13
+ //#region src/planmagnet/pages/PortfoliosEditPortfolioPagePage.tsx
14
+ var C = f`
15
+ query GetPortfolioForEdit($id: ID!) {
16
+ planmagnetPortfolio(id: $id) {
17
+ id
18
+ name
19
+ description
20
+ status
21
+ alignment
22
+ alignmentLabel
23
+ budgetTotal
24
+ currency
25
+ fiscalPeriod
26
+ tags
27
+ }
28
+ }
29
+ `, w = f`
30
+ mutation UpdatePortfolio($id: ID!, $input: PlanmagnetUpdatePortfolioInput!) {
31
+ planmagnetUpdatePortfolio(id: $id, input: $input) {
32
+ id
33
+ name
34
+ description
35
+ status
36
+ alignment
37
+ alignmentLabel
38
+ budgetTotal
39
+ currency
40
+ fiscalPeriod
41
+ tags
42
+ updatedAt
43
+ }
44
+ }
45
+ `;
46
+ function T() {
47
+ let f = e(), T = d().id ?? "", { data: E, loading: D, error: O } = m(C, {
48
+ variables: { id: T },
49
+ skip: !T
50
+ }), [k, { loading: A }] = p(w, {
51
+ refetchQueries: [
52
+ "ListPortfolios",
53
+ "GetPortfolioForEdit",
54
+ "GetPortfolio",
55
+ "GetPortfolioOverview"
56
+ ],
57
+ awaitRefetchQueries: !0
58
+ }), [j, M] = s(null), [N, P] = s(!1), F = E?.planmagnetPortfolio, I = o(() => ({
59
+ name: F?.name ?? "",
60
+ description: F?.description ?? "",
61
+ status: F?.status ?? "ACTIVE",
62
+ alignment: F?.alignment ?? "",
63
+ alignmentLabel: F?.alignmentLabel ?? "",
64
+ budgetTotal: F?.budgetTotal == null ? "" : String(F.budgetTotal),
65
+ currency: F?.currency ?? "",
66
+ fiscalPeriod: F?.fiscalPeriod ?? "",
67
+ tags: F?.tags?.join(", ") ?? ""
68
+ }), [F]), [L, R] = s({}), z = {
69
+ ...I,
70
+ ...L
71
+ }, B = (e) => R(e), V = i.includes(z.status) ? i : [z.status, ...i], H = z.alignment === "CUSTOM" && !z.alignmentLabel.trim(), U = z.budgetTotal !== "" && Number(z.budgetTotal) < 0;
72
+ return D && !E ? /* @__PURE__ */ h("div", {
73
+ className: "flex items-center justify-center p-12",
74
+ children: /* @__PURE__ */ h(l, { className: "h-8 w-8 animate-spin text-muted-foreground" })
75
+ }) : O ? /* @__PURE__ */ g("div", {
76
+ className: "p-6 text-destructive",
77
+ children: ["Failed to load portfolio: ", O.message]
78
+ }) : F ? /* @__PURE__ */ g("div", {
79
+ className: "space-y-6 p-6 max-w-4xl",
80
+ children: [
81
+ /* @__PURE__ */ g("div", {
82
+ className: "flex items-center justify-between",
83
+ children: [/* @__PURE__ */ g("div", {
84
+ className: "flex items-center gap-3",
85
+ children: [/* @__PURE__ */ h(_, {
86
+ variant: "ghost",
87
+ size: "sm",
88
+ "aria-label": "Back to the portfolio",
89
+ onClick: () => f(n("/products/portfolio-detail-page", T)),
90
+ children: /* @__PURE__ */ h(c, { className: "h-4 w-4" })
91
+ }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
92
+ className: "text-2xl font-semibold",
93
+ children: "Edit Portfolio"
94
+ }), /* @__PURE__ */ h("p", {
95
+ className: "text-sm text-muted-foreground",
96
+ children: F.name
97
+ })] })]
98
+ }), /* @__PURE__ */ g(_, {
99
+ size: "sm",
100
+ onClick: async () => {
101
+ if (!F || !z.name.trim() || H || U) return;
102
+ let e = (e) => e.split(",").map((e) => e.trim()).filter(Boolean), n = {
103
+ name: z.name.trim(),
104
+ description: z.description.trim() || null,
105
+ status: z.status,
106
+ alignment: z.alignment || null,
107
+ alignmentLabel: z.alignment === "CUSTOM" ? z.alignmentLabel.trim() : null,
108
+ budgetTotal: z.budgetTotal === "" ? null : Number(z.budgetTotal),
109
+ currency: z.currency.trim() || null,
110
+ fiscalPeriod: z.fiscalPeriod.trim() || null,
111
+ tags: e(z.tags)
112
+ }, r = {
113
+ name: I.name,
114
+ description: I.description.trim() || null,
115
+ status: I.status,
116
+ alignment: I.alignment || null,
117
+ alignmentLabel: I.alignment === "CUSTOM" ? I.alignmentLabel.trim() : null,
118
+ budgetTotal: I.budgetTotal === "" ? null : Number(I.budgetTotal),
119
+ currency: I.currency.trim() || null,
120
+ fiscalPeriod: I.fiscalPeriod.trim() || null,
121
+ tags: e(I.tags)
122
+ }, i = Object.fromEntries(Object.entries(n).filter(([e, t]) => JSON.stringify(t) !== JSON.stringify(r[e])));
123
+ if (M(null), P(!1), Object.keys(i).length === 0) {
124
+ P(!0);
125
+ return;
126
+ }
127
+ try {
128
+ await t(k({ variables: {
129
+ id: T,
130
+ input: i
131
+ } })), P(!0);
132
+ } catch (e) {
133
+ M(e instanceof Error ? e.message : "The portfolio could not be saved.");
134
+ }
135
+ },
136
+ disabled: A || H || U,
137
+ children: [A ? /* @__PURE__ */ h(l, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Save Changes"]
138
+ })]
139
+ }),
140
+ N && /* @__PURE__ */ h("div", {
141
+ role: "status",
142
+ className: "p-3 bg-muted text-status-success-text rounded-md text-sm",
143
+ children: "Changes saved."
144
+ }),
145
+ j && /* @__PURE__ */ h("div", {
146
+ role: "alert",
147
+ className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
148
+ children: j
149
+ }),
150
+ /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Portfolio Details" }) }), /* @__PURE__ */ g(y, {
151
+ className: "space-y-4",
152
+ children: [
153
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
154
+ className: "text-sm font-medium",
155
+ children: "Name"
156
+ }), /* @__PURE__ */ h(S, {
157
+ value: z.name,
158
+ onChange: (e) => B({
159
+ ...z,
160
+ name: e.target.value
161
+ }),
162
+ className: "mt-1"
163
+ })] }),
164
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
165
+ className: "text-sm font-medium",
166
+ children: "Description"
167
+ }), /* @__PURE__ */ h("textarea", {
168
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
169
+ value: z.description,
170
+ onChange: (e) => B({
171
+ ...z,
172
+ description: e.target.value
173
+ })
174
+ })] }),
175
+ /* @__PURE__ */ g("div", {
176
+ className: "grid grid-cols-2 gap-4",
177
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
178
+ htmlFor: "portfolio-edit-status",
179
+ className: "text-sm font-medium",
180
+ children: "Status"
181
+ }), /* @__PURE__ */ h("select", {
182
+ id: "portfolio-edit-status",
183
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
184
+ value: z.status,
185
+ onChange: (e) => B({
186
+ ...z,
187
+ status: e.target.value
188
+ }),
189
+ children: V.map((e) => /* @__PURE__ */ h("option", {
190
+ value: e,
191
+ children: r(e)
192
+ }, e))
193
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
194
+ htmlFor: "portfolio-edit-alignment",
195
+ className: "text-sm font-medium",
196
+ children: "Strategic Alignment"
197
+ }), /* @__PURE__ */ g("select", {
198
+ id: "portfolio-edit-alignment",
199
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
200
+ value: z.alignment,
201
+ onChange: (e) => B({
202
+ ...z,
203
+ alignment: e.target.value
204
+ }),
205
+ children: [/* @__PURE__ */ h("option", {
206
+ value: "",
207
+ children: "Not set"
208
+ }), a.map((e) => /* @__PURE__ */ h("option", {
209
+ value: e,
210
+ children: e === "CUSTOM" ? "Custom…" : r(e)
211
+ }, e))]
212
+ })] })]
213
+ }),
214
+ z.alignment === "CUSTOM" && /* @__PURE__ */ g("div", { children: [
215
+ /* @__PURE__ */ h("label", {
216
+ className: "text-sm font-medium",
217
+ children: "Custom Alignment Label"
218
+ }),
219
+ /* @__PURE__ */ h(S, {
220
+ value: z.alignmentLabel,
221
+ onChange: (e) => B({
222
+ ...z,
223
+ alignmentLabel: e.target.value
224
+ }),
225
+ className: "mt-1",
226
+ "aria-invalid": H
227
+ }),
228
+ H && /* @__PURE__ */ h("p", {
229
+ role: "alert",
230
+ className: "text-sm text-destructive mt-1",
231
+ children: "A custom alignment needs a label."
232
+ })
233
+ ] }),
234
+ /* @__PURE__ */ g("div", {
235
+ className: "grid grid-cols-3 gap-4",
236
+ children: [
237
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
238
+ className: "text-sm font-medium",
239
+ children: "Total Budget"
240
+ }), /* @__PURE__ */ h(S, {
241
+ type: "number",
242
+ min: "0",
243
+ value: z.budgetTotal,
244
+ onChange: (e) => B({
245
+ ...z,
246
+ budgetTotal: e.target.value
247
+ }),
248
+ className: "mt-1",
249
+ "aria-invalid": U
250
+ })] }),
251
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
252
+ className: "text-sm font-medium",
253
+ children: "Currency"
254
+ }), /* @__PURE__ */ h(S, {
255
+ value: z.currency,
256
+ onChange: (e) => B({
257
+ ...z,
258
+ currency: e.target.value
259
+ }),
260
+ className: "mt-1"
261
+ })] }),
262
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
263
+ className: "text-sm font-medium",
264
+ children: "Fiscal Period"
265
+ }), /* @__PURE__ */ h(S, {
266
+ value: z.fiscalPeriod,
267
+ onChange: (e) => B({
268
+ ...z,
269
+ fiscalPeriod: e.target.value
270
+ }),
271
+ className: "mt-1"
272
+ })] })
273
+ ]
274
+ }),
275
+ U && /* @__PURE__ */ h("p", {
276
+ role: "alert",
277
+ className: "text-sm text-destructive",
278
+ children: "The budget cannot be negative."
279
+ }),
280
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
281
+ className: "text-sm font-medium",
282
+ children: "Tags (comma-separated)"
283
+ }), /* @__PURE__ */ h(S, {
284
+ value: z.tags,
285
+ onChange: (e) => B({
286
+ ...z,
287
+ tags: e.target.value
288
+ }),
289
+ className: "mt-1"
290
+ })] })
291
+ ]
292
+ })] })
293
+ ]
294
+ }) : /* @__PURE__ */ h("div", {
295
+ className: "p-6 text-muted-foreground",
296
+ children: "Portfolio not found."
297
+ });
298
+ }
299
+ //#endregion
300
+ export { T as PortfoliosEditPortfolioPagePage };
301
+
302
+ //# sourceMappingURL=PortfoliosEditPortfolioPagePage.js.map