Merge pull request #24 from jedmund/i18n
Add internationalization support
|
|
@ -1,21 +1,24 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
|
||||
const AboutModal = () => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>About</span>
|
||||
<span>{t('modals.about.title')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="About Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">About</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{t('menu.about')}</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@
|
|||
font-size: $font-small;
|
||||
line-height: 1.1;
|
||||
max-width: 300px;
|
||||
|
||||
&.jp {
|
||||
max-width: 270px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Switch from '@radix-ui/react-switch'
|
||||
|
|
@ -17,13 +19,16 @@ import './index.scss'
|
|||
const AccountModal = () => {
|
||||
const { account } = useSnapshot(accountState)
|
||||
|
||||
// Cookies
|
||||
const [accountCookies] = useCookies(['account'])
|
||||
const [userCookies, setUserCookies] = useCookies(['user'])
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const headers = (accountCookies.account != null) ? {
|
||||
// Cookies
|
||||
const [cookies, setCookies] = useCookies()
|
||||
|
||||
const headers = (cookies.account != null) ? {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accountCookies.account.access_token}`
|
||||
'Authorization': `Bearer ${cookies.account.access_token}`
|
||||
}
|
||||
} : {}
|
||||
|
||||
|
|
@ -39,14 +44,15 @@ const AccountModal = () => {
|
|||
const privateSelect = React.createRef<HTMLInputElement>()
|
||||
|
||||
useEffect(() => {
|
||||
if (userCookies.user) setPicture(userCookies.user.picture)
|
||||
if (userCookies.user) setLanguage(userCookies.user.language)
|
||||
}, [userCookies])
|
||||
console.log(cookies.user)
|
||||
if (cookies.user) setPicture(cookies.user.picture)
|
||||
if (cookies.user) setLanguage(cookies.user.language)
|
||||
}, [cookies])
|
||||
|
||||
const pictureOptions = (
|
||||
pictureData.sort((a, b) => (a.name.en > b.name.en) ? 1 : -1).map((item, i) => {
|
||||
return (
|
||||
<option key={`picture-${i}`} value={item.filename}>{item.name.en}</option>
|
||||
<option key={`picture-${i}`} value={item.filename}>{item.name[locale]}</option>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
@ -77,7 +83,7 @@ const AccountModal = () => {
|
|||
}
|
||||
}
|
||||
|
||||
api.endpoints.users.update(accountCookies.account.user_id, object, headers)
|
||||
api.endpoints.users.update(cookies.account.user_id, object, headers)
|
||||
.then(response => {
|
||||
const user = response.data.user
|
||||
|
||||
|
|
@ -87,9 +93,8 @@ const AccountModal = () => {
|
|||
language: user.language,
|
||||
}
|
||||
|
||||
setUserCookies('user', cookieObj, { path: '/'})
|
||||
setCookies('user', cookieObj, { path: '/'})
|
||||
|
||||
accountState.account.language = user.language
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
|
|
@ -98,9 +103,17 @@ const AccountModal = () => {
|
|||
}
|
||||
|
||||
setOpen(false)
|
||||
changeLanguage(user.language)
|
||||
})
|
||||
}
|
||||
|
||||
function changeLanguage(newLanguage: string) {
|
||||
if (newLanguage !== router.locale) {
|
||||
setCookies('NEXT_LOCALE', newLanguage, { path: '/'})
|
||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
}
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
}
|
||||
|
|
@ -109,14 +122,14 @@ const AccountModal = () => {
|
|||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>Settings</span>
|
||||
<span>{t('menu.settings')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Account Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">Account Settings</Dialog.Title>
|
||||
<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>
|
||||
|
|
@ -129,7 +142,7 @@ const AccountModal = () => {
|
|||
<form onSubmit={update}>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>Picture</label>
|
||||
<label>{t('modals.settings.labels.picture')}</label>
|
||||
</div>
|
||||
|
||||
<div className={`preview ${pictureData.find(i => i.filename === picture)?.element}`}>
|
||||
|
|
@ -147,18 +160,18 @@ const AccountModal = () => {
|
|||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>Language</label>
|
||||
<label>{t('modals.settings.labels.language')}</label>
|
||||
</div>
|
||||
|
||||
<select name="language" onChange={handleLanguageChange} value={language} ref={languageSelect}>
|
||||
<option key="en" value="en">English</option>
|
||||
<option key="jp" value="jp">Japanese</option>
|
||||
<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>Private</label>
|
||||
<p>Hide your profile and prevent your grids from showing up in collections</p>
|
||||
<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}>
|
||||
|
|
@ -166,7 +179,7 @@ const AccountModal = () => {
|
|||
</Switch.Root>
|
||||
</div>
|
||||
|
||||
<Button>Save settings</Button>
|
||||
<Button>{t('modals.settings.buttons.confirm')}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { axData } from '~utils/axData'
|
||||
|
|
@ -19,6 +22,10 @@ interface Props {
|
|||
}
|
||||
|
||||
const AXSelect = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up form states and error handling
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
axValue1: '',
|
||||
|
|
@ -84,7 +91,7 @@ const AXSelect = (props: Props) => {
|
|||
if (modifierSet == 0) {
|
||||
axOptionElements = axOptions.map((ax, i) => {
|
||||
return (
|
||||
<option key={i} value={ax.id}>{ax.name.en}</option>
|
||||
<option key={i} value={ax.id}>{ax.name[locale]}</option>
|
||||
)
|
||||
})
|
||||
} else {
|
||||
|
|
@ -103,14 +110,14 @@ const AXSelect = (props: Props) => {
|
|||
const secondaryAxOptions = primarySkill.secondary
|
||||
axOptionElements = secondaryAxOptions.map((ax, i) => {
|
||||
return (
|
||||
<option key={i} value={ax.id}>{ax.name.en}</option>
|
||||
<option key={i} value={ax.id}>{ax.name[locale]}</option>
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
axOptionElements?.unshift(<option key={-1} value={-1}>No AX Skill</option>)
|
||||
axOptionElements?.unshift(<option key={-1} value={-1}>{t('ax.no_skill')}</option>)
|
||||
return axOptionElements
|
||||
}
|
||||
|
||||
|
|
@ -156,11 +163,19 @@ const AXSelect = (props: Props) => {
|
|||
let newErrors = {...errors}
|
||||
|
||||
if (value < primaryAxSkill.minValue) {
|
||||
newErrors.axValue1 = `${primaryAxSkill.name.en} must be at least ${primaryAxSkill.minValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}`
|
||||
newErrors.axValue1 = t('ax.errors.value_too_low', {
|
||||
name: primaryAxSkill.name[locale],
|
||||
minValue: primaryAxSkill.minValue,
|
||||
suffix: (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''
|
||||
})
|
||||
} else if (value > primaryAxSkill.maxValue) {
|
||||
newErrors.axValue1 = `${primaryAxSkill.name.en} cannot be greater than ${primaryAxSkill.maxValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}`
|
||||
newErrors.axValue1 = t('ax.errors.value_too_high', {
|
||||
name: primaryAxSkill.name[locale],
|
||||
maxValue: primaryAxSkill.minValue,
|
||||
suffix: (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''
|
||||
})
|
||||
} else if (!value || value <= 0) {
|
||||
newErrors.axValue1 = `${primaryAxSkill.name.en} must have a value`
|
||||
newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale] })
|
||||
} else {
|
||||
newErrors.axValue1 = ''
|
||||
}
|
||||
|
|
@ -179,13 +194,21 @@ const AXSelect = (props: Props) => {
|
|||
|
||||
if (secondaryAxSkill) {
|
||||
if (value < secondaryAxSkill.minValue) {
|
||||
newErrors.axValue2 = `${secondaryAxSkill.name.en} must be at least ${secondaryAxSkill.minValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}`
|
||||
newErrors.axValue2 = t('ax.errors.value_too_low', {
|
||||
name: secondaryAxSkill.name[locale],
|
||||
minValue: secondaryAxSkill.minValue,
|
||||
suffix: (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''
|
||||
})
|
||||
} else if (value > secondaryAxSkill.maxValue) {
|
||||
newErrors.axValue2 = `${secondaryAxSkill.name.en} cannot be greater than ${secondaryAxSkill.maxValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}`
|
||||
newErrors.axValue2 = t('ax.errors.value_too_high', {
|
||||
name: secondaryAxSkill.name[locale],
|
||||
maxValue: secondaryAxSkill.minValue,
|
||||
suffix: (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''
|
||||
})
|
||||
} else if (!secondaryAxSkill.suffix && value % 1 !== 0) {
|
||||
newErrors.axValue2 = `${secondaryAxSkill.name.en} must be a whole number`
|
||||
newErrors.axValue2 = t('ax.errors.value_not_whole', { name: secondaryAxSkill.name[locale] })
|
||||
} else if (primaryAxValue <= 0) {
|
||||
newErrors.axValue1 = `${primaryAxSkill.name.en} must have a value`
|
||||
newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale] })
|
||||
} else {
|
||||
newErrors.axValue2 = ''
|
||||
}
|
||||
|
|
@ -224,7 +247,7 @@ const AXSelect = (props: Props) => {
|
|||
<div className="AXSet">
|
||||
<div className="fields">
|
||||
<select key="ax1" defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].modifier : -1 } onChange={handleSelectChange} ref={primaryAxModifierSelect}>{ generateOptions(0) }</select>
|
||||
<input defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={primaryAxValueInput} disabled />
|
||||
<input defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={primaryAxValueInput} disabled={primaryAxValue != 0} />
|
||||
</div>
|
||||
<p className={primaryErrorClasses}>{errors.axValue1}</p>
|
||||
</div>
|
||||
|
|
@ -232,7 +255,7 @@ const AXSelect = (props: Props) => {
|
|||
<div className={secondarySetClasses}>
|
||||
<div className="fields">
|
||||
<select key="ax2" defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].modifier : -1 } onChange={handleSelectChange} ref={secondaryAxModifierSelect}>{ generateOptions(1) }</select>
|
||||
<input defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={secondaryAxValueInput} disabled />
|
||||
<input defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={secondaryAxValueInput} disabled={secondaryAxValue != 0} />
|
||||
</div>
|
||||
<p className={secondaryErrorClasses}>{errors.axValue2}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React from 'react'
|
|||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
import * as Scroll from 'react-scroll'
|
||||
|
||||
|
|
@ -11,15 +13,13 @@ import Header from '~components/Header'
|
|||
import Button from '~components/Button'
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { appState, initialAppState } from '~utils/appState'
|
||||
|
||||
import { ButtonType } from '~utils/enums'
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import { route } from 'next/dist/server/router'
|
||||
|
||||
const BottomHeader = () => {
|
||||
const account = useSnapshot(accountState)
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const app = useSnapshot(appState)
|
||||
|
||||
const router = useRouter()
|
||||
|
|
@ -67,9 +67,9 @@ const BottomHeader = () => {
|
|||
const leftNav = () => {
|
||||
if (router.pathname === '/p/[party]' || router.pathname === '/new') {
|
||||
if (app.party.detailsVisible) {
|
||||
return (<Button icon="edit" active={true} onClick={toggleDetails}>Hide info</Button>)
|
||||
return (<Button icon="edit" active={true} onClick={toggleDetails}>{t('buttons.hide_info')}</Button>)
|
||||
} else {
|
||||
return (<Button icon="edit" onClick={toggleDetails}>Edit info</Button>)
|
||||
return (<Button icon="edit" onClick={toggleDetails}>{t('buttons.show_info')}</Button>)
|
||||
}
|
||||
} else {
|
||||
return (<div />)
|
||||
|
|
@ -84,20 +84,20 @@ const BottomHeader = () => {
|
|||
<span className='icon'>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
<span className="text">Delete team</span>
|
||||
<span className="text">{t('buttons.delete')}</span>
|
||||
</AlertDialog.Trigger>
|
||||
<AlertDialog.Portal>
|
||||
<AlertDialog.Overlay className="Overlay" />
|
||||
<AlertDialog.Content className="Dialog">
|
||||
<AlertDialog.Title className="DialogTitle">
|
||||
Delete team
|
||||
{t('modals.delete_team.title')}
|
||||
</AlertDialog.Title>
|
||||
<AlertDialog.Description className="DialogDescription">
|
||||
Are you sure you want to permanently delete this team?
|
||||
{t('modals.delete_team.description')}
|
||||
</AlertDialog.Description>
|
||||
<div className="actions">
|
||||
<AlertDialog.Cancel className="Button modal">Nevermind</AlertDialog.Cancel>
|
||||
<AlertDialog.Action className="Button modal destructive" onClick={(e) => deleteTeam(e)}>Yes, delete</AlertDialog.Action>
|
||||
<AlertDialog.Cancel className="Button modal">{t('modals.delete_team.buttons.cancel')}</AlertDialog.Cancel>
|
||||
<AlertDialog.Action className="Button modal destructive" onClick={(e) => deleteTeam(e)}>{t('modals.delete_team.buttons.confirm')}</AlertDialog.Action>
|
||||
</div>
|
||||
</AlertDialog.Content>
|
||||
</AlertDialog.Portal>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import * as HoverCard from '@radix-ui/react-hover-card'
|
||||
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
|
||||
import { axData } from '~utils/axData'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -21,6 +22,10 @@ interface KeyNames {
|
|||
}
|
||||
|
||||
const CharacterHovercard = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
||||
const Proficiency = ['none', 'sword', 'dagger', 'axe', 'spear', 'bow', 'staff', 'fist', 'harp', 'gun', 'katana']
|
||||
|
||||
|
|
@ -56,8 +61,8 @@ const CharacterHovercard = (props: Props) => {
|
|||
<HoverCard.Content className="Weapon Hovercard">
|
||||
<div className="top">
|
||||
<div className="title">
|
||||
<h4>{ props.gridCharacter.object.name.en }</h4>
|
||||
<img alt={props.gridCharacter.object.name.en} src={characterImage()} />
|
||||
<h4>{ props.gridCharacter.object.name[locale] }</h4>
|
||||
<img alt={props.gridCharacter.object.name[locale]} src={characterImage()} />
|
||||
</div>
|
||||
<div className="subInfo">
|
||||
<div className="icons">
|
||||
|
|
@ -76,7 +81,7 @@ const CharacterHovercard = (props: Props) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">View more on gbf.wiki</a>
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">{t('buttons.wiki')}</a>
|
||||
<HoverCard.Arrow />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Root>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
|
|
@ -11,28 +13,29 @@ interface Props {
|
|||
|
||||
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
||||
|
||||
class CharacterResult extends React.Component<Props> {
|
||||
render() {
|
||||
const character = this.props.data
|
||||
const CharacterResult = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
return (
|
||||
<li className="CharacterResult" onClick={this.props.onClick}>
|
||||
<img alt={character.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-grid/${character.granblue_id}_01.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{character.name.en}</h5>
|
||||
<UncapIndicator
|
||||
type="character"
|
||||
flb={character.uncap.flb}
|
||||
ulb={character.uncap.ulb}
|
||||
special={character.special}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[character.element]} />
|
||||
</div>
|
||||
const character = props.data
|
||||
|
||||
return(
|
||||
<li className="CharacterResult" onClick={props.onClick}>
|
||||
<img alt={character.name[locale]} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-grid/${character.granblue_id}_01.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{character.name[locale]}</h5>
|
||||
<UncapIndicator
|
||||
type="character"
|
||||
flb={character.uncap.flb}
|
||||
ulb={character.uncap.ulb}
|
||||
special={character.special}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[character.element]} />
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default CharacterResult
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
|
|
@ -18,6 +20,11 @@ interface Props {
|
|||
}
|
||||
|
||||
const CharacterUnit = (props: Props) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const [imageUrl, setImageUrl] = useState('')
|
||||
|
||||
const classes = classnames({
|
||||
|
|
@ -68,7 +75,7 @@ const CharacterUnit = (props: Props) => {
|
|||
|
||||
const editableImage = (
|
||||
<SearchModal
|
||||
placeholderText="Search for a character..."
|
||||
placeholderText={t('search.placeholders.character')}
|
||||
fromPosition={props.position}
|
||||
object="characters"
|
||||
send={props.updateObject}>
|
||||
|
|
@ -88,7 +95,7 @@ const CharacterUnit = (props: Props) => {
|
|||
updateUncap={passUncapData}
|
||||
special={character.special}
|
||||
/> : '' }
|
||||
<h3 className="CharacterName">{character?.name.en}</h3>
|
||||
<h3 className="CharacterName">{character?.name[locale]}</h3>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
font-size: $font-regular;
|
||||
padding: ($unit) $unit * 2;
|
||||
|
||||
&.ja {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import * as ToggleGroup from '@radix-ui/react-toggle-group'
|
||||
|
||||
import './index.scss'
|
||||
|
|
@ -9,28 +12,32 @@ interface Props {
|
|||
}
|
||||
|
||||
const ElementToggle = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
return (
|
||||
<ToggleGroup.Root className="ToggleGroup" type="single" defaultValue={`${props.currentElement}`} aria-label="Element" onValueChange={props.sendValue}>
|
||||
<ToggleGroup.Item className="ToggleItem" value="0" aria-label="null">
|
||||
Null
|
||||
<ToggleGroup.Item className={`ToggleItem ${locale}`} value="0" aria-label="null">
|
||||
{t('elements.null')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem wind" value="1" aria-label="wind">
|
||||
Wind
|
||||
<ToggleGroup.Item className={`ToggleItem wind ${locale}`} value="1" aria-label="wind">
|
||||
{t('elements.wind')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem fire" value="2" aria-label="fire">
|
||||
Fire
|
||||
<ToggleGroup.Item className={`ToggleItem fire ${locale}`} value="2" aria-label="fire">
|
||||
{t('elements.fire')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem water" value="3" aria-label="water">
|
||||
Water
|
||||
<ToggleGroup.Item className={`ToggleItem water ${locale}`} value="3" aria-label="water">
|
||||
{t('elements.water')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem earth" value="4" aria-label="earth">
|
||||
Earth
|
||||
<ToggleGroup.Item className={`ToggleItem earth ${locale}`} value="4" aria-label="earth">
|
||||
{t('elements.earth')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem dark" value="5" aria-label="dark">
|
||||
Dark
|
||||
<ToggleGroup.Item className={`ToggleItem dark ${locale}`} value="5" aria-label="dark">
|
||||
{t('elements.dark')}
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item className="ToggleItem light" value="6" aria-label="light">
|
||||
Light
|
||||
<ToggleGroup.Item className={`ToggleItem light ${locale}`} value="6" aria-label="light">
|
||||
{t('elements.light')}
|
||||
</ToggleGroup.Item>
|
||||
</ToggleGroup.Root>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import SummonUnit from '~components/SummonUnit'
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -16,9 +17,11 @@ interface Props {
|
|||
const ExtraSummons = (props: Props) => {
|
||||
const numSummons: number = 2
|
||||
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
return (
|
||||
<div id="ExtraSummons">
|
||||
<span>Sub Aura Summons</span>
|
||||
<span>{t('summons.subaura')}</span>
|
||||
<ul id="grid_summons">
|
||||
{
|
||||
Array.from(Array(numSummons)).map((x, i) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import WeaponUnit from '~components/WeaponUnit'
|
||||
|
||||
import './index.scss'
|
||||
|
|
@ -15,10 +16,11 @@ interface Props {
|
|||
|
||||
const ExtraWeapons = (props: Props) => {
|
||||
const numWeapons: number = 3
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
return (
|
||||
<div id="ExtraGrid">
|
||||
<span>Additional<br />Weapons</span>
|
||||
<span>{t('extra_weapons')}</span>
|
||||
<ul className="grid_weapons">
|
||||
{
|
||||
Array.from(Array(numWeapons)).map((x, i) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import RaidDropdown from '~components/RaidDropdown'
|
||||
|
|
@ -12,6 +13,8 @@ interface Props {
|
|||
}
|
||||
|
||||
const FilterBar = (props: Props) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const elementSelect = React.createRef<HTMLSelectElement>()
|
||||
const raidSelect = React.createRef<HTMLSelectElement>()
|
||||
const recencySelect = React.createRef<HTMLSelectElement>()
|
||||
|
|
@ -33,14 +36,14 @@ const FilterBar = (props: Props) => {
|
|||
<div className={classes}>
|
||||
{props.children}
|
||||
<select onChange={selectChanged} ref={elementSelect}>
|
||||
<option key={-1} value={-1}>All elements</option>
|
||||
<option key={-0} value={0}>Null</option>
|
||||
<option key={1}value={1}>Wind</option>
|
||||
<option key={2}value={2}>Fire</option>
|
||||
<option key={3}value={3}>Water</option>
|
||||
<option key={4}value={4}>Earth</option>
|
||||
<option key={5}value={5}>Dark</option>
|
||||
<option key={6}value={6}>Light</option>
|
||||
<option key={-1} value={-1}>{t('elements.full.all')}</option>
|
||||
<option key={-0} value={0}>{t('elements.full.null')}</option>
|
||||
<option key={1}value={1}>{t('elements.full.wind')}</option>
|
||||
<option key={2}value={2}>{t('elements.full.fire')}</option>
|
||||
<option key={3}value={3}>{t('elements.full.water')}</option>
|
||||
<option key={4}value={4}>{t('elements.full.earth')}</option>
|
||||
<option key={5}value={5}>{t('elements.full.dark')}</option>
|
||||
<option key={6}value={6}>{t('elements.full.light')}</option>
|
||||
</select>
|
||||
<RaidDropdown
|
||||
allOption={true}
|
||||
|
|
@ -48,13 +51,13 @@ const FilterBar = (props: Props) => {
|
|||
ref={raidSelect}
|
||||
/>
|
||||
<select onChange={selectChanged} ref={recencySelect}>
|
||||
<option key={-1} value={-1}>All time</option>
|
||||
<option key={86400} value={86400}>Last day</option>
|
||||
<option key={604800} value={604800}>Last week </option>
|
||||
<option key={2629746} value={2629746}>Last month</option>
|
||||
<option key={7889238} value={7889238}>Last 3 months</option>
|
||||
<option key={15778476} value={15778476}>Last 6 months</option>
|
||||
<option key={31556952} value={31556952}>Last year</option>
|
||||
<option key={-1} value={-1}>{t('recency.all_time')}</option>
|
||||
<option key={86400} value={86400}>{t('recency.last_day')}</option>
|
||||
<option key={604800} value={604800}>{t('recency.last_week')}</option>
|
||||
<option key={2629746} value={2629746}>{t('recency.last_month')}</option>
|
||||
<option key={7889238} value={7889238}>{t('recency.last_3_months')}</option>
|
||||
<option key={15778476} value={15778476}>{t('recency.last_6_months')}</option>
|
||||
<option key={31556952} value={31556952}>{t('recency.last_year')}</option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { accountState } from '~utils/accountState'
|
||||
|
|
@ -30,6 +32,10 @@ const GridRep = (props: Props) => {
|
|||
|
||||
const { account } = useSnapshot(accountState)
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const [mainhand, setMainhand] = useState<Weapon>()
|
||||
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
||||
|
||||
|
|
@ -66,12 +72,12 @@ const GridRep = (props: Props) => {
|
|||
|
||||
function generateMainhandImage() {
|
||||
return (mainhand) ?
|
||||
<img alt={mainhand?.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand?.granblue_id}.jpg`} /> : ''
|
||||
<img alt={mainhand?.name[locale]} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand?.granblue_id}.jpg`} /> : ''
|
||||
}
|
||||
|
||||
function generateGridImage(position: number) {
|
||||
return (weapons[position]) ?
|
||||
<img alt={weapons[position]?.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`} /> : ''
|
||||
<img alt={weapons[position]?.name[locale]} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`} /> : ''
|
||||
}
|
||||
|
||||
function sendSaveData() {
|
||||
|
|
@ -96,22 +102,22 @@ const GridRep = (props: Props) => {
|
|||
|
||||
const details = (
|
||||
<div className="Details">
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : t('no_title') }</h2>
|
||||
<div className="bottom">
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name.en : 'No raid set' }</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, 'en-us')}</time>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name[locale] : t('no_raid') }</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, locale)}</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
const detailsWithUsername = (
|
||||
<div className="Details">
|
||||
<div className="top">
|
||||
<div className="info">
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name.en : 'No raid set' }</div>
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : t('no_title') }</h2>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name[locale] : t('no_raid') }</div>
|
||||
</div>
|
||||
{ (!props.user || (account.user && account.user.id !== props.user.id)) ?
|
||||
{ (account.authorized && (props.user && account.user && account.user.id !== props.user.id)) ?
|
||||
<Button
|
||||
active={props.favorited}
|
||||
icon="save"
|
||||
|
|
@ -122,9 +128,9 @@ const GridRep = (props: Props) => {
|
|||
<div className="bottom">
|
||||
<div className={userClass}>
|
||||
{ userImage() }
|
||||
{ (props.user) ? props.user.username : 'Anonymous' }
|
||||
{ (props.user) ? props.user.username : t('no_user') }
|
||||
</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, 'en-us')}</time>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, locale)}</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,73 @@
|
|||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
&.language {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
padding-right: $unit;
|
||||
|
||||
span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Switch {
|
||||
$height: 24px;
|
||||
|
||||
background: $grey-60;
|
||||
border-radius: calc($height / 2);
|
||||
border: none;
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: $height;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Thumb {
|
||||
$diameter: 18px;
|
||||
|
||||
background: white;
|
||||
border-radius: calc($diameter / 2);
|
||||
display: block;
|
||||
height: $diameter;
|
||||
width: $diameter;
|
||||
transition: transform 100ms;
|
||||
transform: translateX(-2px);
|
||||
z-index: 3;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&[data-state="checked"] {
|
||||
background: white;
|
||||
transform: translateX(17px);
|
||||
}
|
||||
}
|
||||
|
||||
.left, .right {
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
font-weight: $bold;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.left {
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.right {
|
||||
top: 6px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $grey-40;
|
||||
}
|
||||
|
|
@ -54,7 +121,7 @@
|
|||
|
||||
img {
|
||||
$diameter: 32px;
|
||||
border-radius: $diameter / 2;
|
||||
border-radius: calc($diameter / 2);
|
||||
height: $diameter;
|
||||
width: $diameter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import Router, { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import Link from 'next/link'
|
||||
import * as Switch from '@radix-ui/react-switch'
|
||||
|
||||
import AboutModal from '~components/AboutModal'
|
||||
import AccountModal from '~components/AccountModal'
|
||||
|
|
@ -16,8 +20,27 @@ interface Props {
|
|||
}
|
||||
|
||||
const HeaderMenu = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const [accountCookies] = useCookies(['account'])
|
||||
const [userCookies] = useCookies(['user'])
|
||||
const [cookies, setCookies] = useCookies()
|
||||
|
||||
const [checked, setChecked] = useState(false)
|
||||
|
||||
// console.log(`Currently: ${checked} ${cookies['NEXT_LOCALE']}`)
|
||||
|
||||
useEffect(() => {
|
||||
const locale = cookies['NEXT_LOCALE']
|
||||
setChecked((locale === 'ja') ? true : false)
|
||||
}, [cookies])
|
||||
|
||||
function handleCheckedChange(value: boolean) {
|
||||
const language = (value) ? 'ja' : 'en'
|
||||
setCookies('NEXT_LOCALE', language, { path: '/'})
|
||||
router.push(router.asPath, undefined, { locale: language })
|
||||
}
|
||||
|
||||
function authItems() {
|
||||
return (
|
||||
|
|
@ -35,22 +58,22 @@ const HeaderMenu = (props: Props) => {
|
|||
/profile/${userCookies.user.picture}@2x.png 2x`}
|
||||
src={`/profile/${userCookies.user.picture}.png`}
|
||||
/>
|
||||
</div
|
||||
></Link>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="MenuItem">
|
||||
<Link href={`/saved` || ''}>Saved</Link>
|
||||
<Link href={`/saved` || ''}>{t('menu.saved')}</Link>
|
||||
</li>
|
||||
</div>
|
||||
<div className="MenuGroup">
|
||||
<li className="MenuItem">
|
||||
<Link href='/teams'>Teams</Link>
|
||||
<Link href='/teams'>{t('menu.teams')}</Link>
|
||||
</li>
|
||||
|
||||
<li className="MenuItem disabled">
|
||||
<div>
|
||||
<span>Guides</span>
|
||||
<i className="tag">Coming Soon</i>
|
||||
<span>{t('menu.guides')}</span>
|
||||
<i className="tag">{t('coming_soon')}</i>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
|
|
@ -58,7 +81,7 @@ const HeaderMenu = (props: Props) => {
|
|||
<AboutModal />
|
||||
<AccountModal />
|
||||
<li className="MenuItem" onClick={props.logout}>
|
||||
<span>Logout</span>
|
||||
<span>{t('menu.logout')}</span>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
|
|
@ -70,20 +93,30 @@ const HeaderMenu = (props: Props) => {
|
|||
return (
|
||||
<ul className="Menu unauth">
|
||||
<div className="MenuGroup">
|
||||
<AboutModal />
|
||||
<li className="MenuItem language">
|
||||
<span>{t('menu.language')}</span>
|
||||
<Switch.Root className="Switch" onCheckedChange={handleCheckedChange} checked={checked}>
|
||||
<Switch.Thumb className="Thumb" />
|
||||
<span className="left">JP</span>
|
||||
<span className="right">EN</span>
|
||||
</Switch.Root>
|
||||
</li>
|
||||
</div>
|
||||
<div className="MenuGroup">
|
||||
<li className="MenuItem">
|
||||
<Link href='/teams'>Teams</Link>
|
||||
</li>
|
||||
<li className="MenuItem">
|
||||
<Link href='/teams'>{t('menu.teams')}</Link>
|
||||
</li>
|
||||
|
||||
<li className="MenuItem disabled">
|
||||
<div>
|
||||
<span>Guides</span>
|
||||
<i className="tag">Coming Soon</i>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
<li className="MenuItem disabled">
|
||||
<div>
|
||||
<span>{t('menu.guides')}</span>
|
||||
<i className="tag">{t('coming_soon')}</i>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
<div className="MenuGroup">
|
||||
<AboutModal />
|
||||
</div>
|
||||
<div className="MenuGroup">
|
||||
<LoginModal />
|
||||
<SignupModal />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import Router, { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
|
|
@ -24,6 +26,9 @@ interface ErrorMap {
|
|||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
const LoginModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
|
|
@ -49,16 +54,16 @@ const LoginModal = (props: Props) => {
|
|||
switch(name) {
|
||||
case 'email':
|
||||
if (value.length == 0)
|
||||
newErrors.email = 'Please enter your email'
|
||||
newErrors.email = t('modals.login.errors.empty_email')
|
||||
else if (!emailRegex.test(value))
|
||||
newErrors.email = 'That email address is not valid'
|
||||
newErrors.email = t('modals.login.errors.invalid_email')
|
||||
else
|
||||
newErrors.email = ''
|
||||
break
|
||||
|
||||
case 'password':
|
||||
newErrors.password = value.length == 0
|
||||
? 'Please enter your password'
|
||||
? t('modals.login.errors.empty_password')
|
||||
: ''
|
||||
break
|
||||
|
||||
|
|
@ -117,7 +122,7 @@ const LoginModal = (props: Props) => {
|
|||
access_token: response.data.access_token
|
||||
}
|
||||
|
||||
setCookies('account', cookieObj, { path: '/'})
|
||||
setCookies('account', cookieObj, { path: '/' })
|
||||
}
|
||||
|
||||
function storeUserInfo(response: AxiosResponse) {
|
||||
|
|
@ -129,9 +134,8 @@ const LoginModal = (props: Props) => {
|
|||
language: user.language,
|
||||
}
|
||||
|
||||
setCookies('user', cookieObj, { path: '/'})
|
||||
setCookies('user', cookieObj, { path: '/' })
|
||||
|
||||
accountState.account.language = user.language
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
|
|
@ -140,7 +144,16 @@ const LoginModal = (props: Props) => {
|
|||
}
|
||||
|
||||
accountState.account.authorized = true
|
||||
|
||||
setOpen(false)
|
||||
changeLanguage(user.language)
|
||||
}
|
||||
|
||||
function changeLanguage(newLanguage: string) {
|
||||
if (newLanguage !== router.locale) {
|
||||
setCookies('NEXT_LOCALE', newLanguage, { path: '/'})
|
||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
}
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
|
|
@ -155,13 +168,13 @@ const LoginModal = (props: Props) => {
|
|||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>Log in</span>
|
||||
<span>{t('menu.login')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Login Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">Log in</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{t('modals.login.title')}</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
|
|
@ -172,7 +185,7 @@ const LoginModal = (props: Props) => {
|
|||
<form className="form" onSubmit={login}>
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder="Email address"
|
||||
placeholder={t('modals.login.placeholders.email')}
|
||||
onChange={handleChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
|
|
@ -180,13 +193,13 @@ const LoginModal = (props: Props) => {
|
|||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder="Password"
|
||||
placeholder={t('modals.login.placeholders.password')}
|
||||
onChange={handleChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
/>
|
||||
|
||||
<Button>Log in</Button>
|
||||
<Button>{t('modals.login.buttons.confirm')}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react'
|
||||
import './index.scss'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import { appState } from '~utils/appState'
|
||||
|
||||
|
|
@ -8,7 +9,9 @@ import Segment from '~components/Segment'
|
|||
import ToggleSwitch from '~components/ToggleSwitch'
|
||||
|
||||
import { GridType } from '~utils/enums'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
selectedTab: GridType
|
||||
|
|
@ -17,6 +20,8 @@ interface Props {
|
|||
}
|
||||
|
||||
const PartySegmentedControl = (props: Props) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const { party, grid } = useSnapshot(appState)
|
||||
|
||||
function getElement() {
|
||||
|
|
@ -62,21 +67,21 @@ const PartySegmentedControl = (props: Props) => {
|
|||
name="characters"
|
||||
selected={props.selectedTab == GridType.Character}
|
||||
onClick={props.onClick}
|
||||
>Characters</Segment>
|
||||
>{t('party.segmented_control.characters')}</Segment>
|
||||
|
||||
<Segment
|
||||
groupName="grid"
|
||||
name="weapons"
|
||||
selected={props.selectedTab == GridType.Weapon}
|
||||
onClick={props.onClick}
|
||||
>Weapons</Segment>
|
||||
>{t('party.segmented_control.weapons')}</Segment>
|
||||
|
||||
<Segment
|
||||
groupName="grid"
|
||||
name="summons"
|
||||
selected={props.selectedTab == GridType.Summon}
|
||||
onClick={props.onClick}
|
||||
>Summons</Segment>
|
||||
>{t('party.segmented_control.summons')}</Segment>
|
||||
</SegmentedControl>
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import { appState } from '~utils/appState'
|
||||
import api from '~utils/api'
|
||||
|
|
@ -14,6 +16,10 @@ interface Props {
|
|||
}
|
||||
|
||||
const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(function useFieldSet(props, ref) {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const [raids, setRaids] = useState<Raid[][]>()
|
||||
|
||||
const raidGroups = [
|
||||
|
|
@ -37,7 +43,7 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(function useFiel
|
|||
id: '0',
|
||||
name: {
|
||||
en: 'All raids',
|
||||
jp: '全て'
|
||||
ja: '全てのマルチ'
|
||||
},
|
||||
level: 0,
|
||||
group: 0,
|
||||
|
|
@ -65,7 +71,7 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(function useFiel
|
|||
const options = raids && raids.length > 0 && raids[index].length > 0 &&
|
||||
raids[index].sort((a, b) => a.element - b.element).map((item, i) => {
|
||||
return (
|
||||
<option key={i} value={item.id}>{item.name.en}</option>
|
||||
<option key={i} value={item.id}>{item.name[locale]}</option>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Trans, useTranslation } from 'next-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
|
|
@ -26,6 +29,9 @@ interface ErrorMap {
|
|||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
const SignupModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
|
|
@ -56,7 +62,8 @@ const SignupModal = (props: Props) => {
|
|||
username: usernameInput.current?.value,
|
||||
email: emailInput.current?.value,
|
||||
password: passwordInput.current?.value,
|
||||
password_confirmation: passwordConfirmationInput.current?.value
|
||||
password_confirmation: passwordConfirmationInput.current?.value,
|
||||
language: router.locale
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +102,9 @@ const SignupModal = (props: Props) => {
|
|||
language: user.language,
|
||||
}
|
||||
|
||||
// TODO: Set language
|
||||
setCookies('user', cookieObj, { path: '/'})
|
||||
|
||||
accountState.account.language = user.language
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
|
|
@ -138,7 +145,7 @@ const SignupModal = (props: Props) => {
|
|||
|
||||
validateName(fieldName, value)
|
||||
} else {
|
||||
newErrors[fieldName] = `This ${fieldName} is already in use`
|
||||
newErrors[fieldName] = t('modals.signup.errors.field_in_use', { field: fieldName})
|
||||
setErrors(newErrors)
|
||||
setFormValid(false)
|
||||
}
|
||||
|
|
@ -150,9 +157,9 @@ const SignupModal = (props: Props) => {
|
|||
switch(fieldName) {
|
||||
case 'username':
|
||||
if (value.length < 3)
|
||||
newErrors.username = 'Username must be at least 3 characters'
|
||||
newErrors.username = t('modals.signup.errors.username_too_short')
|
||||
else if (value.length > 20)
|
||||
newErrors.username = 'Username must be less than 20 characters'
|
||||
newErrors.username = t('modals.signup.errors.username_too_long')
|
||||
else
|
||||
newErrors.username = ''
|
||||
|
||||
|
|
@ -161,7 +168,7 @@ const SignupModal = (props: Props) => {
|
|||
case 'email':
|
||||
newErrors.email = emailRegex.test(value)
|
||||
? ''
|
||||
: 'That email address is not valid'
|
||||
: t('modals.signup.errors.invalid_email')
|
||||
break
|
||||
|
||||
default:
|
||||
|
|
@ -180,20 +187,20 @@ const SignupModal = (props: Props) => {
|
|||
switch(name) {
|
||||
case 'password':
|
||||
newErrors.password = passwordInput.current?.value.includes(usernameInput.current?.value!)
|
||||
? 'Your password should not contain your username'
|
||||
? t('modals.signup.errors.password_contains_username')
|
||||
: ''
|
||||
break
|
||||
|
||||
case 'password':
|
||||
newErrors.password = value.length < 8
|
||||
? 'Password must be at least 8 characters'
|
||||
? t('modals.signup.errors.password_too_short')
|
||||
: ''
|
||||
break
|
||||
|
||||
case 'confirm_password':
|
||||
newErrors.passwordConfirmation = passwordInput.current?.value === passwordConfirmationInput.current?.value
|
||||
? ''
|
||||
: 'Your passwords don\'t match'
|
||||
: t('modals.signup.errors.passwords_dont_match')
|
||||
break
|
||||
|
||||
default:
|
||||
|
|
@ -231,13 +238,13 @@ const SignupModal = (props: Props) => {
|
|||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>Sign up</span>
|
||||
<span>{t('menu.signup')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Signup Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">Sign up</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{t('modals.signup.title')}</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
|
|
@ -248,7 +255,7 @@ const SignupModal = (props: Props) => {
|
|||
<form className="form" onSubmit={register}>
|
||||
<Fieldset
|
||||
fieldName="username"
|
||||
placeholder="Username"
|
||||
placeholder={t('modals.signup.placeholders.username')}
|
||||
onChange={handleNameChange}
|
||||
error={errors.username}
|
||||
ref={usernameInput}
|
||||
|
|
@ -256,7 +263,7 @@ const SignupModal = (props: Props) => {
|
|||
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder="Email address"
|
||||
placeholder={t('modals.signup.placeholders.email')}
|
||||
onChange={handleNameChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
|
|
@ -264,7 +271,7 @@ const SignupModal = (props: Props) => {
|
|||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder="Password"
|
||||
placeholder={t('modals.signup.placeholders.password')}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
|
|
@ -272,16 +279,18 @@ const SignupModal = (props: Props) => {
|
|||
|
||||
<Fieldset
|
||||
fieldName="confirm_password"
|
||||
placeholder="Password (again)"
|
||||
placeholder={t('modals.signup.placeholders.password_confirm')}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.passwordConfirmation}
|
||||
ref={passwordConfirmationInput}
|
||||
/>
|
||||
|
||||
<Button>Sign up</Button>
|
||||
<Button>{t('modals.signup.buttons.confirm')}</Button>
|
||||
|
||||
<Dialog.Description className="terms">
|
||||
By signing up, I agree to the<br /><a href="#">Terms and Conditions</a> and <a href="#">Usage Guidelines</a>.
|
||||
<Trans i18nKey="modals.signup.agreement">
|
||||
By signing up, I agree to the <Link href="/privacy">Privacy Policy</Link><Link href="/usage">Usage Guidelines</Link>.
|
||||
</Trans>
|
||||
</Dialog.Description>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import { AxiosResponse } from 'axios'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
|
@ -26,6 +27,8 @@ const SummonGrid = (props: Props) => {
|
|||
// Constants
|
||||
const numSummons: number = 4
|
||||
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [cookies, _] = useCookies(['account'])
|
||||
const headers = (cookies.account != null) ? {
|
||||
|
|
@ -239,7 +242,7 @@ const SummonGrid = (props: Props) => {
|
|||
// Render: JSX components
|
||||
const mainSummonElement = (
|
||||
<div className="LabeledUnit">
|
||||
<div className="Label">Main Summon</div>
|
||||
<div className="Label">{t('summons.main')}</div>
|
||||
<SummonUnit
|
||||
gridSummon={grid.summons.mainSummon}
|
||||
editable={party.editable}
|
||||
|
|
@ -254,7 +257,7 @@ const SummonGrid = (props: Props) => {
|
|||
|
||||
const friendSummonElement = (
|
||||
<div className="LabeledUnit">
|
||||
<div className="Label">Friend Summon</div>
|
||||
<div className="Label">{t('summons.friend')}</div>
|
||||
<SummonUnit
|
||||
gridSummon={grid.summons.friendSummon}
|
||||
editable={party.editable}
|
||||
|
|
@ -268,7 +271,7 @@ const SummonGrid = (props: Props) => {
|
|||
)
|
||||
const summonGridElement = (
|
||||
<div id="LabeledGrid">
|
||||
<div className="Label">Summons</div>
|
||||
<div className="Label">{t('summons.summons')}</div>
|
||||
<ul id="grid_summons">
|
||||
{Array.from(Array(numSummons)).map((x, i) => {
|
||||
return (<li key={`grid_unit_${i}`} >
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import * as HoverCard from '@radix-ui/react-hover-card'
|
||||
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
|
||||
import { axData } from '~utils/axData'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -21,8 +22,11 @@ interface KeyNames {
|
|||
}
|
||||
|
||||
const SummonHovercard = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
||||
const Proficiency = ['none', 'sword', 'dagger', 'axe', 'spear', 'bow', 'staff', 'fist', 'harp', 'gun', 'katana']
|
||||
|
||||
const tintElement = Element[props.gridSummon.object.element]
|
||||
const wikiUrl = `https://gbf.wiki/${props.gridSummon.object.name.en.replaceAll(' ', '_')}`
|
||||
|
|
@ -57,8 +61,8 @@ const SummonHovercard = (props: Props) => {
|
|||
<HoverCard.Content className="Weapon Hovercard">
|
||||
<div className="top">
|
||||
<div className="title">
|
||||
<h4>{ props.gridSummon.object.name.en }</h4>
|
||||
<img alt={props.gridSummon.object.name.en} src={summonImage()} />
|
||||
<h4>{ props.gridSummon.object.name[locale] }</h4>
|
||||
<img alt={props.gridSummon.object.name[locale]} src={summonImage()} />
|
||||
</div>
|
||||
<div className="subInfo">
|
||||
<div className="icons">
|
||||
|
|
@ -72,7 +76,7 @@ const SummonHovercard = (props: Props) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">View more on gbf.wiki</a>
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">{t('buttons.wiki')}</a>
|
||||
<HoverCard.Arrow />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Root>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
|
|
@ -11,28 +13,29 @@ interface Props {
|
|||
|
||||
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
||||
|
||||
class SummonResult extends React.Component<Props> {
|
||||
render() {
|
||||
const summon = this.props.data
|
||||
const SummonResult = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
return (
|
||||
<li className="SummonResult" onClick={this.props.onClick}>
|
||||
<img alt={summon.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{summon.name.en}</h5>
|
||||
<UncapIndicator
|
||||
type="summon"
|
||||
flb={summon.uncap.flb}
|
||||
ulb={summon.uncap.ulb}
|
||||
special={false}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[summon.element]} />
|
||||
</div>
|
||||
const summon = props.data
|
||||
|
||||
return (
|
||||
<li className="SummonResult" onClick={props.onClick}>
|
||||
<img alt={summon.name[locale]} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{summon.name[locale]}</h5>
|
||||
<UncapIndicator
|
||||
type="summon"
|
||||
flb={summon.uncap.flb}
|
||||
ulb={summon.uncap.ulb}
|
||||
special={false}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[summon.element]} />
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default SummonResult
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
|
|
@ -18,8 +20,13 @@ interface Props {
|
|||
}
|
||||
|
||||
const SummonUnit = (props: Props) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const [imageUrl, setImageUrl] = useState('')
|
||||
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const classes = classnames({
|
||||
SummonUnit: true,
|
||||
'main': props.unitType == 0,
|
||||
|
|
@ -76,7 +83,7 @@ const SummonUnit = (props: Props) => {
|
|||
|
||||
const editableImage = (
|
||||
<SearchModal
|
||||
placeholderText="Search for a summon..."
|
||||
placeholderText={t('search.placeholders.summon')}
|
||||
fromPosition={props.position}
|
||||
object="summons"
|
||||
send={props.updateObject}>
|
||||
|
|
@ -97,7 +104,7 @@ const SummonUnit = (props: Props) => {
|
|||
special={false}
|
||||
/> : ''
|
||||
}
|
||||
<h3 className="SummonName">{summon?.name.en}</h3>
|
||||
<h3 className="SummonName">{summon?.name[locale]}</h3>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState, initialAccountState } from '~utils/accountState'
|
||||
|
|
@ -13,6 +15,8 @@ import Button from '~components/Button'
|
|||
import HeaderMenu from '~components/HeaderMenu'
|
||||
|
||||
const TopHeader = () => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [accountCookies, setAccountCookie, removeAccountCookie] = useCookies(['account'])
|
||||
const [userCookies, setUserCookies, removeUserCookie] = useCookies(['user'])
|
||||
|
|
@ -100,7 +104,7 @@ const TopHeader = () => {
|
|||
const leftNav = () => {
|
||||
return (
|
||||
<div className="dropdown">
|
||||
<Button icon="menu">Menu</Button>
|
||||
<Button icon="menu">{t('buttons.menu')}</Button>
|
||||
{ (account.user) ?
|
||||
<HeaderMenu authenticated={account.authorized} username={account.user.username} logout={logout} /> :
|
||||
<HeaderMenu authenticated={account.authorized} />
|
||||
|
|
@ -123,9 +127,9 @@ const TopHeader = () => {
|
|||
saveButton() : ''
|
||||
}
|
||||
{ (router.route === '/p/[party]') ?
|
||||
<Button icon="link" onClick={copyToClipboard}>Copy link</Button> : ''
|
||||
<Button icon="link" onClick={copyToClipboard}>{t('buttons.copy')}</Button> : ''
|
||||
}
|
||||
<Button icon="new" onClick={newParty}>New</Button>
|
||||
<Button icon="new" onClick={newParty}>{t('buttons.new')}</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import * as HoverCard from '@radix-ui/react-hover-card'
|
||||
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
|
@ -15,30 +18,35 @@ interface Props {
|
|||
|
||||
interface KeyNames {
|
||||
[key: string]: {
|
||||
[key: string]: string
|
||||
en: string,
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
}
|
||||
|
||||
const WeaponHovercard = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
||||
const Proficiency = ['none', 'sword', 'dagger', 'axe', 'spear', 'bow', 'staff', 'fist', 'harp', 'gun', 'katana']
|
||||
const WeaponKeyNames: KeyNames = {
|
||||
'2': {
|
||||
en: 'Pendulum',
|
||||
jp: ''
|
||||
ja: 'ペンデュラム'
|
||||
},
|
||||
'3': {
|
||||
en: 'Teluma',
|
||||
jp: ''
|
||||
ja: 'テルマ'
|
||||
},
|
||||
'17': {
|
||||
en: 'Gauph Key',
|
||||
jp: ''
|
||||
ja: 'ガフスキー'
|
||||
},
|
||||
'22': {
|
||||
en: 'Emblem',
|
||||
jp: ''
|
||||
ja: 'エンブレム'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +69,7 @@ const WeaponHovercard = (props: Props) => {
|
|||
const simpleAxSkill = props.gridWeapon.ax[0]
|
||||
const axSkill = primaryAxSkills.find(skill => skill.id == simpleAxSkill.modifier)
|
||||
|
||||
return `${axSkill?.name.en} +${simpleAxSkill.strength}${ (axSkill?.suffix) ? axSkill.suffix : '' }`
|
||||
return `${axSkill?.name[locale]} +${simpleAxSkill.strength}${ (axSkill?.suffix) ? axSkill.suffix : '' }`
|
||||
}
|
||||
|
||||
return ''
|
||||
|
|
@ -78,7 +86,7 @@ const WeaponHovercard = (props: Props) => {
|
|||
|
||||
if (primaryAxSkill && primaryAxSkill.secondary) {
|
||||
const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondarySimpleAxSkill.modifier)
|
||||
return `${secondaryAxSkill?.name.en} +${secondarySimpleAxSkill.strength}${ (secondaryAxSkill?.suffix) ? secondaryAxSkill.suffix : '' }`
|
||||
return `${secondaryAxSkill?.name[locale]} +${secondarySimpleAxSkill.strength}${ (secondaryAxSkill?.suffix) ? secondaryAxSkill.suffix : '' }`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,14 +105,14 @@ const WeaponHovercard = (props: Props) => {
|
|||
const keysSection = (
|
||||
<section className="weaponKeys">
|
||||
{ (WeaponKeyNames[props.gridWeapon.object.series]) ?
|
||||
<h5 className={tintElement}>{ WeaponKeyNames[props.gridWeapon.object.series].en }s</h5> : ''
|
||||
<h5 className={tintElement}>{ WeaponKeyNames[props.gridWeapon.object.series][locale] }{ (locale === 'en') ? 's' : '' }</h5> : ''
|
||||
}
|
||||
|
||||
{ (props.gridWeapon.weapon_keys) ?
|
||||
Array.from(Array(props.gridWeapon.weapon_keys.length)).map((x, i) => {
|
||||
return (
|
||||
<div className="weaponKey" key={props.gridWeapon.weapon_keys![i].id}>
|
||||
<span>{props.gridWeapon.weapon_keys![i].name.en}</span>
|
||||
<span>{props.gridWeapon.weapon_keys![i].name[locale]}</span>
|
||||
</div>
|
||||
)
|
||||
}) : '' }
|
||||
|
|
@ -113,16 +121,16 @@ const WeaponHovercard = (props: Props) => {
|
|||
|
||||
const axSection = (
|
||||
<section className="axSkills">
|
||||
<h5 className={tintElement}>AX Skills</h5>
|
||||
<h5 className={tintElement}>{t('modals.weapon.subtitles.ax_skills')}</h5>
|
||||
<div className="skills">
|
||||
<div className="primary axSkill">
|
||||
<img src={`/icons/ax/primary_${ (props.gridWeapon.ax) ? props.gridWeapon.ax[0].modifier : '' }.png`} />
|
||||
<img alt="AX1" src={`/icons/ax/primary_${ (props.gridWeapon.ax) ? props.gridWeapon.ax[0].modifier : '' }.png`} />
|
||||
<span>{createPrimaryAxSkillString()}</span>
|
||||
</div>
|
||||
|
||||
{ (props.gridWeapon.ax && props.gridWeapon.ax[1].modifier && props.gridWeapon.ax[1].strength) ?
|
||||
<div className="secondary axSkill">
|
||||
<img src={`/icons/ax/secondary_${ (props.gridWeapon.ax) ? props.gridWeapon.ax[1].modifier : '' }.png`} />
|
||||
<img alt="AX2" src={`/icons/ax/secondary_${ (props.gridWeapon.ax) ? props.gridWeapon.ax[1].modifier : '' }.png`} />
|
||||
<span>{createSecondaryAxSkillString()}</span>
|
||||
</div> : ''}
|
||||
</div>
|
||||
|
|
@ -137,8 +145,8 @@ const WeaponHovercard = (props: Props) => {
|
|||
<HoverCard.Content className="Weapon Hovercard" side={hovercardSide()}>
|
||||
<div className="top">
|
||||
<div className="title">
|
||||
<h4>{ props.gridWeapon.object.name.en }</h4>
|
||||
<img alt={props.gridWeapon.object.name.en} src={weaponImage()} />
|
||||
<h4>{ props.gridWeapon.object.name[locale] }</h4>
|
||||
<img alt={props.gridWeapon.object.name[locale]} src={weaponImage()} />
|
||||
</div>
|
||||
<div className="subInfo">
|
||||
<div className="icons">
|
||||
|
|
@ -158,7 +166,7 @@ const WeaponHovercard = (props: Props) => {
|
|||
|
||||
{ (props.gridWeapon.object.ax > 0 && props.gridWeapon.ax && props.gridWeapon.ax[0].modifier && props.gridWeapon.ax[0].strength ) ? axSection : '' }
|
||||
{ (props.gridWeapon.weapon_keys && props.gridWeapon.weapon_keys.length > 0) ? keysSection : '' }
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">View more on gbf.wiki</a>
|
||||
<a className={`Button ${tintElement}`} href={wikiUrl} target="_new">{t('buttons.wiki')}</a>
|
||||
<HoverCard.Arrow />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Root>
|
||||
|
|
|
|||
|
|
@ -6,74 +6,142 @@
|
|||
|
||||
/* Elements */
|
||||
|
||||
&.fire {
|
||||
background-image: url('/labels/element/Label_Element_Fire.png')
|
||||
&.fire.en {
|
||||
background-image: url('/labels/element/fire_en.png')
|
||||
}
|
||||
|
||||
&.water {
|
||||
background-image: url('/labels/element/Label_Element_Water.png')
|
||||
&.fire.ja {
|
||||
background-image: url('/labels/element/fire_ja.png')
|
||||
}
|
||||
|
||||
&.earth {
|
||||
background-image: url('/labels/element/Label_Element_Earth.png')
|
||||
&.water.en {
|
||||
background-image: url('/labels/element/water_en.png')
|
||||
}
|
||||
|
||||
&.wind {
|
||||
background-image: url('/labels/element/Label_Element_Wind.png')
|
||||
&.water.ja {
|
||||
background-image: url('/labels/element/water_ja.png')
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background-image: url('/labels/element/Label_Element_Dark.png')
|
||||
&.earth.en {
|
||||
background-image: url('/labels/element/earth_en.png')
|
||||
}
|
||||
|
||||
&.light {
|
||||
background-image: url('/labels/element/Label_Element_Light.png')
|
||||
&.earth.ja {
|
||||
background-image: url('/labels/element/earth_ja.png')
|
||||
}
|
||||
|
||||
&.null {
|
||||
background-image: url('/labels/element/Label_Element_Any.png')
|
||||
&.wind.en {
|
||||
background-image: url('/labels/element/wind_en.png')
|
||||
}
|
||||
|
||||
&.wind.ja {
|
||||
background-image: url('/labels/element/wind_ja.png')
|
||||
}
|
||||
|
||||
&.dark.en {
|
||||
background-image: url('/labels/element/dark_en.png')
|
||||
}
|
||||
|
||||
&.dark.ja {
|
||||
background-image: url('/labels/element/dark_ja.png')
|
||||
}
|
||||
|
||||
&.light.en {
|
||||
background-image: url('/labels/element/light_en.png')
|
||||
}
|
||||
|
||||
&.light.ja {
|
||||
background-image: url('/labels/element/light_ja.png')
|
||||
}
|
||||
|
||||
&.null.en {
|
||||
background-image: url('/labels/element/any_en.png')
|
||||
}
|
||||
|
||||
&.null.ja {
|
||||
background-image: url('/labels/element/any_ja.png')
|
||||
}
|
||||
|
||||
/* Proficiencies */
|
||||
|
||||
&.sword {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Sabre.png')
|
||||
&.sword.en {
|
||||
background-image: url('/labels/proficiency/sabre_en.png')
|
||||
}
|
||||
|
||||
&.dagger {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Dagger.png')
|
||||
&.sword.ja {
|
||||
background-image: url('/labels/proficiency/sabre_ja.png')
|
||||
}
|
||||
|
||||
&.axe {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Axe.png')
|
||||
&.dagger.en {
|
||||
background-image: url('/labels/proficiency/dagger_en.png')
|
||||
}
|
||||
|
||||
&.spear {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Spear.png')
|
||||
&.dagger.ja {
|
||||
background-image: url('/labels/proficiency/dagger_ja.png')
|
||||
}
|
||||
|
||||
&.staff {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Staff.png')
|
||||
&.axe.en {
|
||||
background-image: url('/labels/proficiency/axe_en.png')
|
||||
}
|
||||
|
||||
&.fist {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Melee.png')
|
||||
&.axe.ja {
|
||||
background-image: url('/labels/proficiency/axe_ja.png')
|
||||
}
|
||||
|
||||
&.harp {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Harp.png')
|
||||
&.spear.en {
|
||||
background-image: url('/labels/proficiency/spear_en.png')
|
||||
}
|
||||
|
||||
&.gun {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Gun.png')
|
||||
&.spear.ja {
|
||||
background-image: url('/labels/proficiency/spear_ja.png')
|
||||
}
|
||||
|
||||
&.bow {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Bow.png')
|
||||
&.staff.en {
|
||||
background-image: url('/labels/proficiency/staff_en.png')
|
||||
}
|
||||
|
||||
&.katana {
|
||||
background-image: url('/labels/proficiency/Label_Weapon_Katana.png')
|
||||
&.staff.ja {
|
||||
background-image: url('/labels/proficiency/staff_ja.png')
|
||||
}
|
||||
|
||||
&.fist.en {
|
||||
background-image: url('/labels/proficiency/melee_en.png')
|
||||
}
|
||||
|
||||
&.fist.ja {
|
||||
background-image: url('/labels/proficiency/melee_ja.png')
|
||||
}
|
||||
|
||||
&.harp.en {
|
||||
background-image: url('/labels/proficiency/harp_en.png')
|
||||
}
|
||||
|
||||
&.harp.ja {
|
||||
background-image: url('/labels/proficiency/harp_ja.png')
|
||||
}
|
||||
|
||||
&.gun.en {
|
||||
background-image: url('/labels/proficiency/gun_en.png')
|
||||
}
|
||||
|
||||
&.gun.ja {
|
||||
background-image: url('/labels/proficiency/gun_ja.png')
|
||||
}
|
||||
|
||||
&.bow.en {
|
||||
background-image: url('/labels/proficiency/bow_en.png')
|
||||
}
|
||||
|
||||
&.bow.ja {
|
||||
background-image: url('/labels/proficiency/bow_ja.png')
|
||||
}
|
||||
|
||||
&.katana.en {
|
||||
background-image: url('/labels/proficiency/katana_en.png')
|
||||
}
|
||||
|
||||
&.katana.ja {
|
||||
background-image: url('/labels/proficiency/katana_ja.png')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -6,12 +7,12 @@ interface Props {
|
|||
labelType: string
|
||||
}
|
||||
|
||||
class WeaponLabelIcon extends React.Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<i className={`WeaponLabelIcon ${this.props.labelType}`} />
|
||||
)
|
||||
}
|
||||
const WeaponLabelIcon = (props: Props) => {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<i className={`WeaponLabelIcon ${props.labelType} ${router.locale}`} />
|
||||
)
|
||||
}
|
||||
|
||||
export default WeaponLabelIcon
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
|
||||
import AXSelect from '~components/AxSelect'
|
||||
|
|
@ -33,6 +36,10 @@ interface Props {
|
|||
}
|
||||
|
||||
const WeaponModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [cookies] = useCookies(['account'])
|
||||
const headers = (cookies.account != null) ? {
|
||||
|
|
@ -122,7 +129,7 @@ const WeaponModal = (props: Props) => {
|
|||
const elementSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>Element</h3>
|
||||
<h3>{t('modals.weapon.subtitles.element')}</h3>
|
||||
<ElementToggle
|
||||
currentElement={props.gridWeapon.element}
|
||||
sendValue={receiveElementValue}
|
||||
|
|
@ -134,7 +141,7 @@ const WeaponModal = (props: Props) => {
|
|||
const keySelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>Weapon Keys</h3>
|
||||
<h3>{t('modals.weapon.subtitles.weapon_keys')}</h3>
|
||||
{ ([2, 3, 17, 22].includes(props.gridWeapon.object.series)) ?
|
||||
<WeaponKeyDropdown
|
||||
currentValue={ (props.gridWeapon.weapon_keys) ? props.gridWeapon.weapon_keys[0] : undefined }
|
||||
|
|
@ -165,6 +172,7 @@ const WeaponModal = (props: Props) => {
|
|||
const axSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.ax_skills')}</h3>
|
||||
<AXSelect
|
||||
axType={props.gridWeapon.object.ax}
|
||||
currentSkills={props.gridWeapon.ax}
|
||||
|
|
@ -189,8 +197,8 @@ const WeaponModal = (props: Props) => {
|
|||
<Dialog.Content className="Weapon Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">Modify Weapon</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{props.gridWeapon.object.name.en}</Dialog.Title>
|
||||
<Dialog.Title className="SubTitle">{t('modals.weapon.title')}</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{props.gridWeapon.object.name[locale]}</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
|
|
@ -203,7 +211,7 @@ const WeaponModal = (props: Props) => {
|
|||
{ (props.gridWeapon.object.element == 0) ? elementSelect() : '' }
|
||||
{ ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' }
|
||||
{ (props.gridWeapon.object.ax > 0) ? axSelect() : '' }
|
||||
<Button onClick={updateWeapon} disabled={props.gridWeapon.object.ax > 0 && !formValid}>Save Weapon</Button>
|
||||
<Button onClick={updateWeapon} disabled={props.gridWeapon.object.ax > 0 && !formValid}>{t('modals.weapon.buttons.confirm')}</Button>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import UncapIndicator from '~components/UncapIndicator'
|
||||
import WeaponLabelIcon from '~components/WeaponLabelIcon'
|
||||
|
||||
|
|
@ -13,29 +15,29 @@ const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
|
|||
const Proficiency = ['none', 'sword', 'dagger', 'axe', 'spear', 'bow', 'staff', 'fist', 'harp', 'gun', 'katana']
|
||||
const Series = ['seraphic', 'grand', 'opus', 'draconic', 'revenant', 'primal', 'beast','regalia', 'omega', 'olden_primal', 'hollowsky', 'xeno', 'astral', 'rose', 'ultima', 'bahamut', 'epic', 'ennead', 'cosmos', 'ancestral', 'superlative', 'vintage', 'class_champion', 'sephira', 'new_world_foundation']
|
||||
|
||||
class WeaponResult extends React.Component<Props> {
|
||||
render() {
|
||||
const weapon = this.props.data
|
||||
const WeaponResult = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const weapon = props.data
|
||||
|
||||
return (
|
||||
<li className="WeaponResult" onClick={this.props.onClick}>
|
||||
<img alt={weapon.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{weapon.name.en}</h5>
|
||||
<UncapIndicator
|
||||
type="weapon"
|
||||
flb={weapon.uncap.flb}
|
||||
ulb={weapon.uncap.ulb}
|
||||
special={false}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[weapon.element]} />
|
||||
<WeaponLabelIcon labelType={Proficiency[weapon.proficiency]} />
|
||||
</div>
|
||||
return (
|
||||
<li className="WeaponResult" onClick={props.onClick}>
|
||||
<img alt={weapon.name[locale]} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`} />
|
||||
<div className="Info">
|
||||
<h5>{weapon.name[locale]}</h5>
|
||||
<UncapIndicator
|
||||
type="weapon"
|
||||
flb={weapon.uncap.flb}
|
||||
ulb={weapon.uncap.ulb}
|
||||
special={false}
|
||||
/>
|
||||
<div className="tags">
|
||||
<WeaponLabelIcon labelType={Element[weapon.element]} />
|
||||
<WeaponLabelIcon labelType={Proficiency[weapon.proficiency]} />
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default WeaponResult
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
|
|
@ -22,8 +24,13 @@ interface Props {
|
|||
}
|
||||
|
||||
const WeaponUnit = (props: Props) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const [imageUrl, setImageUrl] = useState('')
|
||||
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
|
||||
const classes = classnames({
|
||||
WeaponUnit: true,
|
||||
'mainhand': props.unitType == 0,
|
||||
|
|
@ -81,7 +88,7 @@ const WeaponUnit = (props: Props) => {
|
|||
|
||||
const editableImage = (
|
||||
<SearchModal
|
||||
placeholderText="Search for a weapon..."
|
||||
placeholderText={t('search.placeholders.weapon')}
|
||||
fromPosition={props.position}
|
||||
object="weapons"
|
||||
send={props.updateObject}>
|
||||
|
|
@ -108,7 +115,7 @@ const WeaponUnit = (props: Props) => {
|
|||
special={false}
|
||||
/> : ''
|
||||
}
|
||||
<h3 className="WeaponName">{weapon?.name.en}</h3>
|
||||
<h3 className="WeaponName">{weapon?.name[locale]}</h3>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
|
|
|||
6
next-i18next.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'ja']
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const path = require('path')
|
||||
const { i18n } = require('./next-i18next.config')
|
||||
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
|
|
@ -7,6 +8,7 @@ module.exports = {
|
|||
prependData: '@import "variables";',
|
||||
includePaths: [path.join(__dirname, 'styles')],
|
||||
},
|
||||
i18n,
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
|
|
|
|||
1271
package-lock.json
generated
|
|
@ -22,14 +22,19 @@
|
|||
"@types/axios": "^0.14.0",
|
||||
"axios": "^0.25.0",
|
||||
"classnames": "^2.3.1",
|
||||
"i18next": "^21.6.13",
|
||||
"i18next-browser-languagedetector": "^6.1.3",
|
||||
"i18next-http-backend": "^1.3.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"meyer-reset-scss": "^2.0.4",
|
||||
"next": "12.0.8",
|
||||
"next-i18next": "^10.5.0",
|
||||
"next-remote-watch": "^1.0.0",
|
||||
"react": "17.0.2",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^11.15.3",
|
||||
"react-i18next": "^11.15.5",
|
||||
"react-linkify": "^1.0.0-alpha",
|
||||
"react-scroll": "^1.8.5",
|
||||
"sass": "^1.49.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
||||
import api from '~utils/api'
|
||||
|
||||
|
|
@ -13,6 +16,7 @@ const ProfileRoute: React.FC = () => {
|
|||
const router = useRouter()
|
||||
const { username } = router.query
|
||||
|
||||
const { t } = useTranslation('common')
|
||||
const [cookies] = useCookies(['account'])
|
||||
|
||||
const [found, setFound] = useState(false)
|
||||
|
|
@ -169,7 +173,7 @@ const ProfileRoute: React.FC = () => {
|
|||
</GridRepCollection>
|
||||
{ (parties.length == 0) ?
|
||||
<div id="NotFound">
|
||||
<h2>{ (loading) ? 'Loading teams...' : 'No teams found' }</h2>
|
||||
<h2>{ (loading) ? t('teams.loading') : t('teams.not_found') }</h2>
|
||||
</div>
|
||||
: '' }
|
||||
</section>
|
||||
|
|
@ -177,4 +181,23 @@ const ProfileRoute: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default ProfileRoute
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
// Object variant:
|
||||
{ params: { username: 'string' } },
|
||||
],
|
||||
fallback: true,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default ProfileRoute
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useCookies, CookiesProvider } from 'react-cookie'
|
||||
import { appWithTranslation } from 'next-i18next'
|
||||
|
||||
import type { AppProps } from 'next/app'
|
||||
import Layout from '~components/Layout'
|
||||
|
|
@ -16,7 +17,6 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
console.log(`Logged in as user "${cookies.account.username}"`)
|
||||
|
||||
accountState.account.authorized = true
|
||||
accountState.account.language = cookies.account.language
|
||||
accountState.account.user = {
|
||||
id: cookies.account.user_id,
|
||||
username: cookies.account.username,
|
||||
|
|
@ -37,4 +37,4 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
)
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
export default appWithTranslation(MyApp)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react'
|
||||
import Party from '~components/Party'
|
||||
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
||||
const NewRoute = () => {
|
||||
function callback(path: string) {
|
||||
// This is scuffed, how do we do this natively?
|
||||
|
|
@ -14,4 +16,13 @@ const NewRoute = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default NewRoute
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
||||
import Party from '~components/Party'
|
||||
|
||||
|
|
@ -30,4 +31,23 @@ const PartyRoute: React.FC = () => {
|
|||
// }
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
// Object variant:
|
||||
{ params: { party: 'string' } },
|
||||
],
|
||||
fallback: true,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default PartyRoute
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
||||
import api from '~utils/api'
|
||||
|
||||
|
|
@ -12,6 +15,7 @@ import FilterBar from '~components/FilterBar'
|
|||
|
||||
const SavedRoute: React.FC = () => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [cookies] = useCookies(['account'])
|
||||
|
|
@ -143,7 +147,7 @@ const SavedRoute: React.FC = () => {
|
|||
return (
|
||||
<div id="Teams">
|
||||
<Head>
|
||||
<title>Your saved Teams</title>
|
||||
<title>{t('saved.title')}</title>
|
||||
|
||||
<meta property="og:title" content="Your saved Teams" />
|
||||
<meta property="og:url" content="https://app.granblue.team/saved" />
|
||||
|
|
@ -155,7 +159,7 @@ const SavedRoute: React.FC = () => {
|
|||
</Head>
|
||||
|
||||
<FilterBar onFilter={receiveFilters} scrolled={scrolled}>
|
||||
<h1>Your saved Teams</h1>
|
||||
<h1>{t('saved.title')}</h1>
|
||||
</FilterBar>
|
||||
|
||||
<section>
|
||||
|
|
@ -182,7 +186,7 @@ const SavedRoute: React.FC = () => {
|
|||
|
||||
{ (parties.length == 0) ?
|
||||
<div id="NotFound">
|
||||
<h2>{ (loading) ? 'Loading saved teams...' : 'You haven't saved any teams yet' }</h2>
|
||||
<h2>{ (loading) ? t('saved.loading') : t('saved.not_found') }</h2>
|
||||
</div>
|
||||
: '' }
|
||||
</section>
|
||||
|
|
@ -190,4 +194,13 @@ const SavedRoute: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default SavedRoute
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
|
||||
|
||||
import api from '~utils/api'
|
||||
|
||||
import GridRep from '~components/GridRep'
|
||||
|
|
@ -12,6 +18,7 @@ import FilterBar from '~components/FilterBar'
|
|||
|
||||
const TeamsRoute: React.FC = () => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [cookies] = useCookies(['account'])
|
||||
|
|
@ -148,7 +155,7 @@ const TeamsRoute: React.FC = () => {
|
|||
return (
|
||||
<div id="Teams">
|
||||
<Head>
|
||||
<title>Discover Teams</title>
|
||||
<title>{ t('teams.title') }</title>
|
||||
|
||||
<meta property="og:title" content="Discover Teams" />
|
||||
<meta property="og:description" content="Find different Granblue Fantasy teams by raid, element or recency" />
|
||||
|
|
@ -161,7 +168,7 @@ const TeamsRoute: React.FC = () => {
|
|||
<meta name="twitter:description" content="Find different Granblue Fantasy teams by raid, element or recency" />
|
||||
</Head>
|
||||
<FilterBar onFilter={receiveFilters} scrolled={scrolled}>
|
||||
<h1>Discover Teams</h1>
|
||||
<h1>{t('teams.title')}</h1>
|
||||
</FilterBar>
|
||||
|
||||
<section>
|
||||
|
|
@ -188,7 +195,7 @@ const TeamsRoute: React.FC = () => {
|
|||
|
||||
{ (parties.length == 0) ?
|
||||
<div id="NotFound">
|
||||
<h2>{ (loading) ? 'Loading teams...' : 'No teams found' }</h2>
|
||||
<h2>{ (loading) ? t('teams.loading') : t('teams.not_found') }</h2>
|
||||
</div>
|
||||
: '' }
|
||||
</section>
|
||||
|
|
@ -196,4 +203,13 @@ const TeamsRoute: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default TeamsRoute
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
public/labels/element/any_ja.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
public/labels/element/dark_ja.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
public/labels/element/earth_ja.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
public/labels/element/fire_ja.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
BIN
public/labels/element/light_ja.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
public/labels/element/water_ja.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
public/labels/element/wind_ja.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
BIN
public/labels/proficiency/axe_ja.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
public/labels/proficiency/bow_ja.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
public/labels/proficiency/dagger_ja.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
public/labels/proficiency/gun_ja.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
public/labels/proficiency/harp_ja.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
BIN
public/labels/proficiency/katana_ja.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
public/labels/proficiency/melee_ja.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
public/labels/proficiency/sabre_ja.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
BIN
public/labels/proficiency/spear_ja.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
BIN
public/labels/proficiency/staff_ja.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
182
public/locales/en/common.json
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
"ax": {
|
||||
"no_skill": "No AX Skill",
|
||||
"errors": {
|
||||
"value_too_low": "{{name}} must be at least {{minValue}}{{suffix}}",
|
||||
"value_too_high": "{{name}} cannot be greater than {{maxValue}}{{suffix}}",
|
||||
"value_not_whole": "{{name}} must be a whole number",
|
||||
"value_empty": "{{name}} must have a value"
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"copy": "Copy link",
|
||||
"delete": "Delete team",
|
||||
"show_info": "Edit info",
|
||||
"hide_info": "Hide info",
|
||||
"menu": "Menu",
|
||||
"new": "New",
|
||||
"wiki": "View more on gbf.wiki"
|
||||
},
|
||||
"elements": {
|
||||
"null": "Null",
|
||||
"wind": "Wind",
|
||||
"fire": "Fire",
|
||||
"water": "Water",
|
||||
"earth": "Earth",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"full": {
|
||||
"all": "All elements",
|
||||
"null": "Null",
|
||||
"wind": "Wind",
|
||||
"fire": "Fire",
|
||||
"water": "Water",
|
||||
"earth": "Earth",
|
||||
"dark": "Dark",
|
||||
"light": "Light"
|
||||
}
|
||||
},
|
||||
"recency": {
|
||||
"all_time": "All time",
|
||||
"last_day": "Last day",
|
||||
"last_week": "Last week",
|
||||
"last_month": "Last month",
|
||||
"last_3_months": "Last 3 months",
|
||||
"last_6_months": "Last 6 months",
|
||||
"last_year": "Last year"
|
||||
},
|
||||
"summons": {
|
||||
"main": "Main Summon",
|
||||
"friend": "Friend Summon",
|
||||
"summons": "Summons",
|
||||
"subaura": "Sub Aura Summons"
|
||||
},
|
||||
"modals": {
|
||||
"about": {
|
||||
"title": "About"
|
||||
},
|
||||
"delete_team": {
|
||||
"title": "Delete team",
|
||||
"description": "Are you sure you want to permanently delete this team?",
|
||||
"buttons": {
|
||||
"confirm": "Yes, delete",
|
||||
"cancel": "Nevermind"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "Log in",
|
||||
"buttons": {
|
||||
"confirm": "Log in"
|
||||
},
|
||||
"errors": {
|
||||
"empty_email": "Please enter your email",
|
||||
"empty_password": "Please enter your password",
|
||||
"invalid_email": "That email address is not valid",
|
||||
"invalid_credentials": "Your email address or password is incorrect"
|
||||
},
|
||||
"placeholders": {
|
||||
"email": "Email address",
|
||||
"password": "Password"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Account Settings",
|
||||
"labels": {
|
||||
"picture": "Picture",
|
||||
"language": "Language",
|
||||
"private": "Private"
|
||||
},
|
||||
"descriptions": {
|
||||
"private": "Hide your profile and prevent your grids from showing up in collections"
|
||||
},
|
||||
"language": {
|
||||
"english": "English",
|
||||
"japanese": "Japanese"
|
||||
},
|
||||
"buttons": {
|
||||
"confirm": "Save settings"
|
||||
}
|
||||
},
|
||||
"signup": {
|
||||
"title": "Create an account",
|
||||
"buttons": {
|
||||
"confirm": "Sign up"
|
||||
},
|
||||
"agreement": "By signing up, I agree to the <br/><2>Privacy Policy</2> and <1>Usage Guidelines</1>.",
|
||||
"errors": {
|
||||
"field_in_use": "This {{field}} is already in use",
|
||||
"empty_email": "Please enter your email",
|
||||
"invalid_email": "That email address is not valid",
|
||||
"username_too_short": "Username must be at least 3 characters",
|
||||
"username_too_long": "Username must be less than 20 characters",
|
||||
"empty_password": "Please enter your password",
|
||||
"password_contains_username": "Your password should not contain your username",
|
||||
"password_too_short": "Password must be at least 8 characters",
|
||||
"mismatched_passwords": "Your passwords don't match"
|
||||
},
|
||||
"placeholders": {
|
||||
"username": "Username",
|
||||
"email": "Email address",
|
||||
"password": "Password",
|
||||
"password_confirm": "Password (again)"
|
||||
}
|
||||
},
|
||||
"weapon": {
|
||||
"title": "Modify Weapon",
|
||||
"buttons": {
|
||||
"confirm": "Save weapon"
|
||||
},
|
||||
"subtitles": {
|
||||
"element": "Element",
|
||||
"ax_skills": "AX Skills",
|
||||
"weapon_keys": "Weapon Keys"
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"about": "About",
|
||||
"guides": "Guides",
|
||||
"language": "Language",
|
||||
"login": "Log in",
|
||||
"saved": "Saved",
|
||||
"settings": "Settings",
|
||||
"signup": "Sign up",
|
||||
"teams": "Teams",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"party": {
|
||||
"segmented_control": {
|
||||
"class": "Class",
|
||||
"characters": "Characters",
|
||||
"weapons": "Weapons",
|
||||
"summons": "Summons"
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"title": "Your saved Teams",
|
||||
"loading": "Loading saved teams...",
|
||||
"not_found": "You haven't saved any teams"
|
||||
},
|
||||
"search": {
|
||||
"errors": {
|
||||
"start_typing": "Start typing the name of a {{object}}",
|
||||
"min_length": "Type at least 3 characters",
|
||||
"no_results": "No results found for '{{query}}'"
|
||||
},
|
||||
"placeholders": {
|
||||
"weapon": "Search for a weapon...",
|
||||
"summon": "Search for a summon...",
|
||||
"character": "Search for a weapon..."
|
||||
}
|
||||
},
|
||||
"teams": {
|
||||
"title": "Discover Teams",
|
||||
"loading": "Loading teams...",
|
||||
"not_found": "No teams found"
|
||||
},
|
||||
"extra_weapons": "Additional<br/>Weapons",
|
||||
"coming_soon": "Coming Soon",
|
||||
"no_title": "Untitled",
|
||||
"no_raid": "No raid",
|
||||
"no_user": "Anonymous"
|
||||
}
|
||||
184
public/locales/ja/common.json
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
{
|
||||
"ax": {
|
||||
"no_skill": "EXスキルなし",
|
||||
"errors": {
|
||||
"value_too_low": "{{name}}は最低{{minValue}}{{suffix}}を入力してください",
|
||||
"value_too_high": "{{name}}は最大{{maxValue}}を入力してください",
|
||||
"value_not_whole": "{{name}}は整数でなければなりません",
|
||||
"value_empty": "{{name}}を入力してください"
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"copy": "リンクをコピー",
|
||||
"delete": "編成を削除",
|
||||
"show_info": "詳細を編集",
|
||||
"hide_info": "詳細を非表示",
|
||||
"menu": "メニュー",
|
||||
"new": "作成",
|
||||
"wiki": "gbf.wikiで詳しく見る"
|
||||
},
|
||||
"elements": {
|
||||
"null": "無",
|
||||
"wind": "風",
|
||||
"fire": "火",
|
||||
"water": "水",
|
||||
"earth": "土",
|
||||
"dark": "闇",
|
||||
"light": "光",
|
||||
"full": {
|
||||
"all": "全属性",
|
||||
"null": "無属性",
|
||||
"wind": "風属性",
|
||||
"fire": "火属性",
|
||||
"water": "水属性",
|
||||
"earth": "土属性",
|
||||
"dark": "闇属性",
|
||||
"light": "光属性"
|
||||
}
|
||||
},
|
||||
"recency": {
|
||||
"all_time": "全ての期間",
|
||||
"last_day": "1日",
|
||||
"last_week": "7日",
|
||||
"last_month": "1ヶ月",
|
||||
"last_3_months": "3ヶ月",
|
||||
"last_6_months": "6ヶ月",
|
||||
"last_year": "1年"
|
||||
},
|
||||
"summons": {
|
||||
"main": "メイン",
|
||||
"friend": "フレンド",
|
||||
"summons": "召喚石",
|
||||
"subaura": "サブ加護召喚石"
|
||||
},
|
||||
"modals": {
|
||||
"about": {
|
||||
"title": "このサイトについて"
|
||||
},
|
||||
"delete_team": {
|
||||
"title": "編成を削除しますか",
|
||||
"description": "編成を削除する操作は取り消せません。",
|
||||
"buttons": {
|
||||
"confirm": "削除",
|
||||
"cancel": "キャンセル"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "ログイン",
|
||||
"buttons": {
|
||||
"confirm": "ログイン"
|
||||
},
|
||||
"errors": {
|
||||
"empty_email": "メールアドレスを入力して下さい",
|
||||
"empty_password": "パスワードを入力して下さい",
|
||||
"invalid_email": "メールアドレスは有効ではありません",
|
||||
"invalid_credentials": "パスワードまたはメールアドレスが違います"
|
||||
},
|
||||
"placeholders": {
|
||||
"email": "メールアドレス",
|
||||
"password": "パスワード"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "アカウント設定",
|
||||
"labels": {
|
||||
"picture": "プロフィール画像",
|
||||
"language": "言語",
|
||||
"private": "プライベート"
|
||||
},
|
||||
"descriptions": {
|
||||
"private": "プロフィールを隠し、編成をコレクションに表示されないようにします"
|
||||
},
|
||||
"language": {
|
||||
"english": "英語",
|
||||
"japanese": "日本語"
|
||||
},
|
||||
"buttons": {
|
||||
"confirm": "設定を保存する"
|
||||
}
|
||||
},
|
||||
"signup": {
|
||||
"title": "アカウント登録",
|
||||
"buttons": {
|
||||
"confirm": "登録する"
|
||||
},
|
||||
"agreement": "続行することで<1>利用規約</1>に同意し、<br/><1>プライバシーポリシー</1>を読んだものとみなされます。",
|
||||
"errors": {
|
||||
"field_in_use": "入力された{{field}}は既に登録済みです",
|
||||
"empty_email": "メールアドレスを入力して下さい",
|
||||
"invalid_email": "メールアドレスは有効ではありません",
|
||||
"username_too_short": "ユーザーネームは3文字以上で入力してください",
|
||||
"username_too_long": "ユーザーネームは20文字以内で入力してください",
|
||||
"empty_password": "パスワードを入力して下さい",
|
||||
"password_contains_username": "パスワードにはユーザー名を含めないでください",
|
||||
"password_too_short": "パスワードは8文字以上で入力してください",
|
||||
"mismatched_passwords": "パスワードとパスワード確認を確かめてください",
|
||||
"invalid_credentials": "パスワードまたはメールアドレスが違います"
|
||||
},
|
||||
"placeholders": {
|
||||
"username": "ユーザー名",
|
||||
"email": "メールアドレス",
|
||||
"password": "パスワード",
|
||||
"password_confirm": "パスワード確認"
|
||||
}
|
||||
},
|
||||
"weapon": {
|
||||
"title": "武器変更",
|
||||
"buttons": {
|
||||
"confirm": "武器を変更する"
|
||||
},
|
||||
"subtitles": {
|
||||
"element": "属性",
|
||||
"ax_skills": "EXスキル",
|
||||
"weapon_keys": "武器スキル"
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"about": "このサイトについて",
|
||||
"guides": "攻略",
|
||||
"language": "言語",
|
||||
"login": "ログイン",
|
||||
"saved": "保存した編成",
|
||||
"settings": "アカウント設定",
|
||||
"signup": "登録",
|
||||
"teams": "編成一覧",
|
||||
"logout": "ログアウト"
|
||||
},
|
||||
"party": {
|
||||
"segmented_control": {
|
||||
"class": "ジョブ",
|
||||
"characters": "キャラ",
|
||||
"weapons": "武器",
|
||||
"summons": "召喚石"
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"title": "保存した編成",
|
||||
"loading": "ロード中...",
|
||||
"not_found": "編成はまだ保存していません"
|
||||
},
|
||||
"search": {
|
||||
"errors": {
|
||||
"start_typing": "{{object}}名を入力してください",
|
||||
"min_length": "3文字以上を入力してください",
|
||||
"no_results": "'{{query}}'の検索結果が見つかりませんでした"
|
||||
},
|
||||
"placeholders": {
|
||||
"weapon": "武器を検索...",
|
||||
"summon": "召喚石を検索...",
|
||||
"character": "キャラを検索..."
|
||||
}
|
||||
},
|
||||
"teams": {
|
||||
"title": "編成一覧",
|
||||
"loading": "ロード中...",
|
||||
"not_found": "編成は見つかりませんでした"
|
||||
},
|
||||
"extra_weapons": "Additional<br/>Weapons",
|
||||
"coming_soon": "開発中",
|
||||
"no_title": "無題",
|
||||
"no_raid": "マルチなし",
|
||||
"no_user": "無名"
|
||||
}
|
||||
|
||||
3
types/AxSkill.d.ts
vendored
|
|
@ -1,7 +1,8 @@
|
|||
interface AxSkill {
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string,
|
||||
jp: string
|
||||
ja: string
|
||||
},
|
||||
id: number,
|
||||
minValue: number,
|
||||
|
|
|
|||
3
types/Character.d.ts
vendored
|
|
@ -8,8 +8,9 @@ interface Character {
|
|||
gender: number
|
||||
max_level: number
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
hp: {
|
||||
min_hp: number
|
||||
|
|
|
|||
3
types/Raid.d.ts
vendored
|
|
@ -1,8 +1,9 @@
|
|||
interface Raid {
|
||||
id: string
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
level: number
|
||||
group: number
|
||||
|
|
|
|||
3
types/Summon.d.ts
vendored
|
|
@ -6,8 +6,9 @@ interface Summon {
|
|||
element: number
|
||||
max_level: number
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
hp: {
|
||||
min_hp: number
|
||||
|
|
|
|||
3
types/Weapon.d.ts
vendored
|
|
@ -10,8 +10,9 @@ interface Weapon {
|
|||
series: number
|
||||
ax: number
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
hp: {
|
||||
min_hp: number
|
||||
|
|
|
|||
3
types/WeaponKey.d.ts
vendored
|
|
@ -1,8 +1,9 @@
|
|||
interface WeaponKey {
|
||||
id: string
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string,
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
series: integer
|
||||
slot: integer
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ interface AccountState {
|
|||
|
||||
account: {
|
||||
authorized: boolean
|
||||
language: 'en' | 'jp'
|
||||
user: {
|
||||
id: string
|
||||
username: string
|
||||
|
|
@ -18,7 +17,6 @@ interface AccountState {
|
|||
export const initialAccountState: AccountState = {
|
||||
account: {
|
||||
authorized: false,
|
||||
language: 'en',
|
||||
user: undefined
|
||||
}
|
||||
}
|
||||
|
|
|
|||
154
utils/axData.tsx
|
|
@ -3,7 +3,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "ATK",
|
||||
"jp": "攻撃"
|
||||
"ja": "攻撃"
|
||||
},
|
||||
id: 0,
|
||||
minValue: 1,
|
||||
|
|
@ -13,7 +13,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -23,7 +23,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Double Attack Rate",
|
||||
"jp": "DA確率"
|
||||
"ja": "DA確率"
|
||||
},
|
||||
id: 5,
|
||||
minValue: 1,
|
||||
|
|
@ -33,7 +33,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Triple Attack Rate",
|
||||
"jp": "TA確率"
|
||||
"ja": "TA確率"
|
||||
},
|
||||
id: 6,
|
||||
minValue: 1,
|
||||
|
|
@ -43,7 +43,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Skill DMG Cap",
|
||||
"jp": "アビ上限"
|
||||
"ja": "アビ上限"
|
||||
},
|
||||
id: 7,
|
||||
minValue: 1,
|
||||
|
|
@ -55,7 +55,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "DEF",
|
||||
"jp": "防御"
|
||||
"ja": "防御"
|
||||
},
|
||||
id: 1,
|
||||
minValue: 1,
|
||||
|
|
@ -65,7 +65,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "HP",
|
||||
"jp": "HP"
|
||||
"ja": "HP"
|
||||
},
|
||||
id: 2,
|
||||
minValue: 1,
|
||||
|
|
@ -75,7 +75,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -85,7 +85,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -95,7 +95,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Enmity",
|
||||
"jp": "背水"
|
||||
"ja": "背水"
|
||||
},
|
||||
id: 11,
|
||||
minValue: 1,
|
||||
|
|
@ -106,7 +106,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "HP",
|
||||
"jp": "HP"
|
||||
"ja": "HP"
|
||||
},
|
||||
id: 2,
|
||||
minValue: 1,
|
||||
|
|
@ -116,7 +116,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "DEF",
|
||||
"jp": "防御"
|
||||
"ja": "防御"
|
||||
},
|
||||
id: 1,
|
||||
minValue: 1,
|
||||
|
|
@ -126,7 +126,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -136,7 +136,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -146,7 +146,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -157,7 +157,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -167,7 +167,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "ATK",
|
||||
"jp": "攻撃"
|
||||
"ja": "攻撃"
|
||||
},
|
||||
id: 0,
|
||||
minValue: 1,
|
||||
|
|
@ -177,7 +177,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental ATK",
|
||||
"jp": "全属性攻撃力"
|
||||
"ja": "全属性攻撃力"
|
||||
},
|
||||
id: 13,
|
||||
minValue: 1,
|
||||
|
|
@ -187,7 +187,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG Cap",
|
||||
"jp": "奥義上限"
|
||||
"ja": "奥義上限"
|
||||
},
|
||||
id: 8,
|
||||
minValue: 1,
|
||||
|
|
@ -197,7 +197,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -208,7 +208,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Multiattack Rate",
|
||||
"jp": "連撃率"
|
||||
"ja": "連撃率"
|
||||
},
|
||||
id: 4,
|
||||
minValue: 1,
|
||||
|
|
@ -218,7 +218,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -228,7 +228,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental ATK",
|
||||
"jp": "全属性攻撃力"
|
||||
"ja": "全属性攻撃力"
|
||||
},
|
||||
id: 13,
|
||||
minValue: 1,
|
||||
|
|
@ -238,7 +238,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Double Attack Rate",
|
||||
"jp": "DA確率"
|
||||
"ja": "DA確率"
|
||||
},
|
||||
id: 5,
|
||||
minValue: 1,
|
||||
|
|
@ -248,7 +248,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Triple Attack Rate",
|
||||
"jp": "TA確率"
|
||||
"ja": "TA確率"
|
||||
},
|
||||
id: 6,
|
||||
minValue: 1,
|
||||
|
|
@ -261,7 +261,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "ATK",
|
||||
"jp": "攻撃"
|
||||
"ja": "攻撃"
|
||||
},
|
||||
id: 0,
|
||||
minValue: 1,
|
||||
|
|
@ -271,7 +271,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -281,7 +281,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Multiattack Rate",
|
||||
"jp": "連撃確率"
|
||||
"ja": "連撃確率"
|
||||
},
|
||||
id: 4,
|
||||
minValue: 1.5,
|
||||
|
|
@ -291,7 +291,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Normal ATK DMG Cap",
|
||||
"jp": "通常ダメ上限"
|
||||
"ja": "通常ダメ上限"
|
||||
},
|
||||
id: 14,
|
||||
minValue: 0.5,
|
||||
|
|
@ -301,7 +301,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Supplemental Skill DMG",
|
||||
"jp": "アビ与ダメ上昇"
|
||||
"ja": "アビ与ダメ上昇"
|
||||
},
|
||||
id: 15,
|
||||
minValue: 1,
|
||||
|
|
@ -312,7 +312,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "DEF",
|
||||
"jp": "防御"
|
||||
"ja": "防御"
|
||||
},
|
||||
id: 1,
|
||||
minValue: 1,
|
||||
|
|
@ -322,7 +322,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental DMG Reduction",
|
||||
"jp": "属性ダメ軽減"
|
||||
"ja": "属性ダメ軽減"
|
||||
},
|
||||
id: 17,
|
||||
minValue: 1,
|
||||
|
|
@ -332,7 +332,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -342,7 +342,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -352,7 +352,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Enmity",
|
||||
"jp": "背水"
|
||||
"ja": "背水"
|
||||
},
|
||||
id: 11,
|
||||
minValue: 1,
|
||||
|
|
@ -363,7 +363,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "HP",
|
||||
"jp": "HP"
|
||||
"ja": "HP"
|
||||
},
|
||||
id: 2,
|
||||
minValue: 1,
|
||||
|
|
@ -373,7 +373,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental DMG Reduction",
|
||||
"jp": "属性ダメ軽減"
|
||||
"ja": "属性ダメ軽減"
|
||||
},
|
||||
id: 17,
|
||||
minValue: 1,
|
||||
|
|
@ -383,7 +383,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -393,7 +393,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -403,7 +403,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -414,7 +414,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -424,7 +424,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Multiattack Rate",
|
||||
"jp": "連撃率"
|
||||
"ja": "連撃率"
|
||||
},
|
||||
id: 4,
|
||||
minValue: 1.5,
|
||||
|
|
@ -434,7 +434,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Supplemental Skill DMG",
|
||||
"jp": "アビ与ダメ上昇"
|
||||
"ja": "アビ与ダメ上昇"
|
||||
},
|
||||
id: 15,
|
||||
minValue: 1,
|
||||
|
|
@ -443,7 +443,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Supplemental C.A. DMG",
|
||||
"jp": "奥義与ダメ上昇"
|
||||
"ja": "奥義与ダメ上昇"
|
||||
},
|
||||
id: 16,
|
||||
minValue: 1,
|
||||
|
|
@ -452,7 +452,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -463,7 +463,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Multiattack Rate",
|
||||
"jp": "連撃率"
|
||||
"ja": "連撃率"
|
||||
},
|
||||
id: 4,
|
||||
minValue: 1,
|
||||
|
|
@ -473,7 +473,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Supplemental C.A. DMG",
|
||||
"jp": "奥義与ダメ上昇"
|
||||
"ja": "奥義与ダメ上昇"
|
||||
},
|
||||
id: 16,
|
||||
minValue: 1,
|
||||
|
|
@ -482,7 +482,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Normal ATK DMG Cap",
|
||||
"jp": "通常ダメ上限"
|
||||
"ja": "通常ダメ上限"
|
||||
},
|
||||
id: 14,
|
||||
minValue: 0.5,
|
||||
|
|
@ -492,7 +492,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -501,7 +501,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Enmity",
|
||||
"jp": "背水"
|
||||
"ja": "背水"
|
||||
},
|
||||
id: 11,
|
||||
minValue: 1,
|
||||
|
|
@ -513,7 +513,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "ATK",
|
||||
"jp": "攻撃"
|
||||
"ja": "攻撃"
|
||||
},
|
||||
id: 0,
|
||||
minValue: 1,
|
||||
|
|
@ -523,7 +523,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -533,7 +533,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Double Attack Rate",
|
||||
"jp": "DA確率"
|
||||
"ja": "DA確率"
|
||||
},
|
||||
id: 5,
|
||||
minValue: 1,
|
||||
|
|
@ -543,7 +543,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Triple Attack Rate",
|
||||
"jp": "TA確率"
|
||||
"ja": "TA確率"
|
||||
},
|
||||
id: 6,
|
||||
minValue: 1,
|
||||
|
|
@ -553,7 +553,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Skill DMG Cap",
|
||||
"jp": "アビ上限"
|
||||
"ja": "アビ上限"
|
||||
},
|
||||
id: 7,
|
||||
minValue: 1,
|
||||
|
|
@ -565,7 +565,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "DEF",
|
||||
"jp": "防御"
|
||||
"ja": "防御"
|
||||
},
|
||||
id: 1,
|
||||
minValue: 1,
|
||||
|
|
@ -575,7 +575,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "HP",
|
||||
"jp": "HP"
|
||||
"ja": "HP"
|
||||
},
|
||||
id: 2,
|
||||
minValue: 1,
|
||||
|
|
@ -585,7 +585,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -595,7 +595,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -605,7 +605,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Enmity",
|
||||
"jp": "背水"
|
||||
"ja": "背水"
|
||||
},
|
||||
id: 11,
|
||||
minValue: 1,
|
||||
|
|
@ -616,7 +616,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "HP",
|
||||
"jp": "HP"
|
||||
"ja": "HP"
|
||||
},
|
||||
id: 2,
|
||||
minValue: 1,
|
||||
|
|
@ -626,7 +626,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "DEF",
|
||||
"jp": "防御"
|
||||
"ja": "防御"
|
||||
},
|
||||
id: 1,
|
||||
minValue: 1,
|
||||
|
|
@ -636,7 +636,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Debuff Resistance",
|
||||
"jp": "弱体耐性"
|
||||
"ja": "弱体耐性"
|
||||
},
|
||||
id: 9,
|
||||
minValue: 1,
|
||||
|
|
@ -646,7 +646,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Healing",
|
||||
"jp": "回復性能"
|
||||
"ja": "回復性能"
|
||||
},
|
||||
id: 10,
|
||||
minValue: 2,
|
||||
|
|
@ -656,7 +656,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -667,7 +667,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -677,7 +677,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "ATK",
|
||||
"jp": "攻撃"
|
||||
"ja": "攻撃"
|
||||
},
|
||||
id: 0,
|
||||
minValue: 1,
|
||||
|
|
@ -687,7 +687,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental ATK",
|
||||
"jp": "全属性攻撃力"
|
||||
"ja": "全属性攻撃力"
|
||||
},
|
||||
id: 13,
|
||||
minValue: 1,
|
||||
|
|
@ -697,7 +697,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG Cap",
|
||||
"jp": "奥義上限"
|
||||
"ja": "奥義上限"
|
||||
},
|
||||
id: 8,
|
||||
minValue: 1,
|
||||
|
|
@ -707,7 +707,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Stamina",
|
||||
"jp": "渾身"
|
||||
"ja": "渾身"
|
||||
},
|
||||
id: 12,
|
||||
minValue: 1,
|
||||
|
|
@ -718,7 +718,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Multiattack Rate",
|
||||
"jp": "連撃率"
|
||||
"ja": "連撃率"
|
||||
},
|
||||
id: 4,
|
||||
minValue: 1,
|
||||
|
|
@ -728,7 +728,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "C.A. DMG",
|
||||
"jp": "奥義ダメ"
|
||||
"ja": "奥義ダメ"
|
||||
},
|
||||
id: 3,
|
||||
minValue: 2,
|
||||
|
|
@ -738,7 +738,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Elemental ATK",
|
||||
"jp": "全属性攻撃力"
|
||||
"ja": "全属性攻撃力"
|
||||
},
|
||||
id: 13,
|
||||
minValue: 1,
|
||||
|
|
@ -748,7 +748,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Double Attack Rate",
|
||||
"jp": "DA確率"
|
||||
"ja": "DA確率"
|
||||
},
|
||||
id: 5,
|
||||
minValue: 1,
|
||||
|
|
@ -758,7 +758,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Triple Attack Rate",
|
||||
"jp": "TA確率"
|
||||
"ja": "TA確率"
|
||||
},
|
||||
id: 6,
|
||||
minValue: 1,
|
||||
|
|
@ -770,7 +770,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "EXP Up",
|
||||
"jp": "EXP UP"
|
||||
"ja": "EXP UP"
|
||||
},
|
||||
id: 18,
|
||||
minValue: 5,
|
||||
|
|
@ -780,7 +780,7 @@ export const axData: AxSkill[][] = [
|
|||
{
|
||||
name: {
|
||||
"en": "Rupies",
|
||||
"jp": "獲得ルピ"
|
||||
"ja": "獲得ルピ"
|
||||
},
|
||||
id: 19,
|
||||
minValue: 10,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
interface Picture {
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
ja: string
|
||||
}
|
||||
filename: string
|
||||
element: string
|
||||
|
|
@ -11,7 +12,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Gran 2019",
|
||||
jp: "グラン"
|
||||
ja: "グラン"
|
||||
},
|
||||
filename: "gran_19",
|
||||
element: "water"
|
||||
|
|
@ -19,7 +20,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Djeeta 2019",
|
||||
jp: "ジータ"
|
||||
ja: "ジータ"
|
||||
},
|
||||
filename: "djeeta_19",
|
||||
element: "fire"
|
||||
|
|
@ -27,7 +28,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Gran 2020",
|
||||
jp: "グラン"
|
||||
ja: "グラン"
|
||||
},
|
||||
filename: "gran_20",
|
||||
element: "water"
|
||||
|
|
@ -35,7 +36,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Djeeta 2020",
|
||||
jp: "ジータ"
|
||||
ja: "ジータ"
|
||||
},
|
||||
filename: "djeeta_20",
|
||||
element: "fire"
|
||||
|
|
@ -43,7 +44,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Gran - Farer of the Skies",
|
||||
jp: "空駆ける新鋭 グランver"
|
||||
ja: "空駆ける新鋭 グランver"
|
||||
},
|
||||
filename: "gran",
|
||||
element: "water"
|
||||
|
|
@ -51,7 +52,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Djeeta - Farer of the Skies",
|
||||
jp: "空駆ける新鋭 ジータver"
|
||||
ja: "空駆ける新鋭 ジータver"
|
||||
},
|
||||
filename: "djeeta",
|
||||
element: "fire"
|
||||
|
|
@ -59,7 +60,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Cassius",
|
||||
jp: "カシウス"
|
||||
ja: "カシウス"
|
||||
},
|
||||
filename: "cassius",
|
||||
element: "dark"
|
||||
|
|
@ -67,7 +68,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Percival",
|
||||
jp: "パーシヴァル"
|
||||
ja: "パーシヴァル"
|
||||
},
|
||||
filename: "percival",
|
||||
element: "fire"
|
||||
|
|
@ -75,7 +76,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Vane",
|
||||
jp: "ヴェイン"
|
||||
ja: "ヴェイン"
|
||||
},
|
||||
filename: "vane",
|
||||
element: "water"
|
||||
|
|
@ -83,7 +84,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Heles",
|
||||
jp: "ヘルエス"
|
||||
ja: "ヘルエス"
|
||||
},
|
||||
filename: "heles",
|
||||
element: "fire"
|
||||
|
|
@ -91,7 +92,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Lunalu",
|
||||
jp: "ルナール"
|
||||
ja: "ルナール"
|
||||
},
|
||||
filename: "lunalu",
|
||||
element: "dark"
|
||||
|
|
@ -99,7 +100,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Catura",
|
||||
jp: "シャトラ"
|
||||
ja: "シャトラ"
|
||||
},
|
||||
filename: "catura",
|
||||
element: "wind"
|
||||
|
|
@ -107,7 +108,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Yuisis",
|
||||
jp: "ユイシス"
|
||||
ja: "ユイシス"
|
||||
},
|
||||
filename: "yuisis",
|
||||
element: "wind"
|
||||
|
|
@ -115,7 +116,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Meg",
|
||||
jp: "メグ"
|
||||
ja: "メグ"
|
||||
},
|
||||
filename: "meg",
|
||||
element: "dark"
|
||||
|
|
@ -123,7 +124,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Seofon",
|
||||
jp: "シエテ"
|
||||
ja: "シエテ"
|
||||
},
|
||||
filename: "seofon",
|
||||
element: "wind"
|
||||
|
|
@ -131,7 +132,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Quatre",
|
||||
jp: "カトル"
|
||||
ja: "カトル"
|
||||
},
|
||||
filename: "quatre",
|
||||
element: "water"
|
||||
|
|
@ -139,7 +140,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Tien",
|
||||
jp: "エッセル"
|
||||
ja: "エッセル"
|
||||
},
|
||||
filename: "tien",
|
||||
element: "fire"
|
||||
|
|
@ -147,7 +148,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Seox",
|
||||
jp: "シス"
|
||||
ja: "シス"
|
||||
},
|
||||
filename: "seox",
|
||||
element: "dark"
|
||||
|
|
@ -155,7 +156,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Aoidos",
|
||||
jp: "アオイドス"
|
||||
ja: "アオイドス"
|
||||
},
|
||||
filename: "aoidos",
|
||||
element: "fire"
|
||||
|
|
@ -163,7 +164,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Sandalphon",
|
||||
jp: "サンダルフォン"
|
||||
ja: "サンダルフォン"
|
||||
},
|
||||
filename: "sandalphon",
|
||||
element: "light"
|
||||
|
|
@ -171,7 +172,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Vikala",
|
||||
jp: "ビカラ"
|
||||
ja: "ビカラ"
|
||||
},
|
||||
filename: "vikala",
|
||||
element: "dark"
|
||||
|
|
@ -179,7 +180,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Belial",
|
||||
jp: "ベリアル"
|
||||
ja: "ベリアル"
|
||||
},
|
||||
filename: "belial",
|
||||
element: "dark"
|
||||
|
|
@ -187,7 +188,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Zeta",
|
||||
jp: "ゼタ"
|
||||
ja: "ゼタ"
|
||||
},
|
||||
filename: "zeta",
|
||||
element: "fire"
|
||||
|
|
@ -195,7 +196,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Beatrix",
|
||||
jp: "ベアトリックス"
|
||||
ja: "ベアトリックス"
|
||||
},
|
||||
filename: "beatrix",
|
||||
element: "earth"
|
||||
|
|
@ -203,7 +204,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Yuel",
|
||||
jp: "ユエル"
|
||||
ja: "ユエル"
|
||||
},
|
||||
filename: "yuel",
|
||||
element: "fire"
|
||||
|
|
@ -211,7 +212,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Societte",
|
||||
jp: "ソシエ"
|
||||
ja: "ソシエ"
|
||||
},
|
||||
filename: "societte",
|
||||
element: "water"
|
||||
|
|
@ -219,7 +220,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Kumbhira",
|
||||
jp: "クビラ"
|
||||
ja: "クビラ"
|
||||
},
|
||||
filename: "kumbhira",
|
||||
element: "light"
|
||||
|
|
@ -227,7 +228,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Narmaya",
|
||||
jp: "ナルメア"
|
||||
ja: "ナルメア"
|
||||
},
|
||||
filename: "narmaya",
|
||||
element: "dark"
|
||||
|
|
@ -235,7 +236,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Siegfried",
|
||||
jp: "ジークフリード"
|
||||
ja: "ジークフリード"
|
||||
},
|
||||
filename: "siegfried",
|
||||
element: "earth"
|
||||
|
|
@ -243,7 +244,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Naoise",
|
||||
jp: "ノイシュ"
|
||||
ja: "ノイシュ"
|
||||
},
|
||||
filename: "naoise",
|
||||
element: "light"
|
||||
|
|
@ -251,7 +252,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Scathacha",
|
||||
jp: "スカーサハ"
|
||||
ja: "スカーサハ"
|
||||
},
|
||||
filename: "scathacha",
|
||||
element: "wind"
|
||||
|
|
@ -259,7 +260,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Seruel",
|
||||
jp: "セルエル"
|
||||
ja: "セルエル"
|
||||
},
|
||||
filename: "seruel",
|
||||
element: "light"
|
||||
|
|
@ -267,7 +268,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Shiva",
|
||||
jp: "シヴァ"
|
||||
ja: "シヴァ"
|
||||
},
|
||||
filename: "shiva",
|
||||
element: "fire"
|
||||
|
|
@ -275,7 +276,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Europa",
|
||||
jp: "エウロペ"
|
||||
ja: "エウロペ"
|
||||
},
|
||||
filename: "europa",
|
||||
element: "water"
|
||||
|
|
@ -283,7 +284,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Grimnir",
|
||||
jp: "グリームニル"
|
||||
ja: "グリームニル"
|
||||
},
|
||||
filename: "grimnir",
|
||||
element: "wind"
|
||||
|
|
@ -291,7 +292,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Alexiel",
|
||||
jp: "ブローディア"
|
||||
ja: "ブローディア"
|
||||
},
|
||||
filename: "alexiel",
|
||||
element: "earth"
|
||||
|
|
@ -299,7 +300,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Sierokarte",
|
||||
jp: "シェロカルテ"
|
||||
ja: "シェロカルテ"
|
||||
},
|
||||
filename: "siero",
|
||||
element: "wind"
|
||||
|
|
@ -307,7 +308,7 @@ export const pictureData: Picture[] = [
|
|||
{
|
||||
name: {
|
||||
en: "Vajra",
|
||||
jp: "ヴァジラ"
|
||||
ja: "ヴァジラ"
|
||||
},
|
||||
filename: "vajra",
|
||||
element: "water"
|
||||
|
|
|
|||