From 7b51166db847fd8bedbb9ecd92d5cc44a5d27d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Fri, 21 Feb 2025 22:00:32 -0300 Subject: [PATCH] update layu --- dashboard_js/app/components/menu.jsx | 88 ++++++++ dashboard_js/app/hooks/use-auth.jsx | 7 + dashboard_js/app/layouts/app/_user-menu.jsx | 57 +++++ .../app/layouts/{app.jsx => app/layout.jsx} | 48 +++-- dashboard_js/app/layouts/auth/layout.jsx | 4 +- dashboard_js/app/routes.js | 9 +- .../auth/{signin => login}/_password.jsx | 0 .../routes/auth/{signin => login}/index.jsx | 0 dashboard_js/app/routes/auth/logout.jsx | 15 ++ .../auth/{signup => register}/index.jsx | 0 dashboard_js/app/routes/auth/support.jsx | 9 + .../app/routes/auth/support/index.jsx | 5 - dashboard_js/app/routes/users/index.jsx | 2 +- dashboard_js/package-lock.json | 201 ++++++++++++++++++ dashboard_js/package.json | 1 + 15 files changed, 417 insertions(+), 29 deletions(-) create mode 100644 dashboard_js/app/components/menu.jsx create mode 100644 dashboard_js/app/layouts/app/_user-menu.jsx rename dashboard_js/app/layouts/{app.jsx => app/layout.jsx} (57%) rename dashboard_js/app/routes/auth/{signin => login}/_password.jsx (100%) rename dashboard_js/app/routes/auth/{signin => login}/index.jsx (100%) create mode 100644 dashboard_js/app/routes/auth/logout.jsx rename dashboard_js/app/routes/auth/{signup => register}/index.jsx (100%) create mode 100644 dashboard_js/app/routes/auth/support.jsx delete mode 100644 dashboard_js/app/routes/auth/support/index.jsx diff --git a/dashboard_js/app/components/menu.jsx b/dashboard_js/app/components/menu.jsx new file mode 100644 index 0000000..b154822 --- /dev/null +++ b/dashboard_js/app/components/menu.jsx @@ -0,0 +1,88 @@ +import { createContext, useContext, useState } from 'react' +import { + Menu as HeadlessMenu, + MenuButton as HeadlessMenuButton, + MenuItem as HeadlessMenuItem, + MenuItems as HeadlessMenuItems, +} from '@headlessui/react' +import { Loader } from './loader' +import clsx from 'clsx' + +const MenuContext = createContext(null) + +export function useMenu() { + return useContext(MenuContext) +} + +export function Menu({ + children, + isLoading: defaultIsLoading = false, + ...props +}) { + const [isLoading, setIsLoading] = useState(defaultIsLoading) + + return ( + + {children} + + ) +} + +export function MenuButton({ children, className, disabled, ...props }) { + const { isLoading } = useContext(MenuContext) + + return ( + + {isLoading && ( +
+ +
+ )} + + {children} +
+ ) +} + +export function MenuItem({ children, className, ...props }) { + return ( + + {children} + + ) +} + +export function MenuItems({ children, className, ...props }) { + return ( + + {children} + + ) +} + +export function MenuSeparator() { + return
+} diff --git a/dashboard_js/app/hooks/use-auth.jsx b/dashboard_js/app/hooks/use-auth.jsx index 1218c37..bb03f70 100644 --- a/dashboard_js/app/hooks/use-auth.jsx +++ b/dashboard_js/app/hooks/use-auth.jsx @@ -4,6 +4,7 @@ import { useCallback, createContext, useState, + useEffect, } from 'react' import * as Auth from 'aws-amplify/auth' @@ -22,6 +23,12 @@ export function useAuth() { export function AuthProvider({ children }) { const [authUser, setAuthUser] = useState(null) + useEffect(() => { + Auth.fetchUserAttributes().then((user) => { + setAuthUser(user) + }) + }, []) + const signIn = useCallback(async ({ username, password }) => { const signInOut = await Auth.signIn({ username, diff --git a/dashboard_js/app/layouts/app/_user-menu.jsx b/dashboard_js/app/layouts/app/_user-menu.jsx new file mode 100644 index 0000000..786ce9d --- /dev/null +++ b/dashboard_js/app/layouts/app/_user-menu.jsx @@ -0,0 +1,57 @@ +import { useAuth } from '~/hooks/use-auth' +import { MenuButton, MenuItem as HeadlessMenuItem } from '@headlessui/react' +import { + ChevronDownIcon, + AdjustmentsHorizontalIcon, + ArrowRightStartOnRectangleIcon, +} from '@heroicons/react/24/solid' +import { BookOpenIcon } from '@heroicons/react/24/outline' +import { Menu, MenuItem, MenuItems, MenuSeparator } from '~/components/menu' +import clsx from 'clsx' +import { Link } from 'react-router' + +export function UserMenu({ className }) { + const { authUser } = useAuth() + + return ( + + +
+ {/* */} +
+ +
+ + + {/* User */} + +
{authUser?.name}
+
+ {authUser?.email} +
+
+ + + + + Sala de aula + + + + + + Configurações + + + + Sair + +
+
+ ) +} diff --git a/dashboard_js/app/layouts/app.jsx b/dashboard_js/app/layouts/app/layout.jsx similarity index 57% rename from dashboard_js/app/layouts/app.jsx rename to dashboard_js/app/layouts/app/layout.jsx index 5e6beaa..b31c66f 100644 --- a/dashboard_js/app/layouts/app.jsx +++ b/dashboard_js/app/layouts/app/layout.jsx @@ -2,8 +2,10 @@ import { useNavigation, redirect, useNavigate } from 'react-router' import { fetchAuthSession } from 'aws-amplify/auth' import { Link } from 'react-router' import { Outlet } from 'react-router' -import { useAuth } from '~/hooks/use-auth' import { Container } from '~/components/container' +import { Smallest, Regular } from '~/components/logo' +import { clsx } from 'clsx' +import { UserMenu } from './_user-menu' export async function clientLoader() { const session = await fetchAuthSession() @@ -15,14 +17,14 @@ export async function clientLoader() { export default function Layout() { const navigation = useNavigation() - const navigate = useNavigate() - const { signOut } = useAuth() const isNavigating = Boolean(navigation.location) return ( - <> - +
+
+
+ +
+
- {isNavigating ? : } - +
+ {isNavigating ? : } +
+
+ ) } function Loading() { return ( -
+
@@ -67,3 +70,12 @@ function Loading() { ) } + +function Logo({ className }) { + return ( +
+ + +
+ ) +} diff --git a/dashboard_js/app/layouts/auth/layout.jsx b/dashboard_js/app/layouts/auth/layout.jsx index ba30368..21a9e42 100644 --- a/dashboard_js/app/layouts/auth/layout.jsx +++ b/dashboard_js/app/layouts/auth/layout.jsx @@ -1,7 +1,8 @@ -import { Outlet, Link, useSearchParams } from 'react-router' +import { Outlet, Link as RouterLink, useSearchParams } from 'react-router' import { Regular as Logo } from '../../components/logo' import Pulse from './_pulse' import WomanImg from './woman.png' +import { Link } from './_link' export default function Auth() { const [searchParams] = useSearchParams() @@ -48,6 +49,7 @@ export default function Auth() { pathname: '/auth/support', search: searchParams.toString(), }} + as={RouterLink} state={{ location: window.location.href }} > Precisa de ajuda? Fale conosco diff --git a/dashboard_js/app/routes.js b/dashboard_js/app/routes.js index 66a6890..c7b34ca 100644 --- a/dashboard_js/app/routes.js +++ b/dashboard_js/app/routes.js @@ -1,7 +1,7 @@ import { index, route, layout, prefix } from '@react-router/dev/routes' export default [ - layout('layouts/app.jsx', [ + layout('layouts/app/layout.jsx', [ index('routes/overview/index.jsx'), route('users', 'routes/users/index.jsx'), route('users/:id', 'routes/users/$id.jsx'), @@ -13,11 +13,12 @@ export default [ ]), layout('layouts/auth/layout.jsx', [ ...prefix('auth', [ - index('routes/auth/signin/index.jsx'), - route('signup', 'routes/auth/signup/index.jsx'), + index('routes/auth/login/index.jsx'), + route('logout', 'routes/auth/logout.jsx'), + route('register', 'routes/auth/register/index.jsx'), route('forgot', 'routes/auth/forgot/index.jsx'), route('passcode', 'routes/auth/passcode/index.jsx'), - route('support', 'routes/auth/support/index.jsx'), + route('support', 'routes/auth/support.jsx'), ]), ]), ] diff --git a/dashboard_js/app/routes/auth/signin/_password.jsx b/dashboard_js/app/routes/auth/login/_password.jsx similarity index 100% rename from dashboard_js/app/routes/auth/signin/_password.jsx rename to dashboard_js/app/routes/auth/login/_password.jsx diff --git a/dashboard_js/app/routes/auth/signin/index.jsx b/dashboard_js/app/routes/auth/login/index.jsx similarity index 100% rename from dashboard_js/app/routes/auth/signin/index.jsx rename to dashboard_js/app/routes/auth/login/index.jsx diff --git a/dashboard_js/app/routes/auth/logout.jsx b/dashboard_js/app/routes/auth/logout.jsx new file mode 100644 index 0000000..b6bd5c2 --- /dev/null +++ b/dashboard_js/app/routes/auth/logout.jsx @@ -0,0 +1,15 @@ +import { useNavigate } from 'react-router' +import { useEffect } from 'react' +import { useAuth } from '~/hooks/use-auth' + +export default function Component() { + const { signOut } = useAuth() + const navigate = useNavigate() + + useEffect(() => { + signOut() + navigate('/auth') + }, []) + + return <> +} diff --git a/dashboard_js/app/routes/auth/signup/index.jsx b/dashboard_js/app/routes/auth/register/index.jsx similarity index 100% rename from dashboard_js/app/routes/auth/signup/index.jsx rename to dashboard_js/app/routes/auth/register/index.jsx diff --git a/dashboard_js/app/routes/auth/support.jsx b/dashboard_js/app/routes/auth/support.jsx new file mode 100644 index 0000000..9f4fc68 --- /dev/null +++ b/dashboard_js/app/routes/auth/support.jsx @@ -0,0 +1,9 @@ +import { Heading } from '~/components/heading' + +export default function Component() { + return ( + <> + ... + + ) +} diff --git a/dashboard_js/app/routes/auth/support/index.jsx b/dashboard_js/app/routes/auth/support/index.jsx deleted file mode 100644 index aab2041..0000000 --- a/dashboard_js/app/routes/auth/support/index.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Heading } from '../../../components/heading' - -export default function Component() { - return <>... -} diff --git a/dashboard_js/app/routes/users/index.jsx b/dashboard_js/app/routes/users/index.jsx index 231a381..eca6b70 100644 --- a/dashboard_js/app/routes/users/index.jsx +++ b/dashboard_js/app/routes/users/index.jsx @@ -6,7 +6,7 @@ export function meta({}) { export default function Component() { const info = useSuspenseQuery({ - queryKey: ['todos'], + queryKey: ['users'], queryFn: async () => { await new Promise((r) => setTimeout(r, 3000)) return { data: 'todos' } diff --git a/dashboard_js/package-lock.json b/dashboard_js/package-lock.json index 4197db8..268eb54 100644 --- a/dashboard_js/package-lock.json +++ b/dashboard_js/package-lock.json @@ -7,6 +7,7 @@ "name": "dashboard_js", "dependencies": { "@brazilian-utils/brazilian-utils": "^1.0.0-rc.12", + "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.2.0", "@hookform/error-message": "^2.0.1", "@hookform/resolvers": "^4.1.0", @@ -2248,6 +2249,78 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@headlessui/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.0.tgz", + "integrity": "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.17.1", + "@react-aria/interactions": "^3.21.3", + "@tanstack/react-virtual": "^3.8.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/@heroicons/react": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", @@ -2430,6 +2503,71 @@ "node": ">=14" } }, + "node_modules/@react-aria/focus": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.1.tgz", + "integrity": "sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.23.0", + "@react-aria/utils": "^3.27.0", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.23.0.tgz", + "integrity": "sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.27.0", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz", + "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-stately/utils": "^3.10.5", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@react-router/dev": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.2.0.tgz", @@ -2578,6 +2716,27 @@ "react-router": "7.2.0" } }, + "node_modules/@react-stately/utils": { + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz", + "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.8", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", @@ -3640,6 +3799,15 @@ "node": ">=16.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.8.tgz", @@ -3904,6 +4072,33 @@ "react": "^18 || ^19" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.0.tgz", + "integrity": "sha512-CchF0NlLIowiM2GxtsoKBkXA4uqSnY2KvnXo+kyUFD4a4ll6+J0qzoRsUPMwXV/H26lRsxgJIr/YmjYum2oEjg==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.0.tgz", + "integrity": "sha512-NBKJP3OIdmZY3COJdWkSonr50FMVIi+aj5ZJ7hI/DTpEKg2RMfo/KvP8A3B/zOSpMgIe52B5E2yn7rryULzA6g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@types/aws-lambda": { "version": "8.10.147", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.147.tgz", @@ -6892,6 +7087,12 @@ ], "license": "MIT" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.8.tgz", diff --git a/dashboard_js/package.json b/dashboard_js/package.json index 24eb772..3b0dee7 100644 --- a/dashboard_js/package.json +++ b/dashboard_js/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@brazilian-utils/brazilian-utils": "^1.0.0-rc.12", + "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.2.0", "@hookform/error-message": "^2.0.1", "@hookform/resolvers": "^4.1.0",