Merge branch 'main' into weapon-mods
This commit is contained in:
commit
28f6d8fd73
41 changed files with 663 additions and 812 deletions
|
|
@ -24,18 +24,6 @@ const AccountModal = () => {
|
||||||
const locale =
|
const locale =
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||||
|
|
||||||
// Cookies
|
|
||||||
const cookie = getCookie('account')
|
|
||||||
|
|
||||||
const headers = {}
|
|
||||||
// cookies.account != null
|
|
||||||
// ? {
|
|
||||||
// headers: {
|
|
||||||
// Authorization: `Bearer ${cookies.account.access_token}`,
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// : {}
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [picture, setPicture] = useState('')
|
const [picture, setPicture] = useState('')
|
||||||
|
|
@ -171,12 +159,16 @@ const AccountModal = () => {
|
||||||
pictureData.find((i) => i.filename === picture)?.element
|
pictureData.find((i) => i.filename === picture)?.element
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<img
|
{picture ? (
|
||||||
alt="Profile preview"
|
<img
|
||||||
srcSet={`/profile/${picture}.png,
|
alt="Profile preview"
|
||||||
|
srcSet={`/profile/${picture}.png,
|
||||||
/profile/${picture}@2x.png 2x`}
|
/profile/${picture}@2x.png 2x`}
|
||||||
src={`/profile/${picture}.png`}
|
src={`/profile/${picture}.png`}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,10 @@ const Alert = (props: Props) => {
|
||||||
</AlertDialog.Description>
|
</AlertDialog.Description>
|
||||||
<div className="buttons">
|
<div className="buttons">
|
||||||
<AlertDialog.Cancel asChild>
|
<AlertDialog.Cancel asChild>
|
||||||
<Button onClick={props.cancelAction}>
|
<Button
|
||||||
{props.cancelActionText}
|
onClick={props.cancelAction}
|
||||||
</Button>
|
text={props.cancelActionText}
|
||||||
|
/>
|
||||||
</AlertDialog.Cancel>
|
</AlertDialog.Cancel>
|
||||||
{props.primaryAction ? (
|
{props.primaryAction ? (
|
||||||
<AlertDialog.Action onClick={props.primaryAction}>
|
<AlertDialog.Action onClick={props.primaryAction}>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
.Limited {
|
.Limited {
|
||||||
$offset: 2px;
|
$offset: 2px;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
background: var(--input-bg);
|
background: var(--input-bg);
|
||||||
border-radius: $input-corner;
|
border-radius: $input-corner;
|
||||||
border: $offset solid transparent;
|
border: $offset solid transparent;
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ const CharacterConflictModal = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<Button onClick={close}>Nevermind</Button>
|
<Button onClick={close} text="Nevermind" />
|
||||||
<Button onClick={props.resolveConflict}>Confirm</Button>
|
<Button onClick={props.resolveConflict} text="Confirm" />
|
||||||
</footer>
|
</footer>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
<Dialog.Overlay className="Overlay" />
|
<Dialog.Overlay className="Overlay" />
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,6 @@ const CharacterGrid = (props: Props) => {
|
||||||
const accountData: AccountCookie = cookie
|
const accountData: AccountCookie = cookie
|
||||||
? JSON.parse(cookie as string)
|
? JSON.parse(cookie as string)
|
||||||
: null
|
: null
|
||||||
const headers = accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
// Set up state for view management
|
// Set up state for view management
|
||||||
const { party, grid } = useSnapshot(appState)
|
const { party, grid } = useSnapshot(appState)
|
||||||
|
|
@ -104,7 +101,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
|
|
||||||
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
saveCharacter(party.id, character, position)
|
saveCharacter(party.id, character, position)
|
||||||
.then((response) => storeGridCharacter(response.data.grid_character))
|
.then((response) => storeGridCharacter(response.data))
|
||||||
.catch((error) => console.error(error))
|
.catch((error) => console.error(error))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -122,7 +119,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
setPosition(data.position)
|
setPosition(data.position)
|
||||||
setModalOpen(true)
|
setModalOpen(true)
|
||||||
} else {
|
} else {
|
||||||
storeGridCharacter(data.grid_character)
|
storeGridCharacter(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,17 +128,14 @@ const CharacterGrid = (props: Props) => {
|
||||||
character: Character,
|
character: Character,
|
||||||
position: number
|
position: number
|
||||||
) {
|
) {
|
||||||
return await api.endpoints.characters.create(
|
return await api.endpoints.characters.create({
|
||||||
{
|
character: {
|
||||||
character: {
|
party_id: partyId,
|
||||||
party_id: partyId,
|
character_id: character.id,
|
||||||
character_id: character.id,
|
position: position,
|
||||||
position: position,
|
uncap_level: characterUncapLevel(character),
|
||||||
uncap_level: characterUncapLevel(character),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
headers
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeGridCharacter(gridCharacter: GridCharacter) {
|
function storeGridCharacter(gridCharacter: GridCharacter) {
|
||||||
|
|
@ -155,11 +149,10 @@ const CharacterGrid = (props: Props) => {
|
||||||
incoming: incoming.id,
|
incoming: incoming.id,
|
||||||
conflicting: conflicts.map((c) => c.id),
|
conflicting: conflicts.map((c) => c.id),
|
||||||
position: position,
|
position: position,
|
||||||
params: headers,
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// Store new character in state
|
// Store new character in state
|
||||||
storeGridCharacter(response.data.grid_character)
|
storeGridCharacter(response.data)
|
||||||
|
|
||||||
// Remove conflicting characters from state
|
// Remove conflicting characters from state
|
||||||
conflicts.forEach(
|
conflicts.forEach(
|
||||||
|
|
@ -182,24 +175,26 @@ const CharacterGrid = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Saving job and job skills
|
// Methods: Saving job and job skills
|
||||||
const saveJob = function (job: Job) {
|
const saveJob = async function (job?: Job) {
|
||||||
const payload = {
|
const payload = {
|
||||||
party: {
|
party: {
|
||||||
job_id: job ? job.id : '',
|
job_id: job ? job.id : -1,
|
||||||
},
|
},
|
||||||
...headers,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (party.id && appState.party.editable) {
|
if (party.id && appState.party.editable) {
|
||||||
api.updateJob({ partyId: party.id, params: payload }).then((response) => {
|
const response = await api.updateJob({
|
||||||
const newParty = response.data.party
|
partyId: party.id,
|
||||||
|
params: payload,
|
||||||
setJob(newParty.job)
|
|
||||||
appState.party.job = newParty.job
|
|
||||||
|
|
||||||
setJobSkills(newParty.job_skills)
|
|
||||||
appState.party.jobSkills = newParty.job_skills
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const newParty = response.data
|
||||||
|
|
||||||
|
setJob(newParty.job)
|
||||||
|
appState.party.job = newParty.job
|
||||||
|
|
||||||
|
setJobSkills(newParty.job_skills)
|
||||||
|
appState.party.jobSkills = newParty.job_skills
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +212,6 @@ const CharacterGrid = (props: Props) => {
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
party: skillObject,
|
party: skillObject,
|
||||||
...headers,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skillObject[positionedKey] = skill.id
|
skillObject[positionedKey] = skill.id
|
||||||
|
|
@ -225,7 +219,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
.updateJobSkills({ partyId: party.id, params: payload })
|
.updateJobSkills({ partyId: party.id, params: payload })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// Update the current skills
|
// Update the current skills
|
||||||
const newSkills = response.data.party.job_skills
|
const newSkills = response.data.job_skills
|
||||||
setJobSkills(newSkills)
|
setJobSkills(newSkills)
|
||||||
appState.party.jobSkills = newSkills
|
appState.party.jobSkills = newSkills
|
||||||
})
|
})
|
||||||
|
|
@ -269,7 +263,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
try {
|
try {
|
||||||
if (uncapLevel != previousUncapValues[position])
|
if (uncapLevel != previousUncapValues[position])
|
||||||
await api.updateUncap('character', id, uncapLevel).then((response) => {
|
await api.updateUncap('character', id, uncapLevel).then((response) => {
|
||||||
storeGridCharacter(response.data.grid_character)
|
storeGridCharacter(response.data)
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
gap: $unit-half;
|
gap: $unit-half;
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
color: var(--text-secondary);
|
color: var(--text-tertiary);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $font-medium;
|
font-size: $font-medium;
|
||||||
font-weight: $medium;
|
font-weight: $medium;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $unit;
|
gap: $unit;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -38,6 +39,7 @@
|
||||||
|
|
||||||
.DialogClose {
|
.DialogClose {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -58,7 +60,13 @@
|
||||||
.DialogTitle {
|
.DialogTitle {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-size: $font-xlarge;
|
font-size: $font-xlarge;
|
||||||
flex-grow: 1;
|
|
||||||
|
h1 {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: $font-xlarge;
|
||||||
|
font-weight: $medium;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.DialogTop {
|
.DialogTop {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
|
|
||||||
img {
|
img {
|
||||||
$diameter: $unit * 6;
|
$diameter: $unit * 6;
|
||||||
border-radius: $diameter / 2;
|
border-radius: calc($diameter / 2);
|
||||||
height: $diameter;
|
height: $diameter;
|
||||||
width: $diameter;
|
width: $diameter;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,6 @@ const FilterBar = (props: Props) => {
|
||||||
const [recencyOpen, setRecencyOpen] = useState(false)
|
const [recencyOpen, setRecencyOpen] = useState(false)
|
||||||
const [elementOpen, setElementOpen] = useState(false)
|
const [elementOpen, setElementOpen] = useState(false)
|
||||||
|
|
||||||
// Set up refs for filter dropdowns
|
|
||||||
const elementSelect = React.createRef<HTMLSelectElement>()
|
|
||||||
const raidSelect = React.createRef<HTMLSelectElement>()
|
|
||||||
const recencySelect = React.createRef<HTMLSelectElement>()
|
|
||||||
|
|
||||||
// Set up classes object for showing shadow on scroll
|
// Set up classes object for showing shadow on scroll
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
FilterBar: true,
|
FilterBar: true,
|
||||||
|
|
@ -69,9 +64,9 @@ const FilterBar = (props: Props) => {
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
{props.children}
|
{props.children}
|
||||||
<Select
|
<Select
|
||||||
defaultValue={-1}
|
value={`${props.element}`}
|
||||||
open={elementOpen}
|
open={elementOpen}
|
||||||
onChange={elementSelectChanged}
|
onValueChange={elementSelectChanged}
|
||||||
onClick={openElementSelect}
|
onClick={openElementSelect}
|
||||||
>
|
>
|
||||||
<SelectItem data-element="all" key={-1} value={-1}>
|
<SelectItem data-element="all" key={-1} value={-1}>
|
||||||
|
|
@ -105,13 +100,13 @@ const FilterBar = (props: Props) => {
|
||||||
defaultRaid="all"
|
defaultRaid="all"
|
||||||
showAllRaidsOption={true}
|
showAllRaidsOption={true}
|
||||||
onChange={raidSelectChanged}
|
onChange={raidSelectChanged}
|
||||||
ref={raidSelect}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
defaultValue={-1}
|
value={`${props.recency}`}
|
||||||
|
trigger={'All time'}
|
||||||
open={recencyOpen}
|
open={recencyOpen}
|
||||||
onChange={recencySelectChanged}
|
onValueChange={recencySelectChanged}
|
||||||
onClick={openRecencySelect}
|
onClick={openRecencySelect}
|
||||||
>
|
>
|
||||||
<SelectItem key={-1} value={-1}>
|
<SelectItem key={-1} value={-1}>
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ const GridRep = (props: Props) => {
|
||||||
let url = ''
|
let url = ''
|
||||||
|
|
||||||
if (mainhand) {
|
if (mainhand) {
|
||||||
if (mainhand.element == 0 && props.grid[0].element) {
|
if (mainhand.element == 0 && props.grid[0] && props.grid[0].element) {
|
||||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}_${props.grid[0].element}.jpg`
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}_${props.grid[0].element}.jpg`
|
||||||
} else {
|
} else {
|
||||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}.jpg`
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}.jpg`
|
||||||
|
|
@ -119,14 +119,14 @@ const GridRep = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const userImage = () => {
|
const userImage = () => {
|
||||||
if (props.user && props.user.picture) {
|
if (props.user && props.user.avatar) {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
alt={props.user.picture.picture}
|
alt={props.user.avatar.picture}
|
||||||
className={`profile ${props.user.picture.element}`}
|
className={`profile ${props.user.avatar.element}`}
|
||||||
srcSet={`/profile/${props.user.picture.picture}.png,
|
srcSet={`/profile/${props.user.avatar.picture}.png,
|
||||||
/profile/${props.user.picture.picture}@2x.png 2x`}
|
/profile/${props.user.avatar.picture}@2x.png 2x`}
|
||||||
src={`/profile/${props.user.picture.picture}.png`}
|
src={`/profile/${props.user.avatar.picture}.png`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else return <div className="no-user" />
|
} else return <div className="no-user" />
|
||||||
|
|
@ -164,7 +164,7 @@ const GridRep = (props: Props) => {
|
||||||
!props.user) ? (
|
!props.user) ? (
|
||||||
<Button
|
<Button
|
||||||
className="Save"
|
className="Save"
|
||||||
accessoryIcon={<SaveIcon class="stroke" />}
|
accessoryIcon={<SaveIcon className="stroke" />}
|
||||||
active={props.favorited}
|
active={props.favorited}
|
||||||
contained={true}
|
contained={true}
|
||||||
buttonSize="small"
|
buttonSize="small"
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
padding: $unit-2x;
|
padding: ($unit * 1.5) $unit-2x;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&.Contained {
|
&.Bound {
|
||||||
background-color: var(--input-bound-bg);
|
background-color: var(--input-bound-bg);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
||||||
|
|
@ -7,43 +7,33 @@ interface Props
|
||||||
React.InputHTMLAttributes<HTMLInputElement>,
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
HTMLInputElement
|
HTMLInputElement
|
||||||
> {
|
> {
|
||||||
contained?: boolean
|
|
||||||
error?: string
|
error?: string
|
||||||
label?: string
|
label?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps = {
|
|
||||||
contained: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, Props>(function input(
|
const Input = React.forwardRef<HTMLInputElement, Props>(function input(
|
||||||
{ contained, error, label, ...props },
|
props: Props,
|
||||||
forwardedRef
|
forwardedRef
|
||||||
) {
|
) {
|
||||||
const classes = classNames(
|
const classes = classNames({ Input: true }, props.className)
|
||||||
{
|
const { value, ...inputProps } = props
|
||||||
Input: true,
|
|
||||||
Contained: contained,
|
|
||||||
},
|
|
||||||
props.className
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label className="Label" htmlFor={props.name}>
|
<label className="Label" htmlFor={props.name}>
|
||||||
<input
|
<input
|
||||||
{...props}
|
{...inputProps}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={classes}
|
className={classes}
|
||||||
defaultValue={props.value || ''}
|
defaultValue={props.value || ''}
|
||||||
ref={forwardedRef}
|
ref={forwardedRef}
|
||||||
formNoValidate
|
formNoValidate
|
||||||
/>
|
/>
|
||||||
{label}
|
{props.label}
|
||||||
{error && error.length > 0 && <p className="InputError">{error}</p>}
|
{props.error && props.error.length > 0 && (
|
||||||
|
<p className="InputError">{props.error}</p>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
Input.defaultProps = defaultProps
|
|
||||||
|
|
||||||
export default Input
|
export default Input
|
||||||
|
|
|
||||||
|
|
@ -102,17 +102,20 @@ const JobDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
value={currentJob ? currentJob.id : 'no-job'}
|
||||||
placeholder={'Select a class...'}
|
placeholder={'Select a class...'}
|
||||||
open={open}
|
open={open}
|
||||||
onClick={openJobSelect}
|
onClick={openJobSelect}
|
||||||
onChange={handleChange}
|
onValueChange={handleChange}
|
||||||
triggerClass="Job"
|
triggerClass="Job"
|
||||||
>
|
>
|
||||||
<SelectItem key={-1} value="no-job">
|
<SelectItem key={-1} value="no-job">
|
||||||
No class
|
No class
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
{sortedJobs
|
{sortedJobs
|
||||||
? Object.keys(sortedJobs).map((x) => renderJobGroup(x))
|
? Object.keys(sortedJobs)
|
||||||
|
.sort((a, b) => ('' + a).localeCompare(b))
|
||||||
|
.map((x) => renderJobGroup(x))
|
||||||
: ''}
|
: ''}
|
||||||
</Select>
|
</Select>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ interface Props {
|
||||||
job?: Job
|
job?: Job
|
||||||
jobSkills: JobSkillObject
|
jobSkills: JobSkillObject
|
||||||
editable: boolean
|
editable: boolean
|
||||||
saveJob: (job: Job) => void
|
saveJob: (job?: Job) => void
|
||||||
saveSkill: (skill: JobSkill, position: number) => void
|
saveSkill: (skill: JobSkill, position: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,17 +41,15 @@ const JobSection = (props: Props) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Set current job based on ID
|
// Set current job based on ID
|
||||||
if (props.job) {
|
setJob(props.job)
|
||||||
setJob(props.job)
|
setSkills({
|
||||||
setSkills({
|
0: props.jobSkills[0],
|
||||||
0: props.jobSkills[0],
|
1: props.jobSkills[1],
|
||||||
1: props.jobSkills[1],
|
2: props.jobSkills[2],
|
||||||
2: props.jobSkills[2],
|
3: props.jobSkills[3],
|
||||||
3: props.jobSkills[3],
|
})
|
||||||
})
|
|
||||||
|
|
||||||
if (selectRef.current) selectRef.current.value = props.job.id
|
if (selectRef.current && props.job) selectRef.current.value = props.job.id
|
||||||
}
|
|
||||||
}, [props])
|
}, [props])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -68,10 +66,8 @@ const JobSection = (props: Props) => {
|
||||||
}, [job])
|
}, [job])
|
||||||
|
|
||||||
function receiveJob(job?: Job) {
|
function receiveJob(job?: Job) {
|
||||||
if (job) {
|
setJob(job)
|
||||||
setJob(job)
|
props.saveJob(job)
|
||||||
props.saveJob(job)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateImageUrl() {
|
function generateImageUrl() {
|
||||||
|
|
@ -88,11 +84,13 @@ const JobSection = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const canEditSkill = (skill?: JobSkill) => {
|
const canEditSkill = (skill?: JobSkill) => {
|
||||||
if (job && skill) {
|
// If there is a job and a skill present in the slot
|
||||||
if (skill.job.id === job.id && skill.main && !skill.sub) return false
|
if (job) {
|
||||||
}
|
// If the skill's job is one of the job's main skill
|
||||||
|
if (skill && skill.job.id === job.id && skill.main) return false
|
||||||
|
|
||||||
return props.editable
|
return props.editable
|
||||||
|
} else return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const skillItem = (index: number, editable: boolean) => {
|
const skillItem = (index: number, editable: boolean) => {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
color: var(--text-secondary);
|
color: var(--text-tertiary);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $font-medium;
|
font-size: $font-medium;
|
||||||
font-weight: $medium;
|
font-weight: $medium;
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,11 @@ const JobSkillSearchFilterBar = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="SearchFilterBar">
|
<div className="SearchFilterBar">
|
||||||
<Select
|
<Select
|
||||||
defaultValue={-1}
|
value={-1}
|
||||||
|
triggerClass="Bound"
|
||||||
|
trigger={'All elements'}
|
||||||
open={open}
|
open={open}
|
||||||
onChange={onChange}
|
onValueChange={onChange}
|
||||||
onClick={openSelect}
|
onClick={openSelect}
|
||||||
>
|
>
|
||||||
<SelectItem key="all" value={-1}>
|
<SelectItem key="all" value={-1}>
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,17 @@ import Router, { useRouter } from 'next/router'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AxiosResponse } from 'axios'
|
import { AxiosResponse } from 'axios'
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog'
|
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
import { accountState } from '~utils/accountState'
|
import { accountState } from '~utils/accountState'
|
||||||
|
|
||||||
import Button from '~components/Button'
|
import Button from '~components/Button'
|
||||||
import Input from '~components/Input'
|
import Input from '~components/Input'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogClose,
|
||||||
|
} from '~components/Dialog'
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
@ -111,23 +115,23 @@ const LoginModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeCookieInfo(response: AxiosResponse) {
|
function storeCookieInfo(response: AxiosResponse) {
|
||||||
const user = response.data.user
|
const resp = response.data
|
||||||
|
|
||||||
const cookieObj: AccountCookie = {
|
const cookieObj: AccountCookie = {
|
||||||
userId: user.id,
|
userId: resp.user.id,
|
||||||
username: user.username,
|
username: resp.user.username,
|
||||||
token: response.data.access_token,
|
token: resp.access_token,
|
||||||
}
|
}
|
||||||
|
|
||||||
setCookie('account', cookieObj, { path: '/' })
|
setCookie('account', cookieObj, { path: '/' })
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeUserInfo(response: AxiosResponse) {
|
function storeUserInfo(response: AxiosResponse) {
|
||||||
const user = response.data.user
|
const user = response.data
|
||||||
|
|
||||||
const cookieObj: UserCookie = {
|
const cookieObj: UserCookie = {
|
||||||
picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.picture.element,
|
element: user.avatar.element,
|
||||||
language: user.language,
|
language: user.language,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
}
|
}
|
||||||
|
|
@ -137,8 +141,8 @@ const LoginModal = (props: Props) => {
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.picture.element,
|
element: user.avatar.element,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,51 +169,44 @@ const LoginModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
<Dialog open={open} onOpenChange={openChange}>
|
||||||
<Dialog.Trigger asChild>
|
<DialogTrigger asChild>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<span>{t('menu.login')}</span>
|
<span>{t('menu.login')}</span>
|
||||||
</li>
|
</li>
|
||||||
</Dialog.Trigger>
|
</DialogTrigger>
|
||||||
<Dialog.Portal>
|
<DialogContent className="Login Dialog">
|
||||||
<Dialog.Content
|
<div className="DialogHeader">
|
||||||
className="Login Dialog"
|
<div className="DialogTitle">
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
<h1>{t('modals.login.title')}</h1>
|
||||||
>
|
|
||||||
<div className="DialogHeader">
|
|
||||||
<Dialog.Title className="DialogTitle">
|
|
||||||
{t('modals.login.title')}
|
|
||||||
</Dialog.Title>
|
|
||||||
<Dialog.Close className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</Dialog.Close>
|
|
||||||
</div>
|
</div>
|
||||||
|
<DialogClose className="DialogClose">
|
||||||
|
<CrossIcon />
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form className="form" onSubmit={login}>
|
<form className="form" onSubmit={login}>
|
||||||
<Input
|
<Input
|
||||||
name="email"
|
name="email"
|
||||||
placeholder={t('modals.login.placeholders.email')}
|
placeholder={t('modals.login.placeholders.email')}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
error={errors.email}
|
error={errors.email}
|
||||||
ref={emailInput}
|
ref={emailInput}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="password"
|
name="password"
|
||||||
placeholder={t('modals.login.placeholders.password')}
|
placeholder={t('modals.login.placeholders.password')}
|
||||||
onChange={handleChange}
|
type="password"
|
||||||
error={errors.password}
|
onChange={handleChange}
|
||||||
ref={passwordInput}
|
error={errors.password}
|
||||||
/>
|
ref={passwordInput}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button>{t('modals.login.buttons.confirm')}</Button>
|
<Button text={t('modals.login.buttons.confirm')} />
|
||||||
</form>
|
</form>
|
||||||
</Dialog.Content>
|
</DialogContent>
|
||||||
<Dialog.Overlay className="Overlay" />
|
</Dialog>
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Root>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,6 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Party = (props: Props) => {
|
const Party = (props: Props) => {
|
||||||
// Cookies
|
|
||||||
const cookie = getCookie('account')
|
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = useMemo(() => {
|
|
||||||
return accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
}, [accountData])
|
|
||||||
|
|
||||||
// Set up router
|
// Set up router
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
@ -55,12 +43,13 @@ const Party = (props: Props) => {
|
||||||
async function createParty(extra: boolean = false) {
|
async function createParty(extra: boolean = false) {
|
||||||
let body = {
|
let body = {
|
||||||
party: {
|
party: {
|
||||||
...(accountData && { user_id: accountData.userId }),
|
|
||||||
extra: extra,
|
extra: extra,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return await api.endpoints.parties.create(body, headers)
|
console.log(body)
|
||||||
|
|
||||||
|
return await api.endpoints.parties.create(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Updating the party's details
|
// Methods: Updating the party's details
|
||||||
|
|
@ -68,13 +57,9 @@ const Party = (props: Props) => {
|
||||||
appState.party.extra = event.target.checked
|
appState.party.extra = event.target.checked
|
||||||
|
|
||||||
if (party.id) {
|
if (party.id) {
|
||||||
api.endpoints.parties.update(
|
api.endpoints.parties.update(party.id, {
|
||||||
party.id,
|
party: { extra: event.target.checked },
|
||||||
{
|
})
|
||||||
party: { extra: event.target.checked },
|
|
||||||
},
|
|
||||||
headers
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,17 +71,13 @@ const Party = (props: Props) => {
|
||||||
) {
|
) {
|
||||||
if (appState.party.id)
|
if (appState.party.id)
|
||||||
api.endpoints.parties
|
api.endpoints.parties
|
||||||
.update(
|
.update(appState.party.id, {
|
||||||
appState.party.id,
|
party: {
|
||||||
{
|
name: name,
|
||||||
party: {
|
description: description,
|
||||||
name: name,
|
raid_id: raid?.id,
|
||||||
description: description,
|
|
||||||
raid_id: raid?.id,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
headers
|
})
|
||||||
)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
appState.party.name = name
|
appState.party.name = name
|
||||||
appState.party.description = description
|
appState.party.description = description
|
||||||
|
|
@ -110,7 +91,7 @@ const Party = (props: Props) => {
|
||||||
function deleteTeam(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
function deleteTeam(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
||||||
if (appState.party.editable && appState.party.id) {
|
if (appState.party.editable && appState.party.id) {
|
||||||
api.endpoints.parties
|
api.endpoints.parties
|
||||||
.destroy({ id: appState.party.id, params: headers })
|
.destroy({ id: appState.party.id })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Push to route
|
// Push to route
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
|
@ -131,26 +112,28 @@ const Party = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Storing party data
|
// Methods: Storing party data
|
||||||
const storeParty = function (party: Party) {
|
const storeParty = function (team: Party) {
|
||||||
// Store the important party and state-keeping values
|
// Store the important party and state-keeping values
|
||||||
appState.party.name = party.name
|
appState.party.name = team.name
|
||||||
appState.party.description = party.description
|
appState.party.description = team.description
|
||||||
appState.party.raid = party.raid
|
appState.party.raid = team.raid
|
||||||
appState.party.updated_at = party.updated_at
|
appState.party.updated_at = team.updated_at
|
||||||
appState.party.job = party.job
|
appState.party.job = team.job
|
||||||
appState.party.jobSkills = party.job_skills
|
appState.party.jobSkills = team.job_skills
|
||||||
|
|
||||||
appState.party.id = party.id
|
appState.party.id = team.id
|
||||||
appState.party.extra = party.extra
|
appState.party.extra = team.extra
|
||||||
appState.party.user = party.user
|
appState.party.user = team.user
|
||||||
appState.party.favorited = party.favorited
|
appState.party.favorited = team.favorited
|
||||||
appState.party.created_at = party.created_at
|
appState.party.created_at = team.created_at
|
||||||
appState.party.updated_at = party.updated_at
|
appState.party.updated_at = team.updated_at
|
||||||
|
|
||||||
|
appState.party.detailsVisible = false
|
||||||
|
|
||||||
// Populate state
|
// Populate state
|
||||||
storeCharacters(party.characters)
|
storeCharacters(team.characters)
|
||||||
storeWeapons(party.weapons)
|
storeWeapons(team.weapons)
|
||||||
storeSummons(party.summons)
|
storeSummons(team.summons)
|
||||||
}
|
}
|
||||||
|
|
||||||
const storeCharacters = (list: Array<GridCharacter>) => {
|
const storeCharacters = (list: Array<GridCharacter>) => {
|
||||||
|
|
@ -256,13 +239,11 @@ const Party = (props: Props) => {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{navigation}
|
{navigation}
|
||||||
<section id="Party">{currentGrid()}</section>
|
<section id="Party">{currentGrid()}</section>
|
||||||
{
|
<PartyDetails
|
||||||
<PartyDetails
|
editable={party.editable}
|
||||||
editable={party.editable}
|
updateCallback={updateDetails}
|
||||||
updateCallback={updateDetails}
|
deleteCallback={deleteTeam}
|
||||||
deleteCallback={deleteTeam}
|
/>
|
||||||
/>
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import Head from 'next/head'
|
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -14,7 +13,6 @@ import CharLimitedFieldset from '~components/CharLimitedFieldset'
|
||||||
import RaidDropdown from '~components/RaidDropdown'
|
import RaidDropdown from '~components/RaidDropdown'
|
||||||
import TextFieldset from '~components/TextFieldset'
|
import TextFieldset from '~components/TextFieldset'
|
||||||
|
|
||||||
import { accountState } from '~utils/accountState'
|
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
|
|
||||||
import CheckIcon from '~public/icons/Check.svg'
|
import CheckIcon from '~public/icons/Check.svg'
|
||||||
|
|
@ -25,18 +23,6 @@ import './index.scss'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { formatTimeAgo } from '~utils/timeAgo'
|
import { formatTimeAgo } from '~utils/timeAgo'
|
||||||
|
|
||||||
const emptyRaid: Raid = {
|
|
||||||
id: '',
|
|
||||||
name: {
|
|
||||||
en: '',
|
|
||||||
ja: '',
|
|
||||||
},
|
|
||||||
slug: '',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
editable: boolean
|
editable: boolean
|
||||||
|
|
@ -48,7 +34,6 @@ interface Props {
|
||||||
|
|
||||||
const PartyDetails = (props: Props) => {
|
const PartyDetails = (props: Props) => {
|
||||||
const { party, raids } = useSnapshot(appState)
|
const { party, raids } = useSnapshot(appState)
|
||||||
const { account } = useSnapshot(accountState)
|
|
||||||
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -56,7 +41,8 @@ const PartyDetails = (props: Props) => {
|
||||||
|
|
||||||
const nameInput = React.createRef<HTMLInputElement>()
|
const nameInput = React.createRef<HTMLInputElement>()
|
||||||
const descriptionInput = React.createRef<HTMLTextAreaElement>()
|
const descriptionInput = React.createRef<HTMLTextAreaElement>()
|
||||||
const raidSelect = React.createRef<HTMLSelectElement>()
|
|
||||||
|
const [raidSlug, setRaidSlug] = useState('')
|
||||||
|
|
||||||
const readOnlyClasses = classNames({
|
const readOnlyClasses = classNames({
|
||||||
PartyDetails: true,
|
PartyDetails: true,
|
||||||
|
|
@ -116,10 +102,14 @@ const PartyDetails = (props: Props) => {
|
||||||
appState.party.detailsVisible = !appState.party.detailsVisible
|
appState.party.detailsVisible = !appState.party.detailsVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function receiveRaid(slug?: string) {
|
||||||
|
if (slug) setRaidSlug(slug)
|
||||||
|
}
|
||||||
|
|
||||||
function updateDetails(event: React.MouseEvent) {
|
function updateDetails(event: React.MouseEvent) {
|
||||||
const nameValue = nameInput.current?.value
|
const nameValue = nameInput.current?.value
|
||||||
const descriptionValue = descriptionInput.current?.value
|
const descriptionValue = descriptionInput.current?.value
|
||||||
const raid = raids.find((raid) => raid.slug === raidSelect.current?.value)
|
const raid = raids.find((raid) => raid.slug === raidSlug)
|
||||||
|
|
||||||
props.updateCallback(nameValue, descriptionValue, raid)
|
props.updateCallback(nameValue, descriptionValue, raid)
|
||||||
toggleDetails()
|
toggleDetails()
|
||||||
|
|
@ -129,11 +119,11 @@ const PartyDetails = (props: Props) => {
|
||||||
if (party.user)
|
if (party.user)
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
alt={party.user.picture.picture}
|
alt={party.user.avatar.picture}
|
||||||
className={`profile ${party.user.picture.element}`}
|
className={`profile ${party.user.avatar.element}`}
|
||||||
srcSet={`/profile/${party.user.picture.picture}.png,
|
srcSet={`/profile/${party.user.avatar.picture}.png,
|
||||||
/profile/${party.user.picture.picture}@2x.png 2x`}
|
/profile/${party.user.avatar.picture}@2x.png 2x`}
|
||||||
src={`/profile/${party.user.picture.picture}.png`}
|
src={`/profile/${party.user.avatar.picture}.png`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
else return <div className="no-user" />
|
else return <div className="no-user" />
|
||||||
|
|
@ -220,8 +210,8 @@ const PartyDetails = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
<RaidDropdown
|
<RaidDropdown
|
||||||
showAllRaidsOption={false}
|
showAllRaidsOption={false}
|
||||||
currentRaid={party.raid?.slug || ''}
|
currentRaid={party.raid ? party.raid.slug : undefined}
|
||||||
ref={raidSelect}
|
onChange={receiveRaid}
|
||||||
/>
|
/>
|
||||||
<TextFieldset
|
<TextFieldset
|
||||||
fieldName="name"
|
fieldName="name"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import SelectItem from '~components/SelectItem'
|
||||||
import SelectGroup from '~components/SelectGroup'
|
import SelectGroup from '~components/SelectGroup'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
import { raidGroups } from '~utils/raidGroups'
|
import { raidGroups } from '~utils/raidGroups'
|
||||||
|
|
||||||
|
|
@ -20,6 +21,19 @@ interface Props {
|
||||||
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up empty raid for "All raids"
|
||||||
|
const allRaidsOption = {
|
||||||
|
id: '0',
|
||||||
|
name: {
|
||||||
|
en: 'All raids',
|
||||||
|
ja: '全て',
|
||||||
|
},
|
||||||
|
slug: 'all',
|
||||||
|
level: 0,
|
||||||
|
group: 0,
|
||||||
|
element: 0,
|
||||||
|
}
|
||||||
|
|
||||||
const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
function useFieldSet(props, ref) {
|
function useFieldSet(props, ref) {
|
||||||
// Set up router for locale
|
// Set up router for locale
|
||||||
|
|
@ -28,7 +42,7 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
|
|
||||||
// Set up local states for storing raids
|
// Set up local states for storing raids
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [currentRaid, setCurrentRaid] = useState<Raid>()
|
const [currentRaid, setCurrentRaid] = useState<Raid | undefined>(undefined)
|
||||||
const [raids, setRaids] = useState<Raid[]>()
|
const [raids, setRaids] = useState<Raid[]>()
|
||||||
const [sortedRaids, setSortedRaids] = useState<Raid[][]>()
|
const [sortedRaids, setSortedRaids] = useState<Raid[][]>()
|
||||||
|
|
||||||
|
|
@ -37,38 +51,17 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organize raids into groups on mount
|
// Organize raids into groups on mount
|
||||||
const organizeRaids = useCallback(
|
const organizeAllRaids = useCallback(
|
||||||
(raids: Raid[]) => {
|
(raids: Raid[]) => {
|
||||||
// Set up empty raid for "All raids"
|
let { sortedRaids } = organizeRaids(raids)
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.showAllRaidsOption) {
|
if (props.showAllRaidsOption) {
|
||||||
raids.unshift(all)
|
raids.unshift(allRaidsOption)
|
||||||
groupedRaids[0].unshift(all)
|
sortedRaids[0].unshift(allRaidsOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
setRaids(raids)
|
setRaids(raids)
|
||||||
setSortedRaids(groupedRaids)
|
setSortedRaids(sortedRaids)
|
||||||
appState.raids = raids
|
appState.raids = raids
|
||||||
},
|
},
|
||||||
[props.showAllRaidsOption]
|
[props.showAllRaidsOption]
|
||||||
|
|
@ -78,22 +71,19 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.endpoints.raids
|
api.endpoints.raids
|
||||||
.getAll()
|
.getAll()
|
||||||
.then((response) =>
|
.then((response) => organizeAllRaids(response.data))
|
||||||
organizeRaids(response.data.map((r: any) => r.raid))
|
|
||||||
)
|
|
||||||
}, [organizeRaids])
|
}, [organizeRaids])
|
||||||
|
|
||||||
// Set current raid on mount
|
// Set current raid on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (raids && props.currentRaid) {
|
if (raids && props.currentRaid) {
|
||||||
const raid = raids.find((raid) => raid.slug === props.currentRaid)
|
const raid = raids.find((raid) => raid.slug === props.currentRaid)
|
||||||
setCurrentRaid(raid)
|
if (raid) setCurrentRaid(raid)
|
||||||
}
|
}
|
||||||
}, [raids, props.currentRaid])
|
}, [raids, props.currentRaid])
|
||||||
|
|
||||||
// Enable changing select value
|
// Enable changing select value
|
||||||
function handleChange(value: string) {
|
function handleChange(value: string) {
|
||||||
console.log(value)
|
|
||||||
if (props.onChange) props.onChange(value)
|
if (props.onChange) props.onChange(value)
|
||||||
|
|
||||||
if (raids) {
|
if (raids) {
|
||||||
|
|
@ -110,13 +100,12 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
sortedRaids[index].length > 0 &&
|
sortedRaids[index].length > 0 &&
|
||||||
sortedRaids[index]
|
sortedRaids[index]
|
||||||
.sort((a, b) => a.element - b.element)
|
.sort((a, b) => a.element - b.element)
|
||||||
.map((item, i) => {
|
.map((item, i) => (
|
||||||
return (
|
<SelectItem key={i} value={item.slug}>
|
||||||
<SelectItem key={i} value={item.slug}>
|
{item.name[locale]}
|
||||||
{item.name[locale]}
|
</SelectItem>
|
||||||
</SelectItem>
|
))
|
||||||
)
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<SelectGroup
|
<SelectGroup
|
||||||
key={index}
|
key={index}
|
||||||
|
|
@ -129,17 +118,19 @@ const RaidDropdown = React.forwardRef<HTMLSelectElement, Props>(
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<React.Fragment>
|
||||||
defaultValue={props.defaultRaid}
|
<Select
|
||||||
placeholder={'Select a raid...'}
|
value={props.currentRaid}
|
||||||
open={open}
|
placeholder={'Select a raid...'}
|
||||||
onClick={openRaidSelect}
|
open={open}
|
||||||
onChange={handleChange}
|
onClick={openRaidSelect}
|
||||||
>
|
onValueChange={handleChange}
|
||||||
{Array.from(Array(sortedRaids?.length)).map((x, i) =>
|
>
|
||||||
renderRaidGroup(i)
|
{Array.from(Array(sortedRaids?.length)).map((x, i) =>
|
||||||
)}
|
renderRaidGroup(i)
|
||||||
</Select>
|
)}
|
||||||
|
</Select>
|
||||||
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -85,11 +85,11 @@ const SearchModal = (props: Props) => {
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setTotalPages(response.data.total_pages)
|
setTotalPages(response.data.meta.total_pages)
|
||||||
setRecordCount(response.data.count)
|
setRecordCount(response.data.meta.count)
|
||||||
|
|
||||||
if (replace) {
|
if (replace) {
|
||||||
replaceResults(response.data.count, response.data.results)
|
replaceResults(response.data.meta.count, response.data.results)
|
||||||
} else {
|
} else {
|
||||||
appendResults(response.data.results)
|
appendResults(response.data.results)
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +330,7 @@ const SearchModal = (props: Props) => {
|
||||||
<div id="Bar">
|
<div id="Bar">
|
||||||
<Input
|
<Input
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="Search"
|
className="Search Bound"
|
||||||
name="query"
|
name="query"
|
||||||
placeholder={props.placeholderText}
|
placeholder={props.placeholderText}
|
||||||
ref={searchInput}
|
ref={searchInput}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,14 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Bound {
|
||||||
|
background-color: var(--select-contained-bg);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--select-contained-bg-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.SelectIcon {
|
.SelectIcon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import * as RadixSelect from '@radix-ui/react-select'
|
import * as RadixSelect from '@radix-ui/react-select'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -7,31 +7,41 @@ import ArrowIcon from '~public/icons/Arrow.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.SelectHTMLAttributes<HTMLSelectElement>,
|
||||||
|
HTMLSelectElement
|
||||||
|
> {
|
||||||
open: boolean
|
open: boolean
|
||||||
defaultValue?: string | number
|
trigger?: React.ReactNode
|
||||||
placeholder?: string
|
|
||||||
name?: string
|
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
onChange?: (value: string) => void
|
onValueChange?: (value: string) => void
|
||||||
triggerClass?: string
|
triggerClass?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const Select = React.forwardRef<HTMLButtonElement, Props>(function useFieldSet(
|
const Select = (props: Props) => {
|
||||||
props,
|
const [value, setValue] = useState('')
|
||||||
ref
|
|
||||||
) {
|
useEffect(() => {
|
||||||
|
if (props.value && props.value !== '') setValue(`${props.value}`)
|
||||||
|
else setValue('')
|
||||||
|
}, [props.value])
|
||||||
|
|
||||||
|
function onValueChange(newValue: string) {
|
||||||
|
setValue(`${newValue}`)
|
||||||
|
if (props.onValueChange) props.onValueChange(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(value)
|
||||||
return (
|
return (
|
||||||
<RadixSelect.Root
|
<RadixSelect.Root
|
||||||
defaultValue={props.defaultValue as string}
|
value={value !== '' ? value : undefined}
|
||||||
name={props.name}
|
onValueChange={onValueChange}
|
||||||
onValueChange={props.onChange}
|
|
||||||
>
|
>
|
||||||
<RadixSelect.Trigger
|
<RadixSelect.Trigger
|
||||||
className={classNames('SelectTrigger', props.triggerClass)}
|
className={classNames('SelectTrigger', props.triggerClass)}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
ref={ref}
|
|
||||||
>
|
>
|
||||||
<RadixSelect.Value placeholder={props.placeholder} />
|
<RadixSelect.Value placeholder={props.placeholder} />
|
||||||
<RadixSelect.Icon className="SelectIcon">
|
<RadixSelect.Icon className="SelectIcon">
|
||||||
|
|
@ -52,6 +62,6 @@ const Select = React.forwardRef<HTMLButtonElement, Props>(function useFieldSet(
|
||||||
</RadixSelect.Portal>
|
</RadixSelect.Portal>
|
||||||
</RadixSelect.Root>
|
</RadixSelect.Root>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
export default Select
|
export default Select
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,17 @@ import { useRouter } from 'next/router'
|
||||||
import { Trans, useTranslation } from 'next-i18next'
|
import { Trans, useTranslation } from 'next-i18next'
|
||||||
import { AxiosResponse } from 'axios'
|
import { AxiosResponse } from 'axios'
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog'
|
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
import { accountState } from '~utils/accountState'
|
import { accountState } from '~utils/accountState'
|
||||||
|
|
||||||
import Button from '~components/Button'
|
import Button from '~components/Button'
|
||||||
import Input from '~components/Input'
|
import Input from '~components/Input'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogClose,
|
||||||
|
} from '~components/Dialog'
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
@ -75,19 +79,19 @@ const SignupModal = (props: Props) => {
|
||||||
.create(body)
|
.create(body)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
storeCookieInfo(response)
|
storeCookieInfo(response)
|
||||||
return response.data.user.user_id
|
return response.data.id
|
||||||
})
|
})
|
||||||
.then((id) => fetchUserInfo(id))
|
.then((id) => fetchUserInfo(id))
|
||||||
.then((infoResponse) => storeUserInfo(infoResponse))
|
.then((infoResponse) => storeUserInfo(infoResponse))
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeCookieInfo(response: AxiosResponse) {
|
function storeCookieInfo(response: AxiosResponse) {
|
||||||
const user = response.data.user
|
const resp = response.data
|
||||||
|
|
||||||
const cookieObj: AccountCookie = {
|
const cookieObj: AccountCookie = {
|
||||||
userId: user.user_id,
|
userId: resp.id,
|
||||||
username: user.username,
|
username: resp.username,
|
||||||
token: user.token,
|
token: resp.token,
|
||||||
}
|
}
|
||||||
|
|
||||||
setCookie('account', cookieObj, { path: '/' })
|
setCookie('account', cookieObj, { path: '/' })
|
||||||
|
|
@ -98,11 +102,11 @@ const SignupModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeUserInfo(response: AxiosResponse) {
|
function storeUserInfo(response: AxiosResponse) {
|
||||||
const user = response.data.user
|
const user = response.data
|
||||||
|
|
||||||
const cookieObj: UserCookie = {
|
const cookieObj: UserCookie = {
|
||||||
picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.picture.element,
|
element: user.avatar.element,
|
||||||
language: user.language,
|
language: user.language,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
}
|
}
|
||||||
|
|
@ -113,8 +117,8 @@ const SignupModal = (props: Props) => {
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
picture: user.picture.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.picture.element,
|
element: user.avatar.element,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,73 +255,67 @@ const SignupModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
<Dialog open={open} onOpenChange={openChange}>
|
||||||
<Dialog.Trigger asChild>
|
<DialogTrigger asChild>
|
||||||
<li className="MenuItem">
|
<li className="MenuItem">
|
||||||
<span>{t('menu.signup')}</span>
|
<span>{t('menu.signup')}</span>
|
||||||
</li>
|
</li>
|
||||||
</Dialog.Trigger>
|
</DialogTrigger>
|
||||||
<Dialog.Portal>
|
<DialogContent className="Signup Dialog">
|
||||||
<Dialog.Content
|
<div className="DialogHeader">
|
||||||
className="Signup Dialog"
|
<div className="DialogTitle">
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
<h1>{t('modals.signup.title')}</h1>
|
||||||
>
|
|
||||||
<div className="DialogHeader">
|
|
||||||
<Dialog.Title className="DialogTitle">
|
|
||||||
{t('modals.signup.title')}
|
|
||||||
</Dialog.Title>
|
|
||||||
<Dialog.Close className="DialogClose" asChild>
|
|
||||||
<span>
|
|
||||||
<CrossIcon />
|
|
||||||
</span>
|
|
||||||
</Dialog.Close>
|
|
||||||
</div>
|
</div>
|
||||||
|
<DialogClose className="DialogClose">
|
||||||
|
<CrossIcon />
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form className="form" onSubmit={register}>
|
<form className="form" onSubmit={register}>
|
||||||
<Input
|
<Input
|
||||||
name="username"
|
name="username"
|
||||||
placeholder={t('modals.signup.placeholders.username')}
|
placeholder={t('modals.signup.placeholders.username')}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
error={errors.username}
|
error={errors.username}
|
||||||
ref={usernameInput}
|
ref={usernameInput}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="email"
|
name="email"
|
||||||
placeholder={t('modals.signup.placeholders.email')}
|
placeholder={t('modals.signup.placeholders.email')}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
error={errors.email}
|
error={errors.email}
|
||||||
ref={emailInput}
|
ref={emailInput}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="password"
|
name="password"
|
||||||
placeholder={t('modals.signup.placeholders.password')}
|
placeholder={t('modals.signup.placeholders.password')}
|
||||||
onChange={handlePasswordChange}
|
type="password"
|
||||||
error={errors.password}
|
onChange={handlePasswordChange}
|
||||||
ref={passwordInput}
|
error={errors.password}
|
||||||
/>
|
ref={passwordInput}
|
||||||
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="confirm_password"
|
name="confirm_password"
|
||||||
placeholder={t('modals.signup.placeholders.password_confirm')}
|
placeholder={t('modals.signup.placeholders.password_confirm')}
|
||||||
onChange={handlePasswordChange}
|
type="password"
|
||||||
error={errors.passwordConfirmation}
|
onChange={handlePasswordChange}
|
||||||
ref={passwordConfirmationInput}
|
error={errors.passwordConfirmation}
|
||||||
/>
|
ref={passwordConfirmationInput}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button>{t('modals.signup.buttons.confirm')}</Button>
|
<Button text={t('modals.signup.buttons.confirm')} />
|
||||||
|
|
||||||
<Dialog.Description className="terms">
|
<p className="terms">
|
||||||
{/* <Trans i18nKey="modals.signup.agreement">
|
{/* <Trans i18nKey="modals.signup.agreement">
|
||||||
By signing up, I agree to the <Link href="/privacy"><span>Privacy Policy</span></Link><Link href="/usage"><span>Usage Guidelines</span></Link>.
|
By signing up, I agree to the <Link href="/privacy"><span>Privacy Policy</span></Link><Link href="/usage"><span>Usage Guidelines</span></Link>.
|
||||||
</Trans> */}
|
</Trans> */}
|
||||||
</Dialog.Description>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</Dialog.Content>
|
</DialogContent>
|
||||||
<Dialog.Overlay className="Overlay" />
|
</Dialog>
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Root>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,13 +96,13 @@ const SummonGrid = (props: Props) => {
|
||||||
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
|
|
||||||
saveSummon(party.id, summon, position).then((response) =>
|
saveSummon(party.id, summon, position).then((response) =>
|
||||||
storeGridSummon(response.data.grid_summon)
|
storeGridSummon(response.data)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if (party.editable)
|
if (party.editable)
|
||||||
saveSummon(party.id, summon, position).then((response) =>
|
saveSummon(party.id, summon, position).then((response) =>
|
||||||
storeGridSummon(response.data.grid_summon)
|
storeGridSummon(response.data)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
gap: $unit-half;
|
gap: $unit-half;
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
color: var(--text-secondary);
|
color: var(--text-tertiary);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $font-medium;
|
font-size: $font-medium;
|
||||||
font-weight: $medium;
|
font-weight: $medium;
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,12 @@ const WeaponGrid = (props: Props) => {
|
||||||
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
|
|
||||||
saveWeapon(party.id, weapon, position).then((response) =>
|
saveWeapon(party.id, weapon, position).then((response) =>
|
||||||
storeGridWeapon(response.data.grid_weapon)
|
storeGridWeapon(response.data)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
saveWeapon(party.id, weapon, position).then((response) =>
|
saveWeapon(party.id, weapon, position).then((response) =>
|
||||||
storeGridWeapon(response.data.grid_weapon)
|
storeGridWeapon(response.data)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
gap: $unit-half;
|
gap: $unit-half;
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
color: var(--text-secondary);
|
color: var(--text-tertiary);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $font-medium;
|
font-size: $font-medium;
|
||||||
font-weight: $medium;
|
font-weight: $medium;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
import { getCookie } from 'cookies-next'
|
|
||||||
import { queryTypes, useQueryState } from 'next-usequerystate'
|
import { queryTypes, useQueryState } from 'next-usequerystate'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -10,32 +9,29 @@ import InfiniteScroll from 'react-infinite-scroll-component'
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
|
import extractFilters from '~utils/extractFilters'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
import useDidMountEffect from '~utils/useDidMountEffect'
|
import useDidMountEffect from '~utils/useDidMountEffect'
|
||||||
import { elements, allElement } from '~utils/Element'
|
import { elements, allElement } from '~utils/Element'
|
||||||
|
import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
|
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/GridRepCollection'
|
||||||
import FilterBar from '~components/FilterBar'
|
import FilterBar from '~components/FilterBar'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
import type { FilterObject, PaginationObject } from '~types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user?: User
|
user?: User
|
||||||
teams?: { count: number; total_pages: number; results: Party[] }
|
teams?: Party[]
|
||||||
|
meta: PaginationObject
|
||||||
raids: Raid[]
|
raids: Raid[]
|
||||||
sortedRaids: Raid[][]
|
sortedRaids: Raid[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProfileRoute: React.FC<Props> = (props: Props) => {
|
const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set up cookies
|
|
||||||
const cookie = getCookie('account')
|
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
const headers = accountData
|
|
||||||
? { Authorization: `Bearer ${accountData.token}` }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
// Set up router
|
// Set up router
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { username } = router.query
|
const { username } = router.query
|
||||||
|
|
@ -61,16 +57,20 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Recency is in seconds
|
// Recency is in seconds
|
||||||
const [element, setElement] = useQueryState('element', {
|
const [element, setElement] = useQueryState('element', {
|
||||||
defaultValue: -1,
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
parse: (query: string) => parseElement(query),
|
parse: (query: string) => parseElement(query),
|
||||||
serialize: (value) => serializeElement(value),
|
serialize: (value) => serializeElement(value),
|
||||||
})
|
})
|
||||||
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
||||||
defaultValue: 'all',
|
defaultValue: 'all',
|
||||||
|
history: 'push',
|
||||||
|
})
|
||||||
|
const [recency, setRecency] = useQueryState('recency', {
|
||||||
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
|
parse: (query: string) => parseInt(query),
|
||||||
|
serialize: (value) => `${value}`,
|
||||||
})
|
})
|
||||||
const [recency, setRecency] = useQueryState(
|
|
||||||
'recency',
|
|
||||||
queryTypes.integer.withDefault(-1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define transformers for element
|
// Define transformers for element
|
||||||
function parseElement(query: string) {
|
function parseElement(query: string) {
|
||||||
|
|
@ -95,9 +95,9 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set the initial parties from props
|
// Set the initial parties from props
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.teams) {
|
if (props.teams) {
|
||||||
setTotalPages(props.teams.total_pages)
|
setTotalPages(props.meta.totalPages)
|
||||||
setRecordCount(props.teams.count)
|
setRecordCount(props.meta.count)
|
||||||
replaceResults(props.teams.count, props.teams.results)
|
replaceResults(props.meta.count, props.teams)
|
||||||
}
|
}
|
||||||
setCurrentPage(1)
|
setCurrentPage(1)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -113,6 +113,7 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
if (error.response != null) {
|
if (error.response != null) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: Put an alert here
|
||||||
console.error('There was an error.')
|
console.error('There was an error.')
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -132,18 +133,17 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
api.endpoints.users
|
api.endpoints.users
|
||||||
.getOne({
|
.getOne({
|
||||||
id: username,
|
id: username,
|
||||||
params: { ...filters, ...{ headers: headers } },
|
params: { ...filters },
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setTotalPages(response.data.parties.total_pages)
|
const results = response.data.profile.parties
|
||||||
setRecordCount(response.data.parties.count)
|
const meta = response.data.meta
|
||||||
|
|
||||||
if (replace)
|
setTotalPages(meta.total_pages)
|
||||||
replaceResults(
|
setRecordCount(meta.count)
|
||||||
response.data.parties.count,
|
|
||||||
response.data.parties.results
|
if (replace) replaceResults(meta.count, results)
|
||||||
)
|
else appendResults(results)
|
||||||
else appendResults(response.data.parties.results)
|
|
||||||
})
|
})
|
||||||
.catch((error) => handleError(error))
|
.catch((error) => handleError(error))
|
||||||
}
|
}
|
||||||
|
|
@ -166,12 +166,11 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Fetch all raids on mount, then find the raid in the URL if present
|
// Fetch all raids on mount, then find the raid in the URL if present
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.endpoints.raids.getAll().then((response) => {
|
api.endpoints.raids.getAll().then((response) => {
|
||||||
const cleanRaids: Raid[] = response.data.map((r: any) => r.raid)
|
setRaids(response.data)
|
||||||
setRaids(cleanRaids)
|
|
||||||
|
|
||||||
setRaidsLoading(false)
|
setRaidsLoading(false)
|
||||||
|
|
||||||
const raid = cleanRaids.find((r) => r.slug === raidSlug)
|
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
|
|
||||||
return raid
|
return raid
|
||||||
|
|
@ -202,16 +201,16 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
raidSlug?: string
|
raidSlug?: string
|
||||||
recency?: number
|
recency?: number
|
||||||
}) {
|
}) {
|
||||||
if (element == 0) setElement(0)
|
if (element == 0) setElement(0, { shallow: true })
|
||||||
else if (element) setElement(element)
|
else if (element) setElement(element, { shallow: true })
|
||||||
|
|
||||||
if (raids && raidSlug) {
|
if (raids && raidSlug) {
|
||||||
const raid = raids.find((raid) => raid.slug === raidSlug)
|
const raid = raids.find((raid) => raid.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
setRaidSlug(raidSlug)
|
setRaidSlug(raidSlug, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recency) setRecency(recency)
|
if (recency) setRecency(recency, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Navigation
|
// Methods: Navigation
|
||||||
|
|
@ -283,11 +282,11 @@ const ProfileRoute: React.FC<Props> = (props: Props) => {
|
||||||
>
|
>
|
||||||
<div className="UserInfo">
|
<div className="UserInfo">
|
||||||
<img
|
<img
|
||||||
alt={props.user?.picture.picture}
|
alt={props.user?.avatar.picture}
|
||||||
className={`profile ${props.user?.picture.element}`}
|
className={`profile ${props.user?.avatar.element}`}
|
||||||
srcSet={`/profile/${props.user?.picture.picture}.png,
|
srcSet={`/profile/${props.user?.avatar.picture}.png,
|
||||||
/profile/${props.user?.picture.picture}@2x.png 2x`}
|
/profile/${props.user?.avatar.picture}@2x.png 2x`}
|
||||||
src={`/profile/${props.user?.picture.picture}.png`}
|
src={`/profile/${props.user?.avatar.picture}.png`}
|
||||||
/>
|
/>
|
||||||
<h1>{props.user?.username}</h1>
|
<h1>{props.user?.username}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -331,103 +330,54 @@ export const getServerSidePaths = async () => {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
||||||
// Cookies
|
// Set headers for server-side requests
|
||||||
const cookie = getCookie("account", { req, res })
|
setUserToken(req, res)
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
|
// Fetch and organize raids
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data.map((r: any) => r.raid)))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
// Extract recency filter
|
|
||||||
const recencyParam: number = parseInt(query.recency)
|
|
||||||
|
|
||||||
// Extract element filter
|
|
||||||
const elementParam: string = query.element
|
|
||||||
const teamElement: TeamElement | undefined =
|
|
||||||
elementParam === "all"
|
|
||||||
? allElement
|
|
||||||
: elements.find(
|
|
||||||
(element) => element.name.en.toLowerCase() === elementParam
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extract raid filter
|
|
||||||
const raidParam: string = query.raid
|
|
||||||
const raid: Raid | undefined = raids.find((r) => r.slug === raidParam)
|
|
||||||
|
|
||||||
// Create filter object
|
// Create filter object
|
||||||
const filters: {
|
const filters: FilterObject = extractFilters(query, raids)
|
||||||
raid?: string
|
const params = {
|
||||||
element?: number
|
params: { ...filters },
|
||||||
recency?: number
|
}
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (recencyParam) filters.recency = recencyParam
|
// Set up empty variables
|
||||||
if (teamElement && teamElement.id > -1) filters.element = teamElement.id
|
|
||||||
if (raid) filters.raid = raid.id
|
|
||||||
|
|
||||||
// Fetch initial set of parties here
|
|
||||||
let user: User | null = null
|
let user: User | null = null
|
||||||
let teams: Party[] | null = null
|
let teams: Party[] | null = null
|
||||||
|
let meta: PaginationObject = emptyPaginationObject
|
||||||
|
|
||||||
|
// Perform a request only if we received a username
|
||||||
if (query.username) {
|
if (query.username) {
|
||||||
const response = await api.endpoints.users.getOne({
|
const response = await api.endpoints.users.getOne({
|
||||||
id: query.username,
|
id: query.username,
|
||||||
params: {
|
params,
|
||||||
...filters,
|
|
||||||
},
|
|
||||||
...headers,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
user = response.data.user
|
// Assign values to pass to props
|
||||||
teams = response.data.parties
|
user = response.data.profile
|
||||||
|
|
||||||
|
if (response.data.profile.parties) teams = response.data.profile.parties
|
||||||
|
else teams = []
|
||||||
|
|
||||||
|
meta.count = response.data.meta.count
|
||||||
|
meta.totalPages = response.data.meta.total_pages
|
||||||
|
meta.perPage = response.data.meta.per_page
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
user: user,
|
user: user,
|
||||||
teams: teams,
|
teams: teams,
|
||||||
|
meta: meta,
|
||||||
raids: raids,
|
raids: raids,
|
||||||
sortedRaids: sortedRaids,
|
sortedRaids: sortedRaids,
|
||||||
...(await serverSideTranslations(locale, ["common"])),
|
...(await serverSideTranslations(locale, ['common'])),
|
||||||
// Will be passed to the page component as props
|
// Will be passed to the page component as props
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizeRaids = (raids: Raid[]) => {
|
|
||||||
// Set up empty raid for "All raids"
|
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
raids: raids,
|
|
||||||
sortedRaids: groupedRaids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ProfileRoute
|
export default ProfileRoute
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import type { AppProps } from 'next/app'
|
||||||
import Layout from '~components/Layout'
|
import Layout from '~components/Layout'
|
||||||
|
|
||||||
import { accountState } from '~utils/accountState'
|
import { accountState } from '~utils/accountState'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
|
|
||||||
import '../styles/globals.scss'
|
import '../styles/globals.scss'
|
||||||
|
|
||||||
|
|
@ -15,6 +16,8 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
const cookieData: AccountCookie = cookie ? JSON.parse(cookie as string) : null
|
const cookieData: AccountCookie = cookie ? JSON.parse(cookie as string) : null
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setUserToken()
|
||||||
|
|
||||||
if (cookie) {
|
if (cookie) {
|
||||||
console.log(`Logged in as user "${cookieData.username}"`)
|
console.log(`Logged in as user "${cookieData.username}"`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { getCookie } from 'cookies-next'
|
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
|
|
||||||
import Party from '~components/Party'
|
import Party from '~components/Party'
|
||||||
|
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
@ -47,67 +48,33 @@ export const getServerSidePaths = async () => {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
||||||
// Cookies
|
// Set headers for server-side requests
|
||||||
const cookie = getCookie("account", { req, res })
|
setUserToken(req, res)
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data.map((r: any) => r.raid)))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
let jobs = await api.endpoints.jobs
|
let jobs = await api.endpoints.jobs
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => { return response.data })
|
.then((response) => {
|
||||||
|
return response.data
|
||||||
let jobSkills = await api.allSkills(headers)
|
})
|
||||||
.then((response) => { return response.data })
|
|
||||||
|
let jobSkills = await api.allJobSkills().then((response) => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
jobs: jobs,
|
jobs: jobs,
|
||||||
jobSkills: jobSkills,
|
jobSkills: jobSkills,
|
||||||
raids: raids,
|
raids: raids,
|
||||||
sortedRaids: sortedRaids,
|
sortedRaids: sortedRaids,
|
||||||
...(await serverSideTranslations(locale, ["common"])),
|
...(await serverSideTranslations(locale, ['common'])),
|
||||||
// Will be passed to the page component as props
|
// Will be passed to the page component as props
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizeRaids = (raids: Raid[]) => {
|
|
||||||
// Set up empty raid for "All raids"
|
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
raids: raids,
|
|
||||||
sortedRaids: groupedRaids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewRoute
|
export default NewRoute
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
import Party from '~components/Party'
|
import Party from '~components/Party'
|
||||||
|
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
@ -55,7 +56,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
|
||||||
|
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll()
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data.map((r: any) => r.raid)))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
let jobs = await api.endpoints.jobs
|
let jobs = await api.endpoints.jobs
|
||||||
.getAll({ params: headers })
|
.getAll({ params: headers })
|
||||||
|
|
@ -63,9 +64,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
|
||||||
return response.data
|
return response.data
|
||||||
})
|
})
|
||||||
|
|
||||||
let jobSkills = await api.allSkills(headers).then((response) => {
|
let jobSkills = await api.allJobSkills(headers).then((response) => response.data)
|
||||||
return response.data
|
|
||||||
})
|
|
||||||
|
|
||||||
let party: Party | null = null
|
let party: Party | null = null
|
||||||
if (query.party) {
|
if (query.party) {
|
||||||
|
|
@ -88,34 +87,4 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizeRaids = (raids: Raid[]) => {
|
|
||||||
// Set up empty raid for "All raids"
|
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
raids: raids,
|
|
||||||
sortedRaids: groupedRaids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PartyRoute
|
export default PartyRoute
|
||||||
|
|
|
||||||
184
pages/saved.tsx
184
pages/saved.tsx
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
import { getCookie } from 'cookies-next'
|
|
||||||
import { queryTypes, useQueryState } from 'next-usequerystate'
|
import { queryTypes, useQueryState } from 'next-usequerystate'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -11,31 +10,28 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
import clonedeep from 'lodash.clonedeep'
|
import clonedeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
|
import extractFilters from '~utils/extractFilters'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
import useDidMountEffect from '~utils/useDidMountEffect'
|
import useDidMountEffect from '~utils/useDidMountEffect'
|
||||||
import { elements, allElement } from '~utils/Element'
|
import { elements, allElement } from '~utils/Element'
|
||||||
|
import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
|
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/GridRepCollection'
|
||||||
import FilterBar from '~components/FilterBar'
|
import FilterBar from '~components/FilterBar'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
import type { FilterObject, PaginationObject } from '~types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
teams?: { count: number; total_pages: number; results: Party[] }
|
teams?: Party[]
|
||||||
|
meta: PaginationObject
|
||||||
raids: Raid[]
|
raids: Raid[]
|
||||||
sortedRaids: Raid[][]
|
sortedRaids: Raid[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
const SavedRoute: React.FC<Props> = (props: Props) => {
|
const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set up cookies
|
|
||||||
const cookie = getCookie('account')
|
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
const headers = accountData
|
|
||||||
? { Authorization: `Bearer ${accountData.token}` }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
// Set up router
|
// Set up router
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
@ -60,16 +56,20 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Recency is in seconds
|
// Recency is in seconds
|
||||||
const [element, setElement] = useQueryState('element', {
|
const [element, setElement] = useQueryState('element', {
|
||||||
defaultValue: -1,
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
parse: (query: string) => parseElement(query),
|
parse: (query: string) => parseElement(query),
|
||||||
serialize: (value) => serializeElement(value),
|
serialize: (value) => serializeElement(value),
|
||||||
})
|
})
|
||||||
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
||||||
defaultValue: 'all',
|
defaultValue: 'all',
|
||||||
|
history: 'push',
|
||||||
|
})
|
||||||
|
const [recency, setRecency] = useQueryState('recency', {
|
||||||
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
|
parse: (query: string) => parseInt(query),
|
||||||
|
serialize: (value) => `${value}`,
|
||||||
})
|
})
|
||||||
const [recency, setRecency] = useQueryState(
|
|
||||||
'recency',
|
|
||||||
queryTypes.integer.withDefault(-1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define transformers for element
|
// Define transformers for element
|
||||||
function parseElement(query: string) {
|
function parseElement(query: string) {
|
||||||
|
|
@ -94,9 +94,9 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set the initial parties from props
|
// Set the initial parties from props
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.teams) {
|
if (props.teams) {
|
||||||
setTotalPages(props.teams.total_pages)
|
setTotalPages(props.meta.totalPages)
|
||||||
setRecordCount(props.teams.count)
|
setRecordCount(props.meta.count)
|
||||||
replaceResults(props.teams.count, props.teams.results)
|
replaceResults(props.meta.count, props.teams)
|
||||||
}
|
}
|
||||||
setCurrentPage(1)
|
setCurrentPage(1)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -118,24 +118,36 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
const fetchTeams = useCallback(
|
const fetchTeams = useCallback(
|
||||||
({ replace }: { replace: boolean }) => {
|
({ replace }: { replace: boolean }) => {
|
||||||
const filters = {
|
const filters: {
|
||||||
|
[key: string]: any
|
||||||
|
} = {
|
||||||
|
element: element !== -1 ? element : undefined,
|
||||||
|
raid: raid ? raid.id : undefined,
|
||||||
|
recency: recency !== -1 ? recency : undefined,
|
||||||
|
page: currentPage,
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(filters).forEach(
|
||||||
|
(key) => filters[key] === undefined && delete filters[key]
|
||||||
|
)
|
||||||
|
|
||||||
|
const params = {
|
||||||
params: {
|
params: {
|
||||||
element: element != -1 ? element : undefined,
|
...filters,
|
||||||
raid: raid ? raid.id : undefined,
|
|
||||||
recency: recency != -1 ? recency : undefined,
|
|
||||||
page: currentPage,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
api
|
api
|
||||||
.savedTeams({ ...filters, ...{ headers: headers } })
|
.savedTeams(params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setTotalPages(response.data.total_pages)
|
const results = response.data.results
|
||||||
setRecordCount(response.data.count)
|
const meta = response.data.meta
|
||||||
|
|
||||||
if (replace)
|
setTotalPages(meta.total_pages)
|
||||||
replaceResults(response.data.count, response.data.results)
|
setRecordCount(meta.count)
|
||||||
else appendResults(response.data.results)
|
|
||||||
|
if (replace) replaceResults(meta.count, results)
|
||||||
|
else appendResults(results)
|
||||||
})
|
})
|
||||||
.catch((error) => handleError(error))
|
.catch((error) => handleError(error))
|
||||||
},
|
},
|
||||||
|
|
@ -157,12 +169,11 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Fetch all raids on mount, then find the raid in the URL if present
|
// Fetch all raids on mount, then find the raid in the URL if present
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.endpoints.raids.getAll().then((response) => {
|
api.endpoints.raids.getAll().then((response) => {
|
||||||
const cleanRaids: Raid[] = response.data.map((r: any) => r.raid)
|
setRaids(response.data)
|
||||||
setRaids(cleanRaids)
|
|
||||||
|
|
||||||
setRaidsLoading(false)
|
setRaidsLoading(false)
|
||||||
|
|
||||||
const raid = cleanRaids.find((r) => r.slug === raidSlug)
|
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
|
|
||||||
return raid
|
return raid
|
||||||
|
|
@ -193,16 +204,16 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
raidSlug?: string
|
raidSlug?: string
|
||||||
recency?: number
|
recency?: number
|
||||||
}) {
|
}) {
|
||||||
if (element == 0) setElement(0)
|
if (element == 0) setElement(0, { shallow: true })
|
||||||
else if (element) setElement(element)
|
else if (element) setElement(element, { shallow: true })
|
||||||
|
|
||||||
if (raids && raidSlug) {
|
if (raids && raidSlug) {
|
||||||
const raid = raids.find((raid) => raid.slug === raidSlug)
|
const raid = raids.find((raid) => raid.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
setRaidSlug(raidSlug)
|
setRaidSlug(raidSlug, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recency) setRecency(recency)
|
if (recency) setRecency(recency, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Favorites
|
// Methods: Favorites
|
||||||
|
|
@ -212,7 +223,7 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFavorite(teamId: string) {
|
function saveFavorite(teamId: string) {
|
||||||
api.saveTeam({ id: teamId, params: headers }).then((response) => {
|
api.saveTeam({ id: teamId }).then((response) => {
|
||||||
if (response.status == 201) {
|
if (response.status == 201) {
|
||||||
const index = parties.findIndex((p) => p.id === teamId)
|
const index = parties.findIndex((p) => p.id === teamId)
|
||||||
const party = parties[index]
|
const party = parties[index]
|
||||||
|
|
@ -228,7 +239,7 @@ const SavedRoute: React.FC<Props> = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsaveFavorite(teamId: string) {
|
function unsaveFavorite(teamId: string) {
|
||||||
api.unsaveTeam({ id: teamId, params: headers }).then((response) => {
|
api.unsaveTeam({ id: teamId }).then((response) => {
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
const index = parties.findIndex((p) => p.id === teamId)
|
const index = parties.findIndex((p) => p.id === teamId)
|
||||||
const party = parties[index]
|
const party = parties[index]
|
||||||
|
|
@ -336,94 +347,43 @@ export const getServerSidePaths = async () => {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
||||||
// Cookies
|
// Set headers for server-side requests
|
||||||
const cookie = getCookie("account", { req, res })
|
setUserToken(req, res)
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = accountData
|
// Fetch and organize raids
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data.map((r: any) => r.raid)))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
// Extract recency filter
|
|
||||||
const recencyParam: number = parseInt(query.recency)
|
|
||||||
|
|
||||||
// Extract element filter
|
|
||||||
const elementParam: string = query.element
|
|
||||||
const teamElement: TeamElement | undefined =
|
|
||||||
elementParam === "all"
|
|
||||||
? allElement
|
|
||||||
: elements.find(
|
|
||||||
(element) => element.name.en.toLowerCase() === elementParam
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extract raid filter
|
|
||||||
const raidParam: string = query.raid
|
|
||||||
const raid: Raid | undefined = raids.find((r) => r.slug === raidParam)
|
|
||||||
|
|
||||||
// Create filter object
|
// Create filter object
|
||||||
const filters: {
|
const filters: FilterObject = extractFilters(query, raids)
|
||||||
raid?: string
|
const params = {
|
||||||
element?: number
|
params: { ...filters },
|
||||||
recency?: number
|
}
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (recencyParam) filters.recency = recencyParam
|
// Set up empty variables
|
||||||
if (teamElement && teamElement.id > -1) filters.element = teamElement.id
|
let teams: Party[] | null = null
|
||||||
if (raid) filters.raid = raid.id
|
let meta: PaginationObject = emptyPaginationObject
|
||||||
|
|
||||||
// Fetch initial set of parties here
|
// Fetch initial set of saved parties
|
||||||
const response = await api.savedTeams({
|
const response = await api.savedTeams(params)
|
||||||
params: {
|
|
||||||
...filters,
|
// Assign values to pass to props
|
||||||
},
|
teams = response.data.results
|
||||||
...headers
|
meta.count = response.data.meta.count
|
||||||
})
|
meta.totalPages = response.data.meta.total_pages
|
||||||
|
meta.perPage = response.data.meta.per_page
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
teams: response.data,
|
teams: teams,
|
||||||
|
meta: meta,
|
||||||
raids: raids,
|
raids: raids,
|
||||||
sortedRaids: sortedRaids,
|
sortedRaids: sortedRaids,
|
||||||
...(await serverSideTranslations(locale, ["common"])),
|
...(await serverSideTranslations(locale, ['common'])),
|
||||||
// Will be passed to the page component as props
|
// Will be passed to the page component as props
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizeRaids = (raids: Raid[]) => {
|
|
||||||
// Set up empty raid for "All raids"
|
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
raids: raids,
|
|
||||||
sortedRaids: groupedRaids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SavedRoute
|
export default SavedRoute
|
||||||
|
|
|
||||||
185
pages/teams.tsx
185
pages/teams.tsx
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
import { getCookie } from 'cookies-next'
|
|
||||||
import { queryTypes, useQueryState } from 'next-usequerystate'
|
import { queryTypes, useQueryState } from 'next-usequerystate'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -11,31 +10,27 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
import clonedeep from 'lodash.clonedeep'
|
import clonedeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
import setUserToken from '~utils/setUserToken'
|
||||||
|
import extractFilters from '~utils/extractFilters'
|
||||||
|
import organizeRaids from '~utils/organizeRaids'
|
||||||
import useDidMountEffect from '~utils/useDidMountEffect'
|
import useDidMountEffect from '~utils/useDidMountEffect'
|
||||||
import { elements, allElement } from '~utils/Element'
|
import { elements, allElement } from '~utils/Element'
|
||||||
|
import { emptyPaginationObject } from '~utils/emptyStates'
|
||||||
|
|
||||||
import GridRep from '~components/GridRep'
|
import GridRep from '~components/GridRep'
|
||||||
import GridRepCollection from '~components/GridRepCollection'
|
import GridRepCollection from '~components/GridRepCollection'
|
||||||
import FilterBar from '~components/FilterBar'
|
import FilterBar from '~components/FilterBar'
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
import type { FilterObject, PaginationObject } from '~types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
teams?: { count: number; total_pages: number; results: Party[] }
|
teams?: Party[]
|
||||||
raids: Raid[]
|
meta: PaginationObject
|
||||||
sortedRaids: Raid[][]
|
sortedRaids: Raid[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
const TeamsRoute: React.FC<Props> = (props: Props) => {
|
const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set up cookies
|
|
||||||
const cookie = getCookie('account')
|
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
const headers = accountData
|
|
||||||
? { Authorization: `Bearer ${accountData.token}` }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
// Set up router
|
// Set up router
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
@ -60,16 +55,20 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Recency is in seconds
|
// Recency is in seconds
|
||||||
const [element, setElement] = useQueryState('element', {
|
const [element, setElement] = useQueryState('element', {
|
||||||
defaultValue: -1,
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
parse: (query: string) => parseElement(query),
|
parse: (query: string) => parseElement(query),
|
||||||
serialize: (value) => serializeElement(value),
|
serialize: (value) => serializeElement(value),
|
||||||
})
|
})
|
||||||
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
const [raidSlug, setRaidSlug] = useQueryState('raid', {
|
||||||
defaultValue: 'all',
|
defaultValue: 'all',
|
||||||
|
history: 'push',
|
||||||
|
})
|
||||||
|
const [recency, setRecency] = useQueryState('recency', {
|
||||||
|
defaultValue: -1,
|
||||||
|
history: 'push',
|
||||||
|
parse: (query: string) => parseInt(query),
|
||||||
|
serialize: (value) => `${value}`,
|
||||||
})
|
})
|
||||||
const [recency, setRecency] = useQueryState(
|
|
||||||
'recency',
|
|
||||||
queryTypes.integer.withDefault(-1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define transformers for element
|
// Define transformers for element
|
||||||
function parseElement(query: string) {
|
function parseElement(query: string) {
|
||||||
|
|
@ -94,9 +93,9 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Set the initial parties from props
|
// Set the initial parties from props
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.teams) {
|
if (props.teams) {
|
||||||
setTotalPages(props.teams.total_pages)
|
setTotalPages(props.meta.totalPages)
|
||||||
setRecordCount(props.teams.count)
|
setRecordCount(props.meta.count)
|
||||||
replaceResults(props.teams.count, props.teams.results)
|
replaceResults(props.meta.count, props.teams)
|
||||||
}
|
}
|
||||||
setCurrentPage(1)
|
setCurrentPage(1)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -118,24 +117,36 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
const fetchTeams = useCallback(
|
const fetchTeams = useCallback(
|
||||||
({ replace }: { replace: boolean }) => {
|
({ replace }: { replace: boolean }) => {
|
||||||
const filters = {
|
const filters: {
|
||||||
|
[key: string]: any
|
||||||
|
} = {
|
||||||
|
element: element !== -1 ? element : undefined,
|
||||||
|
raid: raid ? raid.id : undefined,
|
||||||
|
recency: recency !== -1 ? recency : undefined,
|
||||||
|
page: currentPage,
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(filters).forEach(
|
||||||
|
(key) => filters[key] === undefined && delete filters[key]
|
||||||
|
)
|
||||||
|
|
||||||
|
const params = {
|
||||||
params: {
|
params: {
|
||||||
element: element != -1 ? element : undefined,
|
...filters,
|
||||||
raid: raid ? raid.id : undefined,
|
|
||||||
recency: recency != -1 ? recency : undefined,
|
|
||||||
page: currentPage,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
api.endpoints.parties
|
api.endpoints.parties
|
||||||
.getAll({ ...filters, ...{ headers: headers } })
|
.getAll(params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setTotalPages(response.data.total_pages)
|
const results = response.data.results
|
||||||
setRecordCount(response.data.count)
|
const meta = response.data.meta
|
||||||
|
|
||||||
if (replace)
|
setTotalPages(meta.total_pages)
|
||||||
replaceResults(response.data.count, response.data.results)
|
setRecordCount(meta.count)
|
||||||
else appendResults(response.data.results)
|
|
||||||
|
if (replace) replaceResults(meta.count, results)
|
||||||
|
else appendResults(results)
|
||||||
})
|
})
|
||||||
.catch((error) => handleError(error))
|
.catch((error) => handleError(error))
|
||||||
},
|
},
|
||||||
|
|
@ -157,12 +168,11 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
// Fetch all raids on mount, then find the raid in the URL if present
|
// Fetch all raids on mount, then find the raid in the URL if present
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.endpoints.raids.getAll().then((response) => {
|
api.endpoints.raids.getAll().then((response) => {
|
||||||
const cleanRaids: Raid[] = response.data.map((r: any) => r.raid)
|
setRaids(response.data)
|
||||||
setRaids(cleanRaids)
|
|
||||||
|
|
||||||
setRaidsLoading(false)
|
setRaidsLoading(false)
|
||||||
|
|
||||||
const raid = cleanRaids.find((r) => r.slug === raidSlug)
|
const raid = response.data.find((r: Raid) => r.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
|
|
||||||
return raid
|
return raid
|
||||||
|
|
@ -193,16 +203,16 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
raidSlug?: string
|
raidSlug?: string
|
||||||
recency?: number
|
recency?: number
|
||||||
}) {
|
}) {
|
||||||
if (element == 0) setElement(0)
|
if (element == 0) setElement(0, { shallow: true })
|
||||||
else if (element) setElement(element)
|
else if (element) setElement(element, { shallow: true })
|
||||||
|
|
||||||
if (raids && raidSlug) {
|
if (raids && raidSlug) {
|
||||||
const raid = raids.find((raid) => raid.slug === raidSlug)
|
const raid = raids.find((raid) => raid.slug === raidSlug)
|
||||||
setRaid(raid)
|
setRaid(raid)
|
||||||
setRaidSlug(raidSlug)
|
setRaidSlug(raidSlug, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recency) setRecency(recency)
|
if (recency) setRecency(recency, { shallow: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Favorites
|
// Methods: Favorites
|
||||||
|
|
@ -212,7 +222,7 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFavorite(teamId: string) {
|
function saveFavorite(teamId: string) {
|
||||||
api.saveTeam({ id: teamId, params: headers }).then((response) => {
|
api.saveTeam({ id: teamId }).then((response) => {
|
||||||
if (response.status == 201) {
|
if (response.status == 201) {
|
||||||
const index = parties.findIndex((p) => p.id === teamId)
|
const index = parties.findIndex((p) => p.id === teamId)
|
||||||
const party = parties[index]
|
const party = parties[index]
|
||||||
|
|
@ -228,7 +238,7 @@ const TeamsRoute: React.FC<Props> = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsaveFavorite(teamId: string) {
|
function unsaveFavorite(teamId: string) {
|
||||||
api.unsaveTeam({ id: teamId, params: headers }).then((response) => {
|
api.unsaveTeam({ id: teamId }).then((response) => {
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
const index = parties.findIndex((p) => p.id === teamId)
|
const index = parties.findIndex((p) => p.id === teamId)
|
||||||
const party = parties[index]
|
const party = parties[index]
|
||||||
|
|
@ -344,94 +354,43 @@ export const getServerSidePaths = async () => {
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
|
||||||
// Cookies
|
// Set headers for server-side requests
|
||||||
const cookie = getCookie("account", { req, res })
|
setUserToken(req, res)
|
||||||
const accountData: AccountCookie = cookie
|
|
||||||
? JSON.parse(cookie as string)
|
|
||||||
: null
|
|
||||||
|
|
||||||
const headers = accountData
|
|
||||||
? { headers: { Authorization: `Bearer ${accountData.token}` } }
|
|
||||||
: {}
|
|
||||||
|
|
||||||
|
// Fetch and organize raids
|
||||||
let { raids, sortedRaids } = await api.endpoints.raids
|
let { raids, sortedRaids } = await api.endpoints.raids
|
||||||
.getAll({ params: headers })
|
.getAll()
|
||||||
.then((response) => organizeRaids(response.data.map((r: any) => r.raid)))
|
.then((response) => organizeRaids(response.data))
|
||||||
|
|
||||||
// Extract recency filter
|
|
||||||
const recencyParam: number = parseInt(query.recency)
|
|
||||||
|
|
||||||
// Extract element filter
|
|
||||||
const elementParam: string = query.element
|
|
||||||
const teamElement: TeamElement | undefined =
|
|
||||||
elementParam === "all"
|
|
||||||
? allElement
|
|
||||||
: elements.find(
|
|
||||||
(element) => element.name.en.toLowerCase() === elementParam
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extract raid filter
|
|
||||||
const raidParam: string = query.raid
|
|
||||||
const raid: Raid | undefined = raids.find((r) => r.slug === raidParam)
|
|
||||||
|
|
||||||
// Create filter object
|
// Create filter object
|
||||||
const filters: {
|
const filters: FilterObject = extractFilters(query, raids)
|
||||||
raid?: string
|
const params = {
|
||||||
element?: number
|
params: { ...filters },
|
||||||
recency?: number
|
}
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (recencyParam) filters.recency = recencyParam
|
// Set up empty variables
|
||||||
if (teamElement && teamElement.id > -1) filters.element = teamElement.id
|
let teams: Party[] | null = null
|
||||||
if (raid) filters.raid = raid.id
|
let meta: PaginationObject = emptyPaginationObject
|
||||||
|
|
||||||
// Fetch initial set of parties here
|
// Fetch initial set of parties
|
||||||
const response = await api.endpoints.parties.getAll({
|
const response = await api.endpoints.parties.getAll(params)
|
||||||
params: {
|
|
||||||
...filters,
|
// Assign values to pass to props
|
||||||
},
|
teams = response.data.results
|
||||||
...headers,
|
meta.count = response.data.meta.count
|
||||||
})
|
meta.totalPages = response.data.meta.total_pages
|
||||||
|
meta.perPage = response.data.meta.per_page
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
teams: response.data,
|
teams: teams,
|
||||||
|
meta: meta,
|
||||||
raids: raids,
|
raids: raids,
|
||||||
sortedRaids: sortedRaids,
|
sortedRaids: sortedRaids,
|
||||||
...(await serverSideTranslations(locale, ["common"])),
|
...(await serverSideTranslations(locale, ['common'])),
|
||||||
// Will be passed to the page component as props
|
// Will be passed to the page component as props
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizeRaids = (raids: Raid[]) => {
|
|
||||||
// Set up empty raid for "All raids"
|
|
||||||
const all = {
|
|
||||||
id: '0',
|
|
||||||
name: {
|
|
||||||
en: 'All raids',
|
|
||||||
ja: '全て',
|
|
||||||
},
|
|
||||||
slug: 'all',
|
|
||||||
level: 0,
|
|
||||||
group: 0,
|
|
||||||
element: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const numGroups = Math.max.apply(
|
|
||||||
Math,
|
|
||||||
raids.map((raid) => raid.group)
|
|
||||||
)
|
|
||||||
let groupedRaids = []
|
|
||||||
|
|
||||||
for (let i = 0; i <= numGroups; i++) {
|
|
||||||
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
raids: raids,
|
|
||||||
sortedRaids: groupedRaids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TeamsRoute
|
export default TeamsRoute
|
||||||
|
|
|
||||||
2
types/User.d.ts
vendored
2
types/User.d.ts
vendored
|
|
@ -2,7 +2,7 @@ interface User {
|
||||||
id: string
|
id: string
|
||||||
username: string
|
username: string
|
||||||
granblueId: number
|
granblueId: number
|
||||||
picture: {
|
avatar: {
|
||||||
picture: string
|
picture: string
|
||||||
element: string
|
element: string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
types/index.d.ts
vendored
12
types/index.d.ts
vendored
|
|
@ -7,3 +7,15 @@ export type JobSkillObject = {
|
||||||
2: JobSkill | undefined
|
2: JobSkill | undefined
|
||||||
3: JobSkill | undefined
|
3: JobSkill | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FilterObject = {
|
||||||
|
raid?: string
|
||||||
|
element?: number
|
||||||
|
recency?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PaginationObject = {
|
||||||
|
count: number
|
||||||
|
totalPages: number
|
||||||
|
perPage: number
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,16 @@ class Api {
|
||||||
return axios.put(resourceUrl, params)
|
return axios.put(resourceUrl, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
allSkills(params: {}) {
|
allJobSkills(params?: {}) {
|
||||||
const resourceUrl = `${this.url}/jobs/skills`
|
const resourceUrl = `${this.url}/jobs/skills`
|
||||||
return axios.get(resourceUrl, params)
|
return axios.get(resourceUrl, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobSkillsForJob(jobId: string, params?: {}) {
|
||||||
|
const resourceUrl = `${this.url}/jobs/${jobId}/skills`
|
||||||
|
return axios.get(resourceUrl, params)
|
||||||
|
}
|
||||||
|
|
||||||
savedTeams(params: {}) {
|
savedTeams(params: {}) {
|
||||||
const resourceUrl = `${this.url}/parties/favorites`
|
const resourceUrl = `${this.url}/parties/favorites`
|
||||||
return axios.get(resourceUrl, params)
|
return axios.get(resourceUrl, params)
|
||||||
|
|
|
||||||
|
|
@ -185,3 +185,9 @@ export const emptyWeaponSeriesState: WeaponSeriesState = {
|
||||||
checked: false,
|
checked: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const emptyPaginationObject = {
|
||||||
|
count: 0,
|
||||||
|
totalPages: 0,
|
||||||
|
perPage: 15,
|
||||||
|
}
|
||||||
|
|
|
||||||
26
utils/extractFilters.tsx
Normal file
26
utils/extractFilters.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { elements, allElement } from '~utils/Element'
|
||||||
|
|
||||||
|
export default (query: { [index: string]: string }, raids: Raid[]) => {
|
||||||
|
// Extract recency filter
|
||||||
|
const recencyParam: number = parseInt(query.recency)
|
||||||
|
|
||||||
|
// Extract element filter
|
||||||
|
const elementParam: string = query.element
|
||||||
|
const teamElement: TeamElement | undefined =
|
||||||
|
elementParam === 'all'
|
||||||
|
? allElement
|
||||||
|
: elements.find(
|
||||||
|
(element) => element.name.en.toLowerCase() === elementParam
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extract raid filter
|
||||||
|
const raidParam: string = query.raid
|
||||||
|
const raid: Raid | undefined = raids.find((r) => r.slug === raidParam)
|
||||||
|
|
||||||
|
// Return filter object
|
||||||
|
return {
|
||||||
|
recency: recencyParam && recencyParam !== -1 ? recencyParam : undefined,
|
||||||
|
element: teamElement && teamElement.id > -1 ? teamElement.id : undefined,
|
||||||
|
raid: raid ? raid.id : undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
16
utils/organizeRaids.tsx
Normal file
16
utils/organizeRaids.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
export default (raids: Raid[]) => {
|
||||||
|
const numGroups = Math.max.apply(
|
||||||
|
Math,
|
||||||
|
raids.map((raid) => raid.group)
|
||||||
|
)
|
||||||
|
let groupedRaids = []
|
||||||
|
|
||||||
|
for (let i = 0; i <= numGroups; i++) {
|
||||||
|
groupedRaids[i] = raids.filter((raid) => raid.group == i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
raids: raids,
|
||||||
|
sortedRaids: groupedRaids,
|
||||||
|
}
|
||||||
|
}
|
||||||
19
utils/setUserToken.tsx
Normal file
19
utils/setUserToken.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import { getCookie } from 'cookies-next'
|
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
|
export default (
|
||||||
|
req: NextApiRequest | undefined = undefined,
|
||||||
|
res: NextApiResponse | undefined = undefined
|
||||||
|
) => {
|
||||||
|
// Set up cookies
|
||||||
|
const options = req && res ? { req, res } : {}
|
||||||
|
const cookie = getCookie('account', options)
|
||||||
|
if (cookie) {
|
||||||
|
axios.defaults.headers.common['Authorization'] = `Bearer ${
|
||||||
|
JSON.parse(cookie as string).token
|
||||||
|
}`
|
||||||
|
} else {
|
||||||
|
delete axios.defaults.headers.common['Authorization']
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue