From 3b72c843db5c18d10b605c07fc2769c2332c8a2e Mon Sep 17 00:00:00 2001 From: grimsi <9295182+grimsi@users.noreply.github.com> Date: Sun, 19 May 2024 11:48:00 +0200 Subject: [PATCH] Added ProfileMenu Bit of refactoring --- frontend/App.tsx | 30 +++++----- frontend/components/ProfileMenu.tsx | 8 ++- frontend/components/theming/ThemeSelector.tsx | 55 ++++++++++++++++++ frontend/index.ts | 5 -- frontend/index.tsx | 13 +++++ frontend/main.css | 8 ++- frontend/routes.tsx | 36 +++++++++--- frontend/views/LoginView.tsx | 12 ++-- frontend/views/MainLayout.tsx | 42 ++++++++------ frontend/views/ProfileView.tsx | 47 +++++++++++++++ frontend/views/SetupView.tsx | 58 +++---------------- 11 files changed, 206 insertions(+), 108 deletions(-) create mode 100644 frontend/components/theming/ThemeSelector.tsx delete mode 100644 frontend/index.ts create mode 100644 frontend/index.tsx create mode 100644 frontend/views/ProfileView.tsx diff --git a/frontend/App.tsx b/frontend/App.tsx index a36b11b..40ece36 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -1,25 +1,23 @@ -import router from 'Frontend/routes.js'; -import {AuthProvider} from 'Frontend/util/auth.js'; -import {RouterProvider} from 'react-router-dom'; +import {Outlet, useNavigate} from 'react-router-dom'; import "./main.css"; -import {IconContext} from "@phosphor-icons/react"; -import {StrictMode} from "react"; import {NextUIProvider} from "@nextui-org/react"; import {ThemeProvider as NextThemesProvider} from "next-themes"; import {themeNames} from "Frontend/theming/themes"; +import {AuthProvider} from "Frontend/util/auth"; +import {IconContext} from "@phosphor-icons/react"; export default function App() { + const navigate = useNavigate(); + return ( - - - - - - - - - - - + + + + + + + + + ); } diff --git a/frontend/components/ProfileMenu.tsx b/frontend/components/ProfileMenu.tsx index 55028c7..ec308c8 100644 --- a/frontend/components/ProfileMenu.tsx +++ b/frontend/components/ProfileMenu.tsx @@ -1,15 +1,17 @@ import {useAuth} from "Frontend/util/auth"; import {GearFine, Question, SignOut, User} from "@phosphor-icons/react"; import {Avatar, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger} from "@nextui-org/react"; +import {useNavigate} from "react-router-dom"; export default function ProfileMenu() { const {state, logout} = useAuth(); + const navigate = useNavigate(); const profileMenuItems = [ { label: "My Profile", icon: , - onClick: () => alert("Profile") + onClick: () => navigate('/profile') }, { label: "Administration", @@ -26,7 +28,7 @@ export default function ProfileMenu() { label: "Sign Out", icon: , onClick: () => logout(), - color: "danger" + color: "primary" }, ]; @@ -38,7 +40,7 @@ export default function ProfileMenu() { as="button" className="transition-transform size-8" classNames={{ - base: "bg-gradient-to-br from-primary-400 to-primary-700", + base: "gradient-primary", icon: "text-background/80" }} /> diff --git a/frontend/components/theming/ThemeSelector.tsx b/frontend/components/theming/ThemeSelector.tsx new file mode 100644 index 0000000..d7a9472 --- /dev/null +++ b/frontend/components/theming/ThemeSelector.tsx @@ -0,0 +1,55 @@ +import {useTheme} from "next-themes"; +import React, {useLayoutEffect, useState} from "react"; +import {Switch} from "@nextui-org/react"; +import {Moon, SunDim} from "@phosphor-icons/react"; +import {themes} from "Frontend/theming/themes"; +import {Theme} from "Frontend/theming/theme"; +import ThemePreview from "Frontend/components/theming/ThemePreview"; + +export function ThemeSelector() { + + const {theme, setTheme} = useTheme(); + const [isSelected, setIsSelected] = useState(theme ? theme.includes("light") : false); + const [currentTheme, setCurrentTheme] = useState(theme?.substring(0, theme?.lastIndexOf("-"))); + + useLayoutEffect(() => setTheme(`${currentTheme}-${mode()}`), [isSelected]); + + function mode(): "light" | "dark" { + return isSelected ? "light" : "dark"; + } + + return ( +
+
+ } + endContent={} + isSelected={isSelected} + onValueChange={() => { + setIsSelected(!isSelected); + }} + /> + +
+
+ { + //min-w-[468px] + themes.map(((t: Theme) => ( +
{ + setCurrentTheme(t.name); + setTheme(`${t.name}-${mode()}`); + }}> + +
+ ))) + } +
+
+ ) +} \ No newline at end of file diff --git a/frontend/index.ts b/frontend/index.ts deleted file mode 100644 index 71585b2..0000000 --- a/frontend/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {createRoot} from 'react-dom/client'; -import {createElement} from "react"; -import App from "Frontend/App"; - -createRoot(document.getElementById('outlet')!).render(createElement(App)); diff --git a/frontend/index.tsx b/frontend/index.tsx new file mode 100644 index 0000000..1fbb360 --- /dev/null +++ b/frontend/index.tsx @@ -0,0 +1,13 @@ +import {createRoot} from 'react-dom/client'; +import {StrictMode} from "react"; +import {RouterProvider} from "react-router-dom"; +import router from "./routes"; + +const container = document.getElementById('outlet')!; +const root = createRoot(container); + +root.render( + + + +); diff --git a/frontend/main.css b/frontend/main.css index bd6213e..ff8b74c 100644 --- a/frontend/main.css +++ b/frontend/main.css @@ -1,3 +1,9 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer components { + .gradient-primary { + @apply bg-gradient-to-br from-primary-400 to-primary-700; + } +} \ No newline at end of file diff --git a/frontend/routes.tsx b/frontend/routes.tsx index b3f6453..0bde41f 100644 --- a/frontend/routes.tsx +++ b/frontend/routes.tsx @@ -4,20 +4,38 @@ import LoginView from "Frontend/views/LoginView"; import MainLayout from "Frontend/views/MainLayout"; import TestView from "Frontend/views/TestView"; import SetupView from "Frontend/views/SetupView"; +import ProfileView from "Frontend/views/ProfileView"; +import {ThemeSelector} from "Frontend/components/theming/ThemeSelector"; +import App from "Frontend/App"; export const routes = protectRoutes([ { - element: , - handle: {requiresLogin: true}, + element: , + handle: {requiresLogin: false}, children: [ - {path: '/', element: , handle: {title: 'Gameyfin - Test'}}, + { + element: , + handle: {requiresLogin: true}, + children: [ + { + index: true, element: + }, + { + path: 'profile', + element: , + children: [ + {path: 'appearance', element: } + ] + } + ] + }, + { + path: '/login', element: , handle: {requiresLogin: false} + }, + { + path: '/setup', element: , handle: {requiresLogin: false} + } ], - }, - { - path: '/login', element: , handle: {requiresLogin: false} - }, - { - path: '/setup', element: , handle: {requiresLogin: false} } ]) as RouteObject[]; diff --git a/frontend/views/LoginView.tsx b/frontend/views/LoginView.tsx index baca827..421c4f3 100644 --- a/frontend/views/LoginView.tsx +++ b/frontend/views/LoginView.tsx @@ -1,9 +1,9 @@ import {useAuth} from "Frontend/util/auth"; -import {useEffect, useLayoutEffect, useState} from "react"; -import {Link, useNavigate} from "react-router-dom"; +import {useLayoutEffect, useState} from "react"; import {XCircle} from "@phosphor-icons/react"; -import {Button, Card, CardBody, CardHeader, Input} from "@nextui-org/react"; +import {Button, Card, CardBody, CardHeader, Input, Link} from "@nextui-org/react"; import {Alert, AlertDescription, AlertTitle} from "Frontend/@/components/ui/alert"; +import {useNavigate} from "react-router-dom"; export default function LoginView() { const {state, login} = useAuth(); @@ -16,13 +16,13 @@ export default function LoginView() { useLayoutEffect(() => { if (state.user) { - const path = url ? new URL(url, document.baseURI).pathname : '/' + const path = url ? new URL(url, document.baseURI).pathname : '/' navigate(path, {replace: true}); } }, [state.user]); return ( -
+
- Forgot password? + Forgot password? diff --git a/frontend/views/MainLayout.tsx b/frontend/views/MainLayout.tsx index 13013bf..7119bf9 100644 --- a/frontend/views/MainLayout.tsx +++ b/frontend/views/MainLayout.tsx @@ -1,20 +1,22 @@ import {useRouteMetadata} from 'Frontend/util/routing.js'; import {useEffect} from 'react'; import ProfileMenu from "Frontend/components/ProfileMenu"; -import {Outlet} from "react-router-dom"; -import {Navbar, NavbarBrand, NavbarContent, NavbarItem} from "@nextui-org/react"; +import {Divider, Link, Navbar, NavbarBrand, NavbarContent, NavbarItem} from "@nextui-org/react"; import GameyfinLogo from "Frontend/components/theming/GameyfinLogo"; +import * as PackageJson from "../../package.json"; +import {Outlet, useNavigate} from "react-router-dom"; export default function MainLayout() { const currentTitle = `Gameyfin - ${useRouteMetadata()?.title}` ?? 'Gameyfin'; useEffect(() => { document.title = currentTitle; }, [currentTitle]); + const navigate = useNavigate(); return ( - <> +
- + navigate('/')}> @@ -24,19 +26,25 @@ export default function MainLayout() { - - +
+
+ +
+
+ + + +
+

Gameyfin {PackageJson.version}

+

+ © {(new Date()).getFullYear()}  + + Gameyfin contributors + +

+
+
); } -/**/ +/**/ diff --git a/frontend/views/ProfileView.tsx b/frontend/views/ProfileView.tsx new file mode 100644 index 0000000..a087933 --- /dev/null +++ b/frontend/views/ProfileView.tsx @@ -0,0 +1,47 @@ +import {Listbox, ListboxItem} from "@nextui-org/react"; +import {GearFine, Palette, User} from "@phosphor-icons/react"; +import {Outlet, useNavigate} from "react-router-dom"; +import {useState} from "react"; + +export default function ProfileView() { + const navigate = useNavigate(); + const [selectedKeys, setSelectedKeys] = useState(new Set(["profile"])); + + const menuItems = [ + { + title: "My Profile", + key: "profile", + icon: , + action: () => navigate('/profile') + }, + { + title: "Appearance", + key: "appearance", + icon: , + action: () => navigate('appearance') + }, + { + title: "Manage account", + icon: , + key: "account-management", + action: () => navigate('account-management') + } + ] + + return ( +
+
+ + {menuItems.map((i) => ( + + {i.title} + + ))} + +
+
+ +
+
+ ); +} \ No newline at end of file diff --git a/frontend/views/SetupView.tsx b/frontend/views/SetupView.tsx index 8af28bc..765c775 100644 --- a/frontend/views/SetupView.tsx +++ b/frontend/views/SetupView.tsx @@ -1,15 +1,12 @@ -import React, {useLayoutEffect, useState} from 'react'; +import React from 'react'; import * as Yup from 'yup'; import Wizard from "Frontend/components/wizard/Wizard"; import WizardStep from "Frontend/components/wizard/WizardStep"; import Input from "Frontend/components/Input"; -import {GearFine, HandWaving, Moon, Palette, SunDim, User} from "@phosphor-icons/react"; -import ThemePreview from "Frontend/components/theming/ThemePreview"; -import {themes} from "Frontend/theming/themes"; -import {Card, Switch} from "@nextui-org/react"; -import {useTheme} from "next-themes"; -import {Theme} from "Frontend/theming/theme"; +import {GearFine, HandWaving, Palette, User} from "@phosphor-icons/react"; +import {Card} from "@nextui-org/react"; import {UserEndpoint} from "Frontend/generated/endpoints"; +import {ThemeSelector} from "Frontend/components/theming/ThemeSelector"; function WelcomeStep() { return ( @@ -36,50 +33,9 @@ function WelcomeStep() { } function ThemeStep() { - const {theme, setTheme} = useTheme(); - const [isSelected, setIsSelected] = useState(theme ? theme.includes("light") : false); - const [currentTheme, setCurrentTheme] = useState(theme?.substring(0, theme?.lastIndexOf("-"))); - - useLayoutEffect(() => setTheme(`${currentTheme}-${mode()}`), [isSelected]); - - function mode(): "light" | "dark" { - return isSelected ? "light" : "dark"; - } - return ( -
-
- } - endContent={} - isSelected={isSelected} - onValueChange={() => { - setIsSelected(!isSelected); - }} - /> - -
-
- { - //min-w-[468px] - themes.map(((t: Theme) => ( -
{ - setCurrentTheme(t.name); - setTheme(`${t.name}-${mode()}`); - }}> - -
- ))) - } -
-
- ) + + ); } function UserStep() { @@ -127,7 +83,7 @@ function SettingsStep() { } const SetupView = () => ( -
+