From 24fa81a3ce70e6bd3868f10bac4ac40418e97f05 Mon Sep 17 00:00:00 2001
From: Joe Karow <58997957+JoeKarow@users.noreply.github.com>
Date: Mon, 21 Aug 2023 18:08:26 -0400
Subject: [PATCH] edit mode toolbar
---
apps/app/public/locales/en/common.json | 8 ++
apps/app/src/pages/org/[slug]/edit.tsx | 69 ++++----------
packages/ui/components/core/Breadcrumb.tsx | 6 +-
packages/ui/components/sections/Navbar.tsx | 105 ++++++++++++++++++++-
packages/ui/hooks/useEditMode.ts | 9 +-
packages/ui/providers/EditMode.tsx | 11 ++-
6 files changed, 148 insertions(+), 60 deletions(-)
diff --git a/apps/app/public/locales/en/common.json b/apps/app/public/locales/en/common.json
index 6f761ecd858..07f5ad60a77 100644
--- a/apps/app/public/locales/en/common.json
+++ b/apps/app/public/locales/en/common.json
@@ -95,6 +95,9 @@
"try-again-text": "Something went wrong! Please try again."
},
"exclude": "Exclude",
+ "exit": {
+ "edit-mode": "Exit edit mode"
+ },
"filter-by-service": "Filter by services",
"find-resources": "Find resources",
"find-x": "Find {{value}}",
@@ -396,9 +399,13 @@
"please-wait": "Please wait...",
"prev": "Prev",
"print": "Print",
+ "publish": "Publish",
+ "restore": "Restore",
+ "reverify": "Reverify",
"review": "Review",
"reviews": "Reviews",
"save": "Save",
+ "save-changes": "Save changes",
"saved": "Saved",
"search": "Search",
"service-hours": "Service hours",
@@ -407,6 +414,7 @@
"sign-up": "Sign up",
"skip": "Skip",
"support": "Support",
+ "unpublish": "Unpublish",
"website": "Website",
"yes": "Yes"
}
diff --git a/apps/app/src/pages/org/[slug]/edit.tsx b/apps/app/src/pages/org/[slug]/edit.tsx
index 6d7574a5075..1e064c66558 100644
--- a/apps/app/src/pages/org/[slug]/edit.tsx
+++ b/apps/app/src/pages/org/[slug]/edit.tsx
@@ -1,7 +1,7 @@
/* eslint-disable i18next/no-literal-string */
import { Grid, Stack, Tabs, Title } from '@mantine/core'
import { useElementSize } from '@mantine/hooks'
-import { type GetServerSideProps, type NextPage } from 'next'
+import { type GetServerSideProps } from 'next'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import { type RoutedQuery } from 'nextjs-routes'
@@ -18,63 +18,31 @@ import { PhotosSection } from '@weareinreach/ui/components/sections/Photos'
import { ReviewSection } from '@weareinreach/ui/components/sections/Reviews'
import { ServicesInfoCard } from '@weareinreach/ui/components/sections/ServicesInfo'
import { VisitCard } from '@weareinreach/ui/components/sections/VisitCard'
+import { type NextPageWithOptions } from '~app/pages/_app'
import { api } from '~app/utils/api'
import { getServerSideTranslations } from '~app/utils/i18n'
-const OrganizationPage: NextPage = () => {
+const OrganizationPage: NextPageWithOptions = () => {
const { t } = useTranslation()
const router = useRouter<'/org/[slug]'>()
const { query } = router.isReady ? router : { query: { slug: '' } }
const [activeTab, setActiveTab] = useState('services')
const [loading, setLoading] = useState(true)
- const { data, status } = api.organization.getBySlug.useQuery(query, { enabled: router.isReady })
+ const { data, status } = api.organization.forOrgPageEdits.useQuery(query, { enabled: router.isReady })
+ const { data: hasRemote } = api.service.forServiceInfoCard.useQuery(
+ { parentId: data?.id ?? '', remoteOnly: true },
+ {
+ enabled: !!data?.id && data?.locations.length > 1,
+ select: (data) => data.length !== 0,
+ }
+ )
const { ref, width } = useElementSize()
useEffect(() => {
if (data && status === 'success') setLoading(false)
}, [data, status])
if (loading || !data) return <>Loading>
- const {
- // emails,
- // phones,
- // socialMedia,
- // websites,
- userLists,
- attributes,
- description,
- slug,
- // photos,
- reviews,
- locations,
- isClaimed,
- id: organizationId,
- } = data
-
- const body =
- locations?.length === 1 ? (
-
-
- {t('services')}
- {t('photo', { count: 2 })}
- {t('review', { count: 2 })}
-
-
-
-
-
-
-
-
-
-
-
- ) : (
- <>
- {locations.map((location) => (
-
- ))}
- >
- )
+ const { attributes, description, slug, reviews, locations, isClaimed, id: organizationId } = data
const sidebar =
locations?.length === 1 ? (
@@ -95,13 +63,15 @@ const OrganizationPage: NextPage = () => {
return (
<>
- EDIT MODE
+
+ EDIT MODE
+
- router.back() }}
saved={Boolean(userLists?.length)}
organizationId={organizationId}
- />
+ /> */}
{
isClaimed,
}}
/>
- {body}
+ {locations.map((location) => (
+
+ ))}
@@ -166,5 +138,4 @@ export const getServerSideProps: GetServerSideProps<
props,
}
}
-
export default OrganizationPage
diff --git a/packages/ui/components/core/Breadcrumb.tsx b/packages/ui/components/core/Breadcrumb.tsx
index 9000c5afa79..96b435fa8b9 100644
--- a/packages/ui/components/core/Breadcrumb.tsx
+++ b/packages/ui/components/core/Breadcrumb.tsx
@@ -35,7 +35,7 @@ const useStyles = createStyles((theme) => ({
const isString = (...args: unknown[]) => args.every((val) => typeof val === 'string')
export const Breadcrumb = (props: BreadcrumbProps) => {
- const { option, backTo, backToText, onClick } = props
+ const { option, backTo, backToText, onClick, children } = props
const { classes } = useStyles()
const theme = useMantineTheme()
const { t } = useTranslation('common')
@@ -125,7 +125,7 @@ export const Breadcrumb = (props: BreadcrumbProps) => {
className={classes.icon}
/>
- {childrenRender}
+ {children || childrenRender}
@@ -150,7 +150,7 @@ type PossibleBreadcrumbProps = {
export type ModalTitleBreadcrumb = Omit & {
onClick?: MouseEventHandler
}
-export type BreadcrumbProps = Close | Back | BackToDynamic
+export type BreadcrumbProps = (Close | Back | BackToDynamic) & { children?: React.ReactNode }
interface Close {
option: 'close'
onClick: MouseEventHandler
diff --git a/packages/ui/components/sections/Navbar.tsx b/packages/ui/components/sections/Navbar.tsx
index ff8c35465ff..0cdaf1316ee 100644
--- a/packages/ui/components/sections/Navbar.tsx
+++ b/packages/ui/components/sections/Navbar.tsx
@@ -1,5 +1,6 @@
-import { Container, createStyles, Flex, Group, rem } from '@mantine/core'
+import { Container, createStyles, Flex, Group, rem, UnstyledButton, useMantineTheme } from '@mantine/core'
import Image from 'next/image'
+import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import InReachLogo from '~ui/assets/inreach.svg'
@@ -7,25 +8,115 @@ import { Button } from '~ui/components/core/Button'
import { Link } from '~ui/components/core/Link'
import { MobileNav } from '~ui/components/core/MobileNav'
import { UserMenu } from '~ui/components/core/UserMenu'
-import { useCustomVariant, useScreenSize } from '~ui/hooks'
+import { useCustomVariant, useEditMode, useScreenSize } from '~ui/hooks'
+import { Icon } from '~ui/icon'
+import { trpc as api } from '~ui/lib/trpcClient'
const useStyles = createStyles((theme) => ({
desktopNav: {
- height: rem(64),
+ minHeight: rem(64),
boxShadow: theme.shadows.xs,
marginBottom: rem(40),
[theme.fn.smallerThan('sm')]: {
display: 'none',
},
+ position: 'sticky',
+ top: 0,
+ left: 0,
+ right: 0,
+ zIndex: 10000,
+ backgroundColor: theme.other.colors.secondary.white,
},
mobileNav: {
[theme.fn.largerThan('sm')]: {
display: 'none',
},
},
+ editBar: {
+ background: `linear-gradient(180deg, ${theme.other.colors.secondary.white} 0%, ${theme.fn.lighten(
+ theme.other.colors.tertiary.red,
+ 0.75
+ )} 10%)`,
+ padding: `${rem(8)} ${rem(128)} ${rem(8)} ${rem(64)}`,
+ margin: `0 ${rem(-64)}`,
+ },
+ editBarButtonText: {
+ ...theme.other.utilityFonts.utility3,
+ ...theme.fn.hover({ backgroundColor: theme.other.colors.secondary.white }),
+ padding: `${rem(4)} ${rem(8)}`,
+ borderRadius: rem(8),
+ '&:disabled': {
+ color: theme.other.colors.secondary.darkGray,
+ cursor: 'not-allowed',
+ ...theme.fn.hover({ backgroundColor: theme.other.colors.primary.lightGray }),
+ },
+ },
}))
+const EditModeBar = () => {
+ const { classes } = useStyles()
+ const theme = useMantineTheme()
+ const { canSave, handleEditSubmit } = useEditMode()
+ const { t } = useTranslation('common')
+ const router = useRouter()
+ const { orgLocationId, slug } = router.query
+ const { data, isLoading } = api.misc.forEditNavbar.useQuery(
+ orgLocationId ? { orgLocationId: orgLocationId as string } : { slug: slug as string },
+ {
+ enabled: typeof orgLocationId === 'string' || typeof slug === 'string',
+ }
+ )
+
+ return (
+
+
+
+
+ {t('exit.edit-mode')}
+
+
+
+ handleEditSubmit(() => console.log('save action from toolbar'))}
+ >
+
+
+ {t('words.save-changes')}
+
+
+ {slug && !orgLocationId && (
+
+
+
+ {t('words.reverify')}
+
+
+ )}
+
+
+
+ {t(data?.published ? 'words.unpublish' : 'words.publish')}
+
+
+
+
+
+ {t(data?.deleted ? 'words.restore' : 'words.delete')}
+
+
+
+
+ )
+}
+
export const Navbar = () => {
+ const { isEditMode } = useEditMode()
const { t } = useTranslation('common')
const { classes } = useStyles()
const variants = useCustomVariant()
@@ -53,7 +144,15 @@ export const Navbar = () => {
+ {isEditMode ? : null}
>
)
}
+
+type NavbarProps = {
+ editMode?: boolean
+ editModeRef?: {
+ handleEditSubmit: (handler: () => void) => void
+ }
+}
diff --git a/packages/ui/hooks/useEditMode.ts b/packages/ui/hooks/useEditMode.ts
index dd0f0006f92..20b39fec319 100644
--- a/packages/ui/hooks/useEditMode.ts
+++ b/packages/ui/hooks/useEditMode.ts
@@ -1,11 +1,18 @@
+import { useRouter } from 'next/router'
import { useContext } from 'react'
import { EditModeContext } from '~ui/providers/EditMode'
export const useEditMode = () => {
+ const router = useRouter()
const ctx = useContext(EditModeContext)
if (!ctx) {
throw new Error('useEditMode must be used within a EditModeProvider')
}
- return ctx
+ const isEditMode = router.pathname.endsWith('/edit')
+
+ return {
+ isEditMode,
+ ...ctx,
+ }
}
diff --git a/packages/ui/providers/EditMode.tsx b/packages/ui/providers/EditMode.tsx
index fb6f026fc37..5808bef150f 100644
--- a/packages/ui/providers/EditMode.tsx
+++ b/packages/ui/providers/EditMode.tsx
@@ -4,14 +4,17 @@ export const EditModeContext = createContext(null)
export const EditModeProvider = ({ children }: { children: React.ReactNode }) => {
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
- const editModeRef = useRef({ handleEditSubmit: (handler: () => void) => handler() })
+ const editModeRef = useRef({
+ handleEditSubmit: (handler: () => void) => handler(),
+ canSave: false,
+ })
const contextValue = {
unsaved: {
set: setHasUnsavedChanges,
state: hasUnsavedChanges,
},
- submitEditHandler: editModeRef.current.handleEditSubmit,
+ ...editModeRef.current,
}
return {children}
@@ -22,8 +25,8 @@ type EditContext = {
set: Dispatch>
state: boolean
}
- submitEditHandler: (handler: () => void) => void
-}
+} & EditModeRef
type EditModeRef = {
handleEditSubmit: (handler: () => void) => void
+ canSave: boolean
}