diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..e4a4063 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,2 @@ +VITE_BACKEND_API_URL=http://localhost:3001 +VITE_COLLABORATION_URL= diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..bb62692 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,19 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + '@typescript-eslint/no-explicit-any': 'off', + }, +} diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..4413984 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,27 @@ +.env +package-lock.json + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..1ebe379 --- /dev/null +++ b/client/README.md @@ -0,0 +1,27 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/client/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/frontend/package.json b/client/package.json similarity index 58% rename from frontend/package.json rename to client/package.json index c997fb5..9e245c8 100644 --- a/frontend/package.json +++ b/client/package.json @@ -1,21 +1,20 @@ { - "name": "frontend", - "version": "0.1.0", + "name": "client", "private": true, + "version": "0.0.0", "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "prebuild": "next telemetry disable", - "lint": "next lint" + "dev": "vite --port 3000", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview --port 3000" }, "dependencies": { "@hocuspocus/provider": "^2.7.0", - "@mantine/core": "^7.1.3", - "@mantine/form": "^7.1.3", - "@mantine/hooks": "^7.1.3", - "@mantine/spotlight": "^7.1.3", - "@mantine/tiptap": "^7.1.3", + "@mantine/core": "^7.1.5", + "@mantine/form": "^7.1.5", + "@mantine/hooks": "^7.1.5", + "@mantine/spotlight": "^7.1.5", + "@mantine/tiptap": "^7.1.5", "@tabler/icons-react": "^2.39.0", "@tanstack/react-query": "^4.36.1", "@tanstack/react-table": "^8.10.7", @@ -38,13 +37,12 @@ "jotai": "^2.4.3", "jotai-optics": "^0.3.1", "js-cookie": "^3.0.5", - "next": "13.5.5", - "react": "18.2.0", + "react": "^18.2.0", "react-arborist": "^3.2.0", - "react-dom": "18.2.0", + "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", + "react-router-dom": "^6.17.0", "socket.io-client": "^4.7.2", - "typescript": "5.2.2", "uuid": "^9.0.1", "y-indexeddb": "^9.0.11", "yjs": "^13.6.8", @@ -53,13 +51,21 @@ "devDependencies": { "@types/js-cookie": "^3.0.4", "@types/node": "20.8.6", - "@types/react": "18.2.28", - "@types/react-dom": "18.2.13", - "eslint": "8.51.0", - "eslint-config-next": "13.5.5", + "@types/react": "^18.2.29", + "@types/react-dom": "^18.2.14", + "@types/uuid": "^9.0.6", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.51.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", "optics-ts": "^2.4.1", "postcss": "^8.4.31", "postcss-preset-mantine": "^1.9.0", - "postcss-simple-vars": "^7.0.1" + "postcss-simple-vars": "^7.0.1", + "prettier": "^3.0.3", + "typescript": "^5.2.2", + "vite": "^4.4.5" } } diff --git a/frontend/postcss.config.js b/client/postcss.config.js similarity index 100% rename from frontend/postcss.config.js rename to client/postcss.config.js diff --git a/client/public/vite.svg b/client/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/client/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..13d342e --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,26 @@ +import { Route, Routes } from 'react-router-dom'; +import { Welcome } from '@/pages/welcome'; +import SignUpPage from '@/pages/auth/signup'; +import LoginPage from '@/pages/auth/login'; +import DashboardLayout from '@/components/layouts/layout'; +import Home from '@/pages/dashboard/home'; +import Page from '@/pages/page/page'; + +export default function App() { + + return ( + <> + + } /> + } /> + } /> + + }> + } /> + } /> + + + + + ); +} diff --git a/client/src/assets/react.svg b/client/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/client/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/layouts/layout.tsx b/client/src/components/layouts/layout.tsx new file mode 100644 index 0000000..ed953ea --- /dev/null +++ b/client/src/components/layouts/layout.tsx @@ -0,0 +1,14 @@ +import { UserProvider } from '@/features/user/user-provider'; +import Shell from './shell'; +import { Outlet } from 'react-router-dom'; + +export default function DashboardLayout() { + + return ( + + + + + + ); +} diff --git a/frontend/src/app/(dashboard)/shell.tsx b/client/src/components/layouts/shell.tsx similarity index 98% rename from frontend/src/app/(dashboard)/shell.tsx rename to client/src/components/layouts/shell.tsx index 1404919..88bc30a 100644 --- a/frontend/src/app/(dashboard)/shell.tsx +++ b/client/src/components/layouts/shell.tsx @@ -1,5 +1,3 @@ -'use client'; - import { desktopSidebarAtom } from '@/components/navbar/atoms/sidebar-atom'; import { useToggleSidebar } from '@/components/navbar/hooks/use-toggle-sidebar'; import { Navbar } from '@/components/navbar/navbar'; @@ -12,7 +10,6 @@ export default function Shell({ children }: { children: React.ReactNode }) { const [desktopOpened] = useAtom(desktopSidebarAtom); const toggleDesktop = useToggleSidebar(desktopSidebarAtom); - return ( import("@/features/page/tree/page-tree"), { - ssr: false, -}); +import PageTree from '@/features/page/tree/page-tree'; interface PrimaryMenuItem { icon: React.ElementType; diff --git a/frontend/src/components/navbar/user-button.module.css b/client/src/components/navbar/user-button.module.css similarity index 100% rename from frontend/src/components/navbar/user-button.module.css rename to client/src/components/navbar/user-button.module.css diff --git a/frontend/src/components/navbar/user-button.tsx b/client/src/components/navbar/user-button.tsx similarity index 98% rename from frontend/src/components/navbar/user-button.tsx rename to client/src/components/navbar/user-button.tsx index 0a13d1d..1785148 100644 --- a/frontend/src/components/navbar/user-button.tsx +++ b/client/src/components/navbar/user-button.tsx @@ -1,5 +1,3 @@ -'use client'; - import { UnstyledButton, Group, Avatar, Text, rem } from '@mantine/core'; import { IconChevronRight } from '@tabler/icons-react'; import classes from './user-button.module.css'; diff --git a/frontend/src/components/providers/tanstack-provider.tsx b/client/src/components/providers/tanstack-provider.tsx similarity index 96% rename from frontend/src/components/providers/tanstack-provider.tsx rename to client/src/components/providers/tanstack-provider.tsx index 9a4a172..c4e9c77 100644 --- a/frontend/src/components/providers/tanstack-provider.tsx +++ b/client/src/components/providers/tanstack-provider.tsx @@ -1,5 +1,3 @@ -'use client'; - import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; diff --git a/frontend/src/components/theme-toggle.tsx b/client/src/components/theme-toggle.tsx similarity index 96% rename from frontend/src/components/theme-toggle.tsx rename to client/src/components/theme-toggle.tsx index 1b803ea..a988db6 100644 --- a/frontend/src/components/theme-toggle.tsx +++ b/client/src/components/theme-toggle.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Button, Group, useMantineColorScheme } from '@mantine/core'; export function ThemeToggle() { diff --git a/frontend/src/components/ui/custom-toaster.tsx b/client/src/components/ui/custom-toaster.tsx similarity index 96% rename from frontend/src/components/ui/custom-toaster.tsx rename to client/src/components/ui/custom-toaster.tsx index 2b487f0..657eaf4 100644 --- a/frontend/src/components/ui/custom-toaster.tsx +++ b/client/src/components/ui/custom-toaster.tsx @@ -1,5 +1,3 @@ -"use client"; - import t, { Toaster, useToasterStore } from "react-hot-toast"; import { useEffect, useState } from "react"; @@ -18,4 +16,4 @@ export default function CustomToaster() { }, [toastLimit, toasts]); return ; -}; +} diff --git a/frontend/src/features/auth/atoms/auth-tokens-atom.ts b/client/src/features/auth/atoms/auth-tokens-atom.ts similarity index 87% rename from frontend/src/features/auth/atoms/auth-tokens-atom.ts rename to client/src/features/auth/atoms/auth-tokens-atom.ts index 4cd7d03..7088d2a 100644 --- a/frontend/src/features/auth/atoms/auth-tokens-atom.ts +++ b/client/src/features/auth/atoms/auth-tokens-atom.ts @@ -1,6 +1,6 @@ import Cookies from "js-cookie"; import { createJSONStorage, atomWithStorage } from "jotai/utils"; -import { ITokens } from "@/features/auth/types/auth.types"; +import { ITokens } from '../types/auth.types'; const cookieStorage = createJSONStorage(() => { diff --git a/frontend/src/features/auth/components/login-form.tsx b/client/src/features/auth/components/login-form.tsx similarity index 92% rename from frontend/src/features/auth/components/login-form.tsx rename to client/src/features/auth/components/login-form.tsx index 52dec31..a849812 100644 --- a/frontend/src/features/auth/components/login-form.tsx +++ b/client/src/features/auth/components/login-form.tsx @@ -1,5 +1,3 @@ -'use client'; - import * as React from 'react'; import * as z from 'zod'; @@ -16,7 +14,7 @@ import { Text, PasswordInput, } from '@mantine/core'; -import Link from 'next/link'; +import { Link } from 'react-router-dom'; const formSchema = z.object({ email: z @@ -46,8 +44,8 @@ export function LoginForm() { Login - Do not have an account yet?{' '} - + Don't have an account yet?{' '} + Create account diff --git a/frontend/src/features/auth/components/sign-up-form.tsx b/client/src/features/auth/components/sign-up-form.tsx similarity index 94% rename from frontend/src/features/auth/components/sign-up-form.tsx rename to client/src/features/auth/components/sign-up-form.tsx index cb0718f..05f4764 100644 --- a/frontend/src/features/auth/components/sign-up-form.tsx +++ b/client/src/features/auth/components/sign-up-form.tsx @@ -1,11 +1,7 @@ -'use client'; - import * as React from 'react'; import * as z from 'zod'; import { useForm, zodResolver } from '@mantine/form'; -import useAuth from '@/features/auth/hooks/use-auth'; -import { IRegister } from '@/features/auth/types/auth.types'; import { Container, Title, @@ -16,7 +12,9 @@ import { Text, PasswordInput, } from '@mantine/core'; -import Link from 'next/link'; +import { Link } from 'react-router-dom'; +import { IRegister } from '@/features/auth/types/auth.types'; +import useAuth from '@/features/auth/hooks/use-auth'; const formSchema = z.object({ email: z @@ -47,7 +45,7 @@ export function SignUpForm() { Already have an account?{' '} - + Login diff --git a/frontend/src/features/auth/hooks/use-auth.ts b/client/src/features/auth/hooks/use-auth.ts similarity index 91% rename from frontend/src/features/auth/hooks/use-auth.ts rename to client/src/features/auth/hooks/use-auth.ts index 38ca33f..980f6a3 100644 --- a/frontend/src/features/auth/hooks/use-auth.ts +++ b/client/src/features/auth/hooks/use-auth.ts @@ -1,6 +1,6 @@ import { useState } from "react"; import { login, register } from "@/features/auth/services/auth-service"; -import { useRouter } from "next/navigation"; +import { useNavigate } from "react-router-dom"; import { useAtom } from "jotai"; import { authTokensAtom } from "@/features/auth/atoms/auth-tokens-atom"; import { currentUserAtom } from "@/features/user/atoms/current-user-atom"; @@ -9,7 +9,7 @@ import toast from "react-hot-toast"; export default function useAuth() { const [isLoading, setIsLoading] = useState(false); - const router = useRouter(); + const navigate = useNavigate(); const [, setCurrentUser] = useAtom(currentUserAtom); const [authToken, setAuthToken] = useAtom(authTokensAtom); @@ -22,7 +22,7 @@ export default function useAuth() { setIsLoading(false); setAuthToken(res.tokens); - router.push("/home"); + navigate("/home"); } catch (err) { setIsLoading(false); toast.error(err.response?.data.message) @@ -38,7 +38,7 @@ export default function useAuth() { setAuthToken(res.tokens); - router.push("/home"); + navigate("/home"); } catch (err) { setIsLoading(false); toast.error(err.response?.data.message) diff --git a/frontend/src/features/auth/services/auth-service.ts b/client/src/features/auth/services/auth-service.ts similarity index 100% rename from frontend/src/features/auth/services/auth-service.ts rename to client/src/features/auth/services/auth-service.ts diff --git a/frontend/src/features/auth/types/auth.types.ts b/client/src/features/auth/types/auth.types.ts similarity index 100% rename from frontend/src/features/auth/types/auth.types.ts rename to client/src/features/auth/types/auth.types.ts diff --git a/client/src/features/editor/editor.tsx b/client/src/features/editor/editor.tsx new file mode 100644 index 0000000..c27ea68 --- /dev/null +++ b/client/src/features/editor/editor.tsx @@ -0,0 +1,161 @@ +import '@/features/editor/styles/editor.css'; + +import { HocuspocusProvider } from '@hocuspocus/provider'; +import * as Y from 'yjs'; +import { EditorContent, useEditor } from '@tiptap/react'; +import { StarterKit } from '@tiptap/starter-kit'; +import { Placeholder } from '@tiptap/extension-placeholder'; +import { Collaboration } from '@tiptap/extension-collaboration'; +import { CollaborationCursor } from '@tiptap/extension-collaboration-cursor'; +import { useEffect, useLayoutEffect, useState } from 'react'; +import { useAtom } from 'jotai'; +import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; +import { authTokensAtom } from '@/features/auth/atoms/auth-tokens-atom'; +import useCollaborationUrl from '@/features/editor/hooks/use-collaboration-url'; +import { IndexeddbPersistence } from 'y-indexeddb'; +import { RichTextEditor } from '@mantine/tiptap'; +import { TextAlign } from '@tiptap/extension-text-align'; +import { Highlight } from '@tiptap/extension-highlight'; +import { Superscript } from '@tiptap/extension-superscript'; +import SubScript from '@tiptap/extension-subscript'; +import { Link } from '@tiptap/extension-link'; +import { Underline } from '@tiptap/extension-underline'; + +interface EditorProps { + pageId: string, + token?: string, +} + +const colors = ['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D']; +const getRandomElement = list => list[Math.floor(Math.random() * list.length)]; +const getRandomColor = () => getRandomElement(colors); + +export default function Editor({ pageId }: EditorProps) { + const [token] = useAtom(authTokensAtom); + const collaborationURL = useCollaborationUrl(); + const [provider, setProvider] = useState(); + const [yDoc] = useState(() => new Y.Doc()); + + + useEffect(() => { + if (token) { + const indexeddbProvider = new IndexeddbPersistence(pageId, yDoc) + const provider = new HocuspocusProvider({ + url: collaborationURL, + name: pageId, + document: yDoc, + token: token.accessToken, + }); + + setProvider(provider); + + return () => { + provider.destroy(); + setProvider(null); + indexeddbProvider.destroy(); + }; + } + }, [pageId, token]); + + if (!provider) { + return null; + } + + return ( + + ); +} + +interface TiptapEditorProps { + ydoc: Y.Doc, + provider: HocuspocusProvider +} + +function TiptapEditor({ ydoc, provider }: TiptapEditorProps) { + const [currentUser] = useAtom(currentUserAtom); + + const extensions = [ + StarterKit.configure({ + history: false, + }), + Placeholder.configure({ + placeholder: 'Write here', + }), + Collaboration.configure({ + document: ydoc, + }), + CollaborationCursor.configure({ + provider, + }), + Underline, + Link, + Superscript, + SubScript, + Highlight, + TextAlign.configure({ types: ['heading', 'paragraph'] }), + ]; + + const editor = useEditor({ + extensions: extensions, + }); + + useEffect(() => { + if (editor && currentUser.user) { + editor.chain().focus().updateUser({ ...currentUser.user, color: getRandomColor() }).run(); + } + }, [editor, currentUser.user]); + + useEffect(() => { + provider.on('status', event => { + console.log(event); + }); + + }, [provider]); + + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/frontend/src/features/editor/hooks/use-collaboration-url.ts b/client/src/features/editor/hooks/use-collaboration-url.ts similarity index 65% rename from frontend/src/features/editor/hooks/use-collaboration-url.ts rename to client/src/features/editor/hooks/use-collaboration-url.ts index 16f6554..5741bec 100644 --- a/frontend/src/features/editor/hooks/use-collaboration-url.ts +++ b/client/src/features/editor/hooks/use-collaboration-url.ts @@ -1,11 +1,11 @@ const useCollaborationURL = (): string => { const PATH = "/collaboration"; - if (process.env.NEXT_PUBLIC_COLLABORATION_URL) { - return process.env.NEXT_PUBLIC_COLLABORATION_URL + PATH; + if (import.meta.env.VITE_COLLABORATION_URL) { + return import.meta.env.VITE_COLLABORATION_URL + PATH; } - const API_URL = process.env.NEXT_PUBLIC_BACKEND_API_URL; + const API_URL = import.meta.env.VITE_BACKEND_API_URL; if (!API_URL) { throw new Error("Backend API URL is not defined"); } diff --git a/client/src/features/editor/styles/editor.css b/client/src/features/editor/styles/editor.css new file mode 100644 index 0000000..7cf3608 --- /dev/null +++ b/client/src/features/editor/styles/editor.css @@ -0,0 +1,199 @@ +/* Basic editor styles */ +.tiptap { + > * + * { + margin-top: 0.75em; + } + + ul, + ol { + padding: 0 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + code { + background-color: rgba(#616161, 0.1); + color: #616161; + } + + pre { + background: #0d0d0d; + border-radius: 0.5rem; + color: #fff; + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; + + code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; + } + } + + mark { + background-color: #faf594; + } + + img { + height: auto; + max-width: 100%; + } + + hr { + margin: 1rem 0; + } + + blockquote { + border-left: 2px solid rgba(#0d0d0d, 0.1); + padding-left: 1rem; + } + + hr { + border: none; + border-top: 2px solid rgba(#0d0d0d, 0.1); + margin: 2rem 0; + } + + ul[data-type="taskList"] { + list-style: none; + padding: 0; + + li { + align-items: center; + display: flex; + + > label { + flex: 0 0 auto; + margin-right: 0.5rem; + user-select: none; + } + + > div { + flex: 1 1 auto; + } + } + } +} + +.editor { + background-color: #fff; + border: 2px solid #0d0d0d; + border-radius: 0.75rem; + box-shadow: 5px 5px #000; + color: #0d0d0d; + display: flex; + flex-direction: column; + max-height: 26rem; + + &__header { + align-items: center; + border-bottom: 2px solid #0d0d0d; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + display: flex; + justify-content: space-between; + padding: 0.5rem 1rem; + } + + &__users { + color: rgba(#000, 0.8); + display: flex; + font-size: 0.85rem; + gap: 1rem; + } + + &__content { + flex: 1 1 auto; + overflow-x: hidden; + overflow-y: auto; + padding: 1.25rem 1rem; + -webkit-overflow-scrolling: touch; + } + + /* Some information about the status */ + &__status { + align-items: center; + border-radius: 5px; + display: flex; + + &::before { + background: rgba(#0d0d0d, 0.5); + border-radius: 50%; + content: " "; + display: inline-block; + flex: 0 0 auto; + height: 0.5rem; + margin-right: 0.5rem; + width: 0.5rem; + } + + &--connecting::before { + background: #616161; + } + + &--connected::before { + background: #b9f18d; + } + } + + &__name button { + appearance: none; + background: transparent; + border: none; + color: inherit; + cursor: pointer; + font: inherit; + line-height: normal; + margin: 0; + padding: 0; + overflow: visible; + width: auto; + } +} + +/* Give a remote user a caret */ +.collaboration-cursor__caret { + border-left: 1px solid #0d0d0d; + border-right: 1px solid #0d0d0d; + margin-left: -1px; + margin-right: -1px; + pointer-events: none; + position: relative; + word-break: normal; +} + +/* Render the username above the caret */ +.collaboration-cursor__label { + border-radius: 3px 3px 3px 0; + color: #0d0d0d; + font-size: 0.75rem; + font-style: normal; + font-weight: 600; + left: -1px; + line-height: normal; + padding: 0.1rem 0.3rem; + position: absolute; + top: -1.4em; + user-select: none; + white-space: nowrap; +} + +.dots { + display: flex; + gap: 6px; +} + +.dot { + background: #000; + border-radius: 100%; + height: 0.625rem; + width: 0.625rem; +} diff --git a/frontend/src/features/page/hooks/usePage.ts b/client/src/features/page/hooks/usePage.ts similarity index 100% rename from frontend/src/features/page/hooks/usePage.ts rename to client/src/features/page/hooks/usePage.ts diff --git a/frontend/src/features/page/services/page-service.ts b/client/src/features/page/services/page-service.ts similarity index 100% rename from frontend/src/features/page/services/page-service.ts rename to client/src/features/page/services/page-service.ts diff --git a/frontend/src/features/page/tree/atoms/tree-api-atom.ts b/client/src/features/page/tree/atoms/tree-api-atom.ts similarity index 100% rename from frontend/src/features/page/tree/atoms/tree-api-atom.ts rename to client/src/features/page/tree/atoms/tree-api-atom.ts diff --git a/frontend/src/features/page/tree/atoms/tree-data-atom.ts b/client/src/features/page/tree/atoms/tree-data-atom.ts similarity index 59% rename from frontend/src/features/page/tree/atoms/tree-data-atom.ts rename to client/src/features/page/tree/atoms/tree-data-atom.ts index 5ce297a..3d4c67f 100644 --- a/frontend/src/features/page/tree/atoms/tree-data-atom.ts +++ b/client/src/features/page/tree/atoms/tree-data-atom.ts @@ -1,4 +1,4 @@ import { atom } from "jotai"; -import { TreeNode } from "../types"; +import { TreeNode } from '@/features/page/tree/types'; export const treeDataAtom = atom([]); diff --git a/frontend/src/features/page/tree/atoms/workspace-page-order-atom.ts b/client/src/features/page/tree/atoms/workspace-page-order-atom.ts similarity index 100% rename from frontend/src/features/page/tree/atoms/workspace-page-order-atom.ts rename to client/src/features/page/tree/atoms/workspace-page-order-atom.ts diff --git a/frontend/src/features/page/tree/components/fill-flex-parent.tsx b/client/src/features/page/tree/components/fill-flex-parent.tsx similarity index 79% rename from frontend/src/features/page/tree/components/fill-flex-parent.tsx rename to client/src/features/page/tree/components/fill-flex-parent.tsx index 7d88a6b..df1a4f8 100644 --- a/frontend/src/features/page/tree/components/fill-flex-parent.tsx +++ b/client/src/features/page/tree/components/fill-flex-parent.tsx @@ -1,6 +1,6 @@ import React, { ReactElement } from 'react'; -import mergeRefs from './merge-refs'; import { useElementSize } from '@mantine/hooks'; +import { useMergedRef } from '@mantine/hooks'; type Props = { children: (dimens: { width: number; height: number }) => ReactElement; @@ -19,8 +19,9 @@ export const FillFlexParent = React.forwardRef(function FillFlexParent( forwardRef ) { const { ref, width, height } = useElementSize(); + const mergedRef = useMergedRef(ref, forwardRef); return ( -
+
{width && height ? props.children({ width, height }) : null}
); diff --git a/frontend/src/features/page/tree/hooks/use-persistence.ts b/client/src/features/page/tree/hooks/use-persistence.ts similarity index 90% rename from frontend/src/features/page/tree/hooks/use-persistence.ts rename to client/src/features/page/tree/hooks/use-persistence.ts index 5c4c77c..4591fc1 100644 --- a/frontend/src/features/page/tree/hooks/use-persistence.ts +++ b/client/src/features/page/tree/hooks/use-persistence.ts @@ -1,5 +1,3 @@ -'use client' - import { useMemo } from 'react'; import { CreateHandler, @@ -13,18 +11,16 @@ import { treeDataAtom } from '@/features/page/tree/atoms/tree-data-atom'; import { createPage, deletePage, movePage, updatePage } from '@/features/page/services/page-service'; import { v4 as uuidv4 } from 'uuid'; import { IMovePage } from '@/features/page/types/page.types'; -import { useRouter } from 'next/navigation'; +import { useNavigate} from 'react-router-dom'; +import { TreeNode } from '@/features/page/tree/types'; + export function usePersistence() { - const [data, setData] = useAtom(treeDataAtom); - const router = useRouter(); + const [data, setData] = useAtom(treeDataAtom); + const navigate = useNavigate(); + + const tree = useMemo(() => new SimpleTree(data), [data]); - const tree = useMemo( - () => - new SimpleTree(data), - [data], - ); const onMove: MoveHandler = (args: { parentId, index, parentNode, dragNodes, dragIds }) => { for (const id of args.dragIds) { @@ -80,7 +76,7 @@ export function usePersistence() { try { await createPage(payload); - router.push(`/p/${payload.id}`); + navigate(`/p/${payload.id}`); } catch (error) { console.error('Error creating the page:', error); } diff --git a/frontend/src/features/page/tree/hooks/use-workspace-page-order.ts b/client/src/features/page/tree/hooks/use-workspace-page-order.ts similarity index 100% rename from frontend/src/features/page/tree/hooks/use-workspace-page-order.ts rename to client/src/features/page/tree/hooks/use-workspace-page-order.ts diff --git a/frontend/src/features/page/tree/page-tree.tsx b/client/src/features/page/tree/page-tree.tsx similarity index 96% rename from frontend/src/features/page/tree/page-tree.tsx rename to client/src/features/page/tree/page-tree.tsx index f6e9a37..c055558 100644 --- a/frontend/src/features/page/tree/page-tree.tsx +++ b/client/src/features/page/tree/page-tree.tsx @@ -1,5 +1,3 @@ -'use client'; - import { NodeApi, NodeRendererProps, Tree, TreeApi } from 'react-arborist'; import { IconArrowsLeftRight, @@ -28,13 +26,13 @@ import { usePersistence } from '@/features/page/tree/hooks/use-persistence'; import { IPage } from '@/features/page/types/page.types'; import { getPages } from '@/features/page/services/page-service'; import useWorkspacePageOrder from '@/features/page/tree/hooks/use-workspace-page-order'; -import { usePathname, useRouter } from 'next/navigation'; +import { useLocation, useNavigate} from 'react-router-dom'; export default function PageTree() { const { data, setData, controllers } = usePersistence>(); const [tree, setTree] = useAtom>(treeApiAtom); const { data: pageOrderData } = useWorkspacePageOrder(); - const pathname = usePathname(); + const location = useLocation(); const fetchAndSetTreeData = async () => { if (pageOrderData?.childrenIds) { @@ -53,11 +51,11 @@ export default function PageTree() { }, [pageOrderData?.childrenIds]); useEffect(() => { - const pageId = pathname?.split('/')[2]; + const pageId = location.pathname.split('/')[2]; setTimeout(() => { tree?.select(pageId); }, 100); - }, [tree, pathname]); + }, [tree, location.pathname]); return (
@@ -86,10 +84,10 @@ export default function PageTree() { } function Node({ node, style, dragHandle }: NodeRendererProps) { - const router = useRouter(); + const navigate = useNavigate(); const handleClick = () => { - router.push(`/p/${node.id}`); + navigate(`/p/${node.id}`); } if (node.willReceiveDrop && node.isClosed){ diff --git a/frontend/src/features/page/tree/styles/tree.module.css b/client/src/features/page/tree/styles/tree.module.css similarity index 100% rename from frontend/src/features/page/tree/styles/tree.module.css rename to client/src/features/page/tree/styles/tree.module.css diff --git a/frontend/src/features/page/tree/types.ts b/client/src/features/page/tree/types.ts similarity index 100% rename from frontend/src/features/page/tree/types.ts rename to client/src/features/page/tree/types.ts diff --git a/frontend/src/features/page/types/page.types.ts b/client/src/features/page/types/page.types.ts similarity index 100% rename from frontend/src/features/page/types/page.types.ts rename to client/src/features/page/types/page.types.ts diff --git a/frontend/src/features/search/search-spotlight.tsx b/client/src/features/search/search-spotlight.tsx similarity index 100% rename from frontend/src/features/search/search-spotlight.tsx rename to client/src/features/search/search-spotlight.tsx diff --git a/frontend/src/features/settings/account/settings/account-settings.tsx b/client/src/features/settings/account/settings/account-settings.tsx similarity index 97% rename from frontend/src/features/settings/account/settings/account-settings.tsx rename to client/src/features/settings/account/settings/account-settings.tsx index a40899a..f10b742 100644 --- a/frontend/src/features/settings/account/settings/account-settings.tsx +++ b/client/src/features/settings/account/settings/account-settings.tsx @@ -1,5 +1,3 @@ -'use client'; - import React from "react"; import AccountNameForm from '@/features/settings/account/settings/components/account-name-form'; import ChangeEmail from '@/features/settings/account/settings/components/change-email'; diff --git a/frontend/src/features/settings/account/settings/components/account-name-form.tsx b/client/src/features/settings/account/settings/components/account-name-form.tsx similarity index 99% rename from frontend/src/features/settings/account/settings/components/account-name-form.tsx rename to client/src/features/settings/account/settings/components/account-name-form.tsx index bce4b09..f92c644 100644 --- a/frontend/src/features/settings/account/settings/components/account-name-form.tsx +++ b/client/src/features/settings/account/settings/components/account-name-form.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useAtom } from 'jotai'; import { focusAtom } from 'jotai-optics'; import * as z from 'zod'; diff --git a/frontend/src/features/settings/account/settings/components/change-email.tsx b/client/src/features/settings/account/settings/components/change-email.tsx similarity index 99% rename from frontend/src/features/settings/account/settings/components/change-email.tsx rename to client/src/features/settings/account/settings/components/change-email.tsx index 3be78c3..e10d481 100644 --- a/frontend/src/features/settings/account/settings/components/change-email.tsx +++ b/client/src/features/settings/account/settings/components/change-email.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Modal, TextInput, Button, Text, Group, PasswordInput } from '@mantine/core'; import * as z from 'zod'; import { useState } from 'react'; diff --git a/frontend/src/features/settings/account/settings/components/change-password.tsx b/client/src/features/settings/account/settings/components/change-password.tsx similarity index 99% rename from frontend/src/features/settings/account/settings/components/change-password.tsx rename to client/src/features/settings/account/settings/components/change-password.tsx index 2d83282..3272f42 100644 --- a/frontend/src/features/settings/account/settings/components/change-password.tsx +++ b/client/src/features/settings/account/settings/components/change-password.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Button, Group, Text, Modal, PasswordInput } from '@mantine/core'; import * as z from 'zod'; import { useState } from 'react'; diff --git a/frontend/src/features/settings/modal/atoms/settings-modal-atom.ts b/client/src/features/settings/modal/atoms/settings-modal-atom.ts similarity index 100% rename from frontend/src/features/settings/modal/atoms/settings-modal-atom.ts rename to client/src/features/settings/modal/atoms/settings-modal-atom.ts diff --git a/frontend/src/features/settings/modal/modal.module.css b/client/src/features/settings/modal/modal.module.css similarity index 100% rename from frontend/src/features/settings/modal/modal.module.css rename to client/src/features/settings/modal/modal.module.css diff --git a/frontend/src/features/settings/modal/settings-modal.tsx b/client/src/features/settings/modal/settings-modal.tsx similarity index 98% rename from frontend/src/features/settings/modal/settings-modal.tsx rename to client/src/features/settings/modal/settings-modal.tsx index 4428868..9fb4448 100644 --- a/frontend/src/features/settings/modal/settings-modal.tsx +++ b/client/src/features/settings/modal/settings-modal.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Modal, Text } from '@mantine/core'; import React from 'react'; import SettingsSidebar from '@/features/settings/modal/settings-sidebar'; diff --git a/frontend/src/features/settings/modal/settings-sidebar.tsx b/client/src/features/settings/modal/settings-sidebar.tsx similarity index 99% rename from frontend/src/features/settings/modal/settings-sidebar.tsx rename to client/src/features/settings/modal/settings-sidebar.tsx index 1c18a7a..0426951 100644 --- a/frontend/src/features/settings/modal/settings-sidebar.tsx +++ b/client/src/features/settings/modal/settings-sidebar.tsx @@ -1,5 +1,3 @@ -'use client'; - import React, { useState } from 'react'; import classes from '@/features/settings/modal/modal.module.css'; import { IconBell, IconFingerprint, IconReceipt, IconSettingsCog, IconUser, IconUsers } from '@tabler/icons-react'; diff --git a/frontend/src/features/settings/workspace/members/components/workspace-invite-form.tsx b/client/src/features/settings/workspace/members/components/workspace-invite-form.tsx similarity index 98% rename from frontend/src/features/settings/workspace/members/components/workspace-invite-form.tsx rename to client/src/features/settings/workspace/members/components/workspace-invite-form.tsx index add22bc..0080916 100644 --- a/frontend/src/features/settings/workspace/members/components/workspace-invite-form.tsx +++ b/client/src/features/settings/workspace/members/components/workspace-invite-form.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Group, Box, diff --git a/frontend/src/features/settings/workspace/members/components/workspace-invite-modal.tsx b/client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx similarity index 98% rename from frontend/src/features/settings/workspace/members/components/workspace-invite-modal.tsx rename to client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx index 9deed56..623be3a 100644 --- a/frontend/src/features/settings/workspace/members/components/workspace-invite-modal.tsx +++ b/client/src/features/settings/workspace/members/components/workspace-invite-modal.tsx @@ -1,5 +1,3 @@ -'use client'; - import { IconUserPlus } from '@tabler/icons-react'; import { WorkspaceInviteForm } from '@/features/settings/workspace/members/components/workspace-invite-form'; import { Button, Divider, Modal, ScrollArea, Text } from '@mantine/core'; diff --git a/frontend/src/features/settings/workspace/members/components/workspace-invite-section.tsx b/client/src/features/settings/workspace/members/components/workspace-invite-section.tsx similarity index 98% rename from frontend/src/features/settings/workspace/members/components/workspace-invite-section.tsx rename to client/src/features/settings/workspace/members/components/workspace-invite-section.tsx index bae66d3..d357631 100644 --- a/frontend/src/features/settings/workspace/members/components/workspace-invite-section.tsx +++ b/client/src/features/settings/workspace/members/components/workspace-invite-section.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useAtom } from 'jotai'; import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; import React, { useEffect, useState } from 'react'; diff --git a/frontend/src/features/settings/workspace/members/components/workspace-members-table.tsx b/client/src/features/settings/workspace/members/components/workspace-members-table.tsx similarity index 99% rename from frontend/src/features/settings/workspace/members/components/workspace-members-table.tsx rename to client/src/features/settings/workspace/members/components/workspace-members-table.tsx index 80280f7..3c38d8e 100644 --- a/frontend/src/features/settings/workspace/members/components/workspace-members-table.tsx +++ b/client/src/features/settings/workspace/members/components/workspace-members-table.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useAtom } from 'jotai'; import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; import { useQuery } from '@tanstack/react-query'; diff --git a/frontend/src/features/settings/workspace/members/workspace-members.tsx b/client/src/features/settings/workspace/members/workspace-members.tsx similarity index 98% rename from frontend/src/features/settings/workspace/members/workspace-members.tsx rename to client/src/features/settings/workspace/members/workspace-members.tsx index 36140e8..5be83fa 100644 --- a/frontend/src/features/settings/workspace/members/workspace-members.tsx +++ b/client/src/features/settings/workspace/members/workspace-members.tsx @@ -1,5 +1,3 @@ -'use client'; - import WorkspaceInviteSection from '@/features/settings/workspace/members/components/workspace-invite-section'; import React from 'react'; import WorkspaceInviteModal from '@/features/settings/workspace/members/components/workspace-invite-modal'; diff --git a/frontend/src/features/settings/workspace/settings/components/workspace-name-form.tsx b/client/src/features/settings/workspace/settings/components/workspace-name-form.tsx similarity index 99% rename from frontend/src/features/settings/workspace/settings/components/workspace-name-form.tsx rename to client/src/features/settings/workspace/settings/components/workspace-name-form.tsx index 2ba8797..73e8b9d 100644 --- a/frontend/src/features/settings/workspace/settings/components/workspace-name-form.tsx +++ b/client/src/features/settings/workspace/settings/components/workspace-name-form.tsx @@ -1,5 +1,3 @@ -'use client'; - import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; import { useAtom } from 'jotai'; import * as z from 'zod'; diff --git a/frontend/src/features/settings/workspace/settings/workspace-settings.tsx b/client/src/features/settings/workspace/settings/workspace-settings.tsx similarity index 92% rename from frontend/src/features/settings/workspace/settings/workspace-settings.tsx rename to client/src/features/settings/workspace/settings/workspace-settings.tsx index 357787b..1c80cb1 100644 --- a/frontend/src/features/settings/workspace/settings/workspace-settings.tsx +++ b/client/src/features/settings/workspace/settings/workspace-settings.tsx @@ -1,5 +1,3 @@ -'use client'; - import WorkspaceNameForm from '@/features/settings/workspace/settings/components/workspace-name-form'; export default function WorkspaceSettings() { diff --git a/frontend/src/features/user/atoms/current-user-atom.ts b/client/src/features/user/atoms/current-user-atom.ts similarity index 100% rename from frontend/src/features/user/atoms/current-user-atom.ts rename to client/src/features/user/atoms/current-user-atom.ts diff --git a/frontend/src/features/user/hooks/use-current-user.ts b/client/src/features/user/hooks/use-current-user.ts similarity index 100% rename from frontend/src/features/user/hooks/use-current-user.ts rename to client/src/features/user/hooks/use-current-user.ts diff --git a/frontend/src/features/user/services/user-service.ts b/client/src/features/user/services/user-service.ts similarity index 100% rename from frontend/src/features/user/services/user-service.ts rename to client/src/features/user/services/user-service.ts diff --git a/frontend/src/features/user/types/user.types.ts b/client/src/features/user/types/user.types.ts similarity index 100% rename from frontend/src/features/user/types/user.types.ts rename to client/src/features/user/types/user.types.ts diff --git a/frontend/src/features/user/user-provider.tsx b/client/src/features/user/user-provider.tsx similarity index 97% rename from frontend/src/features/user/user-provider.tsx rename to client/src/features/user/user-provider.tsx index 610305d..ccdcc8e 100644 --- a/frontend/src/features/user/user-provider.tsx +++ b/client/src/features/user/user-provider.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useAtom } from 'jotai'; import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; import React, { useEffect } from 'react'; diff --git a/frontend/src/features/workspace/services/workspace-service.ts b/client/src/features/workspace/services/workspace-service.ts similarity index 100% rename from frontend/src/features/workspace/services/workspace-service.ts rename to client/src/features/workspace/services/workspace-service.ts diff --git a/frontend/src/features/workspace/types/workspace.types.ts b/client/src/features/workspace/types/workspace.types.ts similarity index 100% rename from frontend/src/features/workspace/types/workspace.types.ts rename to client/src/features/workspace/types/workspace.types.ts diff --git a/frontend/src/lib/api-client.ts b/client/src/lib/api-client.ts similarity index 95% rename from frontend/src/lib/api-client.ts rename to client/src/lib/api-client.ts index e7e4102..f186322 100644 --- a/frontend/src/lib/api-client.ts +++ b/client/src/lib/api-client.ts @@ -3,7 +3,7 @@ import Cookies from "js-cookie"; import Routes from "@/lib/routes"; const api: AxiosInstance = axios.create({ - baseURL: process.env.NEXT_PUBLIC_BACKEND_API_URL, + baseURL: import.meta.env.VITE_BACKEND_API_URL }); api.interceptors.request.use(config => { diff --git a/frontend/src/lib/jotai-helper.ts b/client/src/lib/jotai-helper.ts similarity index 100% rename from frontend/src/lib/jotai-helper.ts rename to client/src/lib/jotai-helper.ts diff --git a/frontend/src/lib/routes.ts b/client/src/lib/routes.ts similarity index 100% rename from frontend/src/lib/routes.ts rename to client/src/lib/routes.ts diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..b2f8cb8 --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,29 @@ +import '@mantine/core/styles.css'; +import '@mantine/spotlight/styles.css'; +import '@mantine/tiptap/styles.css'; + +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; +import { theme } from '@/theme'; +import { MantineProvider } from '@mantine/core'; +import { TanstackProvider } from '@/components/providers/tanstack-provider'; +import CustomToaster from '@/components/ui/custom-toaster'; +import { BrowserRouter } from 'react-router-dom'; + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); + +root.render( + + + + + + + + , +); + + + + diff --git a/frontend/src/app/(auth)/layout.tsx b/client/src/pages/auth/layout.tsx similarity index 100% rename from frontend/src/app/(auth)/layout.tsx rename to client/src/pages/auth/layout.tsx diff --git a/frontend/src/app/(auth)/login/page.tsx b/client/src/pages/auth/login.tsx similarity index 89% rename from frontend/src/app/(auth)/login/page.tsx rename to client/src/pages/auth/login.tsx index 287f941..96c6c11 100644 --- a/frontend/src/app/(auth)/login/page.tsx +++ b/client/src/pages/auth/login.tsx @@ -1,5 +1,3 @@ -'use client'; - import { LoginForm } from '@/features/auth/components/login-form'; export default function LoginPage() { diff --git a/frontend/src/app/(auth)/signup/page.tsx b/client/src/pages/auth/signup.tsx similarity index 90% rename from frontend/src/app/(auth)/signup/page.tsx rename to client/src/pages/auth/signup.tsx index 884e90b..1e07877 100644 --- a/frontend/src/app/(auth)/signup/page.tsx +++ b/client/src/pages/auth/signup.tsx @@ -1,5 +1,3 @@ -'use client'; - import { SignUpForm } from '@/features/auth/components/sign-up-form'; export default function SignUpPage() { diff --git a/frontend/src/app/(dashboard)/home/page.tsx b/client/src/pages/dashboard/home.tsx similarity index 80% rename from frontend/src/app/(dashboard)/home/page.tsx rename to client/src/pages/dashboard/home.tsx index 2befe83..81356ba 100644 --- a/frontend/src/app/(dashboard)/home/page.tsx +++ b/client/src/pages/dashboard/home.tsx @@ -1,8 +1,5 @@ -'use client'; - import { useAtom } from 'jotai'; import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import usePage from '@/features/page/hooks/usePage'; export default function Home() { const [currentUser] = useAtom(currentUserAtom); @@ -10,7 +7,6 @@ export default function Home() { return ( <> Hello {currentUser && currentUser.user.name}! - ); } diff --git a/client/src/pages/page/page.tsx b/client/src/pages/page/page.tsx new file mode 100644 index 0000000..9c8b87f --- /dev/null +++ b/client/src/pages/page/page.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; +import Editor from '@/features/editor/editor'; + +export default function Page() { + const { pageId } = useParams(); + + return ; +} diff --git a/frontend/src/components/welcome/welcome.tsx b/client/src/pages/welcome.tsx similarity index 89% rename from frontend/src/components/welcome/welcome.tsx rename to client/src/pages/welcome.tsx index 60082ff..21fd5de 100644 --- a/frontend/src/components/welcome/welcome.tsx +++ b/client/src/pages/welcome.tsx @@ -1,5 +1,5 @@ import { Title, Text } from '@mantine/core'; -import { ThemeToggle } from '../theme-toggle'; +import { ThemeToggle } from '@/components/theme-toggle'; export function Welcome() { return ( diff --git a/frontend/src/app/theme.ts b/client/src/theme.ts similarity index 85% rename from frontend/src/app/theme.ts rename to client/src/theme.ts index 63df4d8..a3bea32 100644 --- a/frontend/src/app/theme.ts +++ b/client/src/theme.ts @@ -1,5 +1,3 @@ -'use client'; - import { createTheme } from '@mantine/core'; export const theme = createTheme({ diff --git a/client/src/vite-env.d.ts b/client/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..dc77fc4 --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": false, + "strictNullChecks": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..4c71434 --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': '/src' + } + } +}) diff --git a/frontend/.env.example b/frontend/.env.example deleted file mode 100644 index 57c5257..0000000 --- a/frontend/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_BACKEND_API_URL=http://localhost:3001 -NEXT_PUBLIC_COLLABORATION_URL= diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/frontend/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index fa76bed..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -.env -package-lock.json -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index aaba163..0000000 --- a/frontend/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -``` - -Open [http://localhost:3001](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/workspace-members.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! diff --git a/frontend/next.config.js b/frontend/next.config.js deleted file mode 100644 index 767719f..0000000 --- a/frontend/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {} - -module.exports = nextConfig diff --git a/frontend/src/app/(dashboard)/(page)/p/[pageId]/page.tsx b/frontend/src/app/(dashboard)/(page)/p/[pageId]/page.tsx deleted file mode 100644 index 7fc7b9a..0000000 --- a/frontend/src/app/(dashboard)/(page)/p/[pageId]/page.tsx +++ /dev/null @@ -1,16 +0,0 @@ -'use client'; - -import { useParams } from 'next/navigation'; -import dynamic from 'next/dynamic'; - -const Editor = dynamic(() => import("@/features/editor/Editor"), { - ssr: false, -}); - -export default function Page() { - const { pageId } = useParams(); - - return ( - - ); -} diff --git a/frontend/src/app/(dashboard)/layout.tsx b/frontend/src/app/(dashboard)/layout.tsx deleted file mode 100644 index 941c2a9..0000000 --- a/frontend/src/app/(dashboard)/layout.tsx +++ /dev/null @@ -1,21 +0,0 @@ -'use client'; - -import dynamic from 'next/dynamic'; -import { UserProvider } from '@/features/user/user-provider'; - -const Shell = dynamic(() => import('./shell'), { - ssr: false, -}); - -export default function DashboardLayout({ children }: { - children: React.ReactNode -}) { - - return ( - - - {children} - - - ); -} diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/frontend/src/app/favicon.ico and /dev/null differ diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx deleted file mode 100644 index 4b9861e..0000000 --- a/frontend/src/app/layout.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import '@mantine/core/styles.css'; -import '@mantine/spotlight/styles.css'; -import '@mantine/tiptap/styles.css'; - -import type { Metadata } from 'next'; -import { TanstackProvider } from '@/components/providers/tanstack-provider'; -import CustomToaster from '@/components/ui/custom-toaster'; -import { theme } from '@/app/theme'; -import { ColorSchemeScript, MantineProvider } from '@mantine/core'; - -export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', - viewport: { - width: 'device-width', - initialScale: 1, - maximumScale: 1, - }, -}; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - - - - - - {children} - - - - - - ); -} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx deleted file mode 100644 index be72b1a..0000000 --- a/frontend/src/app/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -'use client' -import { Welcome } from '@/components/welcome/welcome'; - -export default function Home() { - return ; -} diff --git a/frontend/src/components/icons.tsx b/frontend/src/components/icons.tsx deleted file mode 100644 index b489aca..0000000 --- a/frontend/src/components/icons.tsx +++ /dev/null @@ -1,48 +0,0 @@ -type IconProps = React.HTMLAttributes - -export const Icons = { - - logo: (props: IconProps) => ( - - - - - - ), - spinner: (props: IconProps) => ( - - - - ), -} diff --git a/frontend/src/features/editor/Editor.tsx b/frontend/src/features/editor/Editor.tsx deleted file mode 100644 index d3b28be..0000000 --- a/frontend/src/features/editor/Editor.tsx +++ /dev/null @@ -1,110 +0,0 @@ -'use client'; - -import { HocuspocusProvider } from '@hocuspocus/provider'; -import * as Y from 'yjs'; -import { EditorContent, useEditor } from '@tiptap/react'; -import { StarterKit } from '@tiptap/starter-kit'; -import { Placeholder } from '@tiptap/extension-placeholder'; -import { Collaboration } from '@tiptap/extension-collaboration'; -import { CollaborationCursor } from '@tiptap/extension-collaboration-cursor'; -import { useEffect, useLayoutEffect, useState } from 'react'; -import { useAtom } from 'jotai/index'; -import { currentUserAtom } from '@/features/user/atoms/current-user-atom'; -import { authTokensAtom } from '@/features/auth/atoms/auth-tokens-atom'; -import useCollaborationUrl from '@/features/editor/hooks/use-collaboration-url'; -import '@/features/editor/styles/editor.css'; - -interface EditorProps{ - pageId: string, -} - -const colors = ['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D'] -const getRandomElement = list => list[Math.floor(Math.random() * list.length)] -const getRandomColor = () => getRandomElement(colors) - -export default function Editor({ pageId }: EditorProps ) { - const [token] = useAtom(authTokensAtom); - const collaborationURL = useCollaborationUrl(); - const [provider, setProvider] = useState(); - const [doc, setDoc] = useState() - - useLayoutEffect(() => { - if (token) { - const ydoc = new Y.Doc(); - - const provider = new HocuspocusProvider({ - url: collaborationURL, - name: pageId, - document: ydoc, - token: token?.accessToken, - }); - - setDoc(ydoc); - setProvider(provider); - - return () => { - ydoc.destroy(); - provider.destroy(); - }; - } - }, [pageId, token]); - console.log(token) - - if(!doc || !provider){ - return null; - } - - console.log(doc) - - return ( - - ); -} - -interface TiptapEditorProps { - ydoc: Y.Doc, - provider: HocuspocusProvider -} - -function TiptapEditor({ ydoc, provider }: TiptapEditorProps) { - const [currentUser] = useAtom(currentUserAtom); - - const extensions = [ - StarterKit.configure({ - history: false, - }), - Placeholder.configure({ - placeholder: 'Write here', - }), - Collaboration.configure({ - document: ydoc, - }), - CollaborationCursor.configure({ - provider - }), - ]; - - const editor = useEditor({ - extensions: extensions, - }); - - useEffect(() => { - if (editor && currentUser.user){ - editor.chain().focus().updateUser({...currentUser.user, color: getRandomColor()}).run() - } - }, [editor, currentUser.user]) - - useEffect(() => { - provider.on('status', event => { - console.log(event) - }) - - }, [provider]) - - - return ( - <> - - - ); -} diff --git a/frontend/src/features/editor/styles/editor.css b/frontend/src/features/editor/styles/editor.css deleted file mode 100644 index 759cff9..0000000 --- a/frontend/src/features/editor/styles/editor.css +++ /dev/null @@ -1,26 +0,0 @@ -/* Give a remote user a caret */ -.collaboration-cursor__caret { - border-left: 1px solid #0d0d0d; - border-right: 1px solid #0d0d0d; - margin-left: -1px; - margin-right: -1px; - pointer-events: none; - position: relative; - word-break: normal; -} - -/* Render the username above the caret */ -.collaboration-cursor__label { - border-radius: 3px 3px 3px 0; - color: #0d0d0d; - font-size: 12px; - font-style: normal; - font-weight: 600; - left: -1px; - line-height: normal; - padding: 0.1rem 0.3rem; - position: absolute; - top: -1.4em; - user-select: none; - white-space: nowrap; -} diff --git a/frontend/src/features/page/tree/components/merge-refs.ts b/frontend/src/features/page/tree/components/merge-refs.ts deleted file mode 100644 index 1f974d2..0000000 --- a/frontend/src/features/page/tree/components/merge-refs.ts +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; - -type AnyRef = React.MutableRefObject | React.RefCallback | null; - -export default function mergeRefs(...refs: AnyRef[]) { - return (instance: any) => { - refs.forEach((ref) => { - if (typeof ref === "function") { - ref(instance); - } else if (ref != null) { - ref.current = instance; - } - }); - }; -} \ No newline at end of file diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json deleted file mode 100644 index f15c41a..0000000 --- a/frontend/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "strictNullChecks": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -}