diff --git a/front/.env b/front/.env index 2226642..d7e5fe9 100644 --- a/front/.env +++ b/front/.env @@ -1,3 +1,3 @@ -NEXTAUTH_URL=http://localhost:3000 +NEXTAUTH_URL=http://localhost:3016 # GEN AUTH_SECRET: $ openssl rand -base64 32 NEXTAUTH_SECRET=6lHRWUvCBtqlgTWc6aFn6s6PudYjuN6oUY+RrcEntTU= diff --git a/front/next.config.mjs b/front/next.config.mjs index beaab4e..2bbec78 100644 --- a/front/next.config.mjs +++ b/front/next.config.mjs @@ -9,6 +9,16 @@ const nextConfig = { prependData: `@import "@/styles/variables";`, includePaths: [path.join("@", "styles")], }, + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "picsum.photos", + port: "", + pathname: "/**", + }, + ], + }, }; export default nextConfig; diff --git a/front/src/app/(home)/layout.tsx b/front/src/app/(home)/layout.tsx index ea1c4bc..24abadc 100644 --- a/front/src/app/(home)/layout.tsx +++ b/front/src/app/(home)/layout.tsx @@ -1 +1 @@ -export { ApplicationLayout as default } from "@/modules/common/layouts/application/application.layout"; +export { HomeLayout as default } from "@/modules/common/layouts/home"; diff --git a/front/src/app/(home)/page.tsx b/front/src/app/(home)/page.tsx index a060dd5..7f16d18 100644 --- a/front/src/app/(home)/page.tsx +++ b/front/src/app/(home)/page.tsx @@ -1,4 +1,3 @@ -import { SignInWidget } from "@/modules/auth/components/sign-in/sign-in.widget"; import ThemeSwitcher from "@/modules/common/theme-switcher"; export default function Home() { @@ -14,7 +13,6 @@ export default function Home() { > Main page - ); } diff --git a/front/src/app/auth/log-in/page.tsx b/front/src/app/auth/log-in/page.tsx new file mode 100644 index 0000000..2e19f64 --- /dev/null +++ b/front/src/app/auth/log-in/page.tsx @@ -0,0 +1 @@ +export { LogInView as default } from "@/modules/auth/views/log-in/log-in.view"; diff --git a/front/src/middleware.ts b/front/src/middleware.ts new file mode 100644 index 0000000..859e5bd --- /dev/null +++ b/front/src/middleware.ts @@ -0,0 +1,17 @@ +import { withAuth } from "next-auth/middleware"; +import { authOptions } from "./modules/auth/configs/auth.options"; + +export default withAuth({ + pages: authOptions.pages, + callbacks: { + authorized({ req, token }) { + if (token) return true; + const pathname = req.nextUrl.pathname; + return ( + pathname.startsWith("/_next/") || + pathname.startsWith("/favicon.ico") || + pathname.startsWith("/assets/") + ); + }, + }, +}); diff --git a/front/src/modules/auth/components/sign-in/sign-in.module.scss b/front/src/modules/auth/components/log-in/log-in.module.scss similarity index 99% rename from front/src/modules/auth/components/sign-in/sign-in.module.scss rename to front/src/modules/auth/components/log-in/log-in.module.scss index c95014b..27fd6af 100644 --- a/front/src/modules/auth/components/sign-in/sign-in.module.scss +++ b/front/src/modules/auth/components/log-in/log-in.module.scss @@ -1,5 +1,6 @@ .container { max-width: 20em; + height: fit-content; display: flex; flex-direction: column; diff --git a/front/src/modules/auth/components/sign-in/sign-in.widget.tsx b/front/src/modules/auth/components/log-in/log-in.widget.tsx similarity index 81% rename from front/src/modules/auth/components/sign-in/sign-in.widget.tsx rename to front/src/modules/auth/components/log-in/log-in.widget.tsx index 8047d15..02cfaf8 100644 --- a/front/src/modules/auth/components/sign-in/sign-in.widget.tsx +++ b/front/src/modules/auth/components/log-in/log-in.widget.tsx @@ -1,18 +1,18 @@ "use client"; -import { FormEvent, useState } from "react"; -import styles from "./sign-in.module.scss"; +import { FormEvent, useEffect, useState } from "react"; +import styles from "./log-in.module.scss"; import { signIn, useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; -type SingInWidgetsProps = { +type LogInWidgetsProps = { afterSuccess?: Function; }; -export const SignInWidget: React.FC = ({ - afterSuccess, -}) => { +export const LogInWidget: React.FC = ({ afterSuccess }) => { const { data } = useSession(); + const router = useRouter(); const [loginStatus, setLoginStatus] = useState< "idle" | "check" | "confirm" | "error" @@ -41,9 +41,15 @@ export const SignInWidget: React.FC = ({ } }; + useEffect(() => { + if (loginStatus === "confirm") { + router.push("/"); + } + }, [loginStatus]); + return ( - Sign in + Log in diff --git a/front/src/modules/auth/components/user-dropdown/index.ts b/front/src/modules/auth/components/user-dropdown/index.ts new file mode 100644 index 0000000..b255352 --- /dev/null +++ b/front/src/modules/auth/components/user-dropdown/index.ts @@ -0,0 +1 @@ +export { UserDropdown } from "./user-dropdown.component"; diff --git a/front/src/modules/auth/components/user-dropdown/user-dropdown.component.tsx b/front/src/modules/auth/components/user-dropdown/user-dropdown.component.tsx new file mode 100644 index 0000000..98e5334 --- /dev/null +++ b/front/src/modules/auth/components/user-dropdown/user-dropdown.component.tsx @@ -0,0 +1,14 @@ +import { UserFrame } from "../user-frame"; +import styles from "./user-dropdown.module.scss"; + +export const UserDropdown = () => { + return ( + + + + + + Log out + + ); +}; diff --git a/front/src/modules/auth/components/user-dropdown/user-dropdown.module.scss b/front/src/modules/auth/components/user-dropdown/user-dropdown.module.scss new file mode 100644 index 0000000..20b6c83 --- /dev/null +++ b/front/src/modules/auth/components/user-dropdown/user-dropdown.module.scss @@ -0,0 +1,8 @@ +.container { + .avatar__container { + width: 6rem; + height: 6rem; + + font-size: 3rem; + } +} diff --git a/front/src/modules/auth/components/user-frame/index.ts b/front/src/modules/auth/components/user-frame/index.ts new file mode 100644 index 0000000..7c2ef0e --- /dev/null +++ b/front/src/modules/auth/components/user-frame/index.ts @@ -0,0 +1 @@ +export { UserFrame } from "./user-frame.component"; diff --git a/front/src/modules/auth/components/user-frame/user-frame.component.tsx b/front/src/modules/auth/components/user-frame/user-frame.component.tsx new file mode 100644 index 0000000..963a107 --- /dev/null +++ b/front/src/modules/auth/components/user-frame/user-frame.component.tsx @@ -0,0 +1,39 @@ +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../../configs/auth.options"; +import Image from "next/image"; + +import styles from "./user-frame.module.scss"; + +export async function UserFrame() { + const session = await getServerSession(authOptions); + + return ( + + + {getInitialsFromName(session?.user?.name || "")} + + + + ); +} + +const getInitialsFromName = (name: string): string => { + if (name.length < 1) return "00"; + + // TODO: this can be done in one line probably + const words = name.split(" "); + let initials = ""; + words.forEach((word) => (initials += word[0])); + if (initials.length < 2) { + initials += name[1]; + } + + return initials.slice(0, 2); +}; diff --git a/front/src/modules/auth/components/user-frame/user-frame.module.scss b/front/src/modules/auth/components/user-frame/user-frame.module.scss new file mode 100644 index 0000000..40ee4e0 --- /dev/null +++ b/front/src/modules/auth/components/user-frame/user-frame.module.scss @@ -0,0 +1,23 @@ +.frame { + position: relative; + font-size: inherit; + + width: 100%; + height: 100%; + border-radius: 50%; + overflow: hidden; + + border: 1px solid $color-grey-medium; + box-sizing: border-box; + + & > span { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + font-size: inherit; + text-transform: uppercase; + color: $color-grey-medium; + } +} diff --git a/front/src/modules/auth/configs/auth.options.ts b/front/src/modules/auth/configs/auth.options.ts index f258e00..d62439f 100644 --- a/front/src/modules/auth/configs/auth.options.ts +++ b/front/src/modules/auth/configs/auth.options.ts @@ -6,7 +6,7 @@ export const authOptions: AuthOptions = { strategy: "jwt", }, pages: { - signIn: "/auth/sign-in", + signIn: "/auth/log-in", }, providers: [ CredentialsProvider({ @@ -50,7 +50,7 @@ export const authOptions: AuthOptions = { const user: User = { id: token_payload.username, roles: token_payload.roles, - image: "https://randomuser.me/api/portraits/women/92.jpg", + image: "https://picsum.photos/200/300", name: token_payload.username, apiSession: { accessToken: response_body.access_token, diff --git a/front/src/modules/auth/views/log-in/log-in.module.scss b/front/src/modules/auth/views/log-in/log-in.module.scss new file mode 100644 index 0000000..dd69285 --- /dev/null +++ b/front/src/modules/auth/views/log-in/log-in.module.scss @@ -0,0 +1,7 @@ +.container { + min-height: 100vh; + + display: flex; + align-items: center; + justify-content: center; +} diff --git a/front/src/modules/auth/views/log-in/log-in.view.tsx b/front/src/modules/auth/views/log-in/log-in.view.tsx new file mode 100644 index 0000000..b873eba --- /dev/null +++ b/front/src/modules/auth/views/log-in/log-in.view.tsx @@ -0,0 +1,12 @@ +import { LogInWidget } from "../../components/log-in/log-in.widget"; +import styles from "./log-in.module.scss"; + +type LogInWidgetsProps = {}; + +export const LogInView: React.FC = ({}) => { + return ( + + + + ); +}; diff --git a/front/src/modules/common/components/header/header.component.tsx b/front/src/modules/common/components/header/header.component.tsx new file mode 100644 index 0000000..e6bf004 --- /dev/null +++ b/front/src/modules/common/components/header/header.component.tsx @@ -0,0 +1,5 @@ +import { UserDropdown } from "@/modules/auth/components/user-dropdown"; + +export const Header = () => { + return ; +}; diff --git a/front/src/modules/common/components/header/header.module.scss b/front/src/modules/common/components/header/header.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/front/src/modules/common/components/header/index.ts b/front/src/modules/common/components/header/index.ts new file mode 100644 index 0000000..67c8585 --- /dev/null +++ b/front/src/modules/common/components/header/index.ts @@ -0,0 +1 @@ +export { Header } from "./header.component"; diff --git a/front/src/modules/common/layouts/application/application.layout.tsx b/front/src/modules/common/layouts/application/application.layout.tsx deleted file mode 100644 index 18e9e91..0000000 --- a/front/src/modules/common/layouts/application/application.layout.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { PropsWithChildren } from "react"; -import { ApplicationProvider } from "../../providers/application.provider"; - -export const ApplicationLayout: React.FC = ({ - children, -}) => { - return {children}; -}; diff --git a/front/src/modules/common/layouts/home/home.layout.tsx b/front/src/modules/common/layouts/home/home.layout.tsx new file mode 100644 index 0000000..d6fa389 --- /dev/null +++ b/front/src/modules/common/layouts/home/home.layout.tsx @@ -0,0 +1,13 @@ +import { PropsWithChildren } from "react"; + +import styles from "./home.module.scss"; +import { Header } from "../../components/header"; + +export const HomeLayout: React.FC = ({ children }) => { + return ( + + + {children} + + ); +}; diff --git a/front/src/modules/common/layouts/home/home.module.scss b/front/src/modules/common/layouts/home/home.module.scss new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/front/src/modules/common/layouts/home/home.module.scss @@ -0,0 +1 @@ + diff --git a/front/src/modules/common/layouts/home/index.ts b/front/src/modules/common/layouts/home/index.ts new file mode 100644 index 0000000..95a05a9 --- /dev/null +++ b/front/src/modules/common/layouts/home/index.ts @@ -0,0 +1 @@ +export { HomeLayout } from "./home.layout"; diff --git a/front/src/modules/common/layouts/root/root.layout.tsx b/front/src/modules/common/layouts/root/root.layout.tsx index 5a707f2..dab246c 100644 --- a/front/src/modules/common/layouts/root/root.layout.tsx +++ b/front/src/modules/common/layouts/root/root.layout.tsx @@ -1,9 +1,11 @@ import type { Metadata } from "next"; import "@/styles/main.scss"; +import { ApplicationProvider } from "../../providers/application.provider"; export const metadata: Metadata = { - title: "Personal finance - Home", - description: "Manage your money blablabla", + title: "Full stack archetype", + description: + "This is a full stack archetype supported in NextJS, NestJS and mysql", }; export function RootLayout({ @@ -13,7 +15,9 @@ export function RootLayout({ }>) { return ( - {children} + + {children} + ); }