From 1bbc17126fbe7809eb9b296b0c4fadfc5c68d6bc Mon Sep 17 00:00:00 2001 From: Hy Nguyen Date: Thu, 7 Mar 2024 21:22:26 -0600 Subject: [PATCH] added prettier --- .prettierrc.json | 4 + app/components/SpinnerSVG.tsx | 115 ++++++++-- app/root.tsx | 197 +++++++++-------- app/routes/_index.tsx | 47 ++-- app/routes/login.tsx | 403 ++++++++++++++++++++++------------ app/routes/test.tsx | 103 ++++----- package.json | 2 + yarn.lock | 10 + 8 files changed, 547 insertions(+), 334 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..1c10199 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "tabWidth": 4, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/app/components/SpinnerSVG.tsx b/app/components/SpinnerSVG.tsx index 89f4be4..9b404a8 100644 --- a/app/components/SpinnerSVG.tsx +++ b/app/components/SpinnerSVG.tsx @@ -1,24 +1,91 @@ - -type Props = { - size: number; -}; - -export default function SidePanel({size} : Props){ - return - - - - - - - - - - - - - - - - ; -} \ No newline at end of file +export default function SidePanel() { + return ( + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/root.tsx b/app/root.tsx index ac29365..8d05bad 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,126 +1,129 @@ - import type { LinksFunction, LoaderFunction } from "@remix-run/node"; import type { User } from "@supabase/supabase-js"; import { Database } from "database.types"; import { json } from "@remix-run/node"; import { - Links, - Meta, - Outlet, - Scripts, - useLoaderData, - useRevalidator + Links, + Meta, + Outlet, + Scripts, + useLoaderData, + useRevalidator, } from "@remix-run/react"; import { useState, useEffect } from "react"; -import { createBrowserClient } from '@supabase/ssr' +import { createBrowserClient } from "@supabase/ssr"; import { useLocation } from "@remix-run/react"; - import stylesheet from "~/tailwind.css"; -import { ThemeProvider, useTheme, ThemeType } from '~/utils/Navbar/ThemeProvider'; -import { getThemeSession } from './utils/Navbar/theme.server'; - +import { + ThemeProvider, + useTheme, + ThemeType, +} from "~/utils/Navbar/ThemeProvider"; +import { getThemeSession } from "./utils/Navbar/theme.server"; import Navbar from "./components/Navbar"; import SidePanel from "./components/SidePanel"; - - - export const links: LinksFunction = () => [ - { rel: "stylesheet", href: stylesheet }, + { rel: "stylesheet", href: stylesheet }, ]; export type LoaderData = { - theme: ThemeType | null; - env: { [key: string]: string } + theme: ThemeType | null; + env: { [key: string]: string }; }; export const loader: LoaderFunction = async ({ request }) => { - const themeSession = await getThemeSession(request); - - const data: LoaderData = { - theme: themeSession.getTheme(), - env: { - SUPABASE_URL: process.env.SUPABASE_URL!, - SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!, - } - }; + const themeSession = await getThemeSession(request); - return json(data) -} + const data: LoaderData = { + theme: themeSession.getTheme(), + env: { + SUPABASE_URL: process.env.SUPABASE_URL!, + SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!, + }, + }; + return json(data); +}; function App() { - const { env } = useLoaderData() - const [supabase] = useState(() => - createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY) - ) - const [user, setUser] = useState(undefined); - - // recalls loaders when authentication state changes - const revalidator = useRevalidator(); - useEffect(() => { - const { - data: { subscription } - } = supabase.auth.onAuthStateChange((event, session) => { - revalidator.revalidate(); - if (event === "SIGNED_IN") { - setUser(session?.user); - } else if (event === "SIGNED_OUT"){ - setUser(undefined); - } - }) - - return () => { - subscription.unsubscribe() - } - }, [supabase /*, revalidator*/]) - - - const [theme] = useTheme(); - const [sidePanelIsShown, setSidePanelIsShown] = useState(false); - - - // hide for specific routes - const location = useLocation(); - const routesToHideNavigation = ['/login', '/signup']; ///// add homepage - const shouldHideNavigation = routesToHideNavigation.includes(location.pathname); - - - - return ( - - - - - - - - - {shouldHideNavigation ? null : <> - - - } - - {/* space for navbar above main page content */} -
- -
- - - - ); + const { env } = useLoaderData(); + const [supabase] = useState(() => + createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY), + ); + const [user, setUser] = useState(undefined); + + // recalls loaders when authentication state changes + const revalidator = useRevalidator(); + useEffect(() => { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((event, session) => { + revalidator.revalidate(); + if (event === "SIGNED_IN") { + setUser(session?.user); + } else if (event === "SIGNED_OUT") { + setUser(undefined); + } + }); + + return () => { + subscription.unsubscribe(); + }; + }, [supabase /*, revalidator*/]); + + const [theme] = useTheme(); + const [sidePanelIsShown, setSidePanelIsShown] = useState(false); + + // hide for specific routes + const location = useLocation(); + const routesToHideNavigation = ["/login", "/signup"]; ///// add homepage + const shouldHideNavigation = routesToHideNavigation.includes( + location.pathname, + ); + + return ( + + + + + + + + + {shouldHideNavigation ? null : ( + <> + + + + )} + + {/* space for navbar above main page content */} +
+ +
+ + + + ); } export default function AppWithProviders() { - const data = useLoaderData(); - return ( - - - - ); -} \ No newline at end of file + const data = useLoaderData(); + return ( + + + + ); +} diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index b269c03..6ab1c02 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,36 +1,29 @@ import type { MetaFunction } from "@remix-run/node"; -import { useState } from "react" +import { useState } from "react"; export const meta: MetaFunction = () => { - return [ - { title: "New App" }, - { name: "description", content: "Project in progress" }, - ]; + return [ + { title: "New App" }, + { name: "description", content: "Project in progress" }, + ]; }; export default function Index() { - const [isDone, setIsDone] = useState(false) - const headingClicked = () => setIsDone(true) + const [isDone, setIsDone] = useState(false); + const headingClicked = () => setIsDone(true); - return ( -
-

{isDone ? "done" : "click here"}

+ return ( +
+

+ {isDone ? "done" : "click here"} +

- - - { - Array.from(Array(50)).map( - (item, index) => ( -

{index}

- ) - ) - } - -
- ); + {Array.from(Array(50)).map((item, index) => ( +

{index}

+ ))} +
+ ); } diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 7a2297a..997fb2e 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,73 +1,72 @@ import { Form, useOutletContext, useNavigate } from "@remix-run/react"; import { useEffect, useState } from "react"; -import { createServerClient, parse, serialize } from '@supabase/ssr'; +import { createServerClient, parse, serialize } from "@supabase/ssr"; -import SpinnerSVG from "~/components/SpinnerSVG" +import SpinnerSVG from "~/components/SpinnerSVG"; import googleIcon from "~/assets/oauth_providers/google-icon.png"; import githubIcon from "~/assets/oauth_providers/github-icon.png"; -import type { Provider } from '@supabase/supabase-js' +import type { Provider } from "@supabase/supabase-js"; import { ContextProps } from "~/utils/types/ContextProps.type"; - -export default function Login(){ +export default function Login() { const navigate = useNavigate(); const [errorMessage, setErrorMessage] = useState(null); const resetErrorMessage = () => setErrorMessage(null); - const [isSubmitting, setIsSubmitting] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); // switch between login & signup const [isAtLogin, setIsAtLogin] = useState(true); - const { supabase } = useOutletContext() + const { supabase } = useOutletContext(); const providerClicked = async (providerName: Provider) => { const { data, error } = await supabase.auth.signInWithOAuth({ - provider: providerName + provider: providerName, }); - if (error) alert("Error while logging in.") + if (error) alert("Error while logging in."); }; - const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [regPassword, setRegPassword] = useState(""); - + const [showPassReqs, setShowPassReqs] = useState(false); const [passReqs, setPassReqs] = useState({ hasSixChar: false, hasUppercase: false, hasLowercase: false, hasNumber: false, - hasSymbol: false + hasSymbol: false, }); // revalidate new password - useEffect(()=>{ + useEffect(() => { setPassReqs({ hasSixChar: regPassword.length >= 6, hasUppercase: /[A-Z]/.test(regPassword), hasLowercase: /[a-z]/.test(regPassword), hasNumber: /\d/.test(regPassword), - hasSymbol: /[!@#$%^&*()_+={}\[\]:;<>,.?~\\|\-]/.test(regPassword) + hasSymbol: /[!@#$%^&*()_+={}\[\]:;<>,.?~\\|\-]/.test(regPassword), }); }, [regPassword]); - - const fillDemoAcc = ()=>{ + const fillDemoAcc = () => { setEmail("hynguyendev@gmail.com"); setPassword("123+Ab"); }; - const loginOnSubmit: React.FormEventHandler = async (event) => { + const loginOnSubmit: React.FormEventHandler = async ( + event, + ) => { event.preventDefault(); resetErrorMessage(); setIsSubmitting(true); - const {error, data} = await supabase.auth.signInWithPassword({ + const { error, data } = await supabase.auth.signInWithPassword({ email: email, - password: password - }) + password: password, + }); if (error) { setIsSubmitting(false); setErrorMessage(error.message); @@ -77,42 +76,54 @@ export default function Login(){ return navigate("/"); }; - const registerOnSubmit: React.FormEventHandler = async (event) => { + const registerOnSubmit: React.FormEventHandler = async ( + event, + ) => { event.preventDefault(); resetErrorMessage(); setIsSubmitting(true); const form = event.currentTarget; - const formValues: {[key:string]: string} = { + const formValues: { [key: string]: string } = { displayName: form["reg_display_name_input"].value, email: form["reg_email_input"].value, password: form["reg_password_input"].value, - passwordConfirm: form["reg_confirm_password_input"].value + passwordConfirm: form["reg_confirm_password_input"].value, }; const validations: [boolean, string][] = [ [passReqs.hasSixChar, "Password should be at least 6 characters"], - [passReqs.hasUppercase, "Password should contain at least 1 uppercase"], - [passReqs.hasLowercase, "Password should contain at least 1 lowercase"], + [ + passReqs.hasUppercase, + "Password should contain at least 1 uppercase", + ], + [ + passReqs.hasLowercase, + "Password should contain at least 1 lowercase", + ], [passReqs.hasNumber, "Password should contain at least 1 number"], [passReqs.hasSymbol, "Password should contain at least 1 symbol"], - [formValues.password === formValues.passwordConfirm, "Passwords don't match"] + [ + formValues.password === formValues.passwordConfirm, + "Passwords don't match", + ], ]; - const validationsPassed = validations.every(vali => { - if (!vali[0]){ - setTimeout(()=>{ + const validationsPassed = validations.every((vali) => { + if (!vali[0]) { + setTimeout(() => { setErrorMessage(vali[1]); }, 1); return false; } else return true; }); - if (validationsPassed){ - const {error, data} = await supabase.auth.signUp({ - email: formValues.email, password: formValues.password + if (validationsPassed) { + const { error, data } = await supabase.auth.signUp({ + email: formValues.email, + password: formValues.password, }); - if (error){ + if (error) { setIsSubmitting(false); setErrorMessage(error.message); return; @@ -124,114 +135,232 @@ export default function Login(){ } }; - - return
-
-

Continue with

-
- - -
-
-
OR
- -
- - -
-
-
-

Login

- - - - setEmail(e.target.value)}/> - - setPassword(e.target.value)}/> - - - {errorMessage} -
-
- + return ( +
+
+

Continue with

+
+ + +
+
OR
+ +
+ - -
-
-
-

Register

- - - - - - - - {/* Password requirements popup div */} -
- +
+ +
+

Login

+ + + + setEmail(e.target.value)} + /> + + setPassword(e.target.value)} + /> + + + {errorMessage}
+ + +
+ +
+
+
+

