diff --git a/src/app/editArea/[slug]/SidebarNav.tsx b/src/app/editArea/[slug]/SidebarNav.tsx index 5ed49e55b..94fa75665 100644 --- a/src/app/editArea/[slug]/SidebarNav.tsx +++ b/src/app/editArea/[slug]/SidebarNav.tsx @@ -1,7 +1,7 @@ 'use client' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { MapPinLine, Graph, Article, FolderSimplePlus } from '@phosphor-icons/react/dist/ssr' +import { Article, FolderSimplePlus } from '@phosphor-icons/react/dist/ssr' /** * Sidebar navigation for area edit @@ -14,28 +14,24 @@ export const SidebarNav: React.FC<{ slug: string }> = ({ slug }) => { const classForActivePage = (myPath: string): string => activePath.endsWith(myPath) ? 'bg-base-300/60 pointer-events-none' : '' return ( ) diff --git a/src/app/editArea/[slug]/components/SingleEntryForm.tsx b/src/app/editArea/[slug]/components/SingleEntryForm.tsx index a4c1148bf..6731b29b2 100644 --- a/src/app/editArea/[slug]/components/SingleEntryForm.tsx +++ b/src/app/editArea/[slug]/components/SingleEntryForm.tsx @@ -47,7 +47,7 @@ export function SingleEntryForm ({ >
-

{title}

+

{title}

{children}
diff --git a/src/app/editArea/[slug]/manage/AddAreaForm.tsx b/src/app/editArea/[slug]/general/components/AddAreaForm.tsx similarity index 94% rename from src/app/editArea/[slug]/manage/AddAreaForm.tsx rename to src/app/editArea/[slug]/general/components/AddAreaForm.tsx index 354a8d8d3..3c0e21c84 100644 --- a/src/app/editArea/[slug]/manage/AddAreaForm.tsx +++ b/src/app/editArea/[slug]/general/components/AddAreaForm.tsx @@ -10,7 +10,7 @@ import { AreaDesignationRadioGroup, areaDesignationToDb, AreaTypeFormProp } from import { AREA_NAME_FORM_VALIDATION_RULES } from '@/components/edit/EditAreaForm' /** - * + * Add a new area/crag/boulder */ export const AddAreaForm: React.FC<{ area: AreaType }> = ({ area }) => { const { uuid } = area @@ -32,7 +32,7 @@ export const AddAreaForm: React.FC<{ area: AreaType }> = ({ area }) => { await addOneAreaCmd({ name: areaName, parentUuid: uuid, isBoulder, isLeaf }) router.refresh() // Ask Next to refresh props from the server }} - className='border-primary border-2' + className='outline outline-2 outline-accent outline-offset-2' > ((props, ref) => ) @@ -20,12 +20,12 @@ const IconMap: Record = { climb: LineSegments } -export const EntityIcon: React.FC<{ type: EType, withLabel?: boolean, className?: string }> = ({ type, withLabel = true, className = '' }) => { +export const EntityIcon: React.FC<{ type: EType, withLabel?: boolean, size?: 20 | 24 | 28, className?: string }> = ({ type, withLabel = true, size = 24, className = '' }) => { const IconComponent = IconMap?.[type] if (IconComponent == null) return null return (
- + {withLabel && {type.toUpperCase()}}
) @@ -41,10 +41,10 @@ export const AreaItem: React.FC<{ area: AreaType, parentUuid: string, index: num
-
+
{index}
-
+
= ({ area: { climbs, metadata: { isBoulder } } }) => { if ((climbs?.length ?? 0) > 0) { - return + return } if (isBoulder) { - return + return } - return + return } const Actions: React.FC<{ @@ -85,10 +85,10 @@ const Actions: React.FC<{ return (
- + - +
= ({ areaName, uuid, pathTokens, ancestors, areas }) => { return ( -
+
{/* Heading */} diff --git a/src/app/editArea/[slug]/general/AreaNameForm.tsx b/src/app/editArea/[slug]/general/components/AreaNameForm.tsx similarity index 100% rename from src/app/editArea/[slug]/general/AreaNameForm.tsx rename to src/app/editArea/[slug]/general/components/AreaNameForm.tsx diff --git a/src/app/editArea/[slug]/general/components/AreaTypeForm.tsx b/src/app/editArea/[slug]/general/components/AreaTypeForm.tsx new file mode 100644 index 000000000..c43ee0848 --- /dev/null +++ b/src/app/editArea/[slug]/general/components/AreaTypeForm.tsx @@ -0,0 +1,44 @@ +'use client' +import { useSession } from 'next-auth/react' +import { useRouter } from 'next/navigation' + +import { SingleEntryForm } from 'app/editArea/[slug]/components/SingleEntryForm' +import useUpdateAreasCmd from '@/js/hooks/useUpdateAreasCmd' +import { AreaType } from '@/js/types' +import { AreaDesignationRadioGroup, areaDesignationToDb, areaDesignationToForm, AreaTypeFormProp } from '@/components/edit/form/AreaDesignationRadioGroup' + +/** + * Set area type: general area | crag | boulder + */ +export const AreaTypeForm: React.FC<{ area: AreaType }> = ({ area }) => { + const { uuid, climbs, children } = area + const session = useSession({ required: true }) + const router = useRouter() + const { updateOneAreaCmd } = useUpdateAreasCmd({ + areaId: uuid, + accessToken: session?.data?.accessToken as string + } + ) + + const areaType = areaDesignationToForm(area.metadata) + + // we're allowed to change area designation when the area has neither climbs nor areas. + const canChangeAreaType = + climbs.length === 0 && + children.length === 0 + return ( + + initialValues={{ areaType }} + keepValuesAfterReset={false} + title='Area Type' + helperText='You can not change the type if this area contains subareas or climbs.' + submitHandler={async ({ areaType }) => { + const { isBoulder, isLeaf } = areaDesignationToDb(areaType) + await updateOneAreaCmd({ uuid, isBoulder, isLeaf }) + router.refresh() // Ask Next to refresh props from the server + }} + > + + + ) +} diff --git a/src/app/editArea/[slug]/general/page.tsx b/src/app/editArea/[slug]/general/page.tsx index dc8093939..e6ba01f92 100644 --- a/src/app/editArea/[slug]/general/page.tsx +++ b/src/app/editArea/[slug]/general/page.tsx @@ -1,14 +1,27 @@ import { notFound } from 'next/navigation' import { validate } from 'uuid' import { ReactNode } from 'react' +import { Metadata } from 'next' import { AreaPageDataProps, getArea } from '@/js/graphql/getArea' -import { AreaNameForm } from './AreaNameForm' -import { AreaDescriptionForm } from './AreaDescriptionForm' +import { AreaNameForm } from './components/AreaNameForm' +import { AreaDescriptionForm } from './components/AreaDescriptionForm' +import { AreaLatLngForm } from './components/AreaLatLngForm' +import { AddAreaForm } from './components/AddAreaForm' +import { AreaList } from './components/AreaList' +import { AreaTypeForm } from './components/AreaTypeForm' // Opt out of caching for all data requests in the route segment export const dynamic = 'force-dynamic' +// Page metadata +export async function generateMetadata ({ params }: DashboardPageProps): Promise { + const { area: { areaName } } = await getPageDataForEdit(params.slug) + return { + title: `Editing area ${areaName}` + } +} + export interface DashboardPageProps { params: { slug: string @@ -16,19 +29,53 @@ export interface DashboardPageProps { } export default async function AreaEditPage ({ params }: DashboardPageProps): Promise { - const { area: { areaName, uuid, content: { description } } } = await getPageDataForEdit(params.slug) + const { area } = await getPageDataForEdit(params.slug) + const { + areaName, uuid, ancestors, pathTokens, children, + content: { description }, + metadata: { lat, lng } + } = area return ( - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + +
) } -export const PageContainer: React.FC<{ children: ReactNode } > = ({ children }) => ( -
- {children} -
+export const PageContainer: React.FC<{ children: ReactNode, id: string }> = ({ id, children }) => ( +
+
+ {children} +
+
) export const getPageDataForEdit = async (pageSlug: string): Promise => { diff --git a/src/app/editArea/[slug]/layout.tsx b/src/app/editArea/[slug]/layout.tsx index 3d3c842bc..bfbc736ce 100644 --- a/src/app/editArea/[slug]/layout.tsx +++ b/src/app/editArea/[slug]/layout.tsx @@ -3,6 +3,9 @@ import { SidebarNav } from './SidebarNav' import { getPageDataForEdit } from './general/page' import { GluttenFreeCrumbs } from '@/components/ui/BreadCrumbs' +// Opt out of caching for all data requests in the route segment +export const dynamic = 'force-dynamic' + /** * Layout for edit area dashboard */ @@ -12,23 +15,22 @@ export default async function RootLayout ({ children: React.ReactNode params: { slug: string } }): Promise { - const { area: { areaName, uuid, pathTokens, ancestors } } = await getPageDataForEdit(params.slug) + const { area: { uuid, pathTokens, ancestors } } = await getPageDataForEdit(params.slug) return (
-
-

Edit area {areaName} -

-
+
Edit area
+

+
diff --git a/src/app/editArea/[slug]/loading.tsx b/src/app/editArea/[slug]/loading.tsx index d28e1a741..3d4e9e9ec 100644 --- a/src/app/editArea/[slug]/loading.tsx +++ b/src/app/editArea/[slug]/loading.tsx @@ -1,8 +1,11 @@ import { PageContainer } from './general/page' +/** + * Loading skeleton + */ export default function Loading (): JSX.Element { return ( - +
diff --git a/src/app/editArea/[slug]/location/page.tsx b/src/app/editArea/[slug]/location/page.tsx deleted file mode 100644 index 3d6d68a1e..000000000 --- a/src/app/editArea/[slug]/location/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { AreaLatLngForm } from '../general/AreaLatLngForm' -import { DashboardPageProps, getPageDataForEdit, PageContainer } from '../general/page' - -export default async function AreaEditPage ({ params }: DashboardPageProps): Promise { - const { area: { uuid, metadata: { lat, lng } } } = await getPageDataForEdit(params.slug) - - return ( - - - - ) -} diff --git a/src/app/editArea/[slug]/manage/page.tsx b/src/app/editArea/[slug]/manage/page.tsx deleted file mode 100644 index c5f291801..000000000 --- a/src/app/editArea/[slug]/manage/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { DashboardPageProps, getPageDataForEdit, PageContainer } from '../general/page' -import { AreaList } from './components/AreaList' -import { AddAreaForm } from './AddAreaForm' - -// Opt out of caching for all data requests in the route segment -export const dynamic = 'force-dynamic' - -/** - * Area management - */ -export default async function Page ({ params }: DashboardPageProps): Promise { - const { area } = await getPageDataForEdit(params.slug) - - const { areaName, uuid, ancestors, pathTokens, children } = area - - return ( - - - - - ) -} diff --git a/src/app/editArea/[slug]/updateCache/route.ts b/src/app/editArea/[slug]/updateCache/route.ts index 5225e8bf1..566a6b02a 100644 --- a/src/app/editArea/[slug]/updateCache/route.ts +++ b/src/app/editArea/[slug]/updateCache/route.ts @@ -3,5 +3,6 @@ import { revalidatePath } from 'next/cache' export async function GET (request: Request, res: NextResponse): Promise { revalidatePath('/editArea/[slug]', 'page') + revalidatePath('/editArea/[slug]/layout', 'page') return NextResponse.json({ message: 'ok' }) } diff --git a/src/app/global.css b/src/app/global.css index 278124ebe..b99afe9ae 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -26,6 +26,10 @@ h3 { @apply text-base-content text-lg font-semibold tracking-tight leading-loose; } +h4 { + @apply tracking-tight leading-loose; +} + .text-primary { @apply text-base-content !important; } diff --git a/src/components/edit/form/AreaDesignationRadioGroup.tsx b/src/components/edit/form/AreaDesignationRadioGroup.tsx index 559f684b7..1bd35f086 100644 --- a/src/components/edit/form/AreaDesignationRadioGroup.tsx +++ b/src/components/edit/form/AreaDesignationRadioGroup.tsx @@ -2,6 +2,7 @@ import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid' import { AreaMetadataType, AreaUpdatableFieldsType } from '../../../js/types' import { RadioGroup } from '../../ui/form' import Tooltip from '../../ui/Tooltip' +import { EntityIcon } from 'app/editArea/[slug]/general/components/AreaItem' export type AreaTypeFormProp = 'crag' | 'area' | 'boulder' @@ -16,16 +17,16 @@ export interface AreaDesignationRadioGroupProps { */ export const AreaDesignationRadioGroup = ({ name = 'areaType', disabled = false }: AreaDesignationRadioGroupProps): JSX.Element => ( } name={name} disabled={disabled} labels={[ - 'Area', - 'Crag (sport, trad, ice)', - 'Boulder']} + , + , + ]} values={['area', 'crag', 'boulder']} - labelTips={['Group other areas.', 'List rope climbing routes.', 'List boulder problems.']} + labelTips={['Group other smaller areas.', 'List rope climbing routes.', 'List boulder problems.']} requiredErrorMessage='Please select an area type' /> ) diff --git a/src/components/layout.tsx b/src/components/layout.tsx index c6fbabee8..26a0d9014 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -21,7 +21,6 @@ function Layout ({ return ( <>
-
{hero}
diff --git a/src/components/ui/BreadCrumbs.tsx b/src/components/ui/BreadCrumbs.tsx index 478eecba6..d1450a18e 100644 --- a/src/components/ui/BreadCrumbs.tsx +++ b/src/components/ui/BreadCrumbs.tsx @@ -130,7 +130,7 @@ export const GluttenFreeCrumbs: React.FC<{ ancestors: string[] }> = ({ pathTokens, ancestors }) => { return ( -
+
  • Home
  • {pathTokens.map((path, index) => { @@ -148,7 +148,7 @@ const GFItem: React.FC<{ path: string, url: string, isLast: boolean }> =
  • {path} diff --git a/src/components/ui/form/Input.tsx b/src/components/ui/form/Input.tsx index ae5e22ad6..3c45ecdbb 100644 --- a/src/components/ui/form/Input.tsx +++ b/src/components/ui/form/Input.tsx @@ -123,7 +123,7 @@ export const DashboardInput: React.FC = ({ name, label, pla return (
    {labels.map((label, index: number) => ( @@ -39,7 +39,7 @@ export default function RadioGroup ({ groupLabel, groupLabelAlt, name, labels, v
    {label}
    {labelTips?.[index] != null && -
    +
    {labelTips[index]}
    }
    diff --git a/src/js/graphql/getArea.ts b/src/js/graphql/getArea.ts index 3c514f6d9..ef341571b 100644 --- a/src/js/graphql/getArea.ts +++ b/src/js/graphql/getArea.ts @@ -8,7 +8,7 @@ export interface AreaPageDataProps { } /** - * Get area page data + * Get area page data. * @param uuid area uuid */ export const getArea = async (uuid: string): Promise => {