Skip to content

Commit

Permalink
Add climb list to manage climbs page
Browse files Browse the repository at this point in the history
  • Loading branch information
viet nguyen committed Dec 23, 2023
1 parent ba9af1d commit 6c6debc
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 68 deletions.
7 changes: 5 additions & 2 deletions src/app/area/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ export default async function Page ({ params }: PageWithCatchAllUuidProps): Prom

</div>

<SubAreasSection area={area} />
<ClimbListSection area={area} />
{/* An area can only have either subareas or climbs, but not both. */}
<div className='mt-6'>
<SubAreasSection area={area} />
<ClimbListSection area={area} />
</div>
</AreaPageContainer>
)
}
Expand Down
24 changes: 12 additions & 12 deletions src/app/area/[[...slug]]/sections/ClimbListSection.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import Link from 'next/link'
import { Plus } from '@phosphor-icons/react/dist/ssr'
import { ClimbList } from '@/app/editArea/[slug]/general/components/climb/ClimbListForm'
import { AreaType } from '@/js/types'
/**
* Sub-areas section
* Climb list section
*/
export const ClimbListSection: React.FC<{ area: AreaType }> = ({ area }) => {
export const ClimbListSection: React.FC<{ area: AreaType, editMode?: boolean }> = ({ area, editMode = false }) => {
const { uuid, gradeContext, climbs, metadata } = area
if (!metadata.leaf) return null
return (
<section className='w-full mt-16'>
<section className='w-full'>
<div className='flex items-center justify-between'>
<div className='flex items-center gap-3'>
<h3 className='flex items-center gap-4'>{climbs.length} Climbs</h3>
</div>
<div className='flex items-center gap-2'>
<span className='text-sm italic'>Coming soon:</span>
<Link href={`/editArea/${uuid}/general#addArea`} className='btn-disabled btn btn-sm'>
<Plus size={18} weight='bold' /> New Climbs
</Link>
</div>
{/* Already in the edit dashboard. Don't show the button */}
{!editMode &&
<div className='flex items-center gap-2'>
<a href={`/editArea/${uuid}/manageClimbs`} className='btn btn-sm btn-accent btn-outline'>
<Plus size={18} weight='bold' /> New Climbs
</a>
</div>}
</div>

<hr className='my-6 border-2 border-base-content' />
<hr className='mt-2 mb-6 border-2 border-base-content' />

<ClimbList gradeContext={gradeContext} areaMetadata={metadata} climbs={climbs} />
<ClimbList gradeContext={gradeContext} areaMetadata={metadata} climbs={climbs} editMode={editMode} />
</section>
)
}
4 changes: 2 additions & 2 deletions src/app/area/[[...slug]]/sections/SubAreasSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const SubAreasSection: React.FC<{ area: AreaType } > = ({ area }) => {
const { uuid, children, metadata: { leaf } } = area
if (leaf) return null
return (
<section className='w-full mt-16'>
<section className='w-full'>
<div className='flex items-center justify-between'>
<div className='flex items-center gap-3'>
<h3 className='flex items-center gap-4'><AreaEntityBullet />{children.length} Areas</h3>
Expand All @@ -21,7 +21,7 @@ export const SubAreasSection: React.FC<{ area: AreaType } > = ({ area }) => {
</Link>
</div>

<hr className='my-6 border-2 border-base-content' />
<hr className='mt-2 mb-6 border-2 border-base-content' />

<AreaList parentUuid={uuid} areas={children} />
</section>
Expand Down
20 changes: 13 additions & 7 deletions src/app/editArea/[slug]/SidebarNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export const SidebarNav: React.FC<{ slug: string, canAddAreas: boolean, canAddCl
</li>
<li>
<a
href={`/editArea/${slug}/addClimbs`}
href={`/editArea/${slug}/manageClimbs`}
className={clx(
classForActivePage('addClimbs')
classForActivePage('manageClimbs')
)}
>
<LineSegments size={24} /> Manage climbs
Expand All @@ -35,17 +35,23 @@ export const SidebarNav: React.FC<{ slug: string, canAddAreas: boolean, canAddCl

<div className='w-56 mt-4'>
<hr className='border-t my-2' />
<a href={`/editArea/${slug}/general#addArea`} className={clx(canAddAreas ? '' : 'cursor-not-allowed pointer-events-none', 'block py-2')}>
<button disabled={!canAddAreas} className='btn btn-accent btn-block justify-start'>
<a
href={`/editArea/${slug}/general#addArea`}
className={clx(canAddAreas ? '' : 'cursor-not-allowed pointer-events-none', 'block py-2')}
>
<button disabled={!canAddAreas} className='btn btn-accent btn-outline btn-block justify-start'>
<Plus size={20} weight='bold' /> Add areas
</button>
</a>

<div className={clx(canAddClimbs ? '' : 'cursor-not-allowed pointer-events-none', 'block py-1')}>
<button disabled={!canAddClimbs} className='btn btn-accent btn-block justify-start'>
<a
href={`/editArea/${slug}/manageClimbs`}
className={clx(canAddClimbs ? '' : 'cursor-not-allowed pointer-events-none', 'block py-1')}
>
<button disabled={!canAddClimbs} className='btn btn-accent btn-outline btn-block justify-start'>
<Plus size={20} weight='bold' /> Add climbs
</button>
</div>
</a>

<hr className='border-t my-2' />

Expand Down
16 changes: 0 additions & 16 deletions src/app/editArea/[slug]/addClimbs/page.tsx

This file was deleted.

68 changes: 42 additions & 26 deletions src/app/editArea/[slug]/general/components/climb/ClimbListForm.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
import Link from 'next/link'
import clx from 'classnames'
import { AreaMetadataType, ClimbDisciplineRecord, ClimbType } from '@/js/types'
import { disciplineTypeToDisplay } from '@/js/grades/util'
import { removeTypenameFromDisciplines } from '@/js/utils'
import Grade, { GradeContexts } from '@/js/grades/Grade'
import { ClimbListMiniToolbar } from '../../../manageClimbs/components/ClimbListMiniToolbar'

export const ClimbList: React.FC<{ gradeContext: GradeContexts, climbs: ClimbType[], areaMetadata: AreaMetadataType }> = ({ gradeContext, climbs, areaMetadata }) => {
const leftRightIndexComparator = (a: ClimbType, b: ClimbType): number => {
const aIndex = a.metadata.leftRightIndex
const bIndex = b.metadata.leftRightIndex
if (aIndex < bIndex) return -1
else if (aIndex > bIndex) return 1
return 0
}

export const ClimbList: React.FC<{ gradeContext: GradeContexts, climbs: ClimbType[], areaMetadata: AreaMetadataType, editMode: boolean }> = ({ gradeContext, climbs, areaMetadata, editMode }) => {
const sortedClimbs = climbs.sort(leftRightIndexComparator)
return (
// it looks better
<ol className={clx(climbs.length < 5 ? 'block max-w-sm' : 'three-column-table', 'divide-y divide-base-200')}>
{climbs.map((climb, index) => {
return (
<ClimbRow
key={climb.id}
gradeContext={gradeContext}
areaMetadata={areaMetadata}
{...climb}
index={index + 1}
/>
)
})}
</ol>
<div>
{climbs.length === 0 && <div className='alert alert-info text-sm'>No climbs found. Use the form below to add new climbs.</div>}
<ol className={clx(climbs.length < 5 ? 'block max-w-sm' : 'three-column-table', 'divide-y divide-base-200')}>
{sortedClimbs.map((climb, index) => {
return (
<ClimbRow
key={climb.id}
gradeContext={gradeContext}
areaMetadata={areaMetadata}
{...climb}
index={index + 1}
editMode={editMode}
/>
)
})}
</ol>
</div>
)
}

const ClimbRow: React.FC<ClimbType & { index: number, gradeContext: GradeContexts, areaMetadata: AreaMetadataType }> = ({ id, name, type: disciplines, index, gradeContext, grades, areaMetadata }) => {
const ClimbRow: React.FC<ClimbType & { index: number, gradeContext: GradeContexts, areaMetadata: AreaMetadataType, editMode?: boolean }> = ({ id, name, type: disciplines, index, gradeContext, grades, areaMetadata, editMode = false }) => {
const sanitizedDisciplines = removeTypenameFromDisciplines(disciplines)
const gradeStr = new Grade(
gradeContext,
Expand All @@ -35,16 +47,20 @@ const ClimbRow: React.FC<ClimbType & { index: number, gradeContext: GradeContext
const url = `/climbs/${id}`
return (
<li className='py-2 break-inside-avoid-column break-inside-avoid'>
<Link href={url} className='flex gap-x-4 flex-nowrap'>
<ListBullet index={index} disciplines={disciplines} />
<div className='w-full'>
<div className='flex justify-between'>
<div className='text-base font-semibold uppercase tracking-tight hover:underline'>{name}</div>
<div>{gradeStr}</div>
<div className={clx('w-full', editMode ? 'card card-compact p-2 card-bordered bg-base-100 shadow' : '')}>
<a href={url} className='flex gap-x-4 flex-nowrap w-full'>
<ListBullet index={index} disciplines={disciplines} />
<div className='w-full'>
<div className='flex justify-between'>
<div className='text-base font-semibold uppercase tracking-tight hover:underline'>{name}</div>
<div>{gradeStr}</div>
</div>
<div><DisciplinesInfo disciplines={disciplines} /></div>
</div>
<div><DisciplinesInfo disciplines={disciplines} /></div>
</div>
</Link>
</a>
</div>
{editMode &&
<ClimbListMiniToolbar climbId={id} parentAreaId={areaMetadata.areaId} climbName={name} />}
</li>
)
}
Expand Down
20 changes: 19 additions & 1 deletion src/app/editArea/[slug]/general/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { notFound } from 'next/navigation'
import { validate } from 'uuid'
import { Metadata } from 'next'
import { FetchPolicy } from '@apollo/client'
import { ArrowCircleRight } from '@phosphor-icons/react/dist/ssr'

import { AreaPageDataProps, getArea } from '@/js/graphql/getArea'
import { AreaNameForm } from './components/AreaNameForm'
Expand All @@ -9,7 +11,6 @@ import { AreaLatLngForm } from './components/AreaLatLngForm'
import { AddAreaForm } from './components/AddAreaForm'
import { AreaListForm } from './components/AreaList'
import { AreaTypeForm } from './components/AreaTypeForm'
import { FetchPolicy } from '@apollo/client'
import { PageContainer, SectionContainer } from '../components/EditAreaContainers'

// Opt out of caching for all data requests in the route segment
Expand Down Expand Up @@ -38,6 +39,8 @@ export default async function AreaEditPage ({ params }: DashboardPageProps): Pro
metadata: { lat, lng, leaf }
} = area

const canAddClimbs = leaf && children.length === 0

return (
<PageContainer>
<SectionContainer id='general'>
Expand Down Expand Up @@ -70,6 +73,21 @@ export default async function AreaEditPage ({ params }: DashboardPageProps): Pro
areas={children}
/>
</SectionContainer>}

<SectionContainer id='manageClimbs'>
<div className='card card-bordered border-base-300 /40 overflow-hidden w-full bg-base-100'>
<div className='card-body'>
<h4 className='font-semibold text-2xl'>Manage Climbs</h4>
{canAddClimbs
? (
<div className='alert'>
<a href={`/editArea/${uuid}/manageClimbs`} className='btn btn-link'>Manage climbs page <ArrowCircleRight size={20} /></a>
</div>
)
: <div className='alert'>This area contains subareas. Please add climbs to areas designated as crags or boulders. </div>}
</div>
</div>
</SectionContainer>
</PageContainer>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const AddClimbsForm: React.FC<{ parentAreaName: string, parentAreaUuid: s
: (
<div role='alert' className='alert alert-info'>
<WarningOctagon size={24} />
<span>This area is either a crag or a boulder. Adding a new child area is not allowed.</span>
<span>This area is either a crag or a boulder. Adding new child areas is not allowed.</span>
</div>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { Trash } from '@phosphor-icons/react/dist/ssr'
import useUpdateClimbsCmd from '@/js/hooks/useUpdateClimbsCmd'
import Confirmation from '@/components/ui/micro/AlertDialogue'

export const ClimbListMiniToolbar: React.FC<{ parentAreaId: string, climbId: string, climbName: string }> = ({ parentAreaId, climbId, climbName }) => {
const session = useSession({ required: true })
const router = useRouter()
const { deleteClimbsCmd } = useUpdateClimbsCmd({ parentId: parentAreaId, accessToken: session.data?.accessToken ?? '' })
const onConfirm = (): void => {
deleteClimbsCmd([climbId]).then((count) => {
router.refresh()
}).catch((error) => {
console.error(error)
})
}
return (
<div className='flex justify-end mb-2 py-1'>
<Confirmation
title='Please confirm'
confirmText='Delete'
button={
<button className='btn btn-xs btn-glass'>
<Trash size={16} /> Delete
</button>
}
onConfirm={onConfirm}
>
You're about to delete climb "<i>{climbName}"</i>. <strong>This cannot be undone.</strong>
</Confirmation>
</div>
)
}
33 changes: 33 additions & 0 deletions src/app/editArea/[slug]/manageClimbs/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Metadata } from 'next'
import { DashboardPageProps, getPageDataForEdit } from '../general/page'
import { PageContainer, SectionContainer } from '../components/EditAreaContainers'
import { AddClimbsForm } from './components/AddClimbsForm'
import { ClimbListSection } from '@/app/area/[[...slug]]/sections/ClimbListSection'

// Opt out of caching for all data requests in the route segment
export const dynamic = 'force-dynamic'
export const fetchCache = 'force-no-store' // opt out of Nextjs version of 'fetch'

// Page metadata
export async function generateMetadata ({ params }: DashboardPageProps): Promise<Metadata> {
const { area: { areaName } } = await getPageDataForEdit(params.slug, 'cache-first')
return {
title: `Maging climbs in area ${areaName}`
}
}

export default async function AddClimbsPage ({ params: { slug } }: DashboardPageProps): Promise<any> {
const { area } = await getPageDataForEdit(slug)
const { areaName, uuid, gradeContext, metadata } = area
const { leaf, isBoulder } = metadata
return (
<PageContainer>
<SectionContainer id='climbList'>
<ClimbListSection area={area} editMode />
</SectionContainer>
<SectionContainer id='addClimbs'>
<AddClimbsForm parentAreaName={areaName} parentAreaUuid={uuid} gradeContext={gradeContext} canAddClimbs={isBoulder || leaf} />
</SectionContainer>
</PageContainer>
)
}
2 changes: 1 addition & 1 deletion src/js/hooks/useUpdateClimbsCmd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default function useUpdateClimbsCmd ({ parentId, accessToken = '', onUpda
MUTATION_DELETE_CLIMBS, {
client: graphqlClient,
onCompleted: (data) => {
void refreshPage(`/api/revalidate?s=${parentId}`)
void updateAreaPageCache(parentId)
toast('Climbs deleted ✔️')
if (onDeleteCompleted != null) {
onDeleteCompleted(data)
Expand Down

0 comments on commit 6c6debc

Please sign in to comment.