Skip to content

Commit

Permalink
save handling, placeholders, dynamic dist types
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeKarow committed Dec 20, 2023
1 parent cfd05a8 commit 34a7850
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 76 deletions.
8 changes: 7 additions & 1 deletion apps/app/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"back-to-dynamic": "Back to <u>{{page}}</u>",
"back-to-search": "Back to search"
},
"cancel": "Cancel",
"can-help-people-in": "Can help people located anywhere in {{location}}",
"cancel": "Cancel",
"claim-org-modal": {
"list": "<textUtility1>🔗 Claim your organization’s profile page and build trust with your audience on InReach</textUtility1>\n<textUtility1>✍🏾 Update your organization's information on InReach</textUtility1>\n<textUtility1>📨 Invite other staff to join your organization on InReach</textUtility1>\n<textUtility1>🔑 Gain access to future features built specifically for affiliated service providers on InReach</textUtility1>",
"title": "<emojiLg>🏠</emojiLg>\n<title2>This organization has not yet been claimed by a service provider.</title2>\n<textDarkGray>With a free InReach Service Provider account, you will soon be able to:</textDarkGray>"
Expand Down Expand Up @@ -211,6 +211,9 @@
"photo_one": "Photo",
"photo_other": "Photos",
"please-specify": "Please specify",
"portal-module": {
"service-area": "Service Area"
},
"powered-by-vercel": "Powered by Vercel",
"prefer-not-to-say": "Prefer not to say",
"privacy-policy": "Privacy policy",
Expand Down Expand Up @@ -265,6 +268,9 @@
"organization-placeholder-searchby": "Search by organization name...",
"suggest-resource": "Can't find it? <strong>Suggest an organization</strong> you think should be included."
},
"select": {
"base": "Select {{- item}}"
},
"send-email": "Send email",
"service": {
"additional-info": "Additional eligibility information",
Expand Down
14 changes: 14 additions & 0 deletions packages/ui/modals/CoverageArea/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useState } from 'react'

export const useServiceAreaSelections = () => {
const [selected, setSelected] = useState<SelectionState>({ country: null, govDist: null, subDist: null })
const setVal = {
country: (value: string) => setSelected({ country: value, govDist: null, subDist: null }),
govDist: (value: string) => setSelected((prev) => ({ ...prev, govDist: value, subDist: null })),
subDist: (value: string) => setSelected((prev) => ({ ...prev, subDist: value })),
blank: () => setSelected({ country: null, govDist: null, subDist: null }),
}

return [selected, setVal] as [typeof selected, typeof setVal]
}
type SelectionState = { country: string | null; govDist: string | null; subDist: string | null }
1 change: 1 addition & 0 deletions packages/ui/modals/CoverageArea/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
fieldOpt.countries,
fieldOpt.govDists,
serviceArea.getServiceArea,
serviceArea.update,
],
rqDevtools: true,
whyDidYouRender: { collapseGroups: true },
Expand Down
174 changes: 99 additions & 75 deletions packages/ui/modals/CoverageArea/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,37 @@ import {
Title,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { compareArrayVals } from 'crud-object-diff'
import compact from 'just-compact'
import { useTranslation } from 'next-i18next'
import { forwardRef, useState } from 'react'
import { type TFunction, useTranslation } from 'next-i18next'
import { forwardRef } from 'react'
import { useForm } from 'react-hook-form'

import { Icon } from '~ui/icon'
import { trpc as api } from '~ui/lib/trpcClient'

import { useServiceAreaSelections } from './hooks'
import { ServiceAreaForm, type ZServiceAreaForm } from './schema'
import { useStyles } from './styles'
import { ModalTitle } from '../ModalTitle'

const reduceDistType = (data: { tsNs: string; tsKey: string }[] | undefined, t: TFunction) => {
if (!data) return ''
const valueSet = data.reduce((prev, curr) => {
const translated = t(curr.tsKey, { ns: curr.tsNs, count: 1 })
prev.add(translated)
return prev
}, new Set<string>())
return [...valueSet].sort().join('/')
}

const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }, ref) => {
const { classes } = useStyles()
const { t, i18n } = useTranslation(['common', 'gov-dist'])
const countryTranslation = new Intl.DisplayNames(i18n.language, { type: 'region' })
const [opened, { open, close }] = useDisclosure(true) //TODO: remove `true` when done with dev

const [selected, setSelected] = useState<SelectionState>({ country: null, govDist: null, subDist: null })
const setVal = {
country: (value: string) => setSelected({ country: value, govDist: null, subDist: null }),
govDist: (value: string) => setSelected((prev) => ({ ...prev, govDist: value, subDist: null })),
subDist: (value: string) => setSelected((prev) => ({ ...prev, subDist: value })),
blank: () => setSelected({ country: null, govDist: null, subDist: null }),
}
const [selected, setVal] = useServiceAreaSelections()

const { data: dataServiceArea } = api.serviceArea.getServiceArea.useQuery(id)
const { data: dataCountry } = api.fieldOpt.countries.useQuery(
{ activeForOrgs: true },
{
Expand All @@ -60,6 +64,7 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
label: t(tsKey, { ns: tsNs }),
tsKey,
tsNs,
parent: null,
...rest,
})) ?? [],
placeholderData: [],
Expand All @@ -78,6 +83,9 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
placeholderData: [],
})
const apiUtils = api.useUtils()

