@burdenoff/microfe-workspaces 2026.626.3 → 2026.627.2

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.
@@ -5,172 +5,191 @@ import { nativeConfirm as r, nativeImpact as i } from "../utils/nativeBridge.js"
5
5
  import { useArchiveProject as a, useDeleteProject as o, useUnarchiveProject as s } from "../hooks/useProjectMutations.js";
6
6
  import { formatDate as c } from "../utils/formatters.js";
7
7
  import { getProjectDisplayTags as l } from "../utils/projectTags.js";
8
- import { Archive as u, Fingerprint as d, FolderKanban as f, MoreVertical as p } from "lucide-react";
9
- import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
10
- import { Badge as _, Button as v, CardContent as y, CardHeader as b, CardTitle as x, DropdownMenu as S, DropdownMenuContent as C, DropdownMenuItem as w, DropdownMenuTrigger as T, EntityIdModal as E, GlassCard as D, useEntityIdModal as O } from "@burdenoff/fe-libs/ui";
11
- import { useNavigate as k } from "react-router-dom";
12
- import { TagBadge as A } from "@burdenoff/fe-libs/tag";
8
+ import { RenameProjectDialog as u } from "./RenameProjectDialog.js";
9
+ import { useState as d } from "react";
10
+ import { Archive as f, Fingerprint as p, FolderKanban as m, MoreVertical as h, Pencil as g } from "lucide-react";
11
+ import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
12
+ import { Badge as b, Button as x, CardContent as S, CardHeader as C, CardTitle as w, DropdownMenu as T, DropdownMenuContent as E, DropdownMenuItem as D, DropdownMenuTrigger as O, EntityIdModal as k, GlassCard as A, useEntityIdModal as j } from "@burdenoff/fe-libs/ui";
13
+ import { useNavigate as M } from "react-router-dom";
14
+ import { TagBadge as N } from "@burdenoff/fe-libs/tag";
13
15
  //#region src/components/ProjectCard.tsx
