generated from taylorbryant/gatsby-starter-tailwind
-
-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
viet nguyen
committed
Nov 19, 2023
1 parent
6c1fa73
commit 3ca5c48
Showing
32 changed files
with
473 additions
and
217 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,42 @@ | ||
'use client' | ||
import Link from 'next/link' | ||
import { MapPinLine, TreeStructure, Article } from '@phosphor-icons/react/dist/ssr' | ||
import { usePathname } from 'next/navigation' | ||
import { MapPinLine, Graph, Article, FolderSimplePlus } from '@phosphor-icons/react/dist/ssr' | ||
|
||
/** | ||
* Sidebar navigation for area edit | ||
*/ | ||
export const SidebarNav: React.FC<{ slug: string }> = ({ slug }) => { | ||
const activePath = usePathname() | ||
/** | ||
* Disable menu item's hover/click when own page is showing | ||
*/ | ||
const classForActivePage = (myPath: string): string => activePath.endsWith(myPath) ? 'bg-base-300/60 pointer-events-none' : '' | ||
return ( | ||
<nav className='px-6'> | ||
<ul className='menu w-56'> | ||
<li> | ||
<Link href={`/editArea/${slug}`}> | ||
<Link href={`/editArea/${slug}/general`} className={classForActivePage('general')}> | ||
<Article size={24} /> General | ||
</Link> | ||
</li> | ||
<li> | ||
<Link href={`/editArea/${slug}/attributes`}> | ||
<Link href={`/editArea/${slug}/location`} className={classForActivePage('location')}> | ||
<MapPinLine size={24} /> Location | ||
</Link> | ||
</li> | ||
<li> | ||
<Link href={`/editArea/${slug}/attributes`}> | ||
<TreeStructure size={24} className='rotate-90' /> Child areas | ||
<Link href={`/editArea/${slug}/manage`} className={classForActivePage('manage')}> | ||
<Graph size={24} weight='duotone' /> Manage areas | ||
</Link> | ||
</li> | ||
</ul> | ||
|
||
<div className='p-2 w-56'> | ||
<Link href={`/editArea/${slug}/attributes`}> | ||
<button className='btn btn-primary btn-block justify-start'> <FolderSimplePlus size={24} /> Add area</button> | ||
</Link> | ||
</div> | ||
</nav> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
'use client' | ||
import { ReactNode } from 'react' | ||
import { FieldValues, FormProvider, useForm, DefaultValues, ValidationMode } from 'react-hook-form' | ||
import { SpinnerGap } from '@phosphor-icons/react/dist/ssr' | ||
import clx from 'classnames' | ||
|
||
export interface SingleEntryFormProps<T> { | ||
children: ReactNode | ||
initialValues: DefaultValues<T> | ||
validationMode?: keyof ValidationMode | ||
submitHandler: (formData: T) => Promise<void> | void | ||
title: string | ||
helperText?: string | ||
keepValuesAfterReset?: boolean | ||
className?: string | ||
} | ||
|
||
export function SingleEntryForm<T extends FieldValues> ({ | ||
children, | ||
initialValues, | ||
submitHandler, | ||
validationMode = 'onBlur', | ||
helperText, | ||
title, | ||
keepValuesAfterReset = true, | ||
className = '' | ||
}: SingleEntryFormProps<T>): ReactNode { | ||
const form = useForm<T>({ | ||
mode: validationMode, | ||
defaultValues: { ...initialValues } | ||
}) | ||
|
||
const { handleSubmit, reset, formState: { isValid, isSubmitting, isDirty } } = form | ||
|
||
return ( | ||
<FormProvider {...form}> | ||
{/* eslint-disable-next-line */} | ||
<form onSubmit={handleSubmit(async data => { | ||
await submitHandler(data) | ||
if (keepValuesAfterReset) { | ||
reset({ ...data }) | ||
} else { | ||
reset() | ||
} | ||
} | ||
)} | ||
> | ||
<div className={clx('card card-bordered border-base-300/40 overflow-hidden w-full bg-base-100', className)}> | ||
<div className='card-body'> | ||
<h2 className='font-semibold text-2xl'>{title}</h2> | ||
<div className='pt-2 flex flex-col gap-y-4'> | ||
{children} | ||
</div> | ||
</div> | ||
<div className='px-8 py-2 w-full flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4 bg-base-200 border-t'> | ||
<span className='text-base-content/50'>{helperText}</span> | ||
<SubmitButton isDirty={isDirty} isSubmitting={isSubmitting} isValid={isValid} /> | ||
</div> | ||
</div> | ||
</form> | ||
</FormProvider> | ||
) | ||
} | ||
|
||
export const SubmitButton: React.FC<{ isValid: boolean, isSubmitting: boolean, isDirty: boolean }> = ({ | ||
isValid, isSubmitting, isDirty | ||
}) => ( | ||
<button | ||
className='btn btn-primary btn-solid w-full lg:w-fit' | ||
disabled={!isValid || isSubmitting || !isDirty} | ||
type='submit' | ||
> | ||
{isSubmitting && <SpinnerGap size={24} className='animate-spin' />} Save | ||
</button> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { notFound } from 'next/navigation' | ||
import { validate } from 'uuid' | ||
import { ReactNode } from 'react' | ||
|
||
import { AreaPageDataProps, getArea } from '@/js/graphql/getArea' | ||
import { AreaNameForm } from './AreaNameForm' | ||
import { AreaDescriptionForm } from './AreaDescriptionForm' | ||
|
||
// Opt out of caching for all data requests in the route segment | ||
export const dynamic = 'force-dynamic' | ||
|
||
export interface DashboardPageProps { | ||
params: { | ||
slug: string | ||
} | ||
} | ||
|
||
export default async function AreaEditPage ({ params }: DashboardPageProps): Promise<any> { | ||
const { area: { areaName, uuid, content: { description } } } = await getPageDataForEdit(params.slug) | ||
return ( | ||
<PageContainer> | ||
<AreaNameForm initialValue={areaName} uuid={uuid} /> | ||
<AreaDescriptionForm initialValue={description} uuid={uuid} /> | ||
</PageContainer> | ||
) | ||
} | ||
|
||
export const PageContainer: React.FC<{ children: ReactNode } > = ({ children }) => ( | ||
<section className='w-full flex flex-col gap-y-8'> | ||
{children} | ||
</section> | ||
) | ||
|
||
export const getPageDataForEdit = async (pageSlug: string): Promise<AreaPageDataProps> => { | ||
if (pageSlug == null) notFound() | ||
|
||
if (!validate(pageSlug)) { | ||
notFound() | ||
} | ||
|
||
const pageData = await getArea(pageSlug) | ||
if (pageData == null) { | ||
notFound() | ||
} | ||
return pageData | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
import { PageContainer } from './page' | ||
import { PageContainer } from './general/page' | ||
|
||
export default function Loading (): JSX.Element { | ||
return ( | ||
<PageContainer> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/60' /> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/60' /> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/60' /> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/20' /> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/20' /> | ||
<div className='card card-compact card-bordered w-full h-56 bg-base-300/20' /> | ||
</PageContainer> | ||
) | ||
} |
3 changes: 1 addition & 2 deletions
3
src/app/editArea/[slug]/attributes/page.tsx → src/app/editArea/[slug]/location/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use client' | ||
import { useSession } from 'next-auth/react' | ||
import { useRouter } from 'next/navigation' | ||
|
||
import { SingleEntryForm } from 'app/editArea/[slug]/components/SingleEntryForm' | ||
import { DashboardInput } from '@/components/ui/form/Input' | ||
import useUpdateAreasCmd from '@/js/hooks/useUpdateAreasCmd' | ||
import { AreaType } from '@/js/types' | ||
import { AreaDesignationRadioGroup, areaDesignationToDb, AreaTypeFormProp } from '@/components/edit/form/AreaDesignationRadioGroup' | ||
import { AREA_NAME_FORM_VALIDATION_RULES } from '@/components/edit/EditAreaForm' | ||
|
||
/** | ||
* | ||
*/ | ||
export const AddAreaForm: React.FC<{ area: AreaType }> = ({ area }) => { | ||
const { uuid } = area | ||
const session = useSession({ required: true }) | ||
const router = useRouter() | ||
const { addOneAreaCmd } = useUpdateAreasCmd({ | ||
areaId: uuid, | ||
accessToken: session?.data?.accessToken as string | ||
} | ||
) | ||
return ( | ||
<SingleEntryForm<{ areaName: string, areaType: AreaTypeFormProp }> | ||
initialValues={{ areaName: '' }} | ||
keepValuesAfterReset={false} | ||
title='Add new area' | ||
helperText='Do not copy description from guidebooks.' | ||
submitHandler={async ({ areaName, areaType }) => { | ||
const { isBoulder, isLeaf } = areaDesignationToDb(areaType) | ||
await addOneAreaCmd({ name: areaName, parentUuid: uuid, isBoulder, isLeaf }) | ||
router.refresh() // Ask Next to refresh props from the server | ||
}} | ||
className='border-primary border-2' | ||
> | ||
<DashboardInput | ||
name='areaName' | ||
label='Enter the area name.' | ||
className='w-full' | ||
registerOptions={AREA_NAME_FORM_VALIDATION_RULES} | ||
/> | ||
<AreaDesignationRadioGroup disabled={false} /> | ||
</SingleEntryForm> | ||
) | ||
} |
Oops, something went wrong.