+ Register +

+ + + + + + + + {/* Password requirements popup div */} +
+ +
- - setShowPassReqs(true)} - onBlur={()=>setShowPassReqs(false)} - onChange={e=>setRegPassword(e.target.value)} - value={regPassword}/> - - - {errorMessage} - - -
-
+ + setShowPassReqs(true)} + onBlur={() => setShowPassReqs(false)} + onChange={(e) => setRegPassword(e.target.value)} + value={regPassword} + /> + + + {errorMessage} + + +
+ +
-
- -
+ ); } - diff --git a/app/routes/test.tsx b/app/routes/test.tsx index 9ed15dc..6602029 100644 --- a/app/routes/test.tsx +++ b/app/routes/test.tsx @@ -1,44 +1,49 @@ - import { useLoaderData, useOutletContext } from "@remix-run/react"; -import { createServerClient, parse, serialize } from '@supabase/ssr'; - +import { createServerClient, parse, serialize } from "@supabase/ssr"; -import type { Database } from '../../database.types' -import type { SupabaseClient } from '@supabase/supabase-js' -import type { LoaderFunctionArgs } from "@remix-run/node" +import type { Database } from "../../database.types"; +import type { SupabaseClient } from "@supabase/supabase-js"; +import type { LoaderFunctionArgs } from "@remix-run/node"; import { useEffect } from "react"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const cookies = parse(request.headers.get('Cookie') ?? '') - const headers = new Headers() - - const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { - cookies: { - get(key) { - return cookies[key] - }, - set(key, value, options) { - headers.append('Set-Cookie', serialize(key, value, options)) + const cookies = parse(request.headers.get("Cookie") ?? ""); + const headers = new Headers(); + + const supabase = createServerClient( + process.env.SUPABASE_URL!, + process.env.SUPABASE_ANON_KEY!, + { + cookies: { + get(key) { + return cookies[key]; + }, + set(key, value, options) { + headers.append( + "Set-Cookie", + serialize(key, value, options), + ); + }, + remove(key, options) { + headers.append("Set-Cookie", serialize(key, "", options)); + }, + }, }, - remove(key, options) { - headers.append('Set-Cookie', serialize(key, '', options)) - }, - }, - }) + ); // const data = await supabase.from('profiles').select() - const { // if user !== null then is authenticated + const { + // if user !== null then is authenticated data: { user: data }, - } = await supabase.auth.getUser() - - return new Response(JSON.stringify(data), {headers}) - -} - + } = await supabase.auth.getUser(); -export default function Test(){ + return new Response(JSON.stringify(data), { headers }); +}; - const { supabase } = useOutletContext<{ supabase: SupabaseClient }>() +export default function Test() { + const { supabase } = useOutletContext<{ + supabase: SupabaseClient; + }>(); ////// get data from server side (in loaders and actions) // const data = useLoaderData() as string @@ -46,7 +51,6 @@ export default function Test(){ // console.log(JSON.parse(data)) // }, []) - ////// get data from client side // useEffect(() => { // (async () => { @@ -55,31 +59,32 @@ export default function Test(){ // })() // }, []) - - function signUp(){ + function signUp() { supabase.auth.signUp({ email: "khanghy2130@gmail.com", - password: "sup3rs3cur3" - }) + password: "sup3rs3cur3", + }); } - function logIn(){ + function logIn() { supabase.auth.signInWithPassword({ email: "khanghy2130@gmail.com", - password: "sup3rs3cur3" - }) + password: "sup3rs3cur3", + }); } - async function signOut(){ - const { error } = await supabase.auth.signOut() - if (error){ - console.log(error) - } + async function signOut() { + const { error } = await supabase.auth.signOut(); + if (error) { + console.log(error); + } } - return
- - - -
-} \ No newline at end of file + return ( +
+ + + +
+ ); +} diff --git a/package.json b/package.json index 801169c..c3058d8 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,8 @@ "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.56.0", "jsdom": "^23.0.1", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.12", "tailwindcss": "^3.4.0", "typescript": "^5.3.3", "vitest": "^1.1.0" diff --git a/yarn.lock b/yarn.lock index 67946a5..bedf993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5782,11 +5782,21 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-plugin-tailwindcss@^0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.12.tgz#655999849344548ecf4d9b47a051ed856f041c72" + integrity sha512-o74kiDBVE73oHW+pdkFSluHBL3cYEvru5YgEqNkBMFF7Cjv+w1vI565lTlfoJT4VLWDe0FMtZ7FkE/7a4pMXSQ== + prettier@^2.7.1: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"