14
- function j({ project: j, selected: M = !1, onToggleSelect: N, onTagClick: P, canArchive: F = !1, canDelete: I = !1 }) {
15
- let L = k(), { basePath: R = "/workspaces" } = e(), { withCurrentSearch: z } = n(), { mutate: B } = a(j.workspaceId), { mutate: V } = s(j.workspaceId), { mutate: H } = o(j.workspaceId), U = O(), W = l(j), G = () => {
16
- L(z(`${R}/${j.workspaceId}/projects/${j.id}`));
16
+ function P({ project: P, selected: F = !1, onToggleSelect: I, onTagClick: L, canArchive: R = !1, canDelete: z = !1, canRename: B = !1 }) {
17
+ let V = M(), { basePath: H = "/workspaces" } = e(), { withCurrentSearch: U } = n(), { mutate: W } = a(P.workspaceId), { mutate: G } = s(P.workspaceId), { mutate: K } = o(P.workspaceId), q = j(), [J, Y] = d(!1), X = l(P), Z = () => {
18
+ V(U(`${H}/${P.workspaceId}/projects/${P.id}`));
17
19
  };
18
- return /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ g(D, {
19
- treatment: "insight",
20
- className: `cursor-pointer relative focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 ${M ? "ring-2 ring-primary/25 border-primary/50" : ""}`,
21
- onClick: G,
22
- onKeyDown: (e) => {
23
- (e.key === "Enter" || e.key === " ") && (e.preventDefault(), G());
24
- },
25
- role: "button",
26
- tabIndex: 0,
27
- "aria-label": `Open project ${j.name}`,
28
- children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ g("div", {
29
- className: "flex items-start justify-between",
30
- children: [/* @__PURE__ */ g("div", {
31
- className: "flex items-center gap-3 flex-1",
32
- children: [
33
- N && /* @__PURE__ */ h("input", {
34
- type: "checkbox",
35
- checked: M,
36
- onChange: (e) => N(e.target.checked),
37
- onClick: (e) => e.stopPropagation(),
38
- "aria-label": `Select project ${j.name}`,
39
- className: "mt-1 size-4 rounded border-border-subtle text-primary"
40
- }),
41
- /* @__PURE__ */ h("div", {
42
- className: "p-3 rounded-lg bg-action-primary-bg/10 transition-colors",
43
- children: /* @__PURE__ */ h(f, {
44
- className: "text-primary",
45
- size: 24
20
+ return /* @__PURE__ */ y(_, { children: [
21
+ /* @__PURE__ */ y(A, {
22
+ treatment: "insight",
23
+ className: `cursor-pointer relative focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 ${F ? "ring-2 ring-primary/25 border-primary/50" : ""}`,
24
+ onClick: Z,
25
+ onKeyDown: (e) => {
26
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), Z());
27
+ },
28
+ role: "button",
29
+ tabIndex: 0,
30
+ "aria-label": `Open project ${P.name}`,
31
+ children: [/* @__PURE__ */ v(C, { children: /* @__PURE__ */ y("div", {
32
+ className: "flex items-start justify-between",
33
+ children: [/* @__PURE__ */ y("div", {
34
+ className: "flex items-center gap-3 flex-1",
35
+ children: [
36
+ I && /* @__PURE__ */ v("input", {
37
+ type: "checkbox",
38
+ checked: F,
39
+ onChange: (e) => I(e.target.checked),
40
+ onClick: (e) => e.stopPropagation(),
41
+ "aria-label": `Select project ${P.name}`,
42
+ className: "mt-1 size-4 rounded border-border-subtle text-primary"
43
+ }),
44
+ /* @__PURE__ */ v("div", {
45
+ className: "p-3 rounded-lg bg-action-primary-bg/10 transition-colors",
46
+ children: /* @__PURE__ */ v(m, {
47
+ className: "text-primary",
48
+ size: 24
49
+ })
50
+ }),
51
+ /* @__PURE__ */ y("div", {
52
+ className: "flex-1 min-w-0",
53
+ children: [/* @__PURE__ */ v(w, {
54
+ className: "truncate",
55
+ children: P.name
56
+ }), /* @__PURE__ */ v("p", {
57
+ className: "truncate text-sm text-text-secondary",
58
+ children: P.key
59
+ })]
46
60
  })
47
- }),
48
- /* @__PURE__ */ g("div", {
49
- className: "flex-1 min-w-0",
50
- children: [/* @__PURE__ */ h(x, {
51
- className: "truncate",
52
- children: j.name
53
- }), /* @__PURE__ */ h("p", {
54
- className: "truncate text-sm text-text-secondary",
55
- children: j.key
61
+ ]
62
+ }), /* @__PURE__ */ y(T, { children: [/* @__PURE__ */ v(O, {
63
+ asChild: !0,
64
+ onClick: (e) => e.stopPropagation(),
65
+ children: /* @__PURE__ */ y(x, {
66
+ variant: "ghost",
67
+ size: "sm",
68
+ className: "size-8 p-0",
69
+ children: [/* @__PURE__ */ v(h, { size: 16 }), /* @__PURE__ */ v("span", {
70
+ className: "sr-only",
71
+ children: "Open project actions"
56
72
  })]
57
73
  })
58
- ]
59
- }), /* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(T, {
60
- asChild: !0,
61
- onClick: (e) => e.stopPropagation(),
62
- children: /* @__PURE__ */ g(v, {
63
- variant: "ghost",
64
- size: "sm",
65
- className: "size-8 p-0",
66
- children: [/* @__PURE__ */ h(p, { size: 16 }), /* @__PURE__ */ h("span", {
67
- className: "sr-only",
68
- children: "Open project actions"
69
- })]
70
- })
71
- }), /* @__PURE__ */ g(C, {
72
- align: "end",
74
+ }), /* @__PURE__ */ y(E, {
75
+ align: "end",
76
+ children: [
77
+ /* @__PURE__ */ y(D, {
78
+ onClick: (e) => {
79
+ e.stopPropagation(), q.show({
80
+ id: P.id,
81
+ name: P.name,
82
+ type: "Project",
83
+ details: [{
84
+ label: "Key",
85
+ value: P.key
86
+ }, {
87
+ label: "Workspace ID",
88
+ value: P.workspaceId
89
+ }]
90
+ });
91
+ },
92
+ children: [/* @__PURE__ */ v(p, {
93
+ size: 16,
94
+ className: "mr-2"
95
+ }), "Get ID"]
96
+ }),
97
+ B && /* @__PURE__ */ y(D, {
98
+ onClick: (e) => {
99
+ e.stopPropagation(), Y(!0);
100
+ },
101
+ children: [/* @__PURE__ */ v(g, {
102
+ size: 16,
103
+ className: "mr-2"
104
+ }), "Rename"]
105
+ }),
106
+ R && /* @__PURE__ */ y(D, {
107
+ onClick: (e) => {
108
+ e.stopPropagation(), P.archived ? G(P.id) : W(P.id);
109
+ },
110
+ children: [/* @__PURE__ */ v(f, {
111
+ size: 16,
112
+ className: "mr-2"
113
+ }), P.archived ? "Unarchive" : "Archive"]
114
+ }),
115
+ z && /* @__PURE__ */ v(D, {
116
+ onClick: async (e) => {
117
+ e.stopPropagation(), i("medium");
118
+ let t = await r({
119
+ title: "Delete project",
120
+ message: `Are you sure you want to delete "${P.name}"?`,
121
+ okButtonTitle: "Delete",
122
+ cancelButtonTitle: "Cancel"
123
+ });
124
+ (t === null ? confirm(`Are you sure you want to delete "${P.name}"?`) : t) && K(P.id);
125
+ },
126
+ className: "text-status-error-text",
127
+ children: "Delete"
128
+ })
129
+ ]
130
+ })] })]
131
+ }) }), /* @__PURE__ */ y(S, {
132
+ className: "space-y-3",
73
133
  children: [
74
- /* @__PURE__ */ g(w, {
75
- onClick: (e) => {
76
- e.stopPropagation(), U.show({
77
- id: j.id,
78
- name: j.name,
79
- type: "Project",
80
- details: [{
81
- label: "Key",
82
- value: j.key
83
- }, {
84
- label: "Workspace ID",
85
- value: j.workspaceId
86
- }]
87
- });
88
- },
89
- children: [/* @__PURE__ */ h(d, {
90
- size: 16,
91
- className: "mr-2"
92
- }), "Get ID"]
134
+ /* @__PURE__ */ y("div", {
135
+ className: "flex items-center gap-2",
136
+ children: [/* @__PURE__ */ v(b, {
137
+ className: `border ${t(P.status)}`,
138
+ children: P.status
139
+ }), P.archived && /* @__PURE__ */ y(b, {
140
+ variant: "outline",
141
+ className: "border-workspace-archived-border text-workspace-archived-text",
142
+ children: [/* @__PURE__ */ v(f, {
143
+ size: 12,
144
+ className: "mr-1"
145
+ }), "Archived"]
146
+ })]
93
147
  }),
94
- F && /* @__PURE__ */ g(w, {
95
- onClick: (e) => {
96
- e.stopPropagation(), j.archived ? V(j.id) : B(j.id);
97
- },
98
- children: [/* @__PURE__ */ h(u, {
99
- size: 16,
100
- className: "mr-2"
101
- }), j.archived ? "Unarchive" : "Archive"]
148
+ /* @__PURE__ */ y("div", {
149
+ className: "text-xs text-text-secondary",
150
+ children: ["Created ", c(P.createdAt)]
102
151
  }),
103
- I && /* @__PURE__ */ h(w, {
104
- onClick: async (e) => {
105
- e.stopPropagation(), i("medium");
106
- let t = await r({
107
- title: "Delete project",
108
- message: `Are you sure you want to delete "${j.name}"?`,
109
- okButtonTitle: "Delete",
110
- cancelButtonTitle: "Cancel"
111
- });
112
- (t === null ? confirm(`Are you sure you want to delete "${j.name}"?`) : t) && H(j.id);
113
- },
114
- className: "text-status-error-text",
115
- children: "Delete"
152
+ X.length > 0 && /* @__PURE__ */ y("div", {
153
+ className: "flex flex-wrap gap-1.5",
154
+ "aria-label": "Project tags",
155
+ children: [X.slice(0, 4).map((e) => /* @__PURE__ */ v("span", {
156
+ ...L ? {
157
+ role: "button",
158
+ tabIndex: 0,
159
+ onClick: (t) => {
160
+ t.stopPropagation(), L(e);
161
+ },
162
+ onKeyDown: (t) => {
163
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), t.stopPropagation(), L(e));
164
+ }
165
+ } : {},
166
+ children: /* @__PURE__ */ v(N, {
167
+ id: e.tagId,
168
+ label: e.label,
169
+ color: e.color ?? void 0,
170
+ icon: e.icon ?? void 0,
171
+ description: e.description ?? void 0,
172
+ size: "sm",
173
+ className: L ? "cursor-pointer" : void 0
174
+ })
175
+ }, e.tagId)), X.length > 4 && /* @__PURE__ */ y(b, {
176
+ variant: "outline",
177
+ className: "text-xs",
178
+ children: ["+", X.length - 4]
179
+ })]
116
180
  })
117
181
  ]
118
- })] })]
119
- }) }), /* @__PURE__ */ g(y, {
120
- className: "space-y-3",
121
- children: [
122
- /* @__PURE__ */ g("div", {
123
- className: "flex items-center gap-2",
124
- children: [/* @__PURE__ */ h(_, {
125
- className: `border ${t(j.status)}`,
126
- children: j.status
127
- }), j.archived && /* @__PURE__ */ g(_, {
128
- variant: "outline",
129
- className: "border-workspace-archived-border text-workspace-archived-text",
130
- children: [/* @__PURE__ */ h(u, {
131
- size: 12,
132
- className: "mr-1"
133
- }), "Archived"]
134
- })]
135
- }),
136
- /* @__PURE__ */ g("div", {
137
- className: "text-xs text-text-secondary",
138
- children: ["Created ", c(j.createdAt)]
139
- }),
140
- W.length > 0 && /* @__PURE__ */ g("div", {
141
- className: "flex flex-wrap gap-1.5",
142
- "aria-label": "Project tags",
143
- children: [W.slice(0, 4).map((e) => /* @__PURE__ */ h("span", {
144
- ...P ? {
145
- role: "button",
146
- tabIndex: 0,
147
- onClick: (t) => {
148
- t.stopPropagation(), P(e);
149
- },
150
- onKeyDown: (t) => {
151
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), t.stopPropagation(), P(e));
152
- }
153
- } : {},
154
- children: /* @__PURE__ */ h(A, {
155
- id: e.tagId,
156
- label: e.label,
157
- color: e.color ?? void 0,
158
- icon: e.icon ?? void 0,
159
- description: e.description ?? void 0,
160
- size: "sm",
161
- className: P ? "cursor-pointer" : void 0
162
- })
163
- }, e.tagId)), W.length > 4 && /* @__PURE__ */ g(_, {
164
- variant: "outline",
165
- className: "text-xs",
166
- children: ["+", W.length - 4]
167
- })]
168
- })
169
- ]
170
- })]
171
- }), /* @__PURE__ */ h(E, { control: U })] });
182
+ })]
183
+ }),
184
+ /* @__PURE__ */ v(k, { control: q }),
185
+ J && /* @__PURE__ */ v(u, {
186
+ project: P,
187
+ open: J,
188
+ onOpenChange: Y
189
+ })
190
+ ] });
172
191
  }
