Skip to content

Commit

Permalink
chore(next): scaffolds admin layout and dashboard view (#4566)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobsfletch authored Dec 20, 2023
1 parent a6d7852 commit cc8c23d
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 148 deletions.
8 changes: 8 additions & 0 deletions packages/dev/src/app/(payload)/admin/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { AdminLayout } from '@payloadcms/next/layouts/Admin'
import configPromise from 'payload-config'

export default async ({ children }: { children: React.ReactNode }) => (
<AdminLayout config={configPromise}>{children}</AdminLayout>
)
18 changes: 18 additions & 0 deletions packages/next/src/layouts/Admin/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'
import { Default as DefaultTemplate } from '@payloadcms/ui/templates'
import { SanitizedConfig } from 'payload/types'
import Link from 'next/link'

import '@payloadcms/ui/scss/app.scss'

export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export const AdminLayout = async ({
children, // config: configPromise,
}: {
children: React.ReactNode
config: Promise<SanitizedConfig>
}) => <DefaultTemplate Link={Link}>{children}</DefaultTemplate>
7 changes: 5 additions & 2 deletions packages/ui/src/elements/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, Props>((

switch (el) {
case 'link':
if (!Link) throw new Error('Link is required when using el="link"')
if (!Link) {
console.error('Link is required when using el="link"')
return null
}

return (
<Link {...buttonProps} to={to || url}>
<Link {...buttonProps} to={to || url} href={to || url}>
<ButtonContents icon={icon} showTooltip={showTooltip} tooltip={tooltip}>
{children}
</ButtonContents>
Expand Down
11 changes: 8 additions & 3 deletions packages/ui/src/elements/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ import './index.scss'
const baseClass = 'card'

export const Card: React.FC<Props> = (props) => {
const { id, actions, buttonAriaLabel, onClick, title, titleAs } = props
const { id, actions, buttonAriaLabel, onClick, title, titleAs, Link, href } = props

const classes = [baseClass, id, onClick && `${baseClass}--has-onclick`].filter(Boolean).join(' ')
const classes = [baseClass, id, (onClick || href) && `${baseClass}--has-onclick`]
.filter(Boolean)
.join(' ')

const Tag = titleAs ?? 'div'

return (
<div className={classes} id={id}>
<Tag className={`${baseClass}__title`}>{title}</Tag>
{actions && <div className={`${baseClass}__actions`}>{actions}</div>}
{onClick && (
{(onClick || href) && (
<Button
aria-label={buttonAriaLabel}
buttonStyle="none"
className={`${baseClass}__click`}
onClick={onClick}
Link={Link}
el="link"
to={href}
/>
)}
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/elements/Card/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export type Props = {
onClick?: () => void
title: string
titleAs?: ElementType
Link?: ElementType
href?: string
}
16 changes: 10 additions & 6 deletions packages/ui/src/elements/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'

import Account from '../../graphics/Account'
import { useConfig } from '../../providers/Config'
Expand All @@ -15,7 +14,9 @@ import { useActions } from '../../providers/ActionsProvider'

const baseClass = 'app-header'

export const AppHeader: React.FC = () => {
export const AppHeader: React.FC<{
Link?: React.ComponentType
}> = ({ Link }) => {
const { t } = useTranslation()

const {
Expand Down Expand Up @@ -47,6 +48,8 @@ export const AppHeader: React.FC = () => {
}
}, [actions])

const LinkElement = Link || 'a'

return (
<header className={[baseClass, navOpen && `${baseClass}--nav-open`].filter(Boolean).join(' ')}>
<div className={`${baseClass}__bg`} />
Expand All @@ -57,7 +60,7 @@ export const AppHeader: React.FC = () => {
</NavToggler>
<div className={`${baseClass}__controls-wrapper`}>
<div className={`${baseClass}__step-nav-wrapper`}>
<StepNav className={`${baseClass}__step-nav`} />
<StepNav className={`${baseClass}__step-nav`} Link={Link} />
</div>
<div className={`${baseClass}__actions-wrapper`}>
<div className={`${baseClass}__actions`} ref={customControlsRef}>
Expand All @@ -78,14 +81,15 @@ export const AppHeader: React.FC = () => {
{localization && (
<LocalizerLabel ariaLabel="invisible" className={`${baseClass}__localizer-spacing`} />
)}
<Link
<LinkElement
aria-label={t('authentication:account')}
className={`${baseClass}__account`}
tabIndex={0}
to={`${adminRoute}/account`}
// to={`${adminRoute}/account`} // for `react-router-dom` Link
href={`${adminRoute}/account`} // for `next/link` Link
>
<Account />
</Link>
</LinkElement>
</div>
</div>
</div>
Expand Down
13 changes: 8 additions & 5 deletions packages/ui/src/elements/Logout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'

import { LogOut } from '../../icons/LogOut'
import { useConfig } from '../../providers/Config'
Expand All @@ -10,7 +9,8 @@ const baseClass = 'nav'

const DefaultLogout: React.FC<{
tabIndex?: number
}> = ({ tabIndex }) => {
Link: React.ComponentType
}> = ({ tabIndex, Link }) => {
const { t } = useTranslation('authentication')
const config = useConfig()

Expand All @@ -19,15 +19,18 @@ const DefaultLogout: React.FC<{
routes: { admin },
} = config

const LinkElement = Link || 'a'

return (
<Link
<LinkElement
aria-label={t('logOut')}
className={`${baseClass}__log-out`}
tabIndex={tabIndex}
to={`${admin}${logoutRoute}`}
// to={`${admin}${logoutRoute}`} // for `react-router-dom`
href={`${admin}${logoutRoute}`} // for `next/link`
>
<LogOut />
</Link>
</LinkElement>
)
}

Expand Down
17 changes: 11 additions & 6 deletions packages/ui/src/elements/Nav/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { NavLink } from 'react-router-dom'

import type { EntityToGroup, Group } from '../../utilities/groupNavItems'

Expand All @@ -19,7 +18,10 @@ import './index.scss'

const baseClass = 'nav'

const DefaultNav: React.FC = () => {
const DefaultNav: React.FC<{
Link?: React.ComponentType
}> = (props) => {
const { Link } = props
const { navOpen, navRef, setNavOpen } = useNav()
const { permissions, user } = useAuth()
const [groups, setGroups] = useState<Group[]>([])
Expand Down Expand Up @@ -97,20 +99,23 @@ const DefaultNav: React.FC = () => {
id = `nav-global-${entity.slug}`
}

const LinkElement = Link || 'a'

return (
<NavLink
activeClassName="active"
<LinkElement
// activeClassName="active"
className={`${baseClass}__link`}
id={id}
key={i}
tabIndex={!navOpen ? -1 : undefined}
to={href}
// to={href} // for `react-router-dom` Link
href={href} // for `next/link` Link
>
<span className={`${baseClass}__link-icon`}>
<Chevron direction="right" />
</span>
<span className={`${baseClass}__link-label`}>{entityLabel}</span>
</NavLink>
</LinkElement>
)
})}
</NavGroup>
Expand Down
25 changes: 18 additions & 7 deletions packages/ui/src/elements/StepNav/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'
import React, { Fragment, createContext, useContext, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'

import type { Context as ContextType } from './types'

Expand Down Expand Up @@ -33,23 +32,32 @@ const useStepNav = (): ContextType => useContext(Context)

const StepNav: React.FC<{
className?: string
}> = (props) => {
const { className } = props
Link?: React.ComponentType
}> = ({ Link, className }) => {
const { i18n } = useTranslation()

const { stepNav } = useStepNav()

const config = useConfig()

const {
routes: { admin },
} = config

const LinkElement = Link || 'a'

return (
<Fragment>
{stepNav.length > 0 ? (
<nav className={[baseClass, className].filter(Boolean).join(' ')}>
<Link className={`${baseClass}__home`} tabIndex={0} to={admin}>
<LinkElement
className={`${baseClass}__home`}
tabIndex={0}
// to={admin} // for `react-router-dom`
href={admin} // for `next/link`
>
<IconGraphic />
</Link>
</LinkElement>
<span>/</span>
{stepNav.map((item, i) => {
const StepLabel = getTranslation(item.label, i18n)
Expand All @@ -62,9 +70,12 @@ const StepNav: React.FC<{
) : (
<Fragment key={i}>
{item.url ? (
<Link to={item.url}>
<LinkElement
// to={item.url} // for `react-router-dom`
href={item.url} // for `next/link`
>
<span key={i}>{StepLabel}</span>
</Link>
</LinkElement>
) : (
<span key={i}>{StepLabel}</span>
)}
Expand Down
9 changes: 5 additions & 4 deletions packages/ui/src/graphics/Account/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
'use client'
import React from 'react'
import { useLocation } from 'react-router-dom'

import { useAuth } from '../../providers/Auth'
import { useConfig } from '../../providers/Config'
import { DefaultAccountIcon } from './Default'
import Gravatar from './Gravatar'
import { usePathname } from 'next/navigation'

const Account = () => {
const {
admin: { avatar: Avatar },
routes: { admin: adminRoute },
} = useConfig()
const { user } = useAuth()
const location = useLocation()
const pathname = usePathname()

const isOnAccountPage = location.pathname === `${adminRoute}/account`
const isOnAccountPage = pathname === `${adminRoute}/account`

if (!user.email || Avatar === 'default') return <DefaultAccountIcon active={isOnAccountPage} />
if (!user?.email || Avatar === 'default') return <DefaultAccountIcon active={isOnAccountPage} />
if (Avatar === 'gravatar') return <Gravatar />
if (Avatar) return <Avatar active={isOnAccountPage} />
return <DefaultAccountIcon active={isOnAccountPage} />
Expand Down
8 changes: 2 additions & 6 deletions packages/ui/src/templates/Default/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use client'
import React, { Fragment } from 'react'
import { useTranslation } from 'react-i18next'

import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types'
import type { Props } from './types'

import { Hamburger } from '../../elements/Hamburger'
Expand All @@ -16,7 +14,7 @@ import './index.scss'

const baseClass = 'template-default'

export const Default: React.FC<Props> = ({ children, className }) => {
export const Default: React.FC<Props> = ({ children, className, Link }) => {
const {
admin: {
components: { Nav: CustomNav } = {
Expand All @@ -25,8 +23,6 @@ export const Default: React.FC<Props> = ({ children, className }) => {
} = {},
} = useConfig()

const { t } = useTranslation('general')

const { navOpen } = useNav()

return (
Expand All @@ -43,7 +39,7 @@ export const Default: React.FC<Props> = ({ children, className }) => {
>
<RenderCustomComponent CustomComponent={CustomNav} DefaultComponent={DefaultNav} />
<div className={`${baseClass}__wrap`}>
<AppHeader />
<AppHeader Link={Link} />
{children}
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/templates/Default/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import type React from 'react'
export type Props = {
children?: React.ReactNode
className?: string
Link?: React.ComponentType<any>
}
Loading

0 comments on commit cc8c23d

Please sign in to comment.