const updateServiceArea = api.serviceArea.update.useMutation()

const form = useForm<ZServiceAreaForm>({
resolver: zodResolver(ServiceAreaForm),
defaultValues: async () => {
Expand All @@ -94,7 +102,13 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
const serviceAreaCountries = form.watch('countries')
const serviceAreaDistricts = form.watch('districts')

console.log(serviceAreaCountries, serviceAreaDistricts)
const placeHolders = {
first: t('select.base', { item: 'Country' }),
second: t('select.base', {
item: reduceDistType(dataDistrict?.map(({ govDistType }) => govDistType), t),
}),
third: t('select.base', { item: reduceDistType(dataSubDist?.map(({ govDistType }) => govDistType), t) }),
}

const handleAdd = () => {
switch (true) {
Expand All @@ -107,7 +121,16 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
if (!valToAdd) return
form.setValue(
'districts',
[...serviceAreaDistricts, { id: valToAdd.value, tsKey: valToAdd.tsKey, tsNs: valToAdd.tsNs }],
[
...serviceAreaDistricts,
{
id: valToAdd.value,
tsKey: valToAdd.tsKey,
tsNs: valToAdd.tsNs,
parent: valToAdd.parent,
country: valToAdd.country,
},
],
{
shouldValidate: true,
}
Expand All @@ -127,68 +150,70 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
}
}

// const LocationSelect = ({ placeholder, data /*, inputPropsName*/ }: SelectFieldProps) => {
// // Display close button when field is not empty
// // const displayClose = form.getInputProps(inputPropsName).value.length > 0
// const rightSection = (
// <Group noWrap spacing={5} position='right'>
// {
// //displayClose && (
// <>
// <ActionIcon
// onClick={() => console.log('clicked')}
// variant='transparent'
// style={{ pointerEvents: 'all' }}
// >
// <Icon width={24} icon='carbon:close' />
// </ActionIcon>
// <Divider orientation='vertical' />
// </>
// /*)*/
// }
// <Icon width={24} icon='carbon:chevron-down' />
// </Group>
// )

// // Disable Select fields unless it's the state select field, or the state field has a value
// // const disabled = inputPropsName.includes('state) || form.getInputProps('state').value.length === 0

// return (
// <Select
// rightSectionWidth={64}
// rightSection={rightSection}
// styles={{ rightSection: { pointerEvents: 'none' } }}
// placeholder={placeholder as string}
// data={data}
// //disabled={disabled}
// //form.getInputProps(inputPropsName)
// />
// )
// }

const activeAreas = compact(
[
serviceAreaCountries?.map((country) => (
<Badge key={country.id} variant='outline' className={classes.locationBadge}>
<Group spacing={8} align='center' noWrap>
<Text>{countryTranslation.of(country.cca2)}</Text>
<CloseButton variant='transparent' onClick={() => console.log('Delete: ', location)} />
<CloseButton
variant='transparent'
onClick={() =>
form.setValue('countries', serviceAreaCountries?.filter(({ id }) => id !== country.id))
}
/>
</Group>
</Badge>
)),

// Display -> Country / District / Sub-District
serviceAreaDistricts?.map((govDist) => (
<Badge key={govDist.id} variant='outline' className={classes.locationBadge}>
<Group spacing={8} align='center' noWrap>
<Text>{t(govDist.tsKey, { ns: govDist.tsNs })}</Text>
<CloseButton variant='transparent' onClick={() => console.log('Delete: ', location)} />
</Group>
</Badge>
)),
serviceAreaDistricts?.map((govDist) => {
const { id, tsKey, tsNs, country, parent } = govDist

const displayName = compact([
country.cca2,
parent ? t(parent.tsKey, { ns: parent.tsNs }) : null,
t(tsKey, { ns: tsNs }),
]).join(' → ')

return (
<Badge key={id} variant='outline' className={classes.locationBadge}>
<Group spacing={8} align='center' noWrap>
<Text>{displayName}</Text>
<CloseButton
variant='transparent'
onClick={() =>
form.setValue('districts', serviceAreaDistricts?.filter(({ id }) => id !== govDist.id))
}
/>
</Group>
</Badge>
)
}),
].flat()
)

const handleSave = () => {
const initialData = {
id: form.formState.defaultValues?.id,
countries: compact(form.formState.defaultValues?.countries?.map((country) => country?.id) ?? []),
districts: compact(form.formState.defaultValues?.districts?.map((district) => district?.id) ?? []),
}
const data = form.getValues()
const currentData = {
id: data.id,
countries: data.countries.map((country) => country.id),
districts: data.districts.map((district) => district.id),
}

const changes = {
id: data.id,
countries: compareArrayVals([initialData.countries, currentData.countries]),
districts: compareArrayVals([initialData.districts, currentData.districts]),
}
updateServiceArea.mutate(changes)
}

return (
<>
<Modal
Expand All @@ -198,7 +223,7 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
>
<Stack spacing={24} className={classes.ModalContent} align='center'>
<Stack align='center'>
<Title order={2}>{t('coverage-area')}</Title>
<Title order={2}>{t('portal-module.service-area')}</Title>
<Text sx={(theme) => ({ ...theme.other.utilityFonts.utility4, color: 'black' })}>
{`${t('organization')}: `}
</Text>
Expand All @@ -207,26 +232,33 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
<Group spacing={12}>{activeAreas}</Group>
</Stack>
<Stack spacing={16}>
<Text sx={(theme) => theme.other.utilityFonts.utility1}>{t('add-coverage-area')}</Text>
<Text sx={(theme) => theme.other.utilityFonts.utility1}>
{t('add', {
item: '$t(portal-module.service-area)',
})}
</Text>
<Grid gutter='xl' gutterXl='xl'>
<Grid.Col xs={9} sm={9}>
<Stack className={classes.selectSectionWrapper}>
<Select
placeholder={t('select-country')}
placeholder={placeHolders.first}
data={dataCountry ?? []}
value={selected.country}
onChange={setVal.country}
/>
{selected.country && !!dataDistrict?.length && (
<Select
placeholder={t('select-next')}
placeholder={placeHolders.second}
data={dataDistrict ?? []}
value={selected.govDist}
onChange={setVal.govDist}
/>
)}
{selected.govDist && !!dataSubDist?.length && (
<Select
placeholder={t('select-next')}
placeholder={placeHolders.third}
data={dataSubDist ?? []}
value={selected.subDist}
onChange={setVal.subDist}
/>
)}
Expand All @@ -239,7 +271,7 @@ const CoverageAreaModal = forwardRef<HTMLButtonElement, Props>(({ id, ...props }
</Grid.Col>
</Grid>
</Stack>
<Button size='lg' radius='md' type='submit' fullWidth>
<Button size='lg' radius='md' type='submit' fullWidth onClick={handleSave}>
{t('save-changes')}
</Button>
</Stack>
Expand All @@ -256,11 +288,3 @@ export const CoverageArea = createPolymorphicComponent<HTMLButtonElement, Props>
interface Props extends ButtonProps {
id: string
}

type SelectFieldProps = {
placeholder: string
data: string[]
// inputPropsName: string
}

type SelectionState = { country: string | null; govDist: string | null; subDist: string | null }

0 comments on commit 34a7850

Please sign in to comment.