@burdenoff/microfe-vibecontrols 2026.801.1 → 2026.805.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/notes/NoteCard.js +10 -26
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteList.js +27 -44
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +179 -194
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/package.json +2 -2
|
@@ -4,8 +4,8 @@ import { Edit as n, FileText as r, Globe as i, Lock as a, MoreVertical as o, Pin
|
|
|
4
4
|
import { AnimatePresence as f, motion as p } from "framer-motion";
|
|
5
5
|
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/notes/NoteCard.tsx
|
|
7
|
-
function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x,
|
|
8
|
-
let { t:
|
|
7
|
+
function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, onShareAccess: S }) {
|
|
8
|
+
let { t: C } = t(), [w, T] = e(!1);
|
|
9
9
|
return /* @__PURE__ */ h(p.div, {
|
|
10
10
|
whileHover: { transition: {
|
|
11
11
|
duration: .15,
|
|
@@ -59,11 +59,11 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
|
|
|
59
59
|
children: [/* @__PURE__ */ m("button", {
|
|
60
60
|
type: "button",
|
|
61
61
|
onClick: (e) => {
|
|
62
|
-
e.stopPropagation(),
|
|
62
|
+
e.stopPropagation(), T(!w);
|
|
63
63
|
},
|
|
64
64
|
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
65
65
|
children: /* @__PURE__ */ m(o, { className: "size-4 text-text-secondary" })
|
|
66
|
-
}), /* @__PURE__ */ m(f, { children:
|
|
66
|
+
}), /* @__PURE__ */ m(f, { children: w && /* @__PURE__ */ h(p.div, {
|
|
67
67
|
initial: {
|
|
68
68
|
opacity: 0,
|
|
69
69
|
scale: .95,
|
|
@@ -85,32 +85,16 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
|
|
|
85
85
|
v && /* @__PURE__ */ h(p.button, {
|
|
86
86
|
whileTap: { scale: .97 },
|
|
87
87
|
onClick: (e) => {
|
|
88
|
-
e.stopPropagation(), v(g),
|
|
88
|
+
e.stopPropagation(), v(g), T(!1);
|
|
89
89
|
},
|
|
90
90
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
91
91
|
children: [/* @__PURE__ */ m(n, { className: "size-4" }), "Edit"]
|
|
92
92
|
}),
|
|
93
|
-
S &&
|
|
94
|
-
whileTap: { scale: .97 },
|
|
95
|
-
onClick: (e) => {
|
|
96
|
-
e.stopPropagation(), S(g), E(!1);
|
|
97
|
-
},
|
|
98
|
-
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
99
|
-
children: [/* @__PURE__ */ m(l, { className: "size-4" }), "Share with workspace"]
|
|
100
|
-
}),
|
|
101
|
-
S && g.visibility === "WORKSPACE" && /* @__PURE__ */ h(p.button, {
|
|
102
|
-
whileTap: { scale: .97 },
|
|
103
|
-
onClick: (e) => {
|
|
104
|
-
e.stopPropagation(), S(g), E(!1);
|
|
105
|
-
},
|
|
106
|
-
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
107
|
-
children: [/* @__PURE__ */ m(a, { className: "size-4" }), "Make private"]
|
|
108
|
-
}),
|
|
109
|
-
C && /* @__PURE__ */ h(p.button, {
|
|
93
|
+
S && /* @__PURE__ */ h(p.button, {
|
|
110
94
|
whileTap: { scale: .97 },
|
|
111
95
|
"data-testid": "note-share-access",
|
|
112
96
|
onClick: (e) => {
|
|
113
|
-
e.stopPropagation(),
|
|
97
|
+
e.stopPropagation(), S(g), T(!1);
|
|
114
98
|
},
|
|
115
99
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
116
100
|
children: [/* @__PURE__ */ m(l, { className: "size-4" }), "Share access…"]
|
|
@@ -118,7 +102,7 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
|
|
|
118
102
|
g.isPinned && x && /* @__PURE__ */ h(p.button, {
|
|
119
103
|
whileTap: { scale: .97 },
|
|
120
104
|
onClick: (e) => {
|
|
121
|
-
e.stopPropagation(), x(g),
|
|
105
|
+
e.stopPropagation(), x(g), T(!1);
|
|
122
106
|
},
|
|
123
107
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
124
108
|
children: [/* @__PURE__ */ m(c, { className: "size-4" }), "Unpin"]
|
|
@@ -126,7 +110,7 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
|
|
|
126
110
|
!g.isPinned && b && /* @__PURE__ */ h(p.button, {
|
|
127
111
|
whileTap: { scale: .97 },
|
|
128
112
|
onClick: (e) => {
|
|
129
|
-
e.stopPropagation(), b(g),
|
|
113
|
+
e.stopPropagation(), b(g), T(!1);
|
|
130
114
|
},
|
|
131
115
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
132
116
|
children: [/* @__PURE__ */ m(s, { className: "size-4" }), "Pin"]
|
|
@@ -134,7 +118,7 @@ function g({ note: g, onOpen: _, onEdit: v, onDelete: y, onPin: b, onUnpin: x, o
|
|
|
134
118
|
y && /* @__PURE__ */ h(p.button, {
|
|
135
119
|
whileTap: { scale: .97 },
|
|
136
120
|
onClick: (e) => {
|
|
137
|
-
e.stopPropagation(), y(g),
|
|
121
|
+
e.stopPropagation(), y(g), T(!1);
|
|
138
122
|
},
|
|
139
123
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg",
|
|
140
124
|
children: [/* @__PURE__ */ m(d, { className: "size-4" }), "Delete"]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteCard.js","names":[],"sources":["../../../src/components/notes/NoteCard.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n FileText,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport type { Note } from './types';\n\ninterface NoteCardProps {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShare?: (note: Note) => void;\n /**\n * Generic-sharing entry point: opens the full Share dialog (users / link /\n * vibe). Distinct from `onShare`, which only toggles PRIVATE/WORKSPACE\n * visibility. Provided only when the caller has share permission + the\n * `vibecontrols.sharing.note` flag is on.\n */\n onShareAccess?: (note: Note) => void;\n}\n\nexport function NoteCard({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShare,\n onShareAccess,\n}: NoteCardProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n // Remove markdown headers and get first 100 chars\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 100 ? text.slice(0, 100) + '...' : text;\n };\n\n return (\n <motion.div\n whileHover={{ transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className=\"bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer\"\n onClick={() => onOpen?.(note)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-status-warning-bg flex items-center justify-center\">\n <FileText className=\"size-5 text-status-warning-text\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary truncate\">{note.title}</h3>\n {note.isPinned && <Pin className=\"size-4 text-status-warning-text flex-shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private note\">\n <Lock className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace note\">\n <Globe className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">{formatDate(note.updatedAt)}</p>\n </div>\n </div>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-36 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {onEdit && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEdit(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Edit className=\"size-4\" />\n Edit\n </motion.button>\n )}\n {onShare && note.visibility === 'PRIVATE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share with workspace\n </motion.button>\n )}\n {onShare && note.visibility === 'WORKSPACE' && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Lock className=\"size-4\" />\n Make private\n </motion.button>\n )}\n {onShareAccess && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </motion.button>\n )}\n {note.isPinned && onUnpin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </motion.button>\n )}\n {!note.isPinned && onPin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Pin className=\"size-4\" />\n Pin\n </motion.button>\n )}\n {onDelete && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </div>\n\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">{getPreview(note.content)}</p>\n\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 3).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `3px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-3\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 3}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n {note.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 3}\n </span>\n )}\n </div>\n ) : null}\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NoteCard.js","names":[],"sources":["../../../src/components/notes/NoteCard.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n FileText,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport type { Note } from './types';\n\ninterface NoteCardProps {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n /**\n * Generic-sharing entry point: opens the full Share dialog (users / link /\n * vibe). Provided only when the caller has share permission + the\n * `vibecontrols.sharing.note` flag is on.\n *\n * This supersedes the old PRIVATE↔WORKSPACE visibility toggle — see\n * `vibecontrols-specs/business/production/visibility-feature-audit-boff-3036.md`.\n * Visibility on a note is now exclusively modelled through share grants; the\n * legacy binary toggle has been removed to avoid two parallel sharing models.\n */\n onShareAccess?: (note: Note) => void;\n}\n\nexport function NoteCard({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShareAccess,\n}: NoteCardProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n // Remove markdown headers and get first 100 chars\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 100 ? text.slice(0, 100) + '...' : text;\n };\n\n return (\n <motion.div\n whileHover={{ transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className=\"bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer\"\n onClick={() => onOpen?.(note)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"flex-shrink-0 size-10 rounded-lg bg-status-warning-bg flex items-center justify-center\">\n <FileText className=\"size-5 text-status-warning-text\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary truncate\">{note.title}</h3>\n {note.isPinned && <Pin className=\"size-4 text-status-warning-text flex-shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private note\">\n <Lock className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace note\">\n <Globe className=\"size-3.5 text-text-muted flex-shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">{formatDate(note.updatedAt)}</p>\n </div>\n </div>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-36 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {onEdit && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEdit(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Edit className=\"size-4\" />\n Edit\n </motion.button>\n )}\n {onShareAccess && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </motion.button>\n )}\n {note.isPinned && onUnpin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </motion.button>\n )}\n {!note.isPinned && onPin && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Pin className=\"size-4\" />\n Pin\n </motion.button>\n )}\n {onDelete && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </div>\n\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">{getPreview(note.content)}</p>\n\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 3).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `3px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-3\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 3}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {note.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n {note.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 3}\n </span>\n )}\n </div>\n ) : null}\n </motion.div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,8 +4,8 @@ import { useState as n } from "react";
|
|
|
4
4
|
import { Edit as r, FileText as i, Globe as a, Lock as o, MoreVertical as s, Pin as c, PinOff as l, Plus as u, Share2 as d, Tag as f, Trash2 as p } from "lucide-react";
|
|
5
5
|
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/notes/NoteList.tsx
|
|
7
|
-
function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v,
|
|
8
|
-
let [
|
|
7
|
+
function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, onShareAccess: y }) {
|
|
8
|
+
let [b, x] = n(!1);
|
|
9
9
|
return /* @__PURE__ */ h("tr", {
|
|
10
10
|
className: "border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors",
|
|
11
11
|
onClick: () => t?.(e),
|
|
@@ -90,47 +90,31 @@ function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, o
|
|
|
90
90
|
className: "px-4 py-3",
|
|
91
91
|
children: /* @__PURE__ */ m("div", {
|
|
92
92
|
className: "flex items-center gap-1 justify-end",
|
|
93
|
-
children: (u || g || _ || v || y
|
|
93
|
+
children: (u || g || _ || v || y) && /* @__PURE__ */ h("div", {
|
|
94
94
|
className: "relative",
|
|
95
95
|
children: [/* @__PURE__ */ m("button", {
|
|
96
96
|
type: "button",
|
|
97
97
|
onClick: (e) => {
|
|
98
|
-
e.stopPropagation(),
|
|
98
|
+
e.stopPropagation(), x(!b);
|
|
99
99
|
},
|
|
100
100
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
101
101
|
children: /* @__PURE__ */ m(s, { className: "size-3.5 text-text-secondary" })
|
|
102
|
-
}),
|
|
102
|
+
}), b && /* @__PURE__ */ h("div", {
|
|
103
103
|
className: "absolute right-0 mt-1 w-44 bg-bg-surface border border-border-default rounded-lg shadow-lg z-50",
|
|
104
104
|
children: [
|
|
105
105
|
u && /* @__PURE__ */ h("button", {
|
|
106
106
|
type: "button",
|
|
107
107
|
onClick: (t) => {
|
|
108
|
-
t.stopPropagation(), u(e),
|
|
108
|
+
t.stopPropagation(), u(e), x(!1);
|
|
109
109
|
},
|
|
110
110
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
111
111
|
children: [/* @__PURE__ */ m(r, { className: "size-4" }), "Edit"]
|
|
112
112
|
}),
|
|
113
|
-
y &&
|
|
114
|
-
type: "button",
|
|
115
|
-
onClick: (t) => {
|
|
116
|
-
t.stopPropagation(), y(e), S(!1);
|
|
117
|
-
},
|
|
118
|
-
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
119
|
-
children: [/* @__PURE__ */ m(d, { className: "size-4" }), "Share with workspace"]
|
|
120
|
-
}),
|
|
121
|
-
y && e.visibility === "WORKSPACE" && /* @__PURE__ */ h("button", {
|
|
122
|
-
type: "button",
|
|
123
|
-
onClick: (t) => {
|
|
124
|
-
t.stopPropagation(), y(e), S(!1);
|
|
125
|
-
},
|
|
126
|
-
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
127
|
-
children: [/* @__PURE__ */ m(o, { className: "size-4" }), "Make private"]
|
|
128
|
-
}),
|
|
129
|
-
b && /* @__PURE__ */ h("button", {
|
|
113
|
+
y && /* @__PURE__ */ h("button", {
|
|
130
114
|
type: "button",
|
|
131
115
|
"data-testid": "note-share-access",
|
|
132
116
|
onClick: (t) => {
|
|
133
|
-
t.stopPropagation(),
|
|
117
|
+
t.stopPropagation(), y(e), x(!1);
|
|
134
118
|
},
|
|
135
119
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
136
120
|
children: [/* @__PURE__ */ m(d, { className: "size-4" }), "Share access…"]
|
|
@@ -138,7 +122,7 @@ function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, o
|
|
|
138
122
|
e.isPinned && v && /* @__PURE__ */ h("button", {
|
|
139
123
|
type: "button",
|
|
140
124
|
onClick: (t) => {
|
|
141
|
-
t.stopPropagation(), v(e),
|
|
125
|
+
t.stopPropagation(), v(e), x(!1);
|
|
142
126
|
},
|
|
143
127
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
144
128
|
children: [/* @__PURE__ */ m(l, { className: "size-4" }), "Unpin"]
|
|
@@ -146,7 +130,7 @@ function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, o
|
|
|
146
130
|
!e.isPinned && _ && /* @__PURE__ */ h("button", {
|
|
147
131
|
type: "button",
|
|
148
132
|
onClick: (t) => {
|
|
149
|
-
t.stopPropagation(), _(e),
|
|
133
|
+
t.stopPropagation(), _(e), x(!1);
|
|
150
134
|
},
|
|
151
135
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
152
136
|
children: [/* @__PURE__ */ m(c, { className: "size-4" }), "Pin"]
|
|
@@ -154,7 +138,7 @@ function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, o
|
|
|
154
138
|
g && /* @__PURE__ */ h("button", {
|
|
155
139
|
type: "button",
|
|
156
140
|
onClick: (t) => {
|
|
157
|
-
t.stopPropagation(), g(e),
|
|
141
|
+
t.stopPropagation(), g(e), x(!1);
|
|
158
142
|
},
|
|
159
143
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg",
|
|
160
144
|
children: [/* @__PURE__ */ m(p, { className: "size-4" }), "Delete"]
|
|
@@ -167,20 +151,20 @@ function g({ note: e, onOpen: t, onEdit: u, onDelete: g, onPin: _, onUnpin: v, o
|
|
|
167
151
|
]
|
|
168
152
|
});
|
|
169
153
|
}
|
|
170
|
-
function _({ notes: n, onOpen: r, onEdit: a, onDelete: o, onPin: s, onUnpin: c,
|
|
171
|
-
let
|
|
172
|
-
if (!
|
|
154
|
+
function _({ notes: n, onOpen: r, onEdit: a, onDelete: o, onPin: s, onUnpin: c, onShareAccess: l, onCreateNew: d, isLoading: f }) {
|
|
155
|
+
let p = e();
|
|
156
|
+
if (!f && n.length === 0) return /* @__PURE__ */ m(t, {
|
|
173
157
|
icon: /* @__PURE__ */ m(i, { className: "size-8 text-text-secondary" }),
|
|
174
|
-
title:
|
|
175
|
-
description:
|
|
176
|
-
action:
|
|
158
|
+
title: p("vibecontrols.notes.emptyTitle", "No notes yet"),
|
|
159
|
+
description: p("vibecontrols.notes.emptyDescription", "Notes help you document your development process. Create your first note to get started."),
|
|
160
|
+
action: d && /* @__PURE__ */ h("button", {
|
|
177
161
|
type: "button",
|
|
178
|
-
onClick:
|
|
162
|
+
onClick: d,
|
|
179
163
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
180
|
-
children: [/* @__PURE__ */ m(u, { className: "size-4" }),
|
|
164
|
+
children: [/* @__PURE__ */ m(u, { className: "size-4" }), p("vibecontrols.notes.createNote", "Create Note")]
|
|
181
165
|
})
|
|
182
166
|
});
|
|
183
|
-
let
|
|
167
|
+
let _ = [...n].sort((e, t) => e.isPinned && !t.isPinned ? -1 : !e.isPinned && t.isPinned ? 1 : new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime());
|
|
184
168
|
return /* @__PURE__ */ m("div", {
|
|
185
169
|
className: "border border-border-default rounded-lg",
|
|
186
170
|
children: /* @__PURE__ */ m("div", {
|
|
@@ -192,34 +176,33 @@ function _({ notes: n, onOpen: r, onEdit: a, onDelete: o, onPin: s, onUnpin: c,
|
|
|
192
176
|
children: [
|
|
193
177
|
/* @__PURE__ */ m("th", {
|
|
194
178
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tl-lg",
|
|
195
|
-
children:
|
|
179
|
+
children: p("vibecontrols.notes.columnTitle", "Title")
|
|
196
180
|
}),
|
|
197
181
|
/* @__PURE__ */ m("th", {
|
|
198
182
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
199
|
-
children:
|
|
183
|
+
children: p("vibecontrols.notes.columnVibe", "Vibe")
|
|
200
184
|
}),
|
|
201
185
|
/* @__PURE__ */ m("th", {
|
|
202
186
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
203
|
-
children:
|
|
187
|
+
children: p("vibecontrols.notes.columnTags", "Tags")
|
|
204
188
|
}),
|
|
205
189
|
/* @__PURE__ */ m("th", {
|
|
206
190
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
207
|
-
children:
|
|
191
|
+
children: p("vibecontrols.notes.columnUpdated", "Updated")
|
|
208
192
|
}),
|
|
209
193
|
/* @__PURE__ */ m("th", {
|
|
210
194
|
className: "px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tr-lg",
|
|
211
|
-
children:
|
|
195
|
+
children: p("vibecontrols.notes.columnActions", "Actions")
|
|
212
196
|
})
|
|
213
197
|
]
|
|
214
|
-
}) }), /* @__PURE__ */ m("tbody", { children:
|
|
198
|
+
}) }), /* @__PURE__ */ m("tbody", { children: _.map((e) => /* @__PURE__ */ m(g, {
|
|
215
199
|
note: e,
|
|
216
200
|
onOpen: r,
|
|
217
201
|
onEdit: a,
|
|
218
202
|
onDelete: o,
|
|
219
203
|
onPin: s,
|
|
220
204
|
onUnpin: c,
|
|
221
|
-
|
|
222
|
-
onShareAccess: d
|
|
205
|
+
onShareAccess: l
|
|
223
206
|
}, e.id)) })]
|
|
224
207
|
})
|
|
225
208
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteList.js","names":[],"sources":["../../../src/components/notes/NoteList.tsx"],"sourcesContent":["import type { Note } from './types';\nimport { EmptyState } from '../shared/EmptyState';\nimport {\n FileText,\n Plus,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface NoteListProps {\n notes: Note[];\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShare?: (note: Note) => void;\n /** Generic-sharing entry point (full Share dialog); see NoteCard.onShareAccess. */\n onShareAccess?: (note: Note) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n}\n\nfunction NoteRow({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShare,\n onShareAccess,\n}: {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShare?: (note: Note) => void;\n onShareAccess?: (note: Note) => void;\n}) {\n const [showMenu, setShowMenu] = useState(false);\n\n const formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 80 ? text.slice(0, 80) + '...' : text;\n };\n\n return (\n <tr\n className=\"border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors\"\n onClick={() => onOpen?.(note)}\n >\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <FileText className=\"size-4 text-status-warning-text shrink-0\" />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{note.title}</p>\n {note.isPinned && <Pin className=\"size-3 text-status-warning-text shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private\">\n <Lock className=\"size-3 text-text-muted shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace\">\n <Globe className=\"size-3 text-text-muted shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-muted truncate max-w-[300px]\">\n {getPreview(note.content)}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary truncate block max-w-[100px]\">\n {note.vibe?.name || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 2).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `2px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-2.5\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 2 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 2}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {note.tags.slice(0, 2).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-2.5\" />\n {tag}\n </span>\n ))}\n {note.tags.length > 2 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 2}\n </span>\n )}\n </div>\n ) : (\n <span className=\"text-xs text-text-muted\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-xs text-text-muted\">{formatDate(note.updatedAt)}</span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {(onEdit || onDelete || onPin || onUnpin || onShare || onShareAccess) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-3.5 text-text-secondary\" />\n </button>\n {showMenu && (\n <div className=\"absolute right-0 mt-1 w-44 bg-bg-surface border border-border-default rounded-lg shadow-lg z-50\">\n {onEdit && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Edit className=\"size-4\" />\n Edit\n </button>\n )}\n {onShare && note.visibility === 'PRIVATE' && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share with workspace\n </button>\n )}\n {onShare && note.visibility === 'WORKSPACE' && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onShare(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Lock className=\"size-4\" />\n Make private\n </button>\n )}\n {onShareAccess && (\n <button\n type=\"button\"\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </button>\n )}\n {note.isPinned && onUnpin && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </button>\n )}\n {!note.isPinned && onPin && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Pin className=\"size-4\" />\n Pin\n </button>\n )}\n {onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function NoteList({\n notes,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShare,\n onShareAccess,\n onCreateNew,\n isLoading,\n}: NoteListProps) {\n const tr = useTr();\n if (!isLoading && notes.length === 0) {\n return (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.notes.emptyTitle', 'No notes yet')}\n description={tr(\n 'vibecontrols.notes.emptyDescription',\n 'Notes help you document your development process. Create your first note to get started.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.notes.createNote', 'Create Note')}\n </button>\n )\n }\n />\n );\n }\n\n // Sort: pinned first, then by updated date\n const sortedNotes = [...notes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n\n return (\n <div className=\"border border-border-default rounded-lg\">\n <div className=\"overflow-x-auto rounded-lg\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tl-lg\">\n {tr('vibecontrols.notes.columnTitle', 'Title')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnVibe', 'Vibe')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnTags', 'Tags')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnUpdated', 'Updated')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tr-lg\">\n {tr('vibecontrols.notes.columnActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {sortedNotes.map((note) => (\n <NoteRow\n key={note.id}\n note={note}\n onOpen={onOpen}\n onEdit={onEdit}\n onDelete={onDelete}\n onPin={onPin}\n onUnpin={onUnpin}\n onShare={onShare}\n onShareAccess={onShareAccess}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAgCA,SAAS,EAAQ,EACf,SACA,WACA,WACA,aACA,UACA,YACA,YACA,oBAUC;CACD,IAAM,CAAC,GAAU,KAAe,EAAS,GAAM;AAkB/C,QACE,kBAAC,MAAD;EACE,WAAU;EACV,eAAe,IAAS,EAAK;YAF/B;GAIE,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA,EACjE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBAAkD,EAAK;SAAU,CAAA;QAC7E,EAAK,YAAY,kBAAC,GAAD,EAAK,WAAU,4CAA6C,CAAA;QAC7E,EAAK,eAAe,YACnB,kBAAC,QAAD;SAAM,OAAM;mBACV,kBAAC,GAAD,EAAM,WAAU,mCAAoC,CAAA;SAC/C,CAAA,GAEP,kBAAC,QAAD;SAAM,OAAM;mBACV,kBAAC,GAAD,EAAO,WAAU,mCAAoC,CAAA;SAChD,CAAA;QAEL;UACN,kBAAC,KAAD;OAAG,WAAU;mBA9BH,MAAoB;QACtC,IAAM,IAAO,EACV,QAAQ,YAAY,GAAG,CACvB,QAAQ,OAAO,IAAI,CACnB,MAAM;AACT,eAAO,EAAK,SAAS,KAAK,EAAK,MAAM,GAAG,GAAG,GAAG,QAAQ;UA0BhC,EAAK,QAAQ;OACvB,CAAA,CACA;QACF;;IACH,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,QAAD;KAAM,WAAU;eACb,EAAK,MAAM,QAAQ;KACf,CAAA;IACJ,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cAEX,EAAK,kBAAkB,EAAK,eAAe,SAAS,IACnD,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAK,eAAe,MAAM,GAAG,EAAE,CAAC,KAAK,MACpC,kBAAC,QAAD;MAEE,WAAU;MACV,OACE,EAAW,KAAK,QACZ,EAAE,YAAY,aAAa,EAAW,IAAI,SAAS,GACnD,KAAA;gBANR,CASE,kBAAC,GAAD,EAAK,WAAU,YAAa,CAAA,EAC3B,EAAW,KAAK,SAAS,EAAW,MAAM,MAAM,GAAG,EAAE,CACjD;QAVA,EAAW,GAUX,CACP,EACD,EAAK,eAAe,SAAS,KAC5B,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAAiF,KAC7E,EAAK,eAAe,SAAS,EAC1B;QAEL;SACJ,EAAK,QAAQ,EAAK,KAAK,SAAS,IAClC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAK,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC1B,kBAAC,QAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,GAAD,EAAK,WAAU,YAAa,CAAA,EAC3B,EACI;QALA,EAKA,CACP,EACD,EAAK,KAAK,SAAS,KAClB,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAAiF,KAC7E,EAAK,KAAK,SAAS,EAChB;QAEL;SAEN,kBAAC,QAAD;KAAM,WAAU;eAA0B;KAAQ,CAAA;IAEjD,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,QAAD;KAAM,WAAU;iBA/FF,MACX,IAAI,KAAK,EAAQ,CAAC,mBAAmB,SAAS;MACnD,OAAO;MACP,KAAK;MACL,MAAM;MACP,CAAC,EA0FwD,EAAK,UAAU;KAAQ,CAAA;IAC1E,CAAA;GACL,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,OAAD;KAAK,WAAU;gBACX,KAAU,KAAY,KAAS,KAAW,KAAW,MACrD,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAY,CAAC,EAAS;;OAExB,WAAU;iBAEV,kBAAC,GAAD,EAAc,WAAU,gCAAiC,CAAA;OAClD,CAAA,EACR,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,KACC,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAO,EAAK,EACZ,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,OAEpB;;QAEV,KAAW,EAAK,eAAe,aAC9B,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAQ,EAAK,EACb,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,uBAEtB;;QAEV,KAAW,EAAK,eAAe,eAC9B,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAQ,EAAK,EACb,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,eAEpB;;QAEV,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAY;SACZ,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAc,EAAK,EACnB,EAAY,GAAM;;SAEpB,WAAU;mBARZ,CAUE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,gBAEtB;;QAEV,EAAK,YAAY,KAChB,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAQ,EAAK,EACb,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,QAEtB;;QAEV,CAAC,EAAK,YAAY,KACjB,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAM,EAAK,EACX,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA,EAAA,MAEnB;;QAEV,KACC,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAS,EAAK,EACd,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,SAEtB;;QAEP;SAEJ;;KAEJ,CAAA;IACH,CAAA;GACF;;;AAIT,SAAgB,EAAS,EACvB,UACA,WACA,WACA,aACA,UACA,YACA,YACA,kBACA,gBACA,gBACgB;CAChB,IAAM,IAAK,GAAO;AAClB,KAAI,CAAC,KAAa,EAAM,WAAW,EACjC,QACE,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA;EACzD,OAAO,EAAG,iCAAiC,eAAe;EAC1D,aAAa,EACX,uCACA,2FACD;EACD,QACE,KACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,iCAAiC,cAAc,CAC5C;;EAGb,CAAA;CAKN,IAAM,IAAc,CAAC,GAAG,EAAM,CAAC,MAAM,GAAG,MAClC,EAAE,YAAY,CAAC,EAAE,WAAiB,KAClC,CAAC,EAAE,YAAY,EAAE,WAAiB,IAC/B,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CACxE;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,kCAAkC,QAAQ;OAC3C,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,iCAAiC,OAAO;OACzC,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,iCAAiC,OAAO;OACzC,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,oCAAoC,UAAU;OAC/C,CAAA;MACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,oCAAoC,UAAU;OAC/C,CAAA;MACF;QACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAY,KAAK,MAChB,kBAAC,GAAD;KAEQ;KACE;KACA;KACE;KACH;KACE;KACA;KACM;KACf,EATK,EAAK,GASV,CACF,EACI,CAAA,CACF;;GACJ,CAAA;EACF,CAAA"}
|
|
1
|
+
{"version":3,"file":"NoteList.js","names":[],"sources":["../../../src/components/notes/NoteList.tsx"],"sourcesContent":["import type { Note } from './types';\nimport { EmptyState } from '../shared/EmptyState';\nimport {\n FileText,\n Plus,\n Pin,\n Tag,\n MoreVertical,\n Edit,\n Trash2,\n PinOff,\n Lock,\n Globe,\n Share2,\n} from 'lucide-react';\nimport { useState } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface NoteListProps {\n notes: Note[];\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n /** Generic-sharing entry point (full Share dialog); see NoteCard.onShareAccess. */\n onShareAccess?: (note: Note) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n}\n\nfunction NoteRow({\n note,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShareAccess,\n}: {\n note: Note;\n onOpen?: (note: Note) => void;\n onEdit?: (note: Note) => void;\n onDelete?: (note: Note) => void;\n onPin?: (note: Note) => void;\n onUnpin?: (note: Note) => void;\n onShareAccess?: (note: Note) => void;\n}) {\n const [showMenu, setShowMenu] = useState(false);\n\n const formatDate = (dateStr: string) => {\n return new Date(dateStr).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n };\n\n const getPreview = (content: string) => {\n const text = content\n .replace(/^#+\\s+/gm, '')\n .replace(/\\n/g, ' ')\n .trim();\n return text.length > 80 ? text.slice(0, 80) + '...' : text;\n };\n\n return (\n <tr\n className=\"border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors\"\n onClick={() => onOpen?.(note)}\n >\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <FileText className=\"size-4 text-status-warning-text shrink-0\" />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{note.title}</p>\n {note.isPinned && <Pin className=\"size-3 text-status-warning-text shrink-0\" />}\n {note.visibility === 'PRIVATE' ? (\n <span title=\"Private\">\n <Lock className=\"size-3 text-text-muted shrink-0\" />\n </span>\n ) : (\n <span title=\"Workspace\">\n <Globe className=\"size-3 text-text-muted shrink-0\" />\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-muted truncate max-w-[300px]\">\n {getPreview(note.content)}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary truncate block max-w-[100px]\">\n {note.vibe?.name || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n {/* Render tagAssignments if available, otherwise fall back to legacy string tags */}\n {note.tagAssignments && note.tagAssignments.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {note.tagAssignments.slice(0, 2).map((assignment) => (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded\"\n style={\n assignment.tag?.color\n ? { borderLeft: `2px solid ${assignment.tag.color}` }\n : undefined\n }\n >\n <Tag className=\"size-2.5\" />\n {assignment.tag?.label ?? assignment.tagId.slice(0, 8)}\n </span>\n ))}\n {note.tagAssignments.length > 2 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tagAssignments.length - 2}\n </span>\n )}\n </div>\n ) : note.tags && note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {note.tags.slice(0, 2).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-2.5\" />\n {tag}\n </span>\n ))}\n {note.tags.length > 2 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{note.tags.length - 2}\n </span>\n )}\n </div>\n ) : (\n <span className=\"text-xs text-text-muted\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-xs text-text-muted\">{formatDate(note.updatedAt)}</span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {(onEdit || onDelete || onPin || onUnpin || onShareAccess) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-3.5 text-text-secondary\" />\n </button>\n {showMenu && (\n <div className=\"absolute right-0 mt-1 w-44 bg-bg-surface border border-border-default rounded-lg shadow-lg z-50\">\n {onEdit && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Edit className=\"size-4\" />\n Edit\n </button>\n )}\n {onShareAccess && (\n <button\n type=\"button\"\n data-testid=\"note-share-access\"\n onClick={(e) => {\n e.stopPropagation();\n onShareAccess(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n Share access…\n </button>\n )}\n {note.isPinned && onUnpin && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onUnpin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <PinOff className=\"size-4\" />\n Unpin\n </button>\n )}\n {!note.isPinned && onPin && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onPin(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Pin className=\"size-4\" />\n Pin\n </button>\n )}\n {onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete(note);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n Delete\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function NoteList({\n notes,\n onOpen,\n onEdit,\n onDelete,\n onPin,\n onUnpin,\n onShareAccess,\n onCreateNew,\n isLoading,\n}: NoteListProps) {\n const tr = useTr();\n if (!isLoading && notes.length === 0) {\n return (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.notes.emptyTitle', 'No notes yet')}\n description={tr(\n 'vibecontrols.notes.emptyDescription',\n 'Notes help you document your development process. Create your first note to get started.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.notes.createNote', 'Create Note')}\n </button>\n )\n }\n />\n );\n }\n\n // Sort: pinned first, then by updated date\n const sortedNotes = [...notes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n\n return (\n <div className=\"border border-border-default rounded-lg\">\n <div className=\"overflow-x-auto rounded-lg\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tl-lg\">\n {tr('vibecontrols.notes.columnTitle', 'Title')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnVibe', 'Vibe')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnTags', 'Tags')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.notes.columnUpdated', 'Updated')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider rounded-tr-lg\">\n {tr('vibecontrols.notes.columnActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {sortedNotes.map((note) => (\n <NoteRow\n key={note.id}\n note={note}\n onOpen={onOpen}\n onEdit={onEdit}\n onDelete={onDelete}\n onPin={onPin}\n onUnpin={onUnpin}\n onShareAccess={onShareAccess}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { ListNotesDocument as e, useAvailableNoteTagsQuery as t, useCreateNoteMutation as n, useDeleteNoteMutation as r, useListNotesQuery as i, usePinNoteMutation as ee, useReplaceNoteTagsMutation as te,
|
|
2
|
-
import { QuotaExhaustedDialog as
|
|
3
|
-
import { isQuotaExhaustedError as a, markQuotaHandledLocally as
|
|
4
|
-
import { LoadingState as
|
|
5
|
-
import { ErrorState as
|
|
6
|
-
import { NoteVisibility as
|
|
1
|
+
import { ListNotesDocument as e, useAvailableNoteTagsQuery as t, useCreateNoteMutation as n, useDeleteNoteMutation as r, useListNotesQuery as i, usePinNoteMutation as ee, useReplaceNoteTagsMutation as te, useUnpinNoteMutation as ne, useUpdateNoteMutation as re } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { QuotaExhaustedDialog as ie } from "../shared/QuotaExhaustedDialog.js";
|
|
3
|
+
import { isQuotaExhaustedError as a, markQuotaHandledLocally as ae } from "../../utils/quotaUtils.js";
|
|
4
|
+
import { LoadingState as oe } from "../shared/LoadingState.js";
|
|
5
|
+
import { ErrorState as se } from "../shared/ErrorState.js";
|
|
6
|
+
import { NoteVisibility as ce } from "../../generated/wspace-types.js";
|
|
7
7
|
import { EmptyState as le } from "../shared/EmptyState.js";
|
|
8
8
|
import { SearchInput as ue } from "../shared/SearchInput.js";
|
|
9
|
-
import { FilterDropdown as
|
|
9
|
+
import { FilterDropdown as o } from "../shared/FilterDropdown.js";
|
|
10
10
|
import "../shared/index.js";
|
|
11
|
-
import { VIBE_PERMISSIONS as
|
|
12
|
-
import { SHARE_REGISTRY as
|
|
11
|
+
import { VIBE_PERMISSIONS as s } from "../../constants/permissions.js";
|
|
12
|
+
import { SHARE_REGISTRY as c } from "../sharing/shareRegistry.js";
|
|
13
13
|
import { NoteShareDialog as de } from "../sharing/NoteShareDialog.js";
|
|
14
14
|
import "../sharing/index.js";
|
|
15
15
|
import { NoteCard as fe } from "./NoteCard.js";
|
|
16
16
|
import { NoteList as pe } from "./NoteList.js";
|
|
17
17
|
import { NoteEditor as me } from "./NoteEditor.js";
|
|
18
|
-
import { useMemo as
|
|
18
|
+
import { useMemo as l, useState as u } from "react";
|
|
19
19
|
import { useI18n as he } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
20
|
-
import { FileText as ge, Globe as _e, Grid as ve, List as ye, Lock as be, Pin as xe, Plus as
|
|
20
|
+
import { FileText as ge, Globe as _e, Grid as ve, List as ye, Lock as be, Pin as xe, Plus as d, RefreshCw as Se } from "lucide-react";
|
|
21
21
|
import { useNavigate as Ce } from "react-router";
|
|
22
22
|
import { useApolloClient as we } from "@apollo/client/react";
|
|
23
23
|
import { usePermissions as Te } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
24
24
|
import { AnimatePresence as Ee } from "framer-motion";
|
|
25
|
-
import { jsx as
|
|
25
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
26
26
|
import { useFeatureFlag as De } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
27
27
|
import { AccessDenied as Oe, PermissionButton as ke } from "@burdenoff/fe-libs/shared/components";
|
|
28
28
|
import { MotionItem as Ae, MotionList as je } from "@burdenoff/fe-libs/shared/motion";
|
|
29
29
|
//#region src/components/notes/NotesTabPanel.tsx
|
|
30
|
-
function
|
|
31
|
-
let { t: Me } = he(), Ne = Ce(),
|
|
32
|
-
|
|
30
|
+
function m({ context: m, vibeId: h, sessionId: g, agentVibes: _ = [] }) {
|
|
31
|
+
let { t: Me } = he(), Ne = Ce(), v = we(), { hasPermission: y, isLoading: b, permissions: Pe } = Te(), x = y(s.NOTE_LIST), Fe = y(s.NOTE_CREATE), S = y(s.NOTE_DELETE), Ie = De(c.note.featureFlag, c.note.defaultEnabled), Le = y(c.note.permission), Re = Ie && Le, [C, w] = u(null), [T, ze] = u(""), [E, Be] = u("all"), [D, Ve] = u("all"), [O, k] = u("all"), [A, He] = u(!1), [j, M] = u("grid"), [Ue, N] = u(!1), [We, P] = u(null), [Ge, F] = u(!1), I = (e) => {
|
|
32
|
+
v.cache.evict({
|
|
33
33
|
id: "ROOT_QUERY",
|
|
34
34
|
fieldName: "notes"
|
|
35
|
-
}),
|
|
35
|
+
}), v.cache.gc(), e();
|
|
36
36
|
}, [Ke] = ee({
|
|
37
37
|
refetchQueries: [e],
|
|
38
38
|
awaitRefetchQueries: !0
|
|
39
|
-
}), [qe] =
|
|
39
|
+
}), [qe] = ne({
|
|
40
40
|
refetchQueries: [e],
|
|
41
41
|
awaitRefetchQueries: !0
|
|
42
42
|
}), [Je] = r({
|
|
@@ -45,45 +45,42 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
45
45
|
}), [Ye] = n({
|
|
46
46
|
refetchQueries: [e],
|
|
47
47
|
awaitRefetchQueries: !0
|
|
48
|
-
}), [Xe] =
|
|
48
|
+
}), [Xe] = re({
|
|
49
49
|
refetchQueries: [e],
|
|
50
50
|
awaitRefetchQueries: !0
|
|
51
|
-
}), [
|
|
51
|
+
}), [L] = te({
|
|
52
52
|
refetchQueries: [e],
|
|
53
53
|
awaitRefetchQueries: !0
|
|
54
|
-
}),
|
|
55
|
-
refetchQueries: [e],
|
|
56
|
-
awaitRefetchQueries: !0
|
|
57
|
-
}), z = x || !S, { data: B } = t({ skip: z }), { data: V, loading: H, error: U, refetch: W } = i({
|
|
54
|
+
}), R = b || !x, { data: z } = t({ skip: R }), { data: B, loading: V, error: H, refetch: U } = i({
|
|
58
55
|
variables: {
|
|
59
|
-
filter:
|
|
60
|
-
let e =
|
|
61
|
-
return
|
|
62
|
-
sessionId:
|
|
63
|
-
isPinned:
|
|
56
|
+
filter: l(() => {
|
|
57
|
+
let e = O === "all" ? void 0 : O;
|
|
58
|
+
return m === "session" && g ? {
|
|
59
|
+
sessionId: g,
|
|
60
|
+
isPinned: A ? !0 : void 0,
|
|
64
61
|
visibility: e
|
|
65
|
-
} :
|
|
66
|
-
vibeId:
|
|
67
|
-
isPinned:
|
|
62
|
+
} : m === "vibe" && h ? {
|
|
63
|
+
vibeId: h,
|
|
64
|
+
isPinned: A ? !0 : void 0,
|
|
68
65
|
visibility: e
|
|
69
66
|
} : {
|
|
70
|
-
vibeId:
|
|
71
|
-
isPinned:
|
|
67
|
+
vibeId: E === "none" ? null : E === "all" ? void 0 : E,
|
|
68
|
+
isPinned: A ? !0 : void 0,
|
|
72
69
|
visibility: e
|
|
73
70
|
};
|
|
74
71
|
}, [
|
|
72
|
+
m,
|
|
75
73
|
h,
|
|
76
74
|
g,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
k
|
|
75
|
+
E,
|
|
76
|
+
A,
|
|
77
|
+
O
|
|
81
78
|
]),
|
|
82
79
|
pagination: { limit: 50 }
|
|
83
80
|
},
|
|
84
81
|
fetchPolicy: "cache-and-network",
|
|
85
|
-
skip:
|
|
86
|
-
}),
|
|
82
|
+
skip: R
|
|
83
|
+
}), Ze = l(() => m === "agent" ? [
|
|
87
84
|
{
|
|
88
85
|
value: "all",
|
|
89
86
|
label: "All Vibes"
|
|
@@ -92,15 +89,15 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
92
89
|
value: "none",
|
|
93
90
|
label: "No Vibe (General)"
|
|
94
91
|
},
|
|
95
|
-
...
|
|
92
|
+
..._.map((e) => ({
|
|
96
93
|
value: e.id,
|
|
97
94
|
label: e.name
|
|
98
95
|
}))
|
|
99
|
-
] : [], [
|
|
100
|
-
let e =
|
|
96
|
+
] : [], [m, _]), W = l(() => {
|
|
97
|
+
let e = z?.availableNoteTags ?? [];
|
|
101
98
|
return new Map(e.map((e) => [e.id, e]));
|
|
102
|
-
}, [
|
|
103
|
-
let e = (
|
|
99
|
+
}, [z?.availableNoteTags]), G = l(() => {
|
|
100
|
+
let e = (B?.notes?.items ?? []).map((e) => ({
|
|
104
101
|
id: e.id,
|
|
105
102
|
workspaceId: e.workspaceId,
|
|
106
103
|
vibeId: e.vibeId || null,
|
|
@@ -113,11 +110,11 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
113
110
|
label: e.tag.label,
|
|
114
111
|
key: e.tag.key,
|
|
115
112
|
color: e.tag.color
|
|
116
|
-
} :
|
|
113
|
+
} : W.has(e.tagId) ? {
|
|
117
114
|
id: e.tagId,
|
|
118
|
-
label:
|
|
119
|
-
key:
|
|
120
|
-
color:
|
|
115
|
+
label: W.get(e.tagId).label,
|
|
116
|
+
key: W.get(e.tagId).key,
|
|
117
|
+
color: W.get(e.tagId).color ?? null
|
|
121
118
|
} : void 0;
|
|
122
119
|
return {
|
|
123
120
|
id: e.id,
|
|
@@ -141,54 +138,54 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
141
138
|
name: e.vibe.name
|
|
142
139
|
} : void 0
|
|
143
140
|
}));
|
|
144
|
-
if (
|
|
145
|
-
let t = new Set(
|
|
141
|
+
if (m === "agent" && _.length > 0) {
|
|
142
|
+
let t = new Set(_.map((e) => e.id));
|
|
146
143
|
return e.filter((e) => e.vibeId && t.has(e.vibeId));
|
|
147
144
|
}
|
|
148
145
|
return e;
|
|
149
146
|
}, [
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
]),
|
|
147
|
+
B?.notes?.items,
|
|
148
|
+
m,
|
|
149
|
+
_,
|
|
150
|
+
W
|
|
151
|
+
]), Qe = l(() => [{
|
|
155
152
|
value: "all",
|
|
156
153
|
label: "All Tags"
|
|
157
|
-
}, ...(
|
|
154
|
+
}, ...(z?.availableNoteTags ?? []).map((e) => ({
|
|
158
155
|
value: e.id,
|
|
159
156
|
label: e.label
|
|
160
|
-
}))], [
|
|
161
|
-
if (
|
|
162
|
-
let t =
|
|
157
|
+
}))], [z?.availableNoteTags]), K = l(() => G.filter((e) => {
|
|
158
|
+
if (T) {
|
|
159
|
+
let t = T.toLowerCase();
|
|
163
160
|
if (!(e.title.toLowerCase().includes(t) || e.content.toLowerCase().includes(t) || e.tagAssignments?.some((e) => e.tag?.label?.toLowerCase().includes(t)))) return !1;
|
|
164
161
|
}
|
|
165
|
-
return !(
|
|
162
|
+
return !(D !== "all" && !e.tagAssignments?.some((e) => e.tagId === D));
|
|
166
163
|
}), [
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
]),
|
|
171
|
-
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
},
|
|
164
|
+
G,
|
|
165
|
+
T,
|
|
166
|
+
D
|
|
167
|
+
]), q = l(() => [...K].sort((e, t) => e.isPinned && !t.isPinned ? -1 : !e.isPinned && t.isPinned ? 1 : new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime()), [K]), J = (e) => {
|
|
168
|
+
P(e), N(!0);
|
|
169
|
+
}, Y = () => {
|
|
170
|
+
P(null), N(!0);
|
|
171
|
+
}, X = async (e) => {
|
|
175
172
|
try {
|
|
176
|
-
e.isPinned ? await qe({ variables: { id: e.id } }) : await Ke({ variables: { id: e.id } }),
|
|
173
|
+
e.isPinned ? await qe({ variables: { id: e.id } }) : await Ke({ variables: { id: e.id } }), I(U);
|
|
177
174
|
} catch (e) {
|
|
178
175
|
console.error("Failed to toggle pin for note:", e);
|
|
179
176
|
}
|
|
180
|
-
},
|
|
177
|
+
}, Z = async (e) => {
|
|
181
178
|
try {
|
|
182
|
-
await Je({ variables: { id: e.id } }),
|
|
179
|
+
await Je({ variables: { id: e.id } }), I(U);
|
|
183
180
|
} catch (e) {
|
|
184
181
|
console.error("Failed to delete note:", e);
|
|
185
182
|
}
|
|
186
|
-
},
|
|
183
|
+
}, $e = async (e) => {
|
|
187
184
|
let t = () => {
|
|
188
|
-
|
|
185
|
+
N(!1), P(null), ae(), F(!0);
|
|
189
186
|
};
|
|
190
187
|
try {
|
|
191
|
-
let n, r =
|
|
188
|
+
let n, r = m === "vibe" ? h : e.vibeId;
|
|
192
189
|
if (e.id) {
|
|
193
190
|
let r;
|
|
194
191
|
try {
|
|
@@ -215,8 +212,8 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
215
212
|
title: e.title || "",
|
|
216
213
|
content: e.content || "",
|
|
217
214
|
vibeId: r,
|
|
218
|
-
sessionId:
|
|
219
|
-
visibility: e.visibility ? e.visibility :
|
|
215
|
+
sessionId: m === "session" ? g : void 0,
|
|
216
|
+
visibility: e.visibility ? e.visibility : ce.Workspace
|
|
220
217
|
} } });
|
|
221
218
|
} catch (e) {
|
|
222
219
|
if (a(e)) {
|
|
@@ -227,51 +224,41 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
227
224
|
}
|
|
228
225
|
n = i.data?.createNote?.id;
|
|
229
226
|
}
|
|
230
|
-
n && e.selectedTagIds && e.selectedTagIds.length > 0 ? await
|
|
227
|
+
n && e.selectedTagIds && e.selectedTagIds.length > 0 ? await L({ variables: { input: {
|
|
231
228
|
noteId: n,
|
|
232
229
|
tagIds: e.selectedTagIds
|
|
233
|
-
} } }) : n && e.selectedTagIds?.length === 0 && e.id && await
|
|
230
|
+
} } }) : n && e.selectedTagIds?.length === 0 && e.id && await L({ variables: { input: {
|
|
234
231
|
noteId: n,
|
|
235
232
|
tagIds: []
|
|
236
|
-
} } }),
|
|
233
|
+
} } }), N(!1), I(U);
|
|
237
234
|
} catch (e) {
|
|
238
235
|
console.error("Failed to save note:", e);
|
|
239
236
|
}
|
|
240
|
-
},
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
visibility: t
|
|
246
|
-
} }), L(W);
|
|
247
|
-
} catch (e) {
|
|
248
|
-
console.error("Failed to toggle note visibility:", e);
|
|
249
|
-
}
|
|
250
|
-
}, rt = Re ? (e) => T(e) : void 0, Q = {
|
|
251
|
-
total: K.length,
|
|
252
|
-
pinned: K.filter((e) => e.isPinned).length
|
|
253
|
-
}, it = u(() => h === "vibe" ? [] : v, [h, v]);
|
|
254
|
-
if (!x && Pe && !S) return /* @__PURE__ */ p(Oe, {
|
|
237
|
+
}, et = Re ? (e) => w(e) : void 0, Q = {
|
|
238
|
+
total: G.length,
|
|
239
|
+
pinned: G.filter((e) => e.isPinned).length
|
|
240
|
+
}, tt = l(() => m === "vibe" ? [] : _, [m, _]);
|
|
241
|
+
if (!b && Pe && !x) return /* @__PURE__ */ f(Oe, {
|
|
255
242
|
title: "Access Denied",
|
|
256
243
|
description: "You don't have permission to view notes.",
|
|
257
244
|
details: "Required permission: note:list"
|
|
258
245
|
});
|
|
259
|
-
if (
|
|
260
|
-
if (
|
|
246
|
+
if (V && G.length === 0) return /* @__PURE__ */ f(oe, { message: "Loading notes..." });
|
|
247
|
+
if (H && G.length === 0) return /* @__PURE__ */ f(se, {
|
|
261
248
|
title: "Failed to load notes",
|
|
262
|
-
message:
|
|
263
|
-
onRetry: () =>
|
|
249
|
+
message: H.message,
|
|
250
|
+
onRetry: () => U()
|
|
264
251
|
});
|
|
265
|
-
let $ =
|
|
266
|
-
return /* @__PURE__ */
|
|
252
|
+
let $ = T || m === "agent" && E !== "all" || D !== "all" || A;
|
|
253
|
+
return /* @__PURE__ */ p("div", {
|
|
267
254
|
className: "space-y-4",
|
|
268
255
|
children: [
|
|
269
|
-
/* @__PURE__ */
|
|
256
|
+
/* @__PURE__ */ p("div", {
|
|
270
257
|
className: "flex items-center justify-between",
|
|
271
|
-
children: [/* @__PURE__ */
|
|
258
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h3", {
|
|
272
259
|
className: "text-lg font-semibold text-text-primary",
|
|
273
260
|
children: "Notes"
|
|
274
|
-
}), /* @__PURE__ */
|
|
261
|
+
}), /* @__PURE__ */ p("p", {
|
|
275
262
|
className: "text-sm text-text-secondary",
|
|
276
263
|
children: [
|
|
277
264
|
Q.total,
|
|
@@ -281,188 +268,186 @@ function h({ context: h, vibeId: g, sessionId: _, agentVibes: v = [] }) {
|
|
|
281
268
|
Q.pinned,
|
|
282
269
|
" pinned"
|
|
283
270
|
]
|
|
284
|
-
})] }), /* @__PURE__ */
|
|
271
|
+
})] }), /* @__PURE__ */ p("div", {
|
|
285
272
|
className: "flex items-center gap-2",
|
|
286
|
-
children: [/* @__PURE__ */
|
|
273
|
+
children: [/* @__PURE__ */ f("button", {
|
|
287
274
|
type: "button",
|
|
288
|
-
onClick: () =>
|
|
289
|
-
disabled:
|
|
275
|
+
onClick: () => U(),
|
|
276
|
+
disabled: V,
|
|
290
277
|
className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
|
|
291
278
|
title: "Refresh notes",
|
|
292
|
-
children: /* @__PURE__ */
|
|
293
|
-
}), /* @__PURE__ */
|
|
294
|
-
permission:
|
|
295
|
-
children: /* @__PURE__ */
|
|
279
|
+
children: /* @__PURE__ */ f(Se, { className: `size-4 ${V ? "animate-spin" : ""}` })
|
|
280
|
+
}), /* @__PURE__ */ f(ke, {
|
|
281
|
+
permission: s.NOTE_CREATE,
|
|
282
|
+
children: /* @__PURE__ */ p("button", {
|
|
296
283
|
type: "button",
|
|
297
|
-
onClick:
|
|
284
|
+
onClick: Y,
|
|
298
285
|
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
299
|
-
children: [/* @__PURE__ */
|
|
286
|
+
children: [/* @__PURE__ */ f(d, { className: "size-4" }), "New Note"]
|
|
300
287
|
})
|
|
301
288
|
})]
|
|
302
289
|
})]
|
|
303
290
|
}),
|
|
304
|
-
/* @__PURE__ */
|
|
291
|
+
/* @__PURE__ */ p("div", {
|
|
305
292
|
className: "space-y-3",
|
|
306
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ p("div", {
|
|
307
294
|
className: "flex items-center gap-2",
|
|
308
|
-
children: [/* @__PURE__ */
|
|
309
|
-
value:
|
|
295
|
+
children: [/* @__PURE__ */ f(ue, {
|
|
296
|
+
value: T,
|
|
310
297
|
onChange: ze,
|
|
311
298
|
placeholder: "Search notes...",
|
|
312
299
|
className: "flex-1 sm:w-64 sm:flex-none"
|
|
313
|
-
}), /* @__PURE__ */
|
|
300
|
+
}), /* @__PURE__ */ p("div", {
|
|
314
301
|
className: "flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0",
|
|
315
|
-
children: [/* @__PURE__ */
|
|
302
|
+
children: [/* @__PURE__ */ f("button", {
|
|
316
303
|
type: "button",
|
|
317
|
-
onClick: () =>
|
|
318
|
-
className: `p-1.5 sm:p-2 rounded ${
|
|
304
|
+
onClick: () => M("grid"),
|
|
305
|
+
className: `p-1.5 sm:p-2 rounded ${j === "grid" ? "bg-bg-surface shadow-sm" : "hover:bg-bg-surface/50"}`,
|
|
319
306
|
title: "Grid view",
|
|
320
|
-
children: /* @__PURE__ */
|
|
321
|
-
}), /* @__PURE__ */
|
|
307
|
+
children: /* @__PURE__ */ f(ve, { className: "size-4 text-text-secondary" })
|
|
308
|
+
}), /* @__PURE__ */ f("button", {
|
|
322
309
|
type: "button",
|
|
323
|
-
onClick: () =>
|
|
324
|
-
className: `p-1.5 sm:p-2 rounded ${
|
|
310
|
+
onClick: () => M("list"),
|
|
311
|
+
className: `p-1.5 sm:p-2 rounded ${j === "list" ? "bg-bg-surface shadow-sm" : "hover:bg-bg-surface/50"}`,
|
|
325
312
|
title: "List view",
|
|
326
|
-
children: /* @__PURE__ */
|
|
313
|
+
children: /* @__PURE__ */ f(ye, { className: "size-4 text-text-secondary" })
|
|
327
314
|
})]
|
|
328
315
|
})]
|
|
329
|
-
}), /* @__PURE__ */
|
|
316
|
+
}), /* @__PURE__ */ p("div", {
|
|
330
317
|
className: "flex items-center gap-2 sm:gap-3 flex-wrap",
|
|
331
318
|
children: [
|
|
332
|
-
|
|
319
|
+
m === "agent" && /* @__PURE__ */ f(o, {
|
|
333
320
|
label: "Vibe",
|
|
334
|
-
value:
|
|
335
|
-
options:
|
|
321
|
+
value: E,
|
|
322
|
+
options: Ze,
|
|
336
323
|
onChange: Be
|
|
337
324
|
}),
|
|
338
|
-
/* @__PURE__ */
|
|
325
|
+
/* @__PURE__ */ f(o, {
|
|
339
326
|
label: "Tag",
|
|
340
|
-
value:
|
|
341
|
-
options:
|
|
327
|
+
value: D,
|
|
328
|
+
options: Qe,
|
|
342
329
|
onChange: Ve
|
|
343
330
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
331
|
+
/* @__PURE__ */ p("div", {
|
|
345
332
|
className: "flex items-center gap-1 bg-bg-sunken rounded-md p-0.5",
|
|
346
333
|
children: [
|
|
347
|
-
/* @__PURE__ */
|
|
334
|
+
/* @__PURE__ */ f("button", {
|
|
348
335
|
type: "button",
|
|
349
|
-
onClick: () =>
|
|
350
|
-
className: `px-2.5 py-1 text-xs rounded transition-colors ${
|
|
336
|
+
onClick: () => k("all"),
|
|
337
|
+
className: `px-2.5 py-1 text-xs rounded transition-colors ${O === "all" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-secondary"}`,
|
|
351
338
|
children: "All"
|
|
352
339
|
}),
|
|
353
|
-
/* @__PURE__ */
|
|
340
|
+
/* @__PURE__ */ p("button", {
|
|
354
341
|
type: "button",
|
|
355
|
-
onClick: () =>
|
|
356
|
-
className: `flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${
|
|
357
|
-
children: [/* @__PURE__ */
|
|
342
|
+
onClick: () => k("WORKSPACE"),
|
|
343
|
+
className: `flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${O === "WORKSPACE" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-secondary"}`,
|
|
344
|
+
children: [/* @__PURE__ */ f(_e, { className: "size-3" }), "Shared"]
|
|
358
345
|
}),
|
|
359
|
-
/* @__PURE__ */
|
|
346
|
+
/* @__PURE__ */ p("button", {
|
|
360
347
|
type: "button",
|
|
361
|
-
onClick: () =>
|
|
362
|
-
className: `flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${
|
|
363
|
-
children: [/* @__PURE__ */
|
|
348
|
+
onClick: () => k("PRIVATE"),
|
|
349
|
+
className: `flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${O === "PRIVATE" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-secondary"}`,
|
|
350
|
+
children: [/* @__PURE__ */ f(be, { className: "size-3" }), "Private"]
|
|
364
351
|
})
|
|
365
352
|
]
|
|
366
353
|
})
|
|
367
354
|
]
|
|
368
355
|
})]
|
|
369
356
|
}),
|
|
370
|
-
/* @__PURE__ */
|
|
357
|
+
/* @__PURE__ */ f("div", {
|
|
371
358
|
className: "flex items-center gap-2",
|
|
372
|
-
children: /* @__PURE__ */
|
|
359
|
+
children: /* @__PURE__ */ p("button", {
|
|
373
360
|
type: "button",
|
|
374
|
-
onClick: () => He(!
|
|
375
|
-
className: `flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${
|
|
361
|
+
onClick: () => He(!A),
|
|
362
|
+
className: `flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${A ? "bg-status-warning-bg-subtle text-status-warning-text" : "bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80"}`,
|
|
376
363
|
children: [
|
|
377
|
-
/* @__PURE__ */
|
|
364
|
+
/* @__PURE__ */ f(xe, { className: "size-3" }),
|
|
378
365
|
"Pinned (",
|
|
379
366
|
Q.pinned,
|
|
380
367
|
")"
|
|
381
368
|
]
|
|
382
369
|
})
|
|
383
370
|
}),
|
|
384
|
-
$ ? /* @__PURE__ */
|
|
371
|
+
$ ? /* @__PURE__ */ p("p", {
|
|
385
372
|
className: "text-sm text-text-secondary",
|
|
386
373
|
children: [
|
|
387
374
|
"Showing ",
|
|
388
|
-
|
|
375
|
+
q.length,
|
|
389
376
|
" of ",
|
|
390
|
-
|
|
377
|
+
G.length,
|
|
391
378
|
" notes",
|
|
392
|
-
|
|
379
|
+
V && /* @__PURE__ */ f("span", {
|
|
393
380
|
className: "ml-2 text-text-muted",
|
|
394
381
|
children: "(refreshing…)"
|
|
395
382
|
})
|
|
396
383
|
]
|
|
397
384
|
}) : null,
|
|
398
|
-
/* @__PURE__ */
|
|
385
|
+
/* @__PURE__ */ f(Ee, {
|
|
399
386
|
mode: "wait",
|
|
400
|
-
children:
|
|
387
|
+
children: q.length > 0 ? j === "grid" ? /* @__PURE__ */ f(je, {
|
|
401
388
|
stagger: .04,
|
|
402
389
|
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
403
|
-
children:
|
|
390
|
+
children: q.map((e) => /* @__PURE__ */ f(Ae, { children: /* @__PURE__ */ f(fe, {
|
|
404
391
|
note: e,
|
|
405
|
-
onOpen:
|
|
406
|
-
onEdit:
|
|
407
|
-
onPin:
|
|
408
|
-
onUnpin:
|
|
409
|
-
onDelete:
|
|
410
|
-
|
|
411
|
-
onShareAccess: rt
|
|
392
|
+
onOpen: J,
|
|
393
|
+
onEdit: J,
|
|
394
|
+
onPin: X,
|
|
395
|
+
onUnpin: X,
|
|
396
|
+
onDelete: S ? Z : void 0,
|
|
397
|
+
onShareAccess: et
|
|
412
398
|
}) }, e.id))
|
|
413
|
-
}, "grid") : /* @__PURE__ */
|
|
414
|
-
notes:
|
|
415
|
-
onOpen:
|
|
416
|
-
onEdit:
|
|
417
|
-
onPin:
|
|
418
|
-
onUnpin:
|
|
419
|
-
onDelete:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
icon: /* @__PURE__ */ p(ge, { className: "size-8 text-text-secondary" }),
|
|
399
|
+
}, "grid") : /* @__PURE__ */ f(pe, {
|
|
400
|
+
notes: q,
|
|
401
|
+
onOpen: J,
|
|
402
|
+
onEdit: J,
|
|
403
|
+
onPin: X,
|
|
404
|
+
onUnpin: X,
|
|
405
|
+
onDelete: S ? Z : void 0,
|
|
406
|
+
onShareAccess: et
|
|
407
|
+
}, "list") : /* @__PURE__ */ f(le, {
|
|
408
|
+
icon: /* @__PURE__ */ f(ge, { className: "size-8 text-text-secondary" }),
|
|
424
409
|
title: $ ? "No notes found" : "No notes yet",
|
|
425
410
|
description: $ ? "Try adjusting your search or filters" : "Create your first note to start documenting your work",
|
|
426
|
-
action: !$ && Fe ? /* @__PURE__ */
|
|
411
|
+
action: !$ && Fe ? /* @__PURE__ */ p("button", {
|
|
427
412
|
type: "button",
|
|
428
|
-
onClick:
|
|
413
|
+
onClick: Y,
|
|
429
414
|
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
430
|
-
children: [/* @__PURE__ */
|
|
415
|
+
children: [/* @__PURE__ */ f(d, { className: "size-4" }), "New Note"]
|
|
431
416
|
}) : void 0
|
|
432
417
|
})
|
|
433
418
|
}),
|
|
434
|
-
Ue && /* @__PURE__ */
|
|
419
|
+
Ue && /* @__PURE__ */ f(me, {
|
|
435
420
|
note: We || void 0,
|
|
436
|
-
vibeOptions:
|
|
437
|
-
defaultVibeId:
|
|
438
|
-
hideVibeSelect:
|
|
439
|
-
onSubmit:
|
|
440
|
-
onCancel: () =>
|
|
421
|
+
vibeOptions: tt,
|
|
422
|
+
defaultVibeId: m === "vibe" ? h : void 0,
|
|
423
|
+
hideVibeSelect: m === "vibe",
|
|
424
|
+
onSubmit: $e,
|
|
425
|
+
onCancel: () => N(!1)
|
|
441
426
|
}),
|
|
442
|
-
/* @__PURE__ */
|
|
427
|
+
/* @__PURE__ */ f(ie, {
|
|
443
428
|
isOpen: Ge,
|
|
444
|
-
onClose: () =>
|
|
429
|
+
onClose: () => F(!1),
|
|
445
430
|
onConfirm: () => {
|
|
446
|
-
|
|
431
|
+
F(!1), Ne("/billing/plans");
|
|
447
432
|
},
|
|
448
433
|
title: "Note Quota Reached",
|
|
449
434
|
message: "You've used all available note quota on your current plan. Upgrade your plan to create or update more notes."
|
|
450
435
|
}),
|
|
451
|
-
|
|
436
|
+
C && /* @__PURE__ */ f(de, {
|
|
452
437
|
note: {
|
|
453
|
-
id:
|
|
454
|
-
title:
|
|
455
|
-
vibeId:
|
|
438
|
+
id: C.id,
|
|
439
|
+
title: C.title,
|
|
440
|
+
vibeId: C.vibeId
|
|
456
441
|
},
|
|
457
|
-
open: !!
|
|
442
|
+
open: !!C,
|
|
458
443
|
onOpenChange: (e) => {
|
|
459
|
-
e ||
|
|
444
|
+
e || w(null);
|
|
460
445
|
}
|
|
461
446
|
})
|
|
462
447
|
]
|
|
463
448
|
});
|
|
464
449
|
}
|
|
465
450
|
//#endregion
|
|
466
|
-
export {
|
|
451
|
+
export { m as NotesTabPanel };
|
|
467
452
|
|
|
468
453
|
//# sourceMappingURL=NotesTabPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotesTabPanel.js","names":[],"sources":["../../../src/components/notes/NotesTabPanel.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useState, useMemo } from 'react';\nimport { Plus, FileText, Grid, List, Pin, RefreshCw, Lock, Globe } from 'lucide-react';\nimport { AnimatePresence } from 'framer-motion';\nimport { useNavigate } from 'react-router';\nimport { useApolloClient } from '@apollo/client/react';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { NoteCard } from './NoteCard';\nimport { NoteList } from './NoteList';\nimport { NoteEditor } from './NoteEditor';\nimport type { Note, NoteVisibility } from './types';\nimport type { NoteEditorSubmitData } from './NoteEditor';\nimport {\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport {\n useListNotesQuery,\n usePinNoteMutation,\n useUnpinNoteMutation,\n useDeleteNoteMutation,\n useCreateNoteMutation,\n useUpdateNoteMutation,\n useReplaceNoteTagsMutation,\n useAvailableNoteTagsQuery,\n useShareNoteMutation,\n ListNotesDocument,\n} from '@/generated/wspace-operations';\nimport { NoteVisibility as GqlNoteVisibility } from '@/generated/wspace-types';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { NoteShareDialog, SHARE_REGISTRY } from '@/components/sharing';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface NotesTabPanelProps {\n /**\n * 'vibe' = single vibe context; 'agent' = multi-vibe agent context;\n * 'session' = notes authored against a single terminal session (F1).\n */\n context: 'vibe' | 'agent' | 'session';\n /** For vibe context: the single vibeId to scope notes to */\n vibeId?: string;\n /** For session context: the single sessionId to scope notes to */\n sessionId?: string;\n /** For agent context: the vibes belonging to the agent */\n agentVibes?: { id: string; name: string }[];\n}\n\n/**\n * Reusable notes panel for embedding as a tab in detail pages.\n * Supports vibe-scoped, agent-scoped, and session-scoped contexts.\n */\nexport function NotesTabPanel({ context, vibeId, sessionId, agentVibes = [] }: NotesTabPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const apolloClient = useApolloClient();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const canListNotes = hasPermission(VIBE_PERMISSIONS.NOTE_LIST);\n const canCreateNote = hasPermission(VIBE_PERMISSIONS.NOTE_CREATE);\n const canDeleteNote = hasPermission(VIBE_PERMISSIONS.NOTE_DELETE);\n\n // Generic note sharing (full users/link/vibe dialog). Gated by the\n // `vibecontrols.sharing.note` flag (default ON) + note share permission.\n // Distinct from the legacy `onShare` visibility toggle, which stays as-is.\n const noteSharingFlagEnabled = useFeatureFlag(\n SHARE_REGISTRY.note.featureFlag,\n SHARE_REGISTRY.note.defaultEnabled\n );\n const canShareNote = hasPermission(SHARE_REGISTRY.note.permission);\n const noteSharingEnabled = noteSharingFlagEnabled && canShareNote;\n const [genericShareNote, setGenericShareNote] = useState<Note | null>(null);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [tagFilter, setTagFilter] = useState('all');\n const [visibilityFilter, setVisibilityFilter] = useState<'all' | 'PRIVATE' | 'WORKSPACE'>('all');\n const [showPinnedOnly, setShowPinnedOnly] = useState(false);\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showEditor, setShowEditor] = useState(false);\n const [selectedNote, setSelectedNote] = useState<Note | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Evict cached notes list and refetch — bypasses stale cache-and-network data\n const evictAndRefetch = (refetch: () => void) => {\n apolloClient.cache.evict({ id: 'ROOT_QUERY', fieldName: 'notes' });\n apolloClient.cache.gc();\n refetch();\n };\n\n const [pinNote] = usePinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [unpinNote] = useUnpinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [deleteNote] = useDeleteNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [createNote] = useCreateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [updateNote] = useUpdateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [replaceNoteTags] = useReplaceNoteTagsMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [shareNote] = useShareNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n\n const shouldSkipQueries = permissionsLoading || !canListNotes;\n\n const { data: availableTagsData } = useAvailableNoteTagsQuery({ skip: shouldSkipQueries });\n\n // Build query filter based on context\n const queryFilter = useMemo(() => {\n const visibilityVal =\n visibilityFilter !== 'all' ? (visibilityFilter as GqlNoteVisibility) : undefined;\n\n if (context === 'session' && sessionId) {\n return {\n sessionId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n if (context === 'vibe' && vibeId) {\n return {\n vibeId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n // Agent context: fetch all notes, filter client-side\n return {\n vibeId: vibeFilter === 'none' ? null : vibeFilter !== 'all' ? vibeFilter : undefined,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }, [context, vibeId, sessionId, vibeFilter, showPinnedOnly, visibilityFilter]);\n\n const { data, loading, error, refetch } = useListNotesQuery({\n variables: { filter: queryFilter, pagination: { limit: 50 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n\n // Vibe filter options (agent context only)\n const vibeOptions = useMemo(() => {\n if (context !== 'agent') return [];\n return [\n { value: 'all', label: 'All Vibes' },\n { value: 'none', label: 'No Vibe (General)' },\n ...agentVibes.map((v) => ({ value: v.id, label: v.name })),\n ];\n }, [context, agentVibes]);\n\n // Tag label lookup by ID — used when federation doesn't return nested tag object\n type AvailableTag = { id: string; key: string; label: string; color?: string | null };\n const tagLabelMap = useMemo((): Map<string, AvailableTag> => {\n const tags = (availableTagsData?.availableNoteTags ?? []) as AvailableTag[];\n return new Map(tags.map((tag) => [tag.id, tag]));\n }, [availableTagsData?.availableNoteTags]);\n\n // Map GraphQL response to Note type\n const notes: Note[] = useMemo(() => {\n const rawNotes = data?.notes?.items ?? [];\n const mapped = rawNotes.map((note: NonNullable<typeof rawNotes>[number]) => ({\n id: note.id,\n workspaceId: note.workspaceId,\n vibeId: note.vibeId || null,\n title: note.title,\n content: note.content,\n tags: note.tags || [],\n tagAssignments:\n note.tagAssignments?.map((a: NonNullable<typeof note.tagAssignments>[number]) => {\n // `a.tag` may be null when federation doesn't resolve the tag entity.\n // Fall back to the availableNoteTags lookup so the label always shows.\n const resolvedTag = a.tag\n ? { id: a.tag.id, label: a.tag.label, key: a.tag.key, color: a.tag.color }\n : tagLabelMap.has(a.tagId)\n ? {\n id: a.tagId,\n label: tagLabelMap.get(a.tagId)!.label,\n key: tagLabelMap.get(a.tagId)!.key,\n color: tagLabelMap.get(a.tagId)!.color ?? null,\n }\n : undefined;\n return {\n id: a.id,\n tagId: a.tagId,\n tag: resolvedTag,\n entityType: a.entityType,\n entityId: a.entityId,\n source: a.source,\n assignedBy: a.assignedBy,\n assignedAt: a.assignedAt,\n };\n }) || [],\n isPinned: note.isPinned,\n visibility: (note.visibility ?? 'WORKSPACE') as NoteVisibility,\n createdBy: note.createdBy ?? '',\n metadata: (note.metadata as Record<string, unknown>) || {},\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n vibe: note.vibe ? { id: note.vibe.id, name: note.vibe.name } : undefined,\n }));\n\n // Agent context: client-side filter to only agent's vibes\n if (context === 'agent' && agentVibes.length > 0) {\n const vibeIdSet = new Set(agentVibes.map((v) => v.id));\n return mapped.filter((n: Note) => n.vibeId && vibeIdSet.has(n.vibeId));\n }\n\n return mapped;\n }, [data?.notes?.items, context, agentVibes, tagLabelMap]);\n\n // Tag filter options\n const tagOptions = useMemo(() => {\n const tags = availableTagsData?.availableNoteTags ?? [];\n return [\n { value: 'all', label: 'All Tags' },\n ...tags.map((tag: AvailableTag) => ({\n value: tag.id,\n label: tag.label,\n })),\n ];\n }, [availableTagsData?.availableNoteTags]);\n\n // Client-side search and tag filtering\n const filteredNotes = useMemo(() => {\n return notes.filter((note) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n note.title.toLowerCase().includes(query) ||\n note.content.toLowerCase().includes(query) ||\n note.tagAssignments?.some((a) => a.tag?.label?.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n if (tagFilter !== 'all') {\n const hasTag = note.tagAssignments?.some((a) => a.tagId === tagFilter);\n if (!hasTag) return false;\n }\n return true;\n });\n }, [notes, searchQuery, tagFilter]);\n\n // Sort: pinned first, then by updatedAt\n const sortedNotes = useMemo(() => {\n return [...filteredNotes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n }, [filteredNotes]);\n\n const handleNoteSelect = (note: Note) => {\n setSelectedNote(note);\n setShowEditor(true);\n };\n\n const handleCreateNote = () => {\n setSelectedNote(null);\n setShowEditor(true);\n };\n\n const handlePinNote = async (note: Note) => {\n try {\n if (note.isPinned) {\n await unpinNote({ variables: { id: note.id } });\n } else {\n await pinNote({ variables: { id: note.id } });\n }\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle pin for note:', err);\n }\n };\n\n const handleDeleteNote = async (note: Note) => {\n try {\n await deleteNote({ variables: { id: note.id } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to delete note:', err);\n }\n };\n\n const handleSaveNote = async (noteData: NoteEditorSubmitData) => {\n const closeEditorForQuota = () => {\n setShowEditor(false);\n setSelectedNote(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n };\n\n try {\n let noteId: string | undefined;\n\n // In vibe context, always force the vibeId\n const effectiveVibeId = context === 'vibe' ? vibeId : noteData.vibeId;\n\n if (noteData.id) {\n let result;\n try {\n result = await updateNote({\n variables: {\n id: noteData.id,\n input: {\n title: noteData.title,\n content: noteData.content,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : undefined,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.updateNote?.id;\n } else {\n let result;\n try {\n result = await createNote({\n variables: {\n input: {\n title: noteData.title || '',\n content: noteData.content || '',\n vibeId: effectiveVibeId,\n // F1 — stamp the session so the note shows in this session's\n // drawer; it stays workspace-reusable everywhere else.\n sessionId: context === 'session' ? sessionId : undefined,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : GqlNoteVisibility.Workspace,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.createNote?.id;\n }\n\n // Assign tags\n if (noteId && noteData.selectedTagIds && noteData.selectedTagIds.length > 0) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: noteData.selectedTagIds } },\n });\n } else if (noteId && noteData.selectedTagIds?.length === 0 && noteData.id) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: [] } },\n });\n }\n\n setShowEditor(false);\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to save note:', err);\n }\n };\n\n const handleShareNote = async (note: Note) => {\n const newVisibility =\n note.visibility === 'PRIVATE' ? GqlNoteVisibility.Workspace : GqlNoteVisibility.Private;\n try {\n await shareNote({ variables: { id: note.id, visibility: newVisibility } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle note visibility:', err);\n }\n };\n\n // Opens the generic Share dialog for a note (users / link / vibe).\n const handleShareNoteAccess = noteSharingEnabled\n ? (note: Note) => setGenericShareNote(note)\n : undefined;\n\n // Stats\n const stats = {\n total: notes.length,\n pinned: notes.filter((n) => n.isPinned).length,\n };\n\n // Vibe options for editor\n const vibeOptionsForEditor = useMemo(() => {\n if (context === 'vibe') return [];\n return agentVibes;\n }, [context, agentVibes]);\n\n // Permission guard\n if (!permissionsLoading && permissions && !canListNotes) {\n return (\n <AccessDenied\n title=\"Access Denied\"\n description=\"You don't have permission to view notes.\"\n details=\"Required permission: note:list\"\n />\n );\n }\n\n if (loading && notes.length === 0) {\n return <LoadingState message=\"Loading notes...\" />;\n }\n\n if (error && notes.length === 0) {\n return (\n <ErrorState title=\"Failed to load notes\" message={error.message} onRetry={() => refetch()} />\n );\n }\n\n const isFiltered =\n searchQuery ||\n (context === 'agent' && vibeFilter !== 'all') ||\n tagFilter !== 'all' ||\n showPinnedOnly;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Notes</h3>\n <p className=\"text-sm text-text-secondary\">\n {stats.total} {stats.total === 1 ? 'note' : 'notes'}, {stats.pinned} pinned\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title=\"Refresh notes\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton permission={VIBE_PERMISSIONS.NOTE_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder=\"Search notes...\"\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"Grid view\"\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"List view\"\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n {context === 'agent' && (\n <FilterDropdown\n label=\"Vibe\"\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n )}\n <FilterDropdown\n label=\"Tag\"\n value={tagFilter}\n options={tagOptions}\n onChange={setTagFilter}\n />\n {/* Visibility filter */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('all')}\n className={`px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'all' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('WORKSPACE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'WORKSPACE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Globe className=\"size-3\" />\n Shared\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('PRIVATE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'PRIVATE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Lock className=\"size-3\" />\n Private\n </button>\n </div>\n </div>\n </div>\n\n {/* Pinned toggle */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowPinnedOnly(!showPinnedOnly)}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n showPinnedOnly\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Pin className=\"size-3\" />\n Pinned ({stats.pinned})\n </button>\n </div>\n\n {/* Results count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\">\n Showing {sortedNotes.length} of {notes.length} notes\n {loading && <span className=\"ml-2 text-text-muted\">(refreshing…)</span>}\n </p>\n ) : null}\n\n {/* Notes display */}\n <AnimatePresence mode=\"wait\">\n {sortedNotes.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {sortedNotes.map((note) => (\n <MotionItem key={note.id}>\n <NoteCard\n note={note}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={canDeleteNote ? handleDeleteNote : undefined}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <NoteList\n key=\"list\"\n notes={sortedNotes}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={canDeleteNote ? handleDeleteNote : undefined}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n )\n ) : (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={isFiltered ? 'No notes found' : 'No notes yet'}\n description={\n isFiltered\n ? 'Try adjusting your search or filters'\n : 'Create your first note to start documenting your work'\n }\n action={\n !isFiltered && canCreateNote ? (\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n ) : undefined\n }\n />\n )}\n </AnimatePresence>\n\n {/* Note Editor Modal */}\n {showEditor && (\n <NoteEditor\n note={selectedNote || undefined}\n vibeOptions={vibeOptionsForEditor}\n defaultVibeId={context === 'vibe' ? vibeId : undefined}\n hideVibeSelect={context === 'vibe'}\n onSubmit={handleSaveNote}\n onCancel={() => setShowEditor(false)}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Note Quota Reached\"\n message=\"You've used all available note quota on your current plan. Upgrade your plan to create or update more notes.\"\n />\n\n {/* Generic note sharing dialog (users / link / vibe) */}\n {genericShareNote && (\n <NoteShareDialog\n note={{\n id: genericShareNote.id,\n title: genericShareNote.title,\n vibeId: genericShareNote.vibeId,\n }}\n open={Boolean(genericShareNote)}\n onOpenChange={(next) => {\n if (!next) setGenericShareNote(null);\n }}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NotesTabPanel.js","names":[],"sources":["../../../src/components/notes/NotesTabPanel.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useState, useMemo } from 'react';\nimport { Plus, FileText, Grid, List, Pin, RefreshCw, Lock, Globe } from 'lucide-react';\nimport { AnimatePresence } from 'framer-motion';\nimport { useNavigate } from 'react-router';\nimport { useApolloClient } from '@apollo/client/react';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { NoteCard } from './NoteCard';\nimport { NoteList } from './NoteList';\nimport { NoteEditor } from './NoteEditor';\nimport type { Note, NoteVisibility } from './types';\nimport type { NoteEditorSubmitData } from './NoteEditor';\nimport {\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport {\n useListNotesQuery,\n usePinNoteMutation,\n useUnpinNoteMutation,\n useDeleteNoteMutation,\n useCreateNoteMutation,\n useUpdateNoteMutation,\n useReplaceNoteTagsMutation,\n useAvailableNoteTagsQuery,\n ListNotesDocument,\n} from '@/generated/wspace-operations';\nimport { NoteVisibility as GqlNoteVisibility } from '@/generated/wspace-types';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { NoteShareDialog, SHARE_REGISTRY } from '@/components/sharing';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface NotesTabPanelProps {\n /**\n * 'vibe' = single vibe context; 'agent' = multi-vibe agent context;\n * 'session' = notes authored against a single terminal session (F1).\n */\n context: 'vibe' | 'agent' | 'session';\n /** For vibe context: the single vibeId to scope notes to */\n vibeId?: string;\n /** For session context: the single sessionId to scope notes to */\n sessionId?: string;\n /** For agent context: the vibes belonging to the agent */\n agentVibes?: { id: string; name: string }[];\n}\n\n/**\n * Reusable notes panel for embedding as a tab in detail pages.\n * Supports vibe-scoped, agent-scoped, and session-scoped contexts.\n */\nexport function NotesTabPanel({ context, vibeId, sessionId, agentVibes = [] }: NotesTabPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const apolloClient = useApolloClient();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const canListNotes = hasPermission(VIBE_PERMISSIONS.NOTE_LIST);\n const canCreateNote = hasPermission(VIBE_PERMISSIONS.NOTE_CREATE);\n const canDeleteNote = hasPermission(VIBE_PERMISSIONS.NOTE_DELETE);\n\n // Generic note sharing (full users/link/vibe dialog). Gated by the\n // `vibecontrols.sharing.note` flag (default ON) + note share permission.\n // This is the only sharing entry point on the notes surface as of\n // BOFF-3036 — the legacy PRIVATE↔WORKSPACE toggle was removed (see\n // `vibecontrols-specs/business/production/visibility-feature-audit-boff-3036.md`).\n const noteSharingFlagEnabled = useFeatureFlag(\n SHARE_REGISTRY.note.featureFlag,\n SHARE_REGISTRY.note.defaultEnabled\n );\n const canShareNote = hasPermission(SHARE_REGISTRY.note.permission);\n const noteSharingEnabled = noteSharingFlagEnabled && canShareNote;\n const [genericShareNote, setGenericShareNote] = useState<Note | null>(null);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [tagFilter, setTagFilter] = useState('all');\n const [visibilityFilter, setVisibilityFilter] = useState<'all' | 'PRIVATE' | 'WORKSPACE'>('all');\n const [showPinnedOnly, setShowPinnedOnly] = useState(false);\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showEditor, setShowEditor] = useState(false);\n const [selectedNote, setSelectedNote] = useState<Note | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Evict cached notes list and refetch — bypasses stale cache-and-network data\n const evictAndRefetch = (refetch: () => void) => {\n apolloClient.cache.evict({ id: 'ROOT_QUERY', fieldName: 'notes' });\n apolloClient.cache.gc();\n refetch();\n };\n\n const [pinNote] = usePinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [unpinNote] = useUnpinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [deleteNote] = useDeleteNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [createNote] = useCreateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [updateNote] = useUpdateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [replaceNoteTags] = useReplaceNoteTagsMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n\n const shouldSkipQueries = permissionsLoading || !canListNotes;\n\n const { data: availableTagsData } = useAvailableNoteTagsQuery({ skip: shouldSkipQueries });\n\n // Build query filter based on context\n const queryFilter = useMemo(() => {\n const visibilityVal =\n visibilityFilter !== 'all' ? (visibilityFilter as GqlNoteVisibility) : undefined;\n\n if (context === 'session' && sessionId) {\n return {\n sessionId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n if (context === 'vibe' && vibeId) {\n return {\n vibeId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n // Agent context: fetch all notes, filter client-side\n return {\n vibeId: vibeFilter === 'none' ? null : vibeFilter !== 'all' ? vibeFilter : undefined,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }, [context, vibeId, sessionId, vibeFilter, showPinnedOnly, visibilityFilter]);\n\n const { data, loading, error, refetch } = useListNotesQuery({\n variables: { filter: queryFilter, pagination: { limit: 50 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n\n // Vibe filter options (agent context only)\n const vibeOptions = useMemo(() => {\n if (context !== 'agent') return [];\n return [\n { value: 'all', label: 'All Vibes' },\n { value: 'none', label: 'No Vibe (General)' },\n ...agentVibes.map((v) => ({ value: v.id, label: v.name })),\n ];\n }, [context, agentVibes]);\n\n // Tag label lookup by ID — used when federation doesn't return nested tag object\n type AvailableTag = { id: string; key: string; label: string; color?: string | null };\n const tagLabelMap = useMemo((): Map<string, AvailableTag> => {\n const tags = (availableTagsData?.availableNoteTags ?? []) as AvailableTag[];\n return new Map(tags.map((tag) => [tag.id, tag]));\n }, [availableTagsData?.availableNoteTags]);\n\n // Map GraphQL response to Note type\n const notes: Note[] = useMemo(() => {\n const rawNotes = data?.notes?.items ?? [];\n const mapped = rawNotes.map((note: NonNullable<typeof rawNotes>[number]) => ({\n id: note.id,\n workspaceId: note.workspaceId,\n vibeId: note.vibeId || null,\n title: note.title,\n content: note.content,\n tags: note.tags || [],\n tagAssignments:\n note.tagAssignments?.map((a: NonNullable<typeof note.tagAssignments>[number]) => {\n // `a.tag` may be null when federation doesn't resolve the tag entity.\n // Fall back to the availableNoteTags lookup so the label always shows.\n const resolvedTag = a.tag\n ? { id: a.tag.id, label: a.tag.label, key: a.tag.key, color: a.tag.color }\n : tagLabelMap.has(a.tagId)\n ? {\n id: a.tagId,\n label: tagLabelMap.get(a.tagId)!.label,\n key: tagLabelMap.get(a.tagId)!.key,\n color: tagLabelMap.get(a.tagId)!.color ?? null,\n }\n : undefined;\n return {\n id: a.id,\n tagId: a.tagId,\n tag: resolvedTag,\n entityType: a.entityType,\n entityId: a.entityId,\n source: a.source,\n assignedBy: a.assignedBy,\n assignedAt: a.assignedAt,\n };\n }) || [],\n isPinned: note.isPinned,\n visibility: (note.visibility ?? 'WORKSPACE') as NoteVisibility,\n createdBy: note.createdBy ?? '',\n metadata: (note.metadata as Record<string, unknown>) || {},\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n vibe: note.vibe ? { id: note.vibe.id, name: note.vibe.name } : undefined,\n }));\n\n // Agent context: client-side filter to only agent's vibes\n if (context === 'agent' && agentVibes.length > 0) {\n const vibeIdSet = new Set(agentVibes.map((v) => v.id));\n return mapped.filter((n: Note) => n.vibeId && vibeIdSet.has(n.vibeId));\n }\n\n return mapped;\n }, [data?.notes?.items, context, agentVibes, tagLabelMap]);\n\n // Tag filter options\n const tagOptions = useMemo(() => {\n const tags = availableTagsData?.availableNoteTags ?? [];\n return [\n { value: 'all', label: 'All Tags' },\n ...tags.map((tag: AvailableTag) => ({\n value: tag.id,\n label: tag.label,\n })),\n ];\n }, [availableTagsData?.availableNoteTags]);\n\n // Client-side search and tag filtering\n const filteredNotes = useMemo(() => {\n return notes.filter((note) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n note.title.toLowerCase().includes(query) ||\n note.content.toLowerCase().includes(query) ||\n note.tagAssignments?.some((a) => a.tag?.label?.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n if (tagFilter !== 'all') {\n const hasTag = note.tagAssignments?.some((a) => a.tagId === tagFilter);\n if (!hasTag) return false;\n }\n return true;\n });\n }, [notes, searchQuery, tagFilter]);\n\n // Sort: pinned first, then by updatedAt\n const sortedNotes = useMemo(() => {\n return [...filteredNotes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n }, [filteredNotes]);\n\n const handleNoteSelect = (note: Note) => {\n setSelectedNote(note);\n setShowEditor(true);\n };\n\n const handleCreateNote = () => {\n setSelectedNote(null);\n setShowEditor(true);\n };\n\n const handlePinNote = async (note: Note) => {\n try {\n if (note.isPinned) {\n await unpinNote({ variables: { id: note.id } });\n } else {\n await pinNote({ variables: { id: note.id } });\n }\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle pin for note:', err);\n }\n };\n\n const handleDeleteNote = async (note: Note) => {\n try {\n await deleteNote({ variables: { id: note.id } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to delete note:', err);\n }\n };\n\n const handleSaveNote = async (noteData: NoteEditorSubmitData) => {\n const closeEditorForQuota = () => {\n setShowEditor(false);\n setSelectedNote(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n };\n\n try {\n let noteId: string | undefined;\n\n // In vibe context, always force the vibeId\n const effectiveVibeId = context === 'vibe' ? vibeId : noteData.vibeId;\n\n if (noteData.id) {\n let result;\n try {\n result = await updateNote({\n variables: {\n id: noteData.id,\n input: {\n title: noteData.title,\n content: noteData.content,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : undefined,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.updateNote?.id;\n } else {\n let result;\n try {\n result = await createNote({\n variables: {\n input: {\n title: noteData.title || '',\n content: noteData.content || '',\n vibeId: effectiveVibeId,\n // F1 — stamp the session so the note shows in this session's\n // drawer; it stays workspace-reusable everywhere else.\n sessionId: context === 'session' ? sessionId : undefined,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : GqlNoteVisibility.Workspace,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.createNote?.id;\n }\n\n // Assign tags\n if (noteId && noteData.selectedTagIds && noteData.selectedTagIds.length > 0) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: noteData.selectedTagIds } },\n });\n } else if (noteId && noteData.selectedTagIds?.length === 0 && noteData.id) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: [] } },\n });\n }\n\n setShowEditor(false);\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to save note:', err);\n }\n };\n\n // Opens the generic Share dialog for a note (users / link / vibe).\n // Note visibility is now exclusively modelled via the generic Share dialog;\n // the legacy PRIVATE↔WORKSPACE toggle (`onShare` / `handleShareNote`) was\n // removed in BOFF-3036 — see\n // `vibecontrols-specs/business/production/visibility-feature-audit-boff-3036.md`.\n const handleShareNoteAccess = noteSharingEnabled\n ? (note: Note) => setGenericShareNote(note)\n : undefined;\n\n // Stats\n const stats = {\n total: notes.length,\n pinned: notes.filter((n) => n.isPinned).length,\n };\n\n // Vibe options for editor\n const vibeOptionsForEditor = useMemo(() => {\n if (context === 'vibe') return [];\n return agentVibes;\n }, [context, agentVibes]);\n\n // Permission guard\n if (!permissionsLoading && permissions && !canListNotes) {\n return (\n <AccessDenied\n title=\"Access Denied\"\n description=\"You don't have permission to view notes.\"\n details=\"Required permission: note:list\"\n />\n );\n }\n\n if (loading && notes.length === 0) {\n return <LoadingState message=\"Loading notes...\" />;\n }\n\n if (error && notes.length === 0) {\n return (\n <ErrorState title=\"Failed to load notes\" message={error.message} onRetry={() => refetch()} />\n );\n }\n\n const isFiltered =\n searchQuery ||\n (context === 'agent' && vibeFilter !== 'all') ||\n tagFilter !== 'all' ||\n showPinnedOnly;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Notes</h3>\n <p className=\"text-sm text-text-secondary\">\n {stats.total} {stats.total === 1 ? 'note' : 'notes'}, {stats.pinned} pinned\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title=\"Refresh notes\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton permission={VIBE_PERMISSIONS.NOTE_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder=\"Search notes...\"\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"Grid view\"\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"List view\"\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n {context === 'agent' && (\n <FilterDropdown\n label=\"Vibe\"\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n )}\n <FilterDropdown\n label=\"Tag\"\n value={tagFilter}\n options={tagOptions}\n onChange={setTagFilter}\n />\n {/* Visibility filter */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('all')}\n className={`px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'all' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('WORKSPACE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'WORKSPACE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Globe className=\"size-3\" />\n Shared\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('PRIVATE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'PRIVATE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Lock className=\"size-3\" />\n Private\n </button>\n </div>\n </div>\n </div>\n\n {/* Pinned toggle */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowPinnedOnly(!showPinnedOnly)}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n showPinnedOnly\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Pin className=\"size-3\" />\n Pinned ({stats.pinned})\n </button>\n </div>\n\n {/* Results count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\">\n Showing {sortedNotes.length} of {notes.length} notes\n {loading && <span className=\"ml-2 text-text-muted\">(refreshing…)</span>}\n </p>\n ) : null}\n\n {/* Notes display */}\n <AnimatePresence mode=\"wait\">\n {sortedNotes.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {sortedNotes.map((note) => (\n <MotionItem key={note.id}>\n <NoteCard\n note={note}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={canDeleteNote ? handleDeleteNote : undefined}\n onShareAccess={handleShareNoteAccess}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <NoteList\n key=\"list\"\n notes={sortedNotes}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={canDeleteNote ? handleDeleteNote : undefined}\n onShareAccess={handleShareNoteAccess}\n />\n )\n ) : (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={isFiltered ? 'No notes found' : 'No notes yet'}\n description={\n isFiltered\n ? 'Try adjusting your search or filters'\n : 'Create your first note to start documenting your work'\n }\n action={\n !isFiltered && canCreateNote ? (\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n ) : undefined\n }\n />\n )}\n </AnimatePresence>\n\n {/* Note Editor Modal */}\n {showEditor && (\n <NoteEditor\n note={selectedNote || undefined}\n vibeOptions={vibeOptionsForEditor}\n defaultVibeId={context === 'vibe' ? vibeId : undefined}\n hideVibeSelect={context === 'vibe'}\n onSubmit={handleSaveNote}\n onCancel={() => setShowEditor(false)}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Note Quota Reached\"\n message=\"You've used all available note quota on your current plan. Upgrade your plan to create or update more notes.\"\n />\n\n {/* Generic note sharing dialog (users / link / vibe) */}\n {genericShareNote && (\n <NoteShareDialog\n note={{\n id: genericShareNote.id,\n title: genericShareNote.title,\n vibeId: genericShareNote.vibeId,\n }}\n open={Boolean(genericShareNote)}\n onOpenChange={(next) => {\n if (!next) setGenericShareNote(null);\n }}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-vibecontrols",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.805.1",
|
|
4
4
|
"description": "VibeControls microfrontend for Burdenoff products",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"framer-motion": "^12.0.0"
|
|
126
126
|
},
|
|
127
127
|
"publishConfig": {
|
|
128
|
-
"access": "
|
|
128
|
+
"access": "public"
|
|
129
129
|
},
|
|
130
130
|
"overrides": {
|
|
131
131
|
"lodash": "4.18.1",
|