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/ @ts-ignore
- T>(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"]
-}