173
192
  //#endregion
174
- export { j as ProjectCard };
193
+ export { P as ProjectCard };
175
194
 
176
195
  //# sourceMappingURL=ProjectCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectCard.js","names":[],"sources":["../../src/components/ProjectCard.tsx"],"sourcesContent":["import type React from 'react';\nimport { FolderKanban, Archive, MoreVertical, Fingerprint } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n GlassCard,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n EntityIdModal,\n useEntityIdModal,\n} from '@burdenoff/fe-libs/ui';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\n\nimport type { Project, ProjectTag } from '../types';\nimport { formatDate } from '../utils/formatters';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n} from '../hooks/useProjectMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\n\ninterface ProjectCardProps {\n project: Project;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n onTagClick?: (tag: ProjectTag) => void;\n canArchive?: boolean;\n canDelete?: boolean;\n}\n\nexport function ProjectCard({\n project,\n selected = false,\n onToggleSelect,\n onTagClick,\n canArchive = false,\n canDelete = false,\n}: ProjectCardProps) {\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { mutate: archiveProject } = useArchiveProject(project.workspaceId);\n const { mutate: unarchiveProject } = useUnarchiveProject(project.workspaceId);\n const { mutate: deleteProject } = useDeleteProject(project.workspaceId);\n const idModal = useEntityIdModal();\n const projectTags = getProjectDisplayTags(project);\n\n const handleCardClick = () => {\n navigate(withCurrentSearch(`${basePath}/${project.workspaceId}/projects/${project.id}`));\n };\n\n const handleArchive = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (project.archived) {\n unarchiveProject(project.id);\n } else {\n archiveProject(project.id);\n }\n };\n\n const handleDelete = async (e: React.MouseEvent) => {\n e.stopPropagation();\n void nativeImpact('medium');\n const native = await nativeConfirm({\n title: 'Delete project',\n message: `Are you sure you want to delete \"${project.name}\"?`,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok =\n native === null ? confirm(`Are you sure you want to delete \"${project.name}\"?`) : native;\n if (ok) {\n deleteProject(project.id);\n }\n };\n\n const handleCardKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n };\n\n return (\n <>\n <GlassCard\n treatment=\"insight\"\n className={`cursor-pointer relative focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 ${\n selected ? 'ring-2 ring-primary/25 border-primary/50' : ''\n }`}\n onClick={handleCardClick}\n onKeyDown={handleCardKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open project ${project.name}`}\n >\n <CardHeader>\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3 flex-1\">\n {onToggleSelect && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(event.target.checked)}\n onClick={(event) => event.stopPropagation()}\n aria-label={`Select project ${project.name}`}\n className=\"mt-1 size-4 rounded border-border-subtle text-primary\"\n />\n )}\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10 transition-colors\">\n <FolderKanban className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"truncate\">{project.name}</CardTitle>\n <p className=\"truncate text-sm text-text-secondary\">{project.key}</p>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>\n <Button variant=\"ghost\" size=\"sm\" className=\"size-8 p-0\">\n <MoreVertical size={16} />\n <span className=\"sr-only\">Open project actions</span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(e) => {\n e.stopPropagation();\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: [\n { label: 'Key', value: project.key },\n { label: 'Workspace ID', value: project.workspaceId },\n ],\n });\n }}\n >\n <Fingerprint size={16} className=\"mr-2\" />\n Get ID\n </DropdownMenuItem>\n {canArchive && (\n <DropdownMenuItem onClick={handleArchive}>\n <Archive size={16} className=\"mr-2\" />\n {project.archived ? 'Unarchive' : 'Archive'}\n </DropdownMenuItem>\n )}\n {canDelete && (\n <DropdownMenuItem onClick={handleDelete} className=\"text-status-error-text\">\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n Archived\n </Badge>\n )}\n </div>\n <div className=\"text-xs text-text-secondary\">Created {formatDate(project.createdAt)}</div>\n {projectTags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Project tags\">\n {projectTags.slice(0, 4).map((tag) => (\n <span\n key={tag.tagId}\n {...(onTagClick\n ? {\n role: 'button',\n tabIndex: 0,\n onClick: (event: React.MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n onTagClick(tag);\n },\n onKeyDown: (event: React.KeyboardEvent<HTMLSpanElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n onTagClick(tag);\n }\n },\n }\n : {})}\n >\n <TagBadge\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n size=\"sm\"\n className={onTagClick ? 'cursor-pointer' : undefined}\n />\n </span>\n ))}\n {projectTags.length > 4 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{projectTags.length - 4}\n </Badge>\n )}\n </div>\n )}\n </CardContent>\n </GlassCard>\n <EntityIdModal control={idModal} />\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectCard.js","names":[],"sources":["../../src/components/ProjectCard.tsx"],"sourcesContent":["import type React from 'react';\nimport { useState } from 'react';\nimport { FolderKanban, Archive, MoreVertical, Fingerprint, Pencil } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n GlassCard,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n EntityIdModal,\n useEntityIdModal,\n} from '@burdenoff/fe-libs/ui';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\n\nimport type { Project, ProjectTag } from '../types';\nimport { formatDate } from '../utils/formatters';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n} from '../hooks/useProjectMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { RenameProjectDialog } from './RenameProjectDialog';\n\ninterface ProjectCardProps {\n project: Project;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n onTagClick?: (tag: ProjectTag) => void;\n canArchive?: boolean;\n canDelete?: boolean;\n canRename?: boolean;\n}\n\nexport function ProjectCard({\n project,\n selected = false,\n onToggleSelect,\n onTagClick,\n canArchive = false,\n canDelete = false,\n canRename = false,\n}: ProjectCardProps) {\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { mutate: archiveProject } = useArchiveProject(project.workspaceId);\n const { mutate: unarchiveProject } = useUnarchiveProject(project.workspaceId);\n const { mutate: deleteProject } = useDeleteProject(project.workspaceId);\n const idModal = useEntityIdModal();\n const [showRenameDialog, setShowRenameDialog] = useState(false);\n const projectTags = getProjectDisplayTags(project);\n\n const handleCardClick = () => {\n navigate(withCurrentSearch(`${basePath}/${project.workspaceId}/projects/${project.id}`));\n };\n\n const handleArchive = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (project.archived) {\n unarchiveProject(project.id);\n } else {\n archiveProject(project.id);\n }\n };\n\n const handleDelete = async (e: React.MouseEvent) => {\n e.stopPropagation();\n void nativeImpact('medium');\n const native = await nativeConfirm({\n title: 'Delete project',\n message: `Are you sure you want to delete \"${project.name}\"?`,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok =\n native === null ? confirm(`Are you sure you want to delete \"${project.name}\"?`) : native;\n if (ok) {\n deleteProject(project.id);\n }\n };\n\n const handleCardKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n };\n\n return (\n <>\n <GlassCard\n treatment=\"insight\"\n className={`cursor-pointer relative focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 ${\n selected ? 'ring-2 ring-primary/25 border-primary/50' : ''\n }`}\n onClick={handleCardClick}\n onKeyDown={handleCardKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open project ${project.name}`}\n >\n <CardHeader>\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3 flex-1\">\n {onToggleSelect && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(event.target.checked)}\n onClick={(event) => event.stopPropagation()}\n aria-label={`Select project ${project.name}`}\n className=\"mt-1 size-4 rounded border-border-subtle text-primary\"\n />\n )}\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10 transition-colors\">\n <FolderKanban className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"truncate\">{project.name}</CardTitle>\n <p className=\"truncate text-sm text-text-secondary\">{project.key}</p>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>\n <Button variant=\"ghost\" size=\"sm\" className=\"size-8 p-0\">\n <MoreVertical size={16} />\n <span className=\"sr-only\">Open project actions</span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(e) => {\n e.stopPropagation();\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: [\n { label: 'Key', value: project.key },\n { label: 'Workspace ID', value: project.workspaceId },\n ],\n });\n }}\n >\n <Fingerprint size={16} className=\"mr-2\" />\n Get ID\n </DropdownMenuItem>\n {canRename && (\n <DropdownMenuItem\n onClick={(e) => {\n e.stopPropagation();\n setShowRenameDialog(true);\n }}\n >\n <Pencil size={16} className=\"mr-2\" />\n Rename\n </DropdownMenuItem>\n )}\n {canArchive && (\n <DropdownMenuItem onClick={handleArchive}>\n <Archive size={16} className=\"mr-2\" />\n {project.archived ? 'Unarchive' : 'Archive'}\n </DropdownMenuItem>\n )}\n {canDelete && (\n <DropdownMenuItem onClick={handleDelete} className=\"text-status-error-text\">\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n Archived\n </Badge>\n )}\n </div>\n <div className=\"text-xs text-text-secondary\">Created {formatDate(project.createdAt)}</div>\n {projectTags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Project tags\">\n {projectTags.slice(0, 4).map((tag) => (\n <span\n key={tag.tagId}\n {...(onTagClick\n ? {\n role: 'button',\n tabIndex: 0,\n onClick: (event: React.MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n onTagClick(tag);\n },\n onKeyDown: (event: React.KeyboardEvent<HTMLSpanElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n onTagClick(tag);\n }\n },\n }\n : {})}\n >\n <TagBadge\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n size=\"sm\"\n className={onTagClick ? 'cursor-pointer' : undefined}\n />\n </span>\n ))}\n {projectTags.length > 4 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{projectTags.length - 4}\n </Badge>\n )}\n </div>\n )}\n </CardContent>\n </GlassCard>\n <EntityIdModal control={idModal} />\n {showRenameDialog && (\n <RenameProjectDialog\n project={project}\n open={showRenameDialog}\n onOpenChange={setShowRenameDialog}\n />\n )}\n </>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import { useUpdateProject as e } from "../hooks/useProjectMutations.js";
