diff --git a/.prettierrc b/.prettierrc index 04d142a..4aed9e0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,7 @@ { - "semi": true, - "trailingComma": "es5", - "singleQuote": false, - "useTabs": true + "semi": true, + "trailingComma": "all", + "singleQuote": false, + "useTabs": true, + "printWidth": 150 } diff --git a/package-lock.json b/package-lock.json index 6e506a8..1ee6b26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "@types/mercadopago": "^1.5.11", "@types/node-cron": "^3.0.11", "@types/react": "^18.3.12", + "@types/sanitize-html": "^2.13.0", "@types/speakeasy": "^2.0.10", "@types/yup": "^0.29.14", "autoprefixer": "^10.4.20", @@ -3412,6 +3413,15 @@ "@types/react": "*" } }, + "node_modules/@types/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==", + "dev": true, + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", diff --git a/package.json b/package.json index 1fdc414..a11d3fb 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@types/mercadopago": "^1.5.11", "@types/node-cron": "^3.0.11", "@types/react": "^18.3.12", + "@types/sanitize-html": "^2.13.0", "@types/speakeasy": "^2.0.10", "@types/yup": "^0.29.14", "autoprefixer": "^10.4.20", diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 60cd372..34e86c3 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -13,9 +13,9 @@ export interface ButtonProps { type?: ButtonType; href?: string; btnColorType?: ButtonColorType; - isLoading: boolean; - isActive: boolean; - loadingText: string; + isLoading?: boolean; + isActive?: boolean; + loadingText?: string; } const Button = ({ @@ -24,8 +24,8 @@ const Button = ({ btnColorType = "primary", size = "md", href, - isLoading, - isActive, + isLoading = false, + isActive = false, loadingText, }: ButtonProps) => { const btn = ( diff --git a/src/components/DropdownButton.tsx b/src/components/DropdownButton.tsx index 2bbba1f..b2c1fde 100644 --- a/src/components/DropdownButton.tsx +++ b/src/components/DropdownButton.tsx @@ -5,22 +5,15 @@ import { IoChevronDown } from "react-icons/io5"; import { Menu, MenuButton, MenuList, MenuItem, Button } from "@chakra-ui/react"; export interface DropdownButtonProps { - hasMenu: boolean; + hasMenu?: boolean; text: string; href?: string; list?: { text: string; url: string; isActive?: boolean }[]; } -const DropdownButton = ({ - hasMenu = false, - text, - href, - list, -}: DropdownButtonProps) => { +const DropdownButton = ({ hasMenu = false, text, href, list }: DropdownButtonProps) => { const router = useRouter(); - const isActive = hasMenu - ? list?.some((item) => router.asPath.startsWith(item.url)) - : router.asPath === href; + const isActive = hasMenu ? list?.some((item) => router.asPath.startsWith(item.url)) : router.asPath === href; if (hasMenu) { return ( diff --git a/src/components/FormWrapper.tsx b/src/components/FormWrapper.tsx index 4e7147a..d71e58f 100644 --- a/src/components/FormWrapper.tsx +++ b/src/components/FormWrapper.tsx @@ -19,7 +19,7 @@ export interface FormField { name: string; label: { text: string }; placeholder?: string; - as?: JSX.Element; + as?: any; options?: { value: string; text: string }[]; } diff --git a/src/components/Link.jsx b/src/components/Link.tsx similarity index 54% rename from src/components/Link.jsx rename to src/components/Link.tsx index 7798d42..d4b275b 100644 --- a/src/components/Link.jsx +++ b/src/components/Link.tsx @@ -2,10 +2,15 @@ import React from "react"; import { Link as ChakraLink } from "@chakra-ui/react"; import NextLink from "next/link"; -const Link = ({ href, text }) => { +export interface LinkProps { + href: string; + text?: string; +} + +const Link = ({ href, text }: LinkProps) => { return ( - {text ? text : href} + {text ?? href} ); }; diff --git a/src/layout/Footer.jsx b/src/layout/Footer.jsx deleted file mode 100644 index da62852..0000000 --- a/src/layout/Footer.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import { Box } from "@chakra-ui/react"; - -const Footer = () => { - return ( - - Copyright © 2021-2023 ShibAAC Devs - - ); -}; - -export default Footer; diff --git a/src/layout/Footer.tsx b/src/layout/Footer.tsx new file mode 100644 index 0000000..d499915 --- /dev/null +++ b/src/layout/Footer.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { Box } from "@chakra-ui/react"; + +const Footer = () => { + return ( + + Copyright © 2021-2023 Shibaac + + ); +}; + +export default Footer; diff --git a/src/layout/Head.tsx b/src/layout/Head.tsx index 75bf5f6..a3916b5 100644 --- a/src/layout/Head.tsx +++ b/src/layout/Head.tsx @@ -3,24 +3,17 @@ import React from "react"; import NextHead from "next/head"; import { NextSeo } from "next-seo"; -type HeadProps = { - title: string; +export interface HeadProps { + title?: string; description?: string; -}; +} -const Head = ({ - title = "shibaac", - description = "Automatic Account Creator", -}: HeadProps) => { +const Head = ({ title = "shibaac", description = "Automatic Account Creator" }: HeadProps) => { return ( <> - + { return ( - + { const router = useRouter(); return ( - + {navigationItems.map((item) => ( - + ))} @@ -164,7 +134,7 @@ const NavBar = () => { }, { fallback: "md", - } + }, ); return ; diff --git a/src/layout/SideBar.tsx b/src/layout/SideBar.tsx index 20ec270..6bfa504 100644 --- a/src/layout/SideBar.tsx +++ b/src/layout/SideBar.tsx @@ -18,7 +18,7 @@ const SideBar = (props: LayoutProps) => { setIsLoading(true); const [players, status] = await Promise.all([ - fetchApi<{ players: player[] }>("GET", `/api/player/top5`), + fetchApi<{ players: any[] }>("GET", `/api/player/top5`), fetchApi<{ status: ProtocolStatusCache }>("GET", `/api/status`), ]); @@ -38,23 +38,10 @@ const SideBar = (props: LayoutProps) => { - - {serverStatus?.online ? ( - ONLINE - ) : ( - OFFLINE - )} - + {serverStatus?.online ? ONLINE : OFFLINE} - - {serverStatus && ( - - )} - + {serverStatus && } @@ -73,7 +60,7 @@ const SideBar = (props: LayoutProps) => { { text: player.level, }, - ]) + ]) : [ [ { @@ -81,7 +68,7 @@ const SideBar = (props: LayoutProps) => { colspan: 2, }, ], - ] + ] } /> diff --git a/src/layout/index.jsx b/src/layout/index.tsx similarity index 53% rename from src/layout/index.jsx rename to src/layout/index.tsx index 18fcd53..c0280a1 100644 --- a/src/layout/index.jsx +++ b/src/layout/index.tsx @@ -3,33 +3,16 @@ import Head from "./Head"; import NavBar from "./NavBar"; import SideBar from "./SideBar"; import Footer from "./Footer"; - import { Box, Image, Flex } from "@chakra-ui/react"; const Layout = ({ children }) => { return ( - - - + + + - + {children} diff --git a/src/lib/request.ts b/src/lib/request.ts index be6f524..b68b996 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -19,7 +19,7 @@ export type ResponseData = { args?: any; }; -export const fetchApi = async ( +export const fetchApi = async ( method: FetchMethods, url: string, options?: FetchOptions diff --git a/src/pages/404.jsx b/src/pages/404.tsx similarity index 100% rename from src/pages/404.jsx rename to src/pages/404.tsx diff --git a/src/pages/_app.jsx b/src/pages/_app.tsx similarity index 73% rename from src/pages/_app.jsx rename to src/pages/_app.tsx index 0cc3f51..dcbf288 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.tsx @@ -1,7 +1,8 @@ -import Layout from "src/layout"; -import { UserContextWrapper } from "src/hooks/useUser"; +import Layout from "../layout"; +import { UserContextWrapper } from "../hooks/useUser"; import { ChakraProvider } from "@chakra-ui/react"; -import { Theme, Fonts } from "src/layout/theme"; +import { Theme, Fonts } from "../layout/theme"; +import React from "react"; // @ts-ignore BigInt.prototype.toJSON = function () { diff --git a/src/pages/account/changeemail.jsx b/src/pages/account/changeemail.tsx similarity index 62% rename from src/pages/account/changeemail.jsx rename to src/pages/account/changeemail.tsx index 3be5eee..590d3fb 100644 --- a/src/pages/account/changeemail.jsx +++ b/src/pages/account/changeemail.tsx @@ -1,32 +1,33 @@ import React, { useState } from "react"; -import Panel from "src/components/Panel"; -import FormWrapper from "src/components/FormWrapper"; -import { fetchApi } from "../../lib/request"; -import { withSessionSsr } from "src/lib/session"; -import { changeEmailSchema } from "src/schemas/ChangeEmail"; +import Panel from "../../components/Panel"; +import FormWrapper, { + FormButton, + FormField, +} from "../../components/FormWrapper"; +import { fetchApi, FetchResult } from "../../lib/request"; +import { withSessionSsr } from "../../lib/session"; +import { changeEmailSchema } from "../../schemas/ChangeEmail"; -const fields = [ +const fields: FormField[] = [ { type: "email", name: "newEmail", - label: { text: "E-mail Address", size: 2 }, - size: 10, + label: { text: "E-mail Address" }, }, { type: "password", name: "password", - label: { text: "Password", size: 2 }, - size: 10, + label: { text: "Password" }, }, ]; -const buttons = [ - { type: "submit", btnType: "primary", value: "Submit" }, +const buttons: FormButton[] = [ + { type: "submit", btnColorType: "primary", value: "Submit" }, { href: "/account", value: "Back" }, ]; export default function ChangeEmail() { - const [response, setResponse] = useState(null); + const [response, setResponse] = useState(null); const onSubmit = async (values, { resetForm }) => { const response = await fetchApi("POST", "/api/account/changeemail", { diff --git a/src/pages/account/changepassword.jsx b/src/pages/account/changepassword.jsx deleted file mode 100644 index c717a6d..0000000 --- a/src/pages/account/changepassword.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useState } from "react"; -import Panel from "src/components/Panel"; -import FormWrapper from "src/components/FormWrapper"; -import { fetchApi } from "src/lib/request"; -import { withSessionSsr } from "src/lib/session"; -import { changePasswordSchema } from "src/schemas/ChangePassword"; -import { Text } from "@chakra-ui/react"; - -const fields = [ - { - type: "password", - name: "newPassword", - placeholder: "6 to 30 characters", - label: { text: "New Password", size: 3 }, - size: 9, - }, - { - type: "password", - name: "repeatNewPassword", - label: { text: "Repeat New Password", size: 3 }, - size: 9, - }, - { - type: "password", - name: "password", - label: { text: "Password", size: 3 }, - size: 9, - }, -]; - -const buttons = [ - { type: "submit", btnType: "primary", value: "Submit" }, - { href: "/account", btnType: "danger", value: "Back" }, -]; - -export default function ChangePassword() { - const [response, setResponse] = useState(null); - - const onSubmit = async (values, { resetForm }) => { - const response = await fetchApi("POST", "/api/account/changepassword", { - data: { - newPassword: values.newPassword, - password: values.password, - }, - }); - - setResponse(response); - resetForm(); - }; - - return ( - <> - - - Please enter your current password and a new password. For your - security, please enter the new password twice. - - - - - > - ); -} - -export const getServerSideProps = withSessionSsr(function ({ req }) { - const { user } = req.session; - if (!user) { - return { - redirect: { - destination: "/account/login", - permanent: false, - }, - }; - } - - return { - props: {}, - }; -}); diff --git a/src/pages/account/changepassword.tsx b/src/pages/account/changepassword.tsx new file mode 100644 index 0000000..d97d265 --- /dev/null +++ b/src/pages/account/changepassword.tsx @@ -0,0 +1,83 @@ +import React, { useState } from "react"; +import Panel from "../../components/Panel"; +import FormWrapper, { + FormButton, + FormField, +} from "../../components/FormWrapper"; +import { fetchApi, FetchResult } from "../../lib/request"; +import { withSessionSsr } from "../../lib/session"; +import { changePasswordSchema } from "../../schemas/ChangePassword"; +import { Text } from "@chakra-ui/react"; + +const fields: FormField[] = [ + { + type: "password", + name: "newPassword", + placeholder: "6 to 30 characters", + label: { text: "New Password" }, + }, + { + type: "password", + name: "repeatNewPassword", + label: { text: "Repeat New Password" }, + }, + { + type: "password", + name: "password", + label: { text: "Password" }, + }, +]; + +const buttons: FormButton[] = [ + { type: "submit", btnColorType: "primary", value: "Submit" }, + { href: "/account", btnColorType: "danger", value: "Back" }, +]; + +export default function ChangePassword() { + const [response, setResponse] = useState(null); + + const onSubmit = async (values, { resetForm }) => { + const response = await fetchApi("POST", "/api/account/changepassword", { + data: { + newPassword: values.newPassword, + password: values.password, + }, + }); + + setResponse(response); + resetForm(); + }; + + return ( + + + Please enter your current password and a new password. For your + security, please enter the new password twice. + + + + + ); +} + +export const getServerSideProps = withSessionSsr(function ({ req }) { + const { user } = req.session; + if (!user) { + return { + redirect: { + destination: "/account/login", + permanent: false, + }, + }; + } + + return { + props: {}, + }; +}); diff --git a/src/pages/account/createcharacter.jsx b/src/pages/account/createcharacter.tsx similarity index 76% rename from src/pages/account/createcharacter.jsx rename to src/pages/account/createcharacter.tsx index 6c7a98c..f2141ce 100644 --- a/src/pages/account/createcharacter.jsx +++ b/src/pages/account/createcharacter.tsx @@ -1,24 +1,27 @@ import React, { useState } from "react"; -import Panel from "src/components/Panel"; -import { withSessionSsr } from "src/lib/session"; -import { fetchApi } from "src/lib/request"; -import FormWrapper from "src/components/FormWrapper"; -import { createCharacterSchema } from "src/schemas/CreateCharacter"; +import Panel from "../../components/Panel"; +import { withSessionSsr } from "../../lib/session"; +import { fetchApi } from "../../lib/request"; +import FormWrapper, { + FormButton, + FormField, +} from "../../components/FormWrapper"; +import { createCharacterSchema } from "../../schemas/CreateCharacter"; import { Select, Text } from "@chakra-ui/react"; -const fields = [ +const fields: FormField[] = [ { type: "text", name: "name", placeholder: "3 to 29 characters", - label: { text: "Name", size: 3 }, - size: 9, + label: { text: "Name" }, }, { + type: "select", as: Select, name: "vocation", - label: { text: "Vocation", size: 3 }, - size: 9, + label: { text: "Vocation" }, + options: [ { value: "1", text: "Sorcerer" }, { value: "2", text: "Druid" }, @@ -27,10 +30,10 @@ const fields = [ ], }, { + type: "select", as: Select, name: "sex", - label: { text: "Sex", size: 3 }, - size: 9, + label: { text: "Sex" }, options: [ { value: "0", text: "Female" }, { value: "1", text: "Male" }, @@ -38,8 +41,8 @@ const fields = [ }, ]; -const buttons = [ - { type: "submit", btnType: "primary", value: "Submit" }, +const buttons: FormButton[] = [ + { type: "submit", btnColorType: "primary", value: "Submit" }, { href: "/account", value: "Back" }, ]; diff --git a/src/pages/account/deletecharacter.jsx b/src/pages/account/deletecharacter.tsx similarity index 80% rename from src/pages/account/deletecharacter.jsx rename to src/pages/account/deletecharacter.tsx index 71eeba6..0f06263 100644 --- a/src/pages/account/deletecharacter.jsx +++ b/src/pages/account/deletecharacter.tsx @@ -1,22 +1,22 @@ import React, { useEffect, useState, useCallback } from "react"; import Panel from "../../components/Panel"; import { withSessionSsr } from "../../lib/session"; -import { fetchApi } from "../../lib/request"; -import FormWrapper from "../../components/FormWrapper"; -import { deleteCharacterSchema } from "src/schemas/DeleteCharacter"; +import { fetchApi, FetchResult } from "../../lib/request"; +import FormWrapper, { FormButton } from "../../components/FormWrapper"; +import { deleteCharacterSchema } from "../../schemas/DeleteCharacter"; import { Select, Text } from "@chakra-ui/react"; -const buttons = [ - { type: "submit", btnType: "primary", value: "Submit" }, +const buttons: FormButton[] = [ + { type: "submit", btnColorType: "primary", value: "Submit" }, { href: "/account", value: "Back" }, ]; export default function DeleteCharacter({ user }) { - const [response, setResponse] = useState(null); - const [data, setData] = useState(null); + const [response, setResponse] = useState(null); + const [data, setData] = useState(null); const fetchCharacters = useCallback(async () => { - const response = await fetchApi("GET", `/api/account/${user.id}`); + const response = await fetchApi("GET", `/api/account/${user.id}`); if (response.success) { setData({ fields: [ diff --git a/src/pages/account/index.jsx b/src/pages/account/index.tsx similarity index 92% rename from src/pages/account/index.jsx rename to src/pages/account/index.tsx index fe4947a..22f68ec 100644 --- a/src/pages/account/index.jsx +++ b/src/pages/account/index.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState, useCallback } from "react"; -import Panel from "src/components/Panel"; -import Head from "src/layout/Head"; -import { fetchApi } from "src/lib/request"; -import { withSessionSsr } from "src/lib/session"; +import Panel from "../../components/Panel"; +import Head from "../../layout/Head"; +import { fetchApi } from "../../lib/request"; +import { withSessionSsr } from "../../lib/session"; import Button from "../../components/Button"; import StripedTable from "../../components/StrippedTable"; import { @@ -23,15 +23,17 @@ import { Text, Wrap, } from "@chakra-ui/react"; -import { vocationIdToName } from "../../lib"; +import { timestampToDate, vocationIdToName } from "../../lib"; import { Toggle } from "../../components/Toggle"; +// TODO: use proper types + export default function Account({ user }) { - const [info, setInfo] = useState(null); + const [info, setInfo] = useState(null); const [is2FAEnabled, setIs2FAEnabled] = useState(false); const [qrCodeDataURL, setQRCodeDataURL] = useState(null); const [isOpenModal, setIsOpenModal] = useState(false); - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); const fetchData = useCallback(async () => { @@ -134,22 +136,22 @@ export default function Account({ user }) { {isLoading && } diff --git a/src/pages/index.jsx b/src/pages/index.tsx similarity index 88% rename from src/pages/index.jsx rename to src/pages/index.tsx index fdabc98..f63883f 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.tsx @@ -1,10 +1,10 @@ import sanitize from "sanitize-html"; import Panel from "../components/Panel"; -import { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { fetchApi } from "../lib/request"; export default function Index() { - const [news, setNews] = useState(null); + const [news, setNews] = useState(null); const [isLoading, setIsLoading] = useState(false); const fetchNews = useCallback(async () => { diff --git a/src/pages/online.jsx b/src/pages/online.tsx similarity index 87% rename from src/pages/online.jsx rename to src/pages/online.tsx index 4496dbb..d335ace 100644 --- a/src/pages/online.jsx +++ b/src/pages/online.tsx @@ -7,7 +7,7 @@ import { vocationIdToName } from "../lib"; import Label from "../components/Label"; export default function Online() { - const [state, setState] = useState(null); + const [state, setState] = useState(null); const fetchData = useCallback(async () => { const players = await fetchApi("GET", `/api/player/online`); @@ -34,10 +34,8 @@ export default function Online() { - Overall Maximum: {state.status ? state.status.maxOnlineCount : "0"}{" "} - players. There are currently{" "} - {state.players ? state.players.length : 0} players online on{" "} - {state.status ? state.status.name : "..."} + Overall Maximum: {state.status ? state.status.maxOnlineCount : "0"} players. There are currently {state.players ? state.players.length : 0}{" "} + players online on {state.status ? state.status.name : "..."} {/* diff --git a/src/services/accountService.ts b/src/services/accountService.ts index d05fc5f..c6a1398 100644 --- a/src/services/accountService.ts +++ b/src/services/accountService.ts @@ -10,10 +10,7 @@ export const getAccountByIdIncludeDefault: Prisma.accountsInclude = { export const getAccountById = async ( accountId: number, - include: - | Prisma.accountsInclude - | null - | undefined = getAccountByIdIncludeDefault + include: Prisma.accountsInclude | null | undefined = getAccountByIdIncludeDefault, ): Account => { try { const account = await prisma.accounts.findFirst({ @@ -29,10 +26,7 @@ export const getAccountById = async ( } }; -export const getAccountByName = async ( - accountName: string, - include?: Prisma.accountsInclude -): Account => { +export const getAccountByName = async (accountName: string, include?: Prisma.accountsInclude): Account => { try { const account = await prisma.accounts.findFirst({ where: { @@ -47,15 +41,12 @@ export const getAccountByName = async ( } }; -export const getAccountBy = async ( - where?: Prisma.accountsWhereInput, - select?: Prisma.accountsSelect -): Account => { +export const getAccountBy = async (where?: Prisma.accountsWhereInput, select?: Prisma.accountsSelect): Account => { try { - const account = (await prisma.accounts.findFirst({ + const account = await prisma.accounts.findFirst({ where, select, - })) as Account; + }); return account; } catch (err) { @@ -63,11 +54,7 @@ export const getAccountBy = async ( } }; -export const createAccount = async ( - name: string, - password: string, - email: string -): Account => { +export const createAccount = async (name: string, password: string, email: string): Account => { const timestampInSeconds = Math.floor(Date.now() / 1000); try {