diff --git a/src/core/components/SearchButton.tsx b/src/core/components/SearchButton.tsx index 33d235e8..9857a97f 100644 --- a/src/core/components/SearchButton.tsx +++ b/src/core/components/SearchButton.tsx @@ -4,9 +4,10 @@ import { MagnifyingGlassIcon } from "@heroicons/react/24/outline" type Props = { onChange?: (searchTerm: string) => void debounceTime?: number + className?: string } -const SearchButton = ({ onChange, debounceTime = 500 }: Props) => { +const SearchButton = ({ onChange, debounceTime = 500, className }: Props) => { const currentSearchTerm = "" const handleSearch = (value) => { if (onChange != undefined) { @@ -15,7 +16,11 @@ const SearchButton = ({ onChange, debounceTime = 500 }: Props) => { } return (
-
+
{ const [includeArchived, setIncludeArchived] = useState(false) + const [sortBy, setSortBy] = useState("updatedAt") + const [searchQuery, setSearchQuery] = useState("") const [notes, { refetch, setQueryData }] = useQuery( listNotes, { projectId, includeArchived }, @@ -18,36 +33,95 @@ export const NotesPanel = ({ projectId }: { projectId: number }) => { const [creating, setCreating] = useState(false) const [editingId, setEditingId] = useState(null) - const sortedNotes = notes - ? [...notes].sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) - : [] + const filteredAndSortedNotes = useMemo(() => { + if (!notes) return [] + let result = [...notes] + + if (searchQuery.trim()) { + const q = searchQuery.toLowerCase() + result = result.filter((n) => (n.title ?? "Untitled").toLowerCase().includes(q)) + } + + result.sort((a, b) => { + if (a.pinned !== b.pinned) return a.pinned ? -1 : 1 + if (sortBy === "title") { + if (!a.title && b.title) return 1 + if (a.title && !b.title) return -1 + return (a.title ?? "").toLowerCase().localeCompare((b.title ?? "").toLowerCase()) + } + if (sortBy === "createdAt") { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + } + return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime() + }) + + return result + }, [notes, searchQuery, sortBy]) const canEditRow = (n: any) => { if (!n) return false - const ownerEditable = !!n.editable - const pmOverride = !!n.canSetContributors && n.visibility === "CONTRIBUTORS" - return ownerEditable || pmOverride + return !!n.editable || (!!n.canSetContributors && n.visibility === "CONTRIBUTORS") + } + + const formatNoteMd = (n: any) => { + const title = n.title || "Untitled" + const created = new Date(n.createdAt).toLocaleString() + const edited = new Date(n.updatedAt).toLocaleString() + const meta = `_Created: ${created} · Last edited: ${edited}_` + const body = n.contentMarkdown ?? "(No content)" + return `# ${title}\n\n${meta}\n\n${body}` + } + + const handleDownloadNote = (n: any) => { + const title = n.title || "Untitled" + const safeTitle = title.replace(/[^a-zA-Z0-9_\- ]/g, "").trim() || "note" + downloadMarkdown(`${safeTitle}.md`, formatNoteMd(n)) + } + + const handleDownloadAll = () => { + if (!filteredAndSortedNotes.length) return + const combined = filteredAndSortedNotes.map(formatNoteMd).join("\n\n---\n\n") + downloadMarkdown("notes.md", combined) } return (
{/* Index toolbar — hidden while editing/creating */} {!(creating || editingId !== null) && ( -
-
- - +
+
+ setSearchQuery(String(val))} className="max-w-none" />
+ + + {filteredAndSortedNotes.length > 0 && ( + + )} +
)} @@ -57,7 +131,6 @@ export const NotesPanel = ({ projectId }: { projectId: number }) => { projectId={projectId} className="shadow" onCreated={async (id) => { - // First create → switch to edit mode so the editor stays open setCreating(false) setEditingId(id) await refetch() @@ -97,7 +170,7 @@ export const NotesPanel = ({ projectId }: { projectId: number }) => { {/* Index list — only when not editing/creating */} {!(creating || editingId !== null) && (
    - {sortedNotes.map((n) => ( + {filteredAndSortedNotes.map((n) => (
  • @@ -145,7 +218,7 @@ export const NotesPanel = ({ projectId }: { projectId: number }) => { {!n.archived && ( )} + @@ -198,13 +273,19 @@ export const NotesPanel = ({ projectId }: { projectId: number }) => {
  • ))} - {!notes?.length && !creating && ( + {!filteredAndSortedNotes.length && !creating && (
    -
    No notes yet
    - + {notes?.length && searchQuery ? ( +
    No notes match your search
    + ) : ( + <> +
    No notes yet
    + + + )}
    )} diff --git a/src/pages/forms/index.tsx b/src/pages/forms/index.tsx index d3e8e275..c72c3b59 100644 --- a/src/pages/forms/index.tsx +++ b/src/pages/forms/index.tsx @@ -67,6 +67,11 @@ const AllFormsPage = () => { setPagination((prev) => ({ ...prev, pageIndex: 0 })) } + const handleFolderSelect = (id: number | null | "all") => { + setSelectedFolderId(id) + setPagination((p) => ({ ...p, pageIndex: 0 })) + } + const handleFolderFilterChange = useCallback((folderId: number | null | "all") => { setSelectedFolderId(folderId) setPagination((prev) => ({ ...prev, pageIndex: 0 }))