diff --git a/backend/app/__pycache__/__init__.cpython-313.pyc b/backend/app/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index bb0ba3b..0000000 Binary files a/backend/app/__pycache__/__init__.cpython-313.pyc and /dev/null differ diff --git a/backend/app/__pycache__/__init__.cpython-314.pyc b/backend/app/__pycache__/__init__.cpython-314.pyc deleted file mode 100644 index e78c6d7..0000000 Binary files a/backend/app/__pycache__/__init__.cpython-314.pyc and /dev/null differ diff --git a/backend/app/__pycache__/auth.cpython-314.pyc b/backend/app/__pycache__/auth.cpython-314.pyc deleted file mode 100644 index a45ea6f..0000000 Binary files a/backend/app/__pycache__/auth.cpython-314.pyc and /dev/null differ diff --git a/backend/app/__pycache__/database.cpython-313.pyc b/backend/app/__pycache__/database.cpython-313.pyc deleted file mode 100644 index 5bb281a..0000000 Binary files a/backend/app/__pycache__/database.cpython-313.pyc and /dev/null differ diff --git a/backend/app/__pycache__/database.cpython-314.pyc b/backend/app/__pycache__/database.cpython-314.pyc deleted file mode 100644 index c394bc6..0000000 Binary files a/backend/app/__pycache__/database.cpython-314.pyc and /dev/null differ diff --git a/backend/app/__pycache__/main.cpython-313.pyc b/backend/app/__pycache__/main.cpython-313.pyc deleted file mode 100644 index ef9f861..0000000 Binary files a/backend/app/__pycache__/main.cpython-313.pyc and /dev/null differ diff --git a/backend/app/__pycache__/main.cpython-314.pyc b/backend/app/__pycache__/main.cpython-314.pyc deleted file mode 100644 index 10d41a4..0000000 Binary files a/backend/app/__pycache__/main.cpython-314.pyc and /dev/null differ diff --git a/backend/app/__pycache__/models.cpython-313.pyc b/backend/app/__pycache__/models.cpython-313.pyc deleted file mode 100644 index 4a8f92d..0000000 Binary files a/backend/app/__pycache__/models.cpython-313.pyc and /dev/null differ diff --git a/backend/app/__pycache__/models.cpython-314.pyc b/backend/app/__pycache__/models.cpython-314.pyc deleted file mode 100644 index ec9635d..0000000 Binary files a/backend/app/__pycache__/models.cpython-314.pyc and /dev/null differ diff --git a/backend/app/routes/__pycache__/folders.cpython-313.pyc b/backend/app/routes/__pycache__/folders.cpython-313.pyc index 36082c2..e61c145 100644 Binary files a/backend/app/routes/__pycache__/folders.cpython-313.pyc and b/backend/app/routes/__pycache__/folders.cpython-313.pyc differ diff --git a/backend/app/routes/__pycache__/notes.cpython-313.pyc b/backend/app/routes/__pycache__/notes.cpython-313.pyc index 3bf747c..0c8332a 100644 Binary files a/backend/app/routes/__pycache__/notes.cpython-313.pyc and b/backend/app/routes/__pycache__/notes.cpython-313.pyc differ diff --git a/backend/notes.db b/backend/notes.db deleted file mode 100644 index 77e2b53..0000000 Binary files a/backend/notes.db and /dev/null differ diff --git a/backend/notes.sqbpro b/backend/notes.sqbpro deleted file mode 100644 index fca50c8..0000000 --- a/backend/notes.sqbpro +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf51c54..4c6fa4c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -151,7 +151,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -728,7 +727,6 @@ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -818,7 +816,6 @@ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", "license": "MIT", - "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -828,7 +825,6 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz", "integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -979,7 +975,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1026,7 +1021,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1503,7 +1497,6 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", - "peer": true, "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" @@ -1557,7 +1550,6 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz", "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==", "license": "MIT", - "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" }, @@ -1922,7 +1914,6 @@ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", "license": "MIT", - "peer": true, "dependencies": { "@lezer/common": "^1.3.0" } @@ -3510,7 +3501,6 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -3861,7 +3851,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz", "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.90.12" }, @@ -3985,7 +3974,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.12.1.tgz", "integrity": "sha512-dn5uTnsTUjMze26iRhcus8+2auW9+/vOpk6suXg/lhBp+UzOM+EALKE3S5086ANJNgBh1PDHoBX+r1T7wEmheg==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4208,7 +4196,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.12.1.tgz", "integrity": "sha512-v3WC9TR8QRVwmubuKjUplAXeTzTq2hiVKGHBbW15LTqqfsEJwt1YHUl/Sc+pSAeJfY7th5wheNfZFCsCBCW3qg==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4327,7 +4314,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.12.1.tgz", "integrity": "sha512-Xtg2Ot3oebg6+ponJ3yp8VcxPtdaHaub62Eoh8DKvBexyfqp+lMDtOpJZXA9NImVG3gKn+5EAIq8kx5AtrVlJQ==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4342,7 +4328,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.12.1.tgz", "integrity": "sha512-YGv8uZrTraXzB3DPQYsyIB90Girx5QZdZOBSDj0R2bWSXc2Huqdb9PaulXqDQjEv/dp9x6w6+Q2VNIagCPUQwA==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -4436,7 +4421,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4583,7 +4569,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4593,7 +4578,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4707,7 +4691,6 @@ "integrity": "sha512-sxSyJMaKp45zI0u+lHrPuZM1ZJQ8FaVD35k+UxVrha1yyvQ+TZuUYllUixwvQXlB7ixoDc7skf3lQPopZIvaQw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "4.0.15", "fflate": "^0.8.2", @@ -4743,7 +4726,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4792,6 +4774,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -4802,6 +4785,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -4941,7 +4925,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -5401,7 +5384,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dot-case": { "version": "3.0.4", @@ -6224,6 +6208,7 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", + "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -6272,7 +6257,6 @@ "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -6379,6 +6363,7 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", "license": "MIT", + "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -8111,7 +8096,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -8140,6 +8124,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -8293,7 +8278,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", - "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -8323,7 +8307,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -8384,7 +8367,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz", "integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -8421,7 +8403,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -8443,7 +8424,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -8938,8 +8918,7 @@ "version": "4.1.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -9128,7 +9107,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9359,7 +9337,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -9435,7 +9412,6 @@ "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.15", "@vitest/mocker": "4.0.15", @@ -9984,7 +9960,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/frontend/src/api/tags.tsx b/frontend/src/api/tags.tsx deleted file mode 100644 index 7dddf64..0000000 --- a/frontend/src/api/tags.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { client } from "./client"; -import { components } from "@/types/api"; -import { encryptString, decryptTagTree } from "./encryption"; -import { useAuthStore } from "../stores/authStore"; -import { CamelCasedPropertiesDeep } from "type-fest"; - -export type Tag = CamelCasedPropertiesDeep; - -export type TagTreeNode = CamelCasedPropertiesDeep< - components["schemas"]["TagTreeNode"] ->; -export type TagCreate = CamelCasedPropertiesDeep< - components["schemas"]["TagCreate"] ->; -export type TagRead = CamelCasedPropertiesDeep< - components["schemas"]["TagRead"] ->; -export type TagTreeResponse = CamelCasedPropertiesDeep< - components["schemas"]["TagTreeResponse"] ->; - -const fetchTags = async () => { - const encryptionKey = useAuthStore.getState().encryptionKey; - if (!encryptionKey) throw new Error("Not authenticated"); - - const response = await client.GET("/api/tags/tree", {}); - - if (response.error) throw new Error("Failed to fetch tags"); - if (!response.data) throw new Error("No data returned"); - - const data = response.data; - - const tags = decryptTagTree(data.tags as any, encryptionKey); - return tags; -}; - -const createTag = async (tag: TagCreate): Promise => { - const encryptionKey = useAuthStore.getState().encryptionKey; - if (!encryptionKey) throw new Error("Not authenticated"); - - const tagName = await encryptString(tag.name, encryptionKey); - - // Use the exact structure from TagCreate schema - const { data, error } = await client.POST("/api/tags/", { - body: { - name: tagName, - parentId: tag.parentId || null, - }, - }); - - if (error) throw new Error("Failed to create tag"); - return data as unknown as TagTreeNode; -}; - -const addTagToNote = async (tagId: number, noteId: number) => { - const { data, error } = await client.POST( - "/api/tags/note/{note_id}/tag/{tag_id}", - { - params: { - path: { - note_id: noteId, - tag_id: tagId, - }, - }, - }, - ); - - if (error) throw new Error("Failed to add tag to note"); - return data; -}; - -const deleteTag = async (tagId: number) => { - const { error } = await client.DELETE("/api/tags/{tag_id}", { - params: { - path: { - tag_id: tagId, - }, - }, - }); - - if (error) throw new Error("Failed to delete tag"); -}; - -export const tagsApi = { - list: fetchTags, - create: createTag, - addToNote: addTagToNote, - delete: deleteTag, -}; diff --git a/frontend/src/hooks/useTags.ts b/frontend/src/hooks/useTags.ts deleted file mode 100644 index 64bbc46..0000000 --- a/frontend/src/hooks/useTags.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { Tag, TagCreate, TagRead, tagsApi } from "@/api/tags"; -import { useAuthStore } from "@/stores/authStore"; -import { DecryptedTagNode } from "@/api/encryption"; - -export const useTagTree = () => { - const { encryptionKey } = useAuthStore(); - - return useQuery({ - queryKey: ["tags", "tree"], - queryFn: tagsApi.list, - enabled: !!encryptionKey, - }); -}; - -export const useCreateTag = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (tag: TagCreate) => tagsApi.create(tag), - - onMutate: async (newTag) => { - await queryClient.cancelQueries({ queryKey: ["tags", "tree"] }); - - const previousTags = queryClient.getQueryData(["tags", "tree"]); - - queryClient.setQueryData(["tags", "tree"], (old: Tag[] | undefined) => { - const tempTag: DecryptedTagNode = { - id: -Date.now(), - name: newTag.name, - parentId: newTag.parentId, - parentPath: "", - createdAt: new Date().toISOString(), - children: [], - }; - return [...(old || []), tempTag]; - }); - - return { previousTags }; - }, - - onError: (err, newTag, context) => { - queryClient.setQueryData(["tags", "tree"], context?.previousTags); - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["tags", "tree"] }); - }, - }); -}; - -export const useAddTagToNote = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: ({ tagId, noteId }: { tagId: number; noteId: number }) => - tagsApi.addToNote(tagId, noteId), - - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["tags", "tree"] }); - queryClient.invalidateQueries({ queryKey: ["folders", "tree"] }); - }, - }); -}; diff --git a/frontend/src/pages/Home/Home.tsx b/frontend/src/pages/Home/Home.tsx index 46a25d5..4053d76 100644 --- a/frontend/src/pages/Home/Home.tsx +++ b/frontend/src/pages/Home/Home.tsx @@ -7,10 +7,8 @@ import { Login } from "../Login"; import { TiptapEditor } from "../TipTap"; import { Sidebar } from "./components/sidebar/SideBar"; import { StatusIndicator } from "./components/StatusIndicator"; -import { useAddTagToNote, useCreateTag, useTagTree } from "@/hooks/useTags"; -import { useFolderTree, useUpdateNote } from "@/hooks/useFolders"; -import { Note, NoteRead } from "@/api/notes"; -import { DecryptedTagNode } from "@/api/encryption"; +import { useUpdateNote } from "@/hooks/useFolders"; +import { NoteRead } from "@/api/notes"; // @ts-ignore import XmarkIcon from "@/assets/fontawesome/svg/xmark.svg?react"; // @ts-ignore @@ -31,7 +29,6 @@ function Home() { const { showModal, setUpdating, selectedNote } = useUIStore(); const newFolderRef = useRef(null); - const folderTree = useFolderTree(); const updateNoteMutation = useUpdateNote(); // Sync editingNote with selectedNote when selection changes @@ -138,23 +135,6 @@ function Home() { onChange={(e) => setTitle(e.target.value)} className="w-full p-4 pb-0 text-3xl font-semibold bg-transparent focus:outline-none border-transparent focus:border-ctp-mauve transition-colors placeholder:text-ctp-overlay0 text-ctp-text" /> - -
- {editingNote?.tags && - editingNote.tags.map((tag) => ( - - ))} -
{ - {/**/} ); }; - -export const TagSelector = ({ - editingNote, - setEditingNote, -}: { - editingNote: NoteRead | null; - setEditingNote: (note: NoteRead | null) => void; -}) => { - const [value, setValue] = useState(""); - const containerRef = useRef(null); - const inputRef = useRef(null); - - const { data: tagTree, isLoading, error } = useTagTree(); - const createTag = useCreateTag(); - const addTagToNote = useAddTagToNote(); - const [expanded, setExpanded] = useState(false); - - // Parse path from input (using > as separator) - const parsedPath = value.includes(">") - ? value - .split(">") - .map((part) => part.trim()) - .filter(Boolean) - : null; - - // Filter existing tags based on search - const filteredTags = tagTree - ? tagTree.filter((tag) => { - if (value === "") return false; - // Don't show filtered tags if user is typing a path - if (parsedPath) return false; - return (tag.name + tag.parentPath) - .toLowerCase() - .includes(value.toLowerCase()); - }) - : []; - - // Close when clicking outside the entire component - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if ( - containerRef.current && - !containerRef.current.contains(event.target as Node) - ) { - handleClose(); - } - }; - - if (expanded) { - document.addEventListener("mousedown", handleClickOutside); - return () => - document.removeEventListener("mousedown", handleClickOutside); - } - }, [expanded]); - - // Focus input when expanded - useEffect(() => { - if (expanded && inputRef.current) { - inputRef.current.focus(); - } - }, [expanded]); - - const handleEnter = () => { - createTag.mutate( - { name: value }, - { - onSuccess: (createdTag) => { - if (editingNote && createdTag.id) { - addTagToNote.mutate( - { - tagId: createdTag.id, - noteId: editingNote.id, - }, - { - onSuccess: () => { - setEditingNote({ - ...editingNote, - tags: [ - ...(editingNote.tags || []), - { ...createdTag, name: value }, - ], - }); - }, - }, - ); - } - }, - }, - ); - }; - - const handleSelectExistingTag = (tag: DecryptedTagNode) => { - if (editingNote && tag.id) { - addTagToNote.mutate( - { - tagId: tag.id, - noteId: editingNote.id, - }, - { - onSuccess: () => { - setEditingNote({ - ...editingNote, - tags: [ - ...(editingNote.tags || []), - { id: tag.id, name: tag.name, parentId: tag.parentId }, - ], - }); - setValue(""); - setExpanded(false); - }, - }, - ); - } - }; - - function handleClose() { - // If there's a value, submit it; otherwise just collapse - if (value.trim()) { - handleEnter(); - } - setValue(""); - setExpanded(false); - } - - function handleKey(e: React.KeyboardEvent) { - if (e.key === "Enter") { - e.preventDefault(); - handleClose(); - } else if (e.key === "Escape") { - // Abort without submitting - setValue(""); - setExpanded(false); - } - } - - return ( -
-
- - - setValue(e.target.value)} - onKeyDown={handleKey} - placeholder={"Type here…"} - className={`text-xs z-30 - bg-transparent px-1.5 - focus:outline-none focus:ring-0 - transition-all duration-200 ease-out - ${expanded ? "w-32 opacity-100" : "w-0 opacity-0 pointer-events-none"} - `} - /> -
- - {/* Dropdown */} - {expanded && value && ( -
- {/* Show hierarchical preview if path is being typed */} - {parsedPath && parsedPath.length > 0 && ( -
-
- Create tag hierarchy: -
-
- {parsedPath.map((part, index) => ( -
- {index > 0 && ( - └─ - )} - - {part} - -
- ))} -
-
- Press Enter to create -
-
- )} - - {/* Show filtered existing tags */} - {!parsedPath && filteredTags.length > 0 && ( -
- {filteredTags.map((tag) => ( - - ))} -
- )} - - {/* Show "create new" option for simple tags */} - {!parsedPath && filteredTags.length === 0 && ( -
- Press Enter to create "{value}" -
- )} -
- )} -
- ); -}; - -// Clickable version of TagTree for selection -export const TagTreeClickable = ({ - tag, - depth = 0, - onSelect, -}: { - tag: DecryptedTagNode; - depth?: number; - onSelect: (tag: DecryptedTagNode) => void; -}) => { - const [collapse, setCollapse] = useState(false); - - return ( -
- - - {/* Show children */} - {tag.children && tag.children.length > 0 && ( -
- {tag.children.map((child) => ( - - ))} -
- )} -
- ); -}; - -export const TagTree = ({ - tag, - depth = 0, -}: { - tag: DecryptedTagNode; - depth?: number; -}) => { - const [collapse, setCollapse] = useState(false); - - return ( -
-
setCollapse(!collapse)}>{tag.name}
- - {collapse && ( - - {/* The line container */} -
- {/* Child tags */} - {tag.children.map((child) => ( - - ))} -
-
- )} -
-
- ); -}; diff --git a/frontend/src/pages/Home/components/sidebar/SideBar.tsx b/frontend/src/pages/Home/components/sidebar/SideBar.tsx index 87ab287..19d06ea 100644 --- a/frontend/src/pages/Home/components/sidebar/SideBar.tsx +++ b/frontend/src/pages/Home/components/sidebar/SideBar.tsx @@ -20,7 +20,6 @@ import { FolderTree } from "./subcomponents/FolderTree.tsx"; import { SidebarHeader } from "./subcomponents/SideBarHeader.tsx"; import { useAuthStore } from "@/stores/authStore.ts"; import { useUIStore } from "@/stores/uiStore.ts"; -import { TagSelector } from "../../Home.tsx"; import { useCreateFolder, useFolderTree, @@ -42,8 +41,7 @@ export const Sidebar = () => { const { encryptionKey } = useAuthStore(); - const { setSideBarResize, sideBarResize, sideBarView, setSideBarView } = - useUIStore(); + const { setSideBarResize, sideBarResize } = useUIStore(); useEffect(() => { if (newFolder && newFolderRef.current) { newFolderRef.current.focus(); @@ -174,95 +172,85 @@ export const Sidebar = () => { >
- {sideBarView == "folders" ? ( - <> -
e.preventDefault()} - onTouchMove={(e) => e.preventDefault()} - > - {/* New folder input */} - {newFolder && ( -
- setNewFolder(false)} - onChange={(e) => setNewFolderText(e.target.value)} - value={newFolderText} - type="text" - placeholder="Folder name..." - className="standard-input" - ref={newFolderRef} - onKeyDown={(e) => { - if (e.key === "Enter") { - handleCreateFolder(); - } - if (e.key === "Escape") { - setNewFolder(false); - } - }} - /> -
- )} + <> +
e.preventDefault()} + onTouchMove={(e) => e.preventDefault()} + > + {/* New folder input */} + {newFolder && ( +
+ setNewFolder(false)} + onChange={(e) => setNewFolderText(e.target.value)} + value={newFolderText} + type="text" + placeholder="Folder name..." + className="standard-input" + ref={newFolderRef} + onKeyDown={(e) => { + if (e.key === "Enter") { + handleCreateFolder(); + } + if (e.key === "Escape") { + setNewFolder(false); + } + }} + /> +
+ )} - {/* Loading state */} - {isLoading && ( -
-
Loading folders...
-
- )} + {/* Loading state */} + {isLoading && ( +
+
Loading folders...
+
+ )} - {/* Error state */} - {error && ( -
-
Failed to load folders
-
- )} + {/* Error state */} + {error && ( +
+
Failed to load folders
+
+ )} - {/* Folder tree */} - {!isLoading && !error && ( - <> -
- {folderTree?.folders.map((folder) => ( - - ))} -
- - {/* Orphaned notes */} - {folderTree?.orphanedNotes && - folderTree.orphanedNotes.length > 0 && ( -
- {folderTree.orphanedNotes.map((note) => ( - - ))} -
- )} - - )} -
- - - {activeItem?.type === "note" && ( -
- {activeItem.data.title} + {/* Folder tree */} + {!isLoading && !error && ( + <> +
+ {folderTree?.folders.map((folder) => ( + + ))}
- )} - {activeItem?.type === "folder" && ( -
- - {activeItem.data.name} -
- )} - - - ) : ( -
- + + {/* Orphaned notes */} + {folderTree?.orphanedNotes && + folderTree.orphanedNotes.length > 0 && ( +
+ {folderTree.orphanedNotes.map((note) => ( + + ))} +
+ )} + + )}
- )} + + + {activeItem?.type === "note" && ( +
+ {activeItem.data.title} +
+ )} + {activeItem?.type === "folder" && ( +
+ + {activeItem.data.name} +
+ )} +
+
diff --git a/frontend/src/pages/Home/components/sidebar/subcomponents/SideBarHeader.tsx b/frontend/src/pages/Home/components/sidebar/subcomponents/SideBarHeader.tsx index 59f9d73..e93aead 100644 --- a/frontend/src/pages/Home/components/sidebar/subcomponents/SideBarHeader.tsx +++ b/frontend/src/pages/Home/components/sidebar/subcomponents/SideBarHeader.tsx @@ -14,7 +14,7 @@ export const SidebarHeader = ({ }: { setNewFolder: React.Dispatch>; }) => { - const { setSideBarView, sideBarView, selectedFolder } = useUIStore(); + const { selectedFolder } = useUIStore(); const createNote = useCreateNote(); const handleCreate = async () => { createNote.mutate({ @@ -33,15 +33,6 @@ export const SidebarHeader = ({ > -