Merge pull request #51 from jedmund/user-settings
Refactor user settings
This commit is contained in:
commit
d64e9824c0
23 changed files with 589 additions and 363 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $unit * 2;
|
gap: $unit * 2;
|
||||||
width: $unit * 60;
|
width: $unit * 64;
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -45,89 +45,6 @@
|
||||||
transform: translateX(21px);
|
transform: translateX(21px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: $unit * 2;
|
|
||||||
|
|
||||||
select {
|
|
||||||
background: no-repeat url('/icons/ArrowDark.svg'), $grey-90;
|
|
||||||
background-position-y: center;
|
|
||||||
background-position-x: 95%;
|
|
||||||
margin: 0;
|
|
||||||
width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
gap: calc($unit / 2);
|
|
||||||
|
|
||||||
label {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: $font-regular;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: $font-small;
|
|
||||||
line-height: 1.1;
|
|
||||||
max-width: 300px;
|
|
||||||
|
|
||||||
&.jp {
|
|
||||||
max-width: 270px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
|
||||||
$diameter: 48px;
|
|
||||||
background-color: $grey-90;
|
|
||||||
border-radius: 999px;
|
|
||||||
height: $diameter;
|
|
||||||
width: $diameter;
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: $diameter;
|
|
||||||
width: $diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fire {
|
|
||||||
background: $fire-bg-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.water {
|
|
||||||
background: $water-bg-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.wind {
|
|
||||||
background: $wind-bg-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.earth {
|
|
||||||
background: $earth-bg-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dark {
|
|
||||||
background: $dark-bg-10;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.light {
|
|
||||||
background: $light-bg-20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
margin-bottom: $unit;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin-bottom: $unit * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.DialogDescription {
|
.DialogDescription {
|
||||||
|
|
|
||||||
|
|
@ -1,74 +1,123 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { getCookie } from 'cookies-next'
|
import { getCookie, setCookie } from 'cookies-next'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog'
|
import {
|
||||||
import * as Switch from '@radix-ui/react-switch'
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from '~components/Dialog'
|
||||||
|
import Button from '~components/Button'
|
||||||
|
import SelectItem from '~components/SelectItem'
|
||||||
|
import PictureSelectItem from '~components/PictureSelectItem'
|
||||||
|
import SelectTableField from '~components/SelectTableField'
|
||||||
|
// import * as Switch from '@radix-ui/react-switch'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
import changeLanguage from 'utils/changeLanguage'
|
||||||
import { accountState } from '~utils/accountState'
|
import { accountState } from '~utils/accountState'
|
||||||
import { pictureData } from '~utils/pictureData'
|
import { pictureData } from '~utils/pictureData'
|
||||||
|
|
||||||
import Button from '~components/Button'
|
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { useTheme } from 'next-themes'
|
||||||
|
|
||||||
const AccountModal = () => {
|
type StateVariables = {
|
||||||
const { account } = useSnapshot(accountState)
|
[key: string]: boolean
|
||||||
|
picture: boolean
|
||||||
|
gender: boolean
|
||||||
|
language: boolean
|
||||||
|
theme: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const router = useRouter()
|
interface Props {
|
||||||
|
username?: string
|
||||||
|
picture?: string
|
||||||
|
gender?: number
|
||||||
|
language?: string
|
||||||
|
theme?: string
|
||||||
|
private?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccountModal = (props: Props) => {
|
||||||
|
// Localization
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
const router = useRouter()
|
||||||
const locale =
|
const locale =
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||||
|
|
||||||
// State
|
// useEffect only runs on the client, so now we can safely show the UI
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
const { theme: appTheme, setTheme: setAppTheme } = useTheme()
|
||||||
|
|
||||||
|
// Cookies
|
||||||
|
const accountCookie = getCookie('account')
|
||||||
|
const userCookie = getCookie('user')
|
||||||
|
|
||||||
|
const cookieData = {
|
||||||
|
account: accountCookie ? JSON.parse(accountCookie as string) : undefined,
|
||||||
|
user: userCookie ? JSON.parse(userCookie as string) : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI State
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [picture, setPicture] = useState('')
|
const [selectOpenState, setSelectOpenState] = useState<StateVariables>({
|
||||||
const [language, setLanguage] = useState('')
|
picture: false,
|
||||||
const [gender, setGender] = useState(0)
|
gender: false,
|
||||||
const [privateProfile, setPrivateProfile] = useState(false)
|
language: false,
|
||||||
|
theme: false,
|
||||||
|
})
|
||||||
|
|
||||||
// Refs
|
// Values
|
||||||
const pictureSelect = React.createRef<HTMLSelectElement>()
|
const [username, setUsername] = useState(props.username || '')
|
||||||
const languageSelect = React.createRef<HTMLSelectElement>()
|
const [picture, setPicture] = useState(props.picture || '')
|
||||||
const genderSelect = React.createRef<HTMLSelectElement>()
|
const [language, setLanguage] = useState(props.language || '')
|
||||||
const privateSelect = React.createRef<HTMLInputElement>()
|
const [gender, setGender] = useState(props.gender || 0)
|
||||||
|
const [theme, setTheme] = useState(props.theme || 'system')
|
||||||
|
// const [privateProfile, setPrivateProfile] = useState(false)
|
||||||
|
|
||||||
// useEffect(() => {
|
// Setup
|
||||||
// if (cookies.user) setPicture(cookies.user.picture)
|
|
||||||
// if (cookies.user) setLanguage(cookies.user.language)
|
|
||||||
// if (cookies.user) setGender(cookies.user.gender)
|
|
||||||
// }, [cookies])
|
|
||||||
|
|
||||||
const pictureOptions = pictureData
|
// UI management
|
||||||
.sort((a, b) => (a.name.en > b.name.en ? 1 : -1))
|
function openChange(open: boolean) {
|
||||||
.map((item, i) => {
|
setOpen(open)
|
||||||
return (
|
}
|
||||||
<option key={`picture-${i}`} value={item.filename}>
|
|
||||||
{item.name[locale]}
|
function openSelect(name: 'picture' | 'gender' | 'language' | 'theme') {
|
||||||
</option>
|
const stateVars = selectOpenState
|
||||||
)
|
Object.keys(stateVars).forEach((key) => {
|
||||||
|
if (key === name) {
|
||||||
|
stateVars[name] = true
|
||||||
|
} else {
|
||||||
|
stateVars[key] = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function handlePictureChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
setSelectOpenState(stateVars)
|
||||||
if (pictureSelect.current) setPicture(pictureSelect.current.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLanguageChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
// Event handlers
|
||||||
if (languageSelect.current) setLanguage(languageSelect.current.value)
|
function handlePictureChange(value: string) {
|
||||||
|
setPicture(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGenderChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
function handleLanguageChange(value: string) {
|
||||||
if (genderSelect.current) setGender(parseInt(genderSelect.current.value))
|
setLanguage(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePrivateChange(checked: boolean) {
|
function handleGenderChange(value: string) {
|
||||||
setPrivateProfile(checked)
|
setGender(parseInt(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleThemeChange(value: string) {
|
||||||
|
setTheme(value)
|
||||||
|
setAppTheme(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API calls
|
||||||
function update(event: React.FormEvent<HTMLFormElement>) {
|
function update(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
|
|
@ -78,172 +127,180 @@ const AccountModal = () => {
|
||||||
element: pictureData.find((i) => i.filename === picture)?.element,
|
element: pictureData.find((i) => i.filename === picture)?.element,
|
||||||
language: language,
|
language: language,
|
||||||
gender: gender,
|
gender: gender,
|
||||||
private: privateProfile,
|
theme: theme,
|
||||||
|
// private: privateProfile,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// api.endpoints.users
|
if (accountState.account.user) {
|
||||||
// .update(cookies.account.user_id, object, headers)
|
api.endpoints.users
|
||||||
// .then((response) => {
|
.update(accountState.account.user?.id, object)
|
||||||
// const user = response.data.user
|
.then((response) => {
|
||||||
|
const user = response.data
|
||||||
|
|
||||||
// const cookieObj = {
|
const cookieObj = {
|
||||||
// picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
// element: user.picture.element,
|
element: user.avatar.element,
|
||||||
// gender: user.gender,
|
gender: user.gender,
|
||||||
// language: user.language,
|
language: user.language,
|
||||||
// }
|
theme: user.theme,
|
||||||
|
}
|
||||||
|
|
||||||
// setCookies("user", cookieObj, { path: "/" })
|
setCookie('user', cookieObj, { path: '/' })
|
||||||
|
|
||||||
// accountState.account.user = {
|
accountState.account.user = {
|
||||||
// id: user.id,
|
id: user.id,
|
||||||
// username: user.username,
|
username: user.username,
|
||||||
// picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
// element: user.picture.element,
|
element: user.avatar.element,
|
||||||
// gender: user.gender,
|
language: user.language,
|
||||||
// }
|
theme: user.theme,
|
||||||
|
gender: user.gender,
|
||||||
|
}
|
||||||
|
|
||||||
// setOpen(false)
|
setOpen(false)
|
||||||
// changeLanguage(user.language)
|
changeLanguage(router, user.language)
|
||||||
// })
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeLanguage(newLanguage: string) {
|
// Views
|
||||||
// if (newLanguage !== router.locale) {
|
const pictureOptions = pictureData
|
||||||
// setCookies("NEXT_LOCALE", newLanguage, { path: "/" })
|
.sort((a, b) => (a.name.en > b.name.en ? 1 : -1))
|
||||||
// router.push(router.asPath, undefined, { locale: newLanguage })
|
.map((item, i) => {
|
||||||
// }
|
return (
|
||||||
}
|
<PictureSelectItem
|
||||||
|
key={`picture-${i}`}
|
||||||
|
element={item.element}
|
||||||
|
src={[
|
||||||
|
`/profile/${item.filename}.png`,
|
||||||
|
`/profile/${item.filename}@2x.png 2x`,
|
||||||
|
]}
|
||||||
|
value={item.filename}
|
||||||
|
>
|
||||||
|
{item.name[locale]}
|
||||||
|
</PictureSelectItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
function openChange(open: boolean) {
|
const pictureField = () => (
|
||||||
setOpen(open)
|
<SelectTableField
|
||||||
|
name="picture"
|
||||||
|
description={t('modals.settings.descriptions.picture')}
|
||||||
|
className="Image"
|
||||||
|
label={t('modals.settings.labels.picture')}
|
||||||
|
open={selectOpenState.picture}
|
||||||
|
onClick={() => openSelect('picture')}
|
||||||
|
onChange={handlePictureChange}
|
||||||
|
imageAlt={t('modals.settings.labels.image_alt')}
|
||||||
|
imageClass={pictureData.find((i) => i.filename === picture)?.element}
|
||||||
|
imageSrc={[`/profile/${picture}.png`, `/profile/${picture}@2x.png 2x`]}
|
||||||
|
value={picture}
|
||||||
|
>
|
||||||
|
{pictureOptions}
|
||||||
|
</SelectTableField>
|
||||||
|
)
|
||||||
|
|
||||||
|
const genderField = () => (
|
||||||
|
<SelectTableField
|
||||||
|
name="gender"
|
||||||
|
description={t('modals.settings.descriptions.gender')}
|
||||||
|
label={t('modals.settings.labels.gender')}
|
||||||
|
open={selectOpenState.gender}
|
||||||
|
onClick={() => openSelect('gender')}
|
||||||
|
onChange={handleGenderChange}
|
||||||
|
value={`${gender}`}
|
||||||
|
>
|
||||||
|
<SelectItem key="gran" value="0">
|
||||||
|
{t('modals.settings.gender.gran')}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem key="djeeta" value="1">
|
||||||
|
{t('modals.settings.gender.djeeta')}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectTableField>
|
||||||
|
)
|
||||||
|
|
||||||
|
const languageField = () => (
|
||||||
|
<SelectTableField
|
||||||
|
name="language"
|
||||||
|
label={t('modals.settings.labels.language')}
|
||||||
|
open={selectOpenState.language}
|
||||||
|
onClick={() => openSelect('language')}
|
||||||
|
onChange={handleLanguageChange}
|
||||||
|
value={language}
|
||||||
|
>
|
||||||
|
<SelectItem key="en" value="en">
|
||||||
|
{t('modals.settings.language.english')}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem key="ja" value="ja">
|
||||||
|
{t('modals.settings.language.japanese')}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectTableField>
|
||||||
|
)
|
||||||
|
|
||||||
|
const themeField = () => (
|
||||||
|
<SelectTableField
|
||||||
|
name="theme"
|
||||||
|
label={t('modals.settings.labels.theme')}
|
||||||
|
open={selectOpenState.theme}
|
||||||
|
onClick={() => openSelect('theme')}
|
||||||
|
onChange={handleThemeChange}
|
||||||
|
value={theme}
|
||||||
|
>
|
||||||
|
<SelectItem key="system" value="system">
|
||||||
|
{t('modals.settings.theme.system')}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem key="light" value="light">
|
||||||
|
{t('modals.settings.theme.light')}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem key="dark" value="dark">
|
||||||
|
{t('modals.settings.theme.dark')}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectTableField>
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
<Dialog open={open} onOpenChange={openChange}>
|
||||||
<Dialog.Trigger asChild>
|
<DialogTrigger asChild>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<span>{t('menu.settings')}</span>
|
<span>{t('menu.settings')}</span>
|
||||||
</li>
|
</li>
|
||||||
</Dialog.Trigger>
|
</DialogTrigger>
|
||||||
<Dialog.Portal>
|
<DialogContent className="Account Dialog">
|
||||||
<Dialog.Content
|
<div className="DialogHeader">
|
||||||
className="Account Dialog"
|
<div className="DialogTop">
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
<DialogTitle className="SubTitle">
|
||||||
>
|
{t('modals.settings.title')}
|
||||||
<div className="DialogHeader">
|
</DialogTitle>
|
||||||
<div className="DialogTop">
|
<DialogTitle className="DialogTitle">@{username}</DialogTitle>
|
||||||
<Dialog.Title className="SubTitle">
|
|
||||||
{t('modals.settings.title')}
|
|
||||||
</Dialog.Title>
|
|
||||||
<Dialog.Title className="DialogTitle">
|
|
||||||
@{account.user?.username}
|
|
||||||
</Dialog.Title>
|
|
||||||
</div>
|
|
||||||
<Dialog.Close className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</Dialog.Close>
|
|
||||||
</div>
|
</div>
|
||||||
|
<DialogClose className="DialogClose" asChild>
|
||||||
|
<span>
|
||||||
|
<CrossIcon />
|
||||||
|
</span>
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form onSubmit={update}>
|
<form onSubmit={update}>
|
||||||
<div className="field">
|
{pictureField()}
|
||||||
<div className="left">
|
{genderField()}
|
||||||
<label>{t('modals.settings.labels.picture')}</label>
|
{languageField()}
|
||||||
</div>
|
{themeField()}
|
||||||
|
<Button
|
||||||
<div
|
contained={true}
|
||||||
className={`preview ${
|
text={t('modals.settings.buttons.confirm')}
|
||||||
pictureData.find((i) => i.filename === picture)?.element
|
/>
|
||||||
}`}
|
</form>
|
||||||
>
|
</DialogContent>
|
||||||
{picture ? (
|
</Dialog>
|
||||||
<img
|
|
||||||
alt="Profile preview"
|
|
||||||
srcSet={`/profile/${picture}.png,
|
|
||||||
/profile/${picture}@2x.png 2x`}
|
|
||||||
src={`/profile/${picture}.png`}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select
|
|
||||||
name="picture"
|
|
||||||
onChange={handlePictureChange}
|
|
||||||
value={picture}
|
|
||||||
ref={pictureSelect}
|
|
||||||
>
|
|
||||||
{pictureOptions}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="field">
|
|
||||||
<div className="left">
|
|
||||||
<label>{t('modals.settings.labels.gender')}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select
|
|
||||||
name="gender"
|
|
||||||
onChange={handleGenderChange}
|
|
||||||
value={gender}
|
|
||||||
ref={genderSelect}
|
|
||||||
>
|
|
||||||
<option key="gran" value="0">
|
|
||||||
{t('modals.settings.gender.gran')}
|
|
||||||
</option>
|
|
||||||
<option key="djeeta" value="1">
|
|
||||||
{t('modals.settings.gender.djeeta')}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="field">
|
|
||||||
<div className="left">
|
|
||||||
<label>{t('modals.settings.labels.language')}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select
|
|
||||||
name="language"
|
|
||||||
onChange={handleLanguageChange}
|
|
||||||
value={language}
|
|
||||||
ref={languageSelect}
|
|
||||||
>
|
|
||||||
<option key="en" value="en">
|
|
||||||
{t('modals.settings.language.english')}
|
|
||||||
</option>
|
|
||||||
<option key="jp" value="ja">
|
|
||||||
{t('modals.settings.language.japanese')}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="field">
|
|
||||||
<div className="left">
|
|
||||||
<label>{t('modals.settings.labels.private')}</label>
|
|
||||||
<p className={locale}>
|
|
||||||
{t('modals.settings.descriptions.private')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Switch.Root
|
|
||||||
className="Switch"
|
|
||||||
onCheckedChange={handlePrivateChange}
|
|
||||||
checked={privateProfile}
|
|
||||||
>
|
|
||||||
<Switch.Thumb className="Thumb" />
|
|
||||||
</Switch.Root>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
contained={true}
|
|
||||||
text={t('modals.settings.buttons.confirm')}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Dialog.Content>
|
|
||||||
<Dialog.Overlay className="Overlay" />
|
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Root>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,6 @@ const AXSelect = (props: Props) => {
|
||||||
}, [props.currentSkills, setSecondaryAxModifier])
|
}, [props.currentSkills, setSecondaryAxModifier])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(
|
|
||||||
primaryAxModifier,
|
|
||||||
primaryAxValue,
|
|
||||||
secondaryAxModifier,
|
|
||||||
secondaryAxValue
|
|
||||||
)
|
|
||||||
|
|
||||||
let noErrors = false
|
let noErrors = false
|
||||||
|
|
||||||
if (errors.axValue1 === '' && errors.axValue2 === '') {
|
if (errors.axValue1 === '' && errors.axValue2 === '') {
|
||||||
|
|
@ -132,13 +125,6 @@ const AXSelect = (props: Props) => {
|
||||||
secondaryAxValue > 0
|
secondaryAxValue > 0
|
||||||
)
|
)
|
||||||
noErrors = true
|
noErrors = true
|
||||||
else
|
|
||||||
console.log(
|
|
||||||
primaryAxModifier >= 0,
|
|
||||||
primaryAxValue > 0,
|
|
||||||
secondaryAxModifier >= 0,
|
|
||||||
secondaryAxValue > 0
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
props.sendValidity(noErrors)
|
props.sendValidity(noErrors)
|
||||||
|
|
|
||||||
|
|
@ -35,5 +35,6 @@ export const DialogContent = React.forwardRef<HTMLDivElement, Props>(
|
||||||
)
|
)
|
||||||
|
|
||||||
export const Dialog = DialogPrimitive.Root
|
export const Dialog = DialogPrimitive.Root
|
||||||
|
export const DialogTitle = DialogPrimitive.Title
|
||||||
export const DialogTrigger = DialogPrimitive.Trigger
|
export const DialogTrigger = DialogPrimitive.Trigger
|
||||||
export const DialogClose = DialogPrimitive.Close
|
export const DialogClose = DialogPrimitive.Close
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
display: none;
|
display: none;
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $unit * 5; // This shouldn't be hardcoded. How to calculate it?
|
top: $unit * 5.75; // This shouldn't be hardcoded. How to calculate it?
|
||||||
// Also, add space that doesn't make the menu disappear if you move your mouse slowly
|
// Also, add space that doesn't make the menu disappear if you move your mouse slowly
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import LoginModal from '~components/LoginModal'
|
||||||
import SignupModal from '~components/SignupModal'
|
import SignupModal from '~components/SignupModal'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import { accountState } from '~utils/accountState'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
authenticated: boolean
|
authenticated: boolean
|
||||||
|
|
@ -59,8 +60,8 @@ const HeaderMenu = (props: Props) => {
|
||||||
<span>{accountData.username}</span>
|
<span>{accountData.username}</span>
|
||||||
<img
|
<img
|
||||||
alt={userData.picture}
|
alt={userData.picture}
|
||||||
className={`profile ${userData.element}`}
|
className={`profile ${accountState.account.user?.element}`}
|
||||||
srcSet={`/profile/${userData.picture}.png,
|
srcSet={`/profile/${accountState.account.user?.picture}.png,
|
||||||
/profile/${userData.picture}@2x.png 2x`}
|
/profile/${userData.picture}@2x.png 2x`}
|
||||||
src={`/profile/${userData.picture}.png`}
|
src={`/profile/${userData.picture}.png`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -85,7 +86,13 @@ const HeaderMenu = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="MenuGroup">
|
<div className="MenuGroup">
|
||||||
<AboutModal />
|
<AboutModal />
|
||||||
<AccountModal />
|
<AccountModal
|
||||||
|
username={accountState.account.user?.username}
|
||||||
|
picture={accountState.account.user?.picture}
|
||||||
|
gender={accountState.account.user?.gender}
|
||||||
|
language={accountState.account.user?.language}
|
||||||
|
theme={accountState.account.user?.theme}
|
||||||
|
/>
|
||||||
<li className="MenuItem" onClick={props.logout}>
|
<li className="MenuItem" onClick={props.logout}>
|
||||||
<span>{t('menu.logout')}</span>
|
<span>{t('menu.logout')}</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import {
|
||||||
DialogClose,
|
DialogClose,
|
||||||
} from '~components/Dialog'
|
} from '~components/Dialog'
|
||||||
|
|
||||||
|
import changeLanguage from '~utils/changeLanguage'
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|
@ -127,37 +129,38 @@ const LoginModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeUserInfo(response: AxiosResponse) {
|
function storeUserInfo(response: AxiosResponse) {
|
||||||
|
// Extract the user
|
||||||
const user = response.data
|
const user = response.data
|
||||||
|
|
||||||
const cookieObj: UserCookie = {
|
// Set user data in the user cookie
|
||||||
picture: user.avatar.picture,
|
setCookie(
|
||||||
element: user.avatar.element,
|
'user',
|
||||||
language: user.language,
|
{
|
||||||
gender: user.gender,
|
picture: user.avatar.picture,
|
||||||
}
|
element: user.avatar.element,
|
||||||
|
language: user.language,
|
||||||
setCookie('user', cookieObj, { path: '/' })
|
gender: user.gender,
|
||||||
|
theme: user.theme,
|
||||||
|
},
|
||||||
|
{ path: '/' }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set the user data in the account state
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
picture: user.avatar.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.avatar.element,
|
element: user.avatar.element,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
|
language: user.language,
|
||||||
|
theme: user.theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Authorizing account...')
|
console.log('Authorizing account...')
|
||||||
accountState.account.authorized = true
|
accountState.account.authorized = true
|
||||||
|
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
changeLanguage(user.language)
|
changeLanguage(router, user.language)
|
||||||
}
|
|
||||||
|
|
||||||
function changeLanguage(newLanguage: string) {
|
|
||||||
if (newLanguage !== router.locale) {
|
|
||||||
setCookie('NEXT_LOCALE', newLanguage, { path: '/' })
|
|
||||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openChange(open: boolean) {
|
function openChange(open: boolean) {
|
||||||
|
|
|
||||||
42
components/PictureSelectItem/index.scss
Normal file
42
components/PictureSelectItem/index.scss
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
.SelectItem.Picture {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: $unit;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
$diameter: $unit-4x;
|
||||||
|
border-radius: $unit-2x;
|
||||||
|
width: $diameter;
|
||||||
|
height: $diameter;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: $diameter;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fire {
|
||||||
|
background: $fire-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.water {
|
||||||
|
background: $water-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.wind {
|
||||||
|
background: $wind-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.earth {
|
||||||
|
background: $earth-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
background: $dark-bg-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
background: $light-bg-20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
components/PictureSelectItem/index.tsx
Normal file
35
components/PictureSelectItem/index.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React, { ComponentProps } from 'react'
|
||||||
|
import * as Select from '@radix-ui/react-select'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
interface Props extends ComponentProps<'div'> {
|
||||||
|
src: string[]
|
||||||
|
element: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const PictureSelectItem = React.forwardRef<HTMLDivElement, Props>(
|
||||||
|
function selectItem({ children, ...props }, forwardedRef) {
|
||||||
|
return (
|
||||||
|
<Select.Item
|
||||||
|
className={classNames('SelectItem Picture', props.className)}
|
||||||
|
{...props}
|
||||||
|
ref={forwardedRef}
|
||||||
|
value={`${props.value}`}
|
||||||
|
>
|
||||||
|
<div className={`preview ${props.element}`}>
|
||||||
|
<img
|
||||||
|
alt={`${props.value}`}
|
||||||
|
src={props.src[0]}
|
||||||
|
srcSet={props.src.join(', ')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Select.ItemText>{children}</Select.ItemText>
|
||||||
|
</Select.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default PictureSelectItem
|
||||||
|
|
@ -43,6 +43,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Table {
|
||||||
|
min-width: $unit * 30;
|
||||||
|
}
|
||||||
|
|
||||||
.SelectIcon {
|
.SelectIcon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
69
components/SelectTableField/index.scss
Normal file
69
components/SelectTableField/index.scss
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
.TableField {
|
||||||
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
gap: $unit * 2;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
|
||||||
|
&.Image {
|
||||||
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc($unit / 2);
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: $font-regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: $font-small;
|
||||||
|
line-height: 1.1;
|
||||||
|
max-width: 300px;
|
||||||
|
|
||||||
|
&.jp {
|
||||||
|
max-width: 270px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
$diameter: $unit * 6;
|
||||||
|
background-color: $grey-90;
|
||||||
|
border-radius: 999px;
|
||||||
|
height: $diameter;
|
||||||
|
width: $diameter;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: $diameter;
|
||||||
|
width: $diameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fire {
|
||||||
|
background: $fire-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.water {
|
||||||
|
background: $water-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.wind {
|
||||||
|
background: $wind-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.earth {
|
||||||
|
background: $earth-bg-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
background: $dark-bg-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
background: $light-bg-20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
components/SelectTableField/index.tsx
Normal file
68
components/SelectTableField/index.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import Select from '~components/Select'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string
|
||||||
|
label: string
|
||||||
|
description?: string
|
||||||
|
open: boolean
|
||||||
|
value?: string
|
||||||
|
className?: string
|
||||||
|
imageAlt?: string
|
||||||
|
imageClass?: string
|
||||||
|
imageSrc?: string[]
|
||||||
|
children: React.ReactNode
|
||||||
|
onClick: () => void
|
||||||
|
onChange: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectTableField = (props: Props) => {
|
||||||
|
const [value, setValue] = useState('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value) setValue(props.value)
|
||||||
|
}, [props.value])
|
||||||
|
|
||||||
|
const image = () => {
|
||||||
|
return props.imageSrc && props.imageSrc.length > 0 ? (
|
||||||
|
<div className={`preview ${props.imageClass}`}>
|
||||||
|
<img
|
||||||
|
alt={props.imageAlt}
|
||||||
|
srcSet={props.imageSrc.join(', ')}
|
||||||
|
src={props.imageSrc[0]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames({ TableField: true }, props.className)}>
|
||||||
|
<div className="Left">
|
||||||
|
<h3>{props.label}</h3>
|
||||||
|
<p>{props.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{image()}
|
||||||
|
|
||||||
|
<div className="Right">
|
||||||
|
<Select
|
||||||
|
name={props.name}
|
||||||
|
open={props.open}
|
||||||
|
onClick={props.onClick}
|
||||||
|
onValueChange={props.onChange}
|
||||||
|
triggerClass={classNames({ Bound: true, Table: true })}
|
||||||
|
value={value}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectTableField
|
||||||
|
|
@ -102,27 +102,36 @@ const SignupModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeUserInfo(response: AxiosResponse) {
|
function storeUserInfo(response: AxiosResponse) {
|
||||||
|
// Extract the user
|
||||||
const user = response.data
|
const user = response.data
|
||||||
|
|
||||||
const cookieObj: UserCookie = {
|
// Set user data in the user cookie
|
||||||
picture: user.avatar.picture,
|
setCookie(
|
||||||
element: user.avatar.element,
|
'user',
|
||||||
language: user.language,
|
{
|
||||||
gender: user.gender,
|
picture: user.avatar.picture,
|
||||||
}
|
element: user.avatar.element,
|
||||||
|
language: user.language,
|
||||||
// TODO: Set language
|
gender: user.gender,
|
||||||
setCookie('user', cookieObj, { path: '/' })
|
theme: user.theme,
|
||||||
|
},
|
||||||
|
{ path: '/' }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set the user data in the account state
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
picture: user.avatar.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.avatar.element,
|
element: user.avatar.element,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
|
language: user.language,
|
||||||
|
theme: user.theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Authorizing account...')
|
||||||
accountState.account.authorized = true
|
accountState.account.authorized = true
|
||||||
|
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,12 +106,8 @@ const WeaponGrid = (props: Props) => {
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const code = error.response.status
|
const code = error.response.status
|
||||||
const data = error.response.data
|
const data = error.response.data
|
||||||
console.log(error.response)
|
|
||||||
|
|
||||||
console.log(data, code)
|
|
||||||
if (code === 422) {
|
if (code === 422) {
|
||||||
if (data.code === 'incompatible_weapon_for_position') {
|
if (data.code === 'incompatible_weapon_for_position') {
|
||||||
console.log('Here')
|
|
||||||
setShowIncompatibleAlert(true)
|
setShowIncompatibleAlert(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +335,6 @@ const WeaponGrid = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const incompatibleAlert = () => {
|
const incompatibleAlert = () => {
|
||||||
console.log(t('alert.incompatible_weapon'))
|
|
||||||
return showIncompatibleAlert ? (
|
return showIncompatibleAlert ? (
|
||||||
<Alert
|
<Alert
|
||||||
open={showIncompatibleAlert}
|
open={showIncompatibleAlert}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { getCookie } from 'cookies-next'
|
import { getCookie, getCookies } from 'cookies-next'
|
||||||
import { appWithTranslation } from 'next-i18next'
|
import { appWithTranslation } from 'next-i18next'
|
||||||
import { ThemeProvider } from 'next-themes'
|
import { ThemeProvider, useTheme } from 'next-themes'
|
||||||
|
|
||||||
import type { AppProps } from 'next/app'
|
import type { AppProps } from 'next/app'
|
||||||
import Layout from '~components/Layout'
|
import Layout from '~components/Layout'
|
||||||
|
|
@ -12,27 +12,34 @@ import setUserToken from '~utils/setUserToken'
|
||||||
import '../styles/globals.scss'
|
import '../styles/globals.scss'
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
const cookie = getCookie('account')
|
const accountCookie = getCookie('account')
|
||||||
const cookieData: AccountCookie = cookie ? JSON.parse(cookie as string) : null
|
const userCookie = getCookie('user')
|
||||||
|
|
||||||
|
const cookieData = {
|
||||||
|
account: accountCookie ? JSON.parse(accountCookie as string) : undefined,
|
||||||
|
user: userCookie ? JSON.parse(userCookie as string) : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUserToken()
|
setUserToken()
|
||||||
|
|
||||||
if (cookie) {
|
if (accountCookie) {
|
||||||
console.log(`Logged in as user "${cookieData.username}"`)
|
console.log(`Logged in as user "${cookieData.account.username}"`)
|
||||||
|
|
||||||
accountState.account.authorized = true
|
accountState.account.authorized = true
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: cookieData.userId,
|
id: cookieData.account.userId,
|
||||||
username: cookieData.username,
|
username: cookieData.account.username,
|
||||||
picture: '',
|
picture: cookieData.user.picture,
|
||||||
element: '',
|
element: cookieData.user.element,
|
||||||
gender: 0,
|
gender: cookieData.user.gender,
|
||||||
|
language: cookieData.user.language,
|
||||||
|
theme: cookieData.user.theme,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`You are not currently logged in.`)
|
console.log(`You are not currently logged in.`)
|
||||||
}
|
}
|
||||||
}, [cookie, cookieData])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import Party from '~components/Party'
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
|
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
|
||||||
import organizeRaids from '~utils/organizeRaids'
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
@ -48,38 +49,35 @@ export const getServerSidePaths = async () => {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
||||||
// Cookies
|
// Set headers for server-side requests
|
||||||
const cookie = getCookie("account", { req, res })
|
setUserToken(req, res)
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll()
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
let jobs = await api.endpoints.jobs
|
let jobs = await api.endpoints.jobs
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.data
|
return response.data
|
||||||
})
|
})
|
||||||
|
|
||||||
let jobSkills = await api.allJobSkills(headers).then((response) => response.data)
|
let jobSkills = await api
|
||||||
|
.allJobSkills()
|
||||||
|
.then((response) => response.data)
|
||||||
|
|
||||||
let weaponKeys = await api.endpoints.weapon_keys
|
let weaponKeys = await api.endpoints.weapon_keys
|
||||||
.getAll()
|
.getAll()
|
||||||
.then((response) => groupWeaponKeys(response.data))
|
.then((response) => groupWeaponKeys(response.data))
|
||||||
|
|
||||||
let party: Party | null = null
|
let party: Party | null = null
|
||||||
if (query.party) {
|
if (query.party) {
|
||||||
let response = await api.endpoints.parties.getOne({ id: query.party, params: headers })
|
let response = await api.endpoints.parties.getOne({
|
||||||
|
id: query.party
|
||||||
|
})
|
||||||
party = response.data.party
|
party = response.data.party
|
||||||
} else {
|
} else {
|
||||||
console.log("No party code")
|
console.log('No party code')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -90,7 +88,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
|
||||||
raids: raids,
|
raids: raids,
|
||||||
sortedRaids: sortedRaids,
|
sortedRaids: sortedRaids,
|
||||||
weaponKeys: weaponKeys,
|
weaponKeys: weaponKeys,
|
||||||
...(await serverSideTranslations(locale, ["common"])),
|
...(await serverSideTranslations(locale, ['common'])),
|
||||||
// Will be passed to the page component as props
|
// Will be passed to the page component as props
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,12 @@
|
||||||
"picture": "Picture",
|
"picture": "Picture",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"gender": "Main Character",
|
"gender": "Main Character",
|
||||||
"private": "Private"
|
"private": "Private",
|
||||||
|
"theme": "Theme"
|
||||||
},
|
},
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
|
"picture": "Displayed next to your name",
|
||||||
|
"gender": "The character shown on your teams",
|
||||||
"private": "Hide your profile and prevent your grids from showing up in collections"
|
"private": "Hide your profile and prevent your grids from showing up in collections"
|
||||||
},
|
},
|
||||||
"gender": {
|
"gender": {
|
||||||
|
|
@ -183,6 +186,11 @@
|
||||||
"english": "English",
|
"english": "English",
|
||||||
"japanese": "Japanese"
|
"japanese": "Japanese"
|
||||||
},
|
},
|
||||||
|
"theme": {
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark",
|
||||||
|
"system": "Follow system"
|
||||||
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"confirm": "Save settings"
|
"confirm": "Save settings"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,12 @@
|
||||||
"picture": "プロフィール画像",
|
"picture": "プロフィール画像",
|
||||||
"language": "言語",
|
"language": "言語",
|
||||||
"gender": "主人公",
|
"gender": "主人公",
|
||||||
"private": "プライベート"
|
"private": "プライベート",
|
||||||
|
"theme": "表示"
|
||||||
},
|
},
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
|
"picture": "名前の隣に表示する",
|
||||||
|
"gender": "編成で表示するキャラクター",
|
||||||
"private": "プロフィールを隠し、編成をコレクションに表示されないようにします"
|
"private": "プロフィールを隠し、編成をコレクションに表示されないようにします"
|
||||||
},
|
},
|
||||||
"gender": {
|
"gender": {
|
||||||
|
|
@ -183,6 +186,11 @@
|
||||||
"english": "英語",
|
"english": "英語",
|
||||||
"japanese": "日本語"
|
"japanese": "日本語"
|
||||||
},
|
},
|
||||||
|
"theme": {
|
||||||
|
"light": "ライト",
|
||||||
|
"dark": "ダーク",
|
||||||
|
"system": "システム"
|
||||||
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"confirm": "設定を保存する"
|
"confirm": "設定を保存する"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ $menu--text--light: $grey-90;
|
||||||
$menu--text--dark: $grey-50;
|
$menu--text--dark: $grey-50;
|
||||||
$menu--separator--light: $grey-90;
|
$menu--separator--light: $grey-90;
|
||||||
$menu--separator--dark: $grey-05;
|
$menu--separator--dark: $grey-05;
|
||||||
$menu--item--bg--light--hover: $grey-90;
|
$menu--item--bg--light--hover: $grey-85;
|
||||||
$menu--item--bg--dark--hover: $grey-00;
|
$menu--item--bg--dark--hover: $grey-00;
|
||||||
$menu--text--light--hover: $grey-100;
|
$menu--text--light--hover: $grey-100;
|
||||||
$menu--text--dark--hover: $grey-15;
|
$menu--text--dark--hover: $grey-15;
|
||||||
|
|
|
||||||
1
types/UserCookie.d.ts
vendored
1
types/UserCookie.d.ts
vendored
|
|
@ -3,4 +3,5 @@ interface UserCookie {
|
||||||
element: string
|
element: string
|
||||||
language: string
|
language: string
|
||||||
gender: number
|
gender: number
|
||||||
|
theme: string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
import { proxy } from 'valtio'
|
import { proxy } from 'valtio'
|
||||||
|
|
||||||
|
export type UserState = {
|
||||||
|
id: string
|
||||||
|
username: string
|
||||||
|
picture: string
|
||||||
|
element: string
|
||||||
|
gender: number
|
||||||
|
language: string
|
||||||
|
theme: string
|
||||||
|
}
|
||||||
|
|
||||||
interface AccountState {
|
interface AccountState {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
|
|
||||||
account: {
|
account: {
|
||||||
authorized: boolean
|
authorized: boolean
|
||||||
user:
|
user: UserState | undefined
|
||||||
| {
|
|
||||||
id: string
|
|
||||||
username: string
|
|
||||||
picture: string
|
|
||||||
element: string
|
|
||||||
gender: number
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
utils/changeLanguage.tsx
Normal file
12
utils/changeLanguage.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { setCookie } from 'cookies-next'
|
||||||
|
import { NextRouter } from 'next/router'
|
||||||
|
|
||||||
|
export default function changeLanguage(
|
||||||
|
router: NextRouter,
|
||||||
|
newLanguage: string
|
||||||
|
) {
|
||||||
|
if (newLanguage !== router.locale) {
|
||||||
|
setCookie('NEXT_LOCALE', newLanguage, { path: '/' })
|
||||||
|
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ export type GroupedWeaponKeys = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function groupWeaponKeys(keys: WeaponKey[]) {
|
export function groupWeaponKeys(keys: WeaponKey[]) {
|
||||||
console.log(keys)
|
|
||||||
const numGroups = Math.max.apply(
|
const numGroups = Math.max.apply(
|
||||||
Math,
|
Math,
|
||||||
keys.map((key) => key.group)
|
keys.map((key) => key.group)
|
||||||
|
|
@ -28,7 +27,5 @@ export function groupWeaponKeys(keys: WeaponKey[]) {
|
||||||
groupedKeys[weaponKeyGroups[i].slug] = keys.filter((key) => key.group == i)
|
groupedKeys[weaponKeyGroups[i].slug] = keys.filter((key) => key.group == i)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(groupedKeys)
|
|
||||||
|
|
||||||
return groupedKeys
|
return groupedKeys
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue