Implement read-only and editable views for party details

This commit is contained in:
Justin Edmund 2022-02-24 18:19:15 -08:00
parent 7df3da2d34
commit 32f864baa6
4 changed files with 258 additions and 5 deletions

View file

@ -3,6 +3,7 @@ import { useRouter } from 'next/router'
import { useCookies } from 'react-cookie'
import { useSnapshot } from 'valtio'
import clonedeep from 'lodash.clonedeep'
import * as Scroll from 'react-scroll'
import * as AlertDialog from '@radix-ui/react-alert-dialog'
@ -21,6 +22,7 @@ const BottomHeader = () => {
const app = useSnapshot(appState)
const router = useRouter()
const scroll = Scroll.animateScroll;
// Cookies
const [cookies] = useCookies(['user'])
@ -30,6 +32,15 @@ const BottomHeader = () => {
}
} : {}
function toggleDetails() {
appState.party.detailsVisible = !appState.party.detailsVisible
if (appState.party.detailsVisible)
scroll.scrollToBottom()
else
scroll.scrollToTop()
}
function deleteTeam(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
if (appState.party.editable && appState.party.id) {
api.endpoints.parties.destroy(appState.party.id, headers)
@ -53,9 +64,11 @@ const BottomHeader = () => {
}
const leftNav = () => {
return (
<Button icon="edit" click={() => {}}>Add more info</Button>
)
if (app.party.detailsVisible) {
return (<Button icon="edit" active={true} click={toggleDetails}>Hide info</Button>)
} else {
return (<Button icon="edit" click={toggleDetails}>Edit info</Button>)
}
}
const rightNav = () => {

View file

@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react'
import { useSnapshot } from 'valtio'
import { useCookies } from 'react-cookie'
import PartySegmentedControl from '~components/PartySegmentedControl'
import PartyDetails from '~components/PartyDetails'
import WeaponGrid from '~components/WeaponGrid'
import SummonGrid from '~components/SummonGrid'
import CharacterGrid from '~components/CharacterGrid'
@ -13,6 +13,7 @@ import { appState } from '~utils/appState'
import { GridType, TeamElement } from '~utils/enums'
import './index.scss'
import { AxiosResponse } from 'axios'
// Props
interface Props {
@ -33,6 +34,13 @@ const Party = (props: Props) => {
const { party } = useSnapshot(appState)
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
// Fetch data from the server
useEffect(() => {
const shortcode = (props.slug) ? props.slug : undefined
if (shortcode) fetchDetails(shortcode)
else appState.party.editable = true
}, [props.slug])
// Methods: Creating a new party
async function createParty(extra: boolean = false) {
let body = {
@ -76,6 +84,39 @@ const Party = (props: Props) => {
}
}
// Methods: Fetch party details
function fetchDetails(shortcode: string) {
return api.endpoints.parties.getOne({ id: shortcode })
.then(response => processResult(response))
.catch(error => processError(error))
}
function processResult(response: AxiosResponse) {
// Store the response
const party = response.data.party
// Store the party's user-generated details
if (party.name)
appState.party.name = party.name
if (party.description)
appState.party.description = party.description
if (party.raid)
appState.party.raid = party.raid
}
function processError(error: any) {
if (error.response != null) {
if (error.response.status == 404) {
// setFound(false)
// setLoading(false)
}
} else {
console.error(error)
}
}
// Render: JSX components
const navigation = (
<PartySegmentedControl
@ -123,7 +164,12 @@ const Party = (props: Props) => {
return (
<div>
{ navigation }
{ currentGrid() }
<section id="Party">
{ currentGrid() }
</section>
{ <PartyDetails
editable={party.editable}
/>}
</div>
)
}

View file

@ -0,0 +1,87 @@
.Details {
display: none; // This breaks transition, find a workaround
opacity: 0;
margin: 0 auto;
max-width: $unit * 95;
position: relative;
&.Editable {
top: $unit;
height: 0;
z-index: 2;
transition: opacity 0.2s ease-in-out,
top 0.2s ease-in-out;
&.Visible {
display: block;
height: auto;
margin-bottom: 40vh;
opacity: 1;
top: 0;
}
fieldset {
display: block;
width: 100%;
textarea {
min-height: $unit * 20;
width: 100%;
}
}
select {
appearance: none;
background-image: url('/icons/Arrow.svg');
background-repeat: no-repeat;
background-position-y: center;
background-position-x: 98%;
background-size: $unit * 1.5;
border: none;
border-radius: 6px;
color: $grey-00;
margin-bottom: $unit;
font-size: $font-regular;
padding: 0 ($unit * 2);
height: $unit * 6;
width: 100%;
&:hover {
cursor: pointer;
}
}
}
&.ReadOnly {
top: $unit * -1;
transition: opacity 0.2s ease-in-out,
top 0.2s ease-in-out;
&.Visible {
display: block;
height: auto;
opacity: 1;
top: 0;
}
h1 {
font-size: $font-xlarge;
font-weight: $normal;
text-align: left;
margin-bottom: $unit;
}
.Raid {
color: $grey-50;
font-size: $font-regular;
font-weight: $medium;
margin-bottom: $unit * 2;
}
p {
font-size: $font-regular;
line-height: $font-regular * 1.2;
}
}
}

View file

@ -0,0 +1,107 @@
import React, { useState } from 'react'
import { useSnapshot } from 'valtio'
import classNames from 'classnames'
import CharLimitedFieldset from '~components/CharLimitedFieldset'
import TextFieldset from '~components/TextFieldset'
import { appState } from '~utils/appState'
import './index.scss'
import RaidDropdown from '~components/RaidDropdown'
// Props
interface Props {
editable: boolean
}
const PartyDetails = (props: Props) => {
const appSnapshot = useSnapshot(appState)
const nameInput = React.createRef<HTMLInputElement>()
const descriptionInput = React.createRef<HTMLTextAreaElement>()
const readOnlyClasses = classNames({
'Details': true,
'ReadOnly': true,
'Visible': !appSnapshot.party.detailsVisible
})
const editableClasses = classNames({
'Details': true,
'Editable': true,
'Visible': appSnapshot.party.detailsVisible
})
const [errors, setErrors] = useState<{ [key: string]: string }>({
name: '',
description: ''
})
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
console.log(event)
event.preventDefault()
const { name, value } = event.target
let newErrors = errors
setErrors(newErrors)
}
function handleTextAreaChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
event.preventDefault()
const { name, value } = event.target
let newErrors = errors
setErrors(newErrors)
}
const editable = (
<section className={editableClasses}>
<CharLimitedFieldset
fieldName="name"
placeholder="Name your team"
value={appSnapshot.party.name}
limit={50}
onBlur={ () => {} }
onChange={handleInputChange}
error={errors.name}
ref={nameInput}
/>
<RaidDropdown />
<select>
<option>Belial</option>
<option>Beelzebub</option>
<option>Lucifer (Hard)</option>
</select>
<TextFieldset
fieldName="name"
placeholder={"Write your notes here\n\n\nWatch out for the 50% trigger!\nMake sure to click Fediels 1 first\nGood luck with RNG!"}
value={appSnapshot.party.description}
onBlur={ () => {} }
onChange={handleTextAreaChange}
error={errors.description}
ref={descriptionInput}
/>
</section>
)
const readOnly = (
<section className={readOnlyClasses}>
<h1>{ (appSnapshot.party.name) ? appSnapshot.party.name : 'No title' }</h1>
{ (appSnapshot.party.raid) ? <div className="Raid">{appSnapshot.party.raid.name.en}</div> : '' }
<p>{ (appSnapshot.party.description) ? appSnapshot.party.description : '' }</p>
</section>
)
return (
<div>
{readOnly}
{editable}
</div>
)
}
export default PartyDetails