2
+ import { useState as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { Button as i, ResponsiveDialog as a, ResponsiveDialogContent as o, ResponsiveDialogDescription as s, ResponsiveDialogHeader as c, ResponsiveDialogTitle as l } from "@burdenoff/fe-libs/ui";
5
+ import { FormControl as u, FormDescription as d, FormField as f, FormItem as p, FormLabel as m, FormMessage as h, FormProvider as g, Input as _, useForm as v, z as y } from "@burdenoff/fe-libs/form";
6
+ //#region src/components/RenameProjectDialog.tsx
7
+ var b = y.object({ name: y.string().trim().min(2, "Name must be at least 2 characters").max(100, "Name must be less than 100 characters") });
8
+ function x({ project: y, open: x, onOpenChange: S }) {
9
+ let { mutate: C, isPending: w } = e(y.workspaceId), [T, E] = t(null), D = v({
10
+ schema: b,
11
+ defaultValues: { name: y.name }
12
+ }), O = D.watch("name").trim() === y.name.trim(), k = (e) => {
13
+ let t = e.name.trim();
14
+ if (t === y.name.trim()) {
15
+ S(!1);
16
+ return;
17
+ }
18
+ E(null), C({
19
+ id: y.id,
20
+ input: { name: t }
21
+ }, {
22
+ onSuccess: () => {
23
+ S(!1);
24
+ },
25
+ onError: (e) => {
26
+ E(e instanceof Error ? e.message : "Failed to rename project");
27
+ }
28
+ });
29
+ };
30
+ return /* @__PURE__ */ n(a, {
31
+ open: x,
32
+ onOpenChange: S,
33
+ children: /* @__PURE__ */ r(o, {
34
+ className: "sm:max-w-[500px]",
35
+ children: [/* @__PURE__ */ r(c, { children: [/* @__PURE__ */ n(l, { children: "Rename Project" }), /* @__PURE__ */ n(s, { children: "Give this project a new name. The project key stays the same." })] }), /* @__PURE__ */ n(g, {
36
+ ...D,
37
+ children: /* @__PURE__ */ r("form", {
38
+ onSubmit: D.handleSubmit(k),
39
+ className: "space-y-4",
40
+ children: [
41
+ /* @__PURE__ */ n(f, {
42
+ control: D.control,
43
+ name: "name",
44
+ render: ({ field: e }) => /* @__PURE__ */ r(p, { children: [
45
+ /* @__PURE__ */ n(m, { children: "Project Name" }),
46
+ /* @__PURE__ */ n(u, { children: /* @__PURE__ */ n(_, {
47
+ ...e,
48
+ placeholder: "My Project"
49
+ }) }),
50
+ /* @__PURE__ */ n(d, { children: "A descriptive name for your project" }),
51
+ /* @__PURE__ */ n(h, {})
52
+ ] })
53
+ }),
54
+ T && /* @__PURE__ */ n("p", {
55
+ className: "text-sm text-status-error-text",
56
+ role: "alert",
57
+ children: T
58
+ }),
59
+ /* @__PURE__ */ r("div", {
60
+ className: "flex justify-end gap-3 pt-4",
61
+ children: [/* @__PURE__ */ n(i, {
62
+ type: "button",
63
+ variant: "outline",
64
+ onClick: () => S(!1),
65
+ disabled: w,
66
+ children: "Cancel"
67
+ }), /* @__PURE__ */ n(i, {
68
+ type: "submit",
69
+ disabled: w || O,
70
+ children: w ? "Saving..." : "Save"
71
+ })]
72
+ })
73
+ ]
74
+ })
75
+ })]
76
+ })
77
+ });
78
+ }
79
+ //#endregion
80
+ export { x as RenameProjectDialog };
81
+
82
+ //# sourceMappingURL=RenameProjectDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenameProjectDialog.js","names":[],"sources":["../../src/components/RenameProjectDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\n\nimport { useUpdateProject } from '../hooks/useProjectMutations';\n\nconst renameProjectSchema = z.object({\n // `.trim()` first so length validation runs on the trimmed value — otherwise a\n // padded name like \" a \" passes min(2) but submits as the 1-char \"a\".\n name: z\n .string()\n .trim()\n .min(2, 'Name must be at least 2 characters')\n .max(100, 'Name must be less than 100 characters'),\n});\n\ntype RenameProjectFormData = z.infer<typeof renameProjectSchema>;\n\ninterface RenameProjectDialogProps {\n project: { id: string; name: string; workspaceId: string };\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nexport function RenameProjectDialog({ project, open, onOpenChange }: RenameProjectDialogProps) {\n const { mutate: updateProject, isPending } = useUpdateProject(project.workspaceId);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n // This component is mounted only while open (the parent gates it with\n // `{open && <RenameProjectDialog .../>}`), so `defaultValues` always reflects\n // the current name on each open — no prop-syncing effect needed.\n const form = useForm({\n schema: renameProjectSchema,\n defaultValues: { name: project.name },\n });\n\n const currentName = form.watch('name');\n const isUnchanged = currentName.trim() === project.name.trim();\n\n const onSubmit = (data: RenameProjectFormData) => {\n const trimmed = data.name.trim();\n if (trimmed === project.name.trim()) {\n onOpenChange(false);\n return;\n }\n\n setSubmitError(null);\n updateProject(\n { id: project.id, input: { name: trimmed } },\n {\n onSuccess: () => {\n onOpenChange(false);\n },\n onError: (error) => {\n setSubmitError(error instanceof Error ? error.message : 'Failed to rename project');\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Rename Project</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Give this project a new name. The project key stays the same.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"name\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Project Name</FormLabel>\n <FormControl>\n <Input {...field} placeholder=\"My Project\" />\n </FormControl>\n <FormDescription>A descriptive name for your project</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {submitError && (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {submitError}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isPending || isUnchanged}>\n {isPending ? 'Saving...' : 'Save'}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;AAwBA,IAAM,IAAsB,EAAE,OAAO,EAGnC,MAAM,EACH,QAAQ,CACR,MAAM,CACN,IAAI,GAAG,qCAAqC,CAC5C,IAAI,KAAK,wCAAwC,EACrD,CAAC;AAUF,SAAgB,EAAoB,EAAE,YAAS,SAAM,mBAA0C;CAC7F,IAAM,EAAE,QAAQ,GAAe,iBAAc,EAAiB,EAAQ,YAAY,EAC5E,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAK7D,IAAO,EAAQ;EACnB,QAAQ;EACR,eAAe,EAAE,MAAM,EAAQ,MAAM;EACtC,CAAC,EAGI,IADc,EAAK,MAAM,OAAO,CACN,MAAM,KAAK,EAAQ,KAAK,MAAM,EAExD,KAAY,MAAgC;EAChD,IAAM,IAAU,EAAK,KAAK,MAAM;AAChC,MAAI,MAAY,EAAQ,KAAK,MAAM,EAAE;AACnC,KAAa,GAAM;AACnB;;AAIF,EADA,EAAe,KAAK,EACpB,EACE;GAAE,IAAI,EAAQ;GAAI,OAAO,EAAE,MAAM,GAAS;GAAE,EAC5C;GACE,iBAAiB;AACf,MAAa,GAAM;;GAErB,UAAU,MAAU;AAClB,MAAe,aAAiB,QAAQ,EAAM,UAAU,2BAA2B;;GAEtF,CACF;;AAGH,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,kBAAsC,CAAA,EAC7D,kBAAC,GAAD,EAAA,UAA6B,iEAEC,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,GAAD;IAAc,GAAI;cAChB,kBAAC,QAAD;KAAM,UAAU,EAAK,aAAa,EAAS;KAAE,WAAU;eAAvD;MACE,kBAAC,GAAD;OACE,SAAS,EAAK;OACd,MAAK;OACL,SAAS,EAAE,eACT,kBAAC,GAAD,EAAA,UAAA;QACE,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA;QACnC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SAAO,GAAI;SAAO,aAAY;SAAe,CAAA,EACjC,CAAA;QACd,kBAAC,GAAD,EAAA,UAAiB,uCAAqD,CAAA;QACtE,kBAAC,GAAD,EAAe,CAAA;QACN,EAAA,CAAA;OAEb,CAAA;MAED,KACC,kBAAC,KAAD;OAAG,WAAU;OAAiC,MAAK;iBAChD;OACC,CAAA;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,eAAe,EAAa,GAAM;QAClC,UAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU,KAAa;kBAC1C,IAAY,cAAc;QACpB,CAAA,CACL;;MACD;;IACM,CAAA,CACS;;EACT,CAAA"}
@@ -2,76 +2,78 @@ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
2
2
  import { getWorkspaceStatusClasses as t } from "../utils/tokenHelpers.js";
3
3
  import { useScopedNavigation as n } from "../hooks/useScopedNavigation.js";
4
4
  import { Building2 as r, FolderKanban as i, Users as a } from "lucide-react";
5
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
6
- import { Badge as c, CardContent as l, CardHeader as u, CardTitle as d, GlassCard as f } from "@burdenoff/fe-libs/ui";
7
- import { useNavigate as p } from "react-router-dom";
5
+ import { useActiveContext as o } from "@burdenoff/fe-libs/shared/providers/shell";
6
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
7
+ import { Badge as l, CardContent as u, CardHeader as d, CardTitle as f, GlassCard as p } from "@burdenoff/fe-libs/ui";
8
+ import { useNavigate as m } from "react-router-dom";
8
9
  //#region src/components/WorkspaceCard.tsx
9
- function m({ workspace: m }) {
10
- let h = p(), { basePath: g = "/" } = e(), { withCurrentSearch: _ } = n(), v = () => {
11
- h(_(`${g}/${m.id}`));
10
+ function h({ workspace: h }) {
11
+ let g = m(), { basePath: _ = "/" } = e(), { withCurrentSearch: v } = n(), { setOrganization: y, setWorkspace: b, setProject: x, setTenant: S } = o(), C = () => {
12
+ let e = h.organizationId ?? h.orgId;
13
+ e && y(e), b(h.id), x(null), typeof h.tenantId == "string" && S(h.tenantId), g(v(`${_}/${h.id}`));
12
14
  };
13
- return /* @__PURE__ */ s(f, {
15
+ return /* @__PURE__ */ c(p, {
14
16
  treatment: "insight",
15
17
  className: "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50",
16
- onClick: v,
18
+ onClick: C,
17
19
  onKeyDown: (e) => {
18
- (e.key === "Enter" || e.key === " ") && (e.preventDefault(), v());
20
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), C());
19
21
  },
20
22
  role: "button",
21
23
  tabIndex: 0,
22
- "aria-label": `Open workspace ${m.name}`,
24
+ "aria-label": `Open workspace ${h.name}`,
23
25
  "data-tour": "workspaces-card",
24
- children: [/* @__PURE__ */ o(u, { children: /* @__PURE__ */ s("div", {
26
+ children: [/* @__PURE__ */ s(d, { children: /* @__PURE__ */ c("div", {
25
27
  className: "flex items-center gap-3",
26
- children: [/* @__PURE__ */ o("div", {
28
+ children: [/* @__PURE__ */ s("div", {
27
29
  className: "p-3 rounded-lg bg-action-primary-bg/10 transition-colors",
28
- children: /* @__PURE__ */ o(r, {
30
+ children: /* @__PURE__ */ s(r, {
29
31
  className: "text-primary",
30
32
  size: 24
31
33
  })
32
- }), /* @__PURE__ */ s("div", {
34
+ }), /* @__PURE__ */ c("div", {
33
35
  className: "flex-1 min-w-0",
34
- children: [/* @__PURE__ */ o(d, {
36
+ children: [/* @__PURE__ */ s(f, {
35
37
  className: "text-lg truncate",
36
- children: m.name
37
- }), /* @__PURE__ */ o("div", {
38
+ children: h.name
39
+ }), /* @__PURE__ */ s("div", {
38
40
  className: "flex items-center gap-2 mt-1",
39
- children: /* @__PURE__ */ o(c, {
40
- className: `text-xs border ${t(m.status)}`,
41
- children: m.status
41
+ children: /* @__PURE__ */ s(l, {
42
+ className: `text-xs border ${t(h.status)}`,
43
+ children: h.status
42
44
  })
43
45
  })]
44
46
  })]
45
- }) }), /* @__PURE__ */ s(l, { children: [/* @__PURE__ */ s("div", {
47
+ }) }), /* @__PURE__ */ c(u, { children: [/* @__PURE__ */ c("div", {
46
48
  className: "flex flex-wrap gap-4 text-sm text-text-secondary",
47
- children: [typeof m.memberCount == "number" && m.memberCount > 0 && /* @__PURE__ */ s("div", {
49
+ children: [typeof h.memberCount == "number" && h.memberCount > 0 && /* @__PURE__ */ c("div", {
48
50
  className: "flex items-center gap-1",
49
- children: [/* @__PURE__ */ o(a, {
51
+ children: [/* @__PURE__ */ s(a, {
50
52
  size: 16,
51
53
  "aria-hidden": "true"
52
- }), /* @__PURE__ */ s("span", { children: [
53
- m.memberCount,
54
+ }), /* @__PURE__ */ c("span", { children: [
55
+ h.memberCount,
54
56
  " member",
55
- m.memberCount === 1 ? "" : "s"
57
+ h.memberCount === 1 ? "" : "s"
56
58
  ] })]
57
- }), typeof m.projectCount == "number" && m.projectCount > 0 && /* @__PURE__ */ s("div", {
59
+ }), typeof h.projectCount == "number" && h.projectCount > 0 && /* @__PURE__ */ c("div", {
58
60
  className: "flex items-center gap-1",
59
- children: [/* @__PURE__ */ o(i, {
61
+ children: [/* @__PURE__ */ s(i, {
60
62
  size: 16,
61
63
  "aria-hidden": "true"
62
- }), /* @__PURE__ */ s("span", { children: [
63
- m.projectCount,
64
+ }), /* @__PURE__ */ c("span", { children: [
65
+ h.projectCount,
64
66
  " project",
65
- m.projectCount === 1 ? "" : "s"
67
+ h.projectCount === 1 ? "" : "s"
66
68
  ] })]
67
69
  })]
68
- }), m.domain && /* @__PURE__ */ o("p", {
70
+ }), h.domain && /* @__PURE__ */ s("p", {
69
71
  className: "mt-2 truncate text-xs text-text-muted",
70
- children: m.domain
72
+ children: h.domain
71
73
  })] })]
72
74
  });
73
75
  }
74
76
  //#endregion
75
- export { m as WorkspaceCard };
77
+ export { h as WorkspaceCard };
76
78
 
77
79
  //# sourceMappingURL=WorkspaceCard.js.map