Skip to content

Commit

Permalink
feat: add/link dropdowns (#1076)
Browse files Browse the repository at this point in the history
* email to location linking dropdown menu

* rollback & block t3-oss/[email protected] - bug

* allow unpublished for elevated users

* skip loading org specific i18n

* phone to location linking dropdown

* set published default to "true" on new item

* audit log routes

* api route handlers

* generated data

* consolidate api queries

* Website add or link

* SocialMedia add or link

* update mock data

* update/replace deps

* set "moduleResolution" to 'Bundler' for 3rd party packages

added export defs for @weareinreach/auth

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
JoeKarow and kodiakhq[bot] authored Feb 12, 2024
1 parent 3d745f8 commit 708456b
Show file tree
Hide file tree
Showing 72 changed files with 1,723 additions and 275 deletions.
6 changes: 6 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
"groupName": "patched packages",
"matchPackageNames": ["@crowdin/ota-client", "trpc-panel", "msw-storybook-addon"],
"matchUpdateTypes": ["major", "minor", "patch"]
},
{
"enabled": false,
"groupName": "Ignored Versions",
"matchCurrentVersion": "0.9.2",
"matchPackageNames": ["@t3-oss/env-nextjs"]
}
],
"semanticCommitScope": "{{parentDir}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,13 @@ export const getServerSideProps: GetServerSideProps = async ({ locale, params, r
const ssg = await trpcServerClient({ session })
const { id: orgId } = await ssg.organization.getIdFromSlug.fetch({ slug })
const [i18n] = await Promise.all([
getServerSideTranslations(locale, ['common', 'services', 'attribute', orgId]),
getServerSideTranslations(locale, ['common', 'services', 'attribute']),
ssg.page.serviceEdit.prefetch({ id: orgServiceId }),
ssg.component.ServiceSelect.prefetch(),
ssg.service.getOptions.prefetch(),
])
const props = {
organizationId: orgId,
session,
trpcState: ssg.dehydrate(),
...i18n,
Expand Down
33 changes: 15 additions & 18 deletions apps/app/src/pages/org/[slug]/[orgLocationId]/edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,9 @@ const OrgLocationPage: NextPage<InferGetServerSidePropsType<typeof getServerSide
const { slug, orgLocationId } = query
const [activeTab, setActiveTab] = useState<string | null>('services')
const [loading, setLoading] = useState(true)
const { data: orgData, status: orgDataStatus } = api.organization.getBySlug.useQuery(query, {
enabled: router.isReady,
})
const { data, status } = api.location.forLocationPageEdits.useQuery({ id: orgLocationId })
const { data: isSaved } = api.savedList.isSaved.useQuery(orgData?.id as string, {
enabled: orgDataStatus === 'success' && Boolean(orgData?.id),
const { data: isSaved } = api.savedList.isSaved.useQuery(data?.organization?.id ?? '', {
enabled: status === 'success' && Boolean(data?.organization?.id),
})
const { data: alertData } = api.location.getAlerts.useQuery(
{ id: orgLocationId },
Expand Down Expand Up @@ -105,9 +102,9 @@ const OrgLocationPage: NextPage<InferGetServerSidePropsType<typeof getServerSide
}
}, [formMethods.formState, unsaved])
useEffect(() => {
if (data && status === 'success' && orgData && orgDataStatus === 'success') setLoading(false)
}, [data, status, orgData, orgDataStatus])
if (loading || !data || !orgData) return <OrgLocationPageLoading />
if (data && status === 'success') setLoading(false)
}, [data, status])
if (loading || !data) return <OrgLocationPageLoading />

const {
// emails,
Expand All @@ -123,22 +120,24 @@ const OrgLocationPage: NextPage<InferGetServerSidePropsType<typeof getServerSide
return (
<>
<Head>
<title>{t('page-title.edit-mode', { ns: 'common', title: `${orgData.name} - ${data.name}` })}</title>
<title>
{t('page-title.edit-mode', { ns: 'common', title: `${data.organization.name} - ${data.name}` })}
</title>
</Head>
<FormProvider {...formMethods}>
<Grid.Col xs={12} sm={8} order={1}>
<Toolbar
breadcrumbProps={{
option: 'back',
backTo: 'dynamicText',
backToText: orgData.name,
backToText: data.organization.name,
onClick: () =>
router.push({
pathname: '/org/[slug]/edit',
query: { slug: orgData.slug },
query: { slug: data.organization.slug },
}),
}}
organizationId={orgData.id}
organizationId={data.organization.id}
saved={Boolean(isSaved)}
/>
<Stack pt={24} align='flex-start' spacing={40}>
Expand All @@ -147,21 +146,21 @@ const OrgLocationPage: NextPage<InferGetServerSidePropsType<typeof getServerSide
<AlertMessage
key={alert.key}
iconKey={alert.icon}
ns={orgData.id}
ns={data.organization.id}
textKey={alert.key}
defaultText={alert.text}
/>
))}
<ListingBasicInfo
data={{
name: data.name || orgData.name,
name: data.name || data.organization.name,
id: data.id,
slug,
locations: [data],
description,
lastVerified: orgData.lastVerified,
lastVerified: data.organization.lastVerified,
attributes,
isClaimed: orgData.isClaimed,
isClaimed: data.organization.isClaimed,
}}
edit
location
Expand Down Expand Up @@ -263,9 +262,7 @@ export const getServerSideProps = async ({
'phone-type',
'country',
'gov-dist',
orgId,
]),
ssg.organization.getBySlug.prefetch({ slug }),
ssg.location.forLocationPageEdits.prefetch({ id }),
ssg.location.getAlerts.prefetch({ id }),
])
Expand Down
5 changes: 3 additions & 2 deletions apps/app/src/pages/org/[slug]/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,15 @@ export const getServerSideProps = async ({
}

const ssg = await trpcServerClient({ session })
const orgId = await ssg.organization.getIdFromSlug.fetch({ slug })
const { id: organizationId } = await ssg.organization.getIdFromSlug.fetch({ slug })

const [i18n] = await Promise.all([
getServerSideTranslations(locale, compact(['common', 'services', 'attribute', 'phone-type', orgId?.id])),
getServerSideTranslations(locale, compact(['common', 'services', 'attribute', 'phone-type'])),
ssg.organization.forOrgPageEdits.prefetch({ slug }),
ssg.fieldOpt.countries.prefetch({ activeForOrgs: true }),
])
const props = {
organizationId,
session,
trpcState: ssg.dehydrate(),
...i18n,
Expand Down
1 change: 0 additions & 1 deletion apps/app/src/pages/org/[slug]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ export const getStaticProps = async ({
getServerSideTranslations(locale, formatNS(orgId)),
ssg.organization.forOrgPage.prefetch({ slug }),
])
// await ssg.organization.getBySlug.prefetch({ slug })

const props = {
trpcState: ssg.dehydrate(),
Expand Down
8 changes: 5 additions & 3 deletions packages/api/lib/middleware/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const checkPermissions = (meta: Meta | undefined, ctx: Context) => {

/** Check multiple permissions */
if (Array.isArray(meta.hasPerm)) {
if (meta.hasPerm.every((perm) => ctx.session?.user.permissions.includes(perm))) {
if (meta.hasPerm.every((perm) => ctx.session?.user?.permissions?.includes(perm))) {
return true
}
return false
Expand All @@ -51,7 +51,9 @@ export const checkRole = (allowedRoles: string[], userRoles: string[]) => {

export const isAdmin = t.middleware(({ ctx, meta, next }) => {
if (!ctx.session || !ctx.session.user) return reject()
if (!(checkRole(['dataAdmin', 'sysadmin', 'root'], ctx.session?.user.roles) && checkPermissions(meta, ctx)))
if (
!(checkRole(['dataAdmin', 'sysadmin', 'root'], ctx.session?.user?.roles) && checkPermissions(meta, ctx))
)
return reject()

return next({
Expand All @@ -66,7 +68,7 @@ export const isStaff = t.middleware(({ ctx, meta, next }) => {
if (!ctx.session || !ctx.session.user) return reject()
if (
!(
checkRole(['dataManager', 'dataAdmin', 'sysadmin', 'system', 'root'], ctx.session?.user.roles) &&
checkRole(['dataManager', 'dataAdmin', 'sysadmin', 'system', 'root'], ctx.session?.user?.roles) &&
checkPermissions(meta, ctx)
)
)
Expand Down
2 changes: 2 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
"just-map-values": "3.2.0",
"just-omit": "2.2.0",
"just-pick": "4.2.0",
"libphonenumber-js": "1.10.55",
"luxon": "3.4.4",
"nanoid": "5.0.5",
"slugify": "1.6.6",
"tiny-invariant": "1.3.1",
"useful-typescript-functions": "4.0.7",
"zod": "3.22.4"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,28 @@ export const forLocationPageEdits = async ({ input }: TRPCHandlerParams<TForLoca
select: { id: true },
},
services: { select: { serviceId: true } },
organization: {
select: {
id: true,
name: true,
slug: true,
lastVerified: true,
allowedEditors: { where: { authorized: true }, select: { userId: true } },
},
},
},
})
return location
const { organization, ...record } = location
const { allowedEditors, ...org } = organization
const formatted = {
...record,
organization: {
...org,
isClaimed: allowedEditors.length !== 0,
},
}

return formatted
} catch (error) {
handleError(error)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/api/router/misc/query.hasContactInfo.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ const whereId = (
locations: { some: { location: { id: input, ...isPublic } } },
...isPublic,
},
socialMedia: { locations: { every: { location: { id: input, ...isPublic }, ...isPublic } } },
website: { locations: { every: { location: { id: input, ...isPublic }, ...isPublic } } },
socialMedia: { locations: { every: { location: { id: input, ...isPublic } } }, ...isPublic },
website: { locations: { every: { location: { id: input, ...isPublic } } }, ...isPublic },
}
}
case isIdFor('orgService', input): {
Expand Down
18 changes: 18 additions & 0 deletions packages/api/router/orgEmail/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,22 @@ export const orgEmailRouter = defineRouter({
)
return handler(opts)
}),
getLinkOptions: permissionedProcedure('updateEmail')
.input(schema.ZGetLinkOptionsSchema)
.query(async (opts) => {
const handler = await importHandler(
namespaced('getLinkOptions'),
() => import('./query.getLinkOptions.handler')
)
return handler(opts)
}),
locationLink: permissionedProcedure('updateEmail')
.input(schema.ZLocationLinkSchema)
.mutation(async (opts) => {
const handler = await importHandler(
namespaced('locationLink'),
() => import('./mutation.locationLink.handler')
)
return handler(opts)
}),
})
39 changes: 39 additions & 0 deletions packages/api/router/orgEmail/mutation.locationLink.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { getAuditedClient } from '@weareinreach/db'
import { handleError } from '~api/lib/errorHandler'
import { type TRPCHandlerParams } from '~api/types/handler'

import { type TLocationLinkSchema } from './mutation.locationLink.schema'

export const locationLink = async ({ ctx, input }: TRPCHandlerParams<TLocationLinkSchema, 'protected'>) => {
try {
const prisma = getAuditedClient(ctx.actorId)
const { action, orgEmailId, orgLocationId } = input

switch (action) {
case 'link': {
const result = await prisma.orgLocationEmail.create({
data: {
orgEmailId,
orgLocationId,
active: true,
},
})
return result
}
case 'unlink': {
const result = await prisma.orgLocationEmail.delete({
where: {
orgEmailId_orgLocationId: {
orgEmailId,
orgLocationId,
},
},
})
return result
}
}
} catch (error) {
handleError(error)
}
}
export default locationLink
10 changes: 10 additions & 0 deletions packages/api/router/orgEmail/mutation.locationLink.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from 'zod'

import { prefixedId } from '~api/schemas/idPrefix'

export const ZLocationLinkSchema = z.object({
orgEmailId: prefixedId('orgEmail'),
orgLocationId: prefixedId('orgLocation'),
action: z.enum(['link', 'unlink']),
})
export type TLocationLinkSchema = z.infer<typeof ZLocationLinkSchema>
34 changes: 34 additions & 0 deletions packages/api/router/orgEmail/query.getLinkOptions.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { prisma } from '@weareinreach/db'
import { handleError } from '~api/lib/errorHandler'
import { type TRPCHandlerParams } from '~api/types/handler'

import { type TGetLinkOptionsSchema } from './query.getLinkOptions.schema'

export const getLinkOptions = async ({ input }: TRPCHandlerParams<TGetLinkOptionsSchema>) => {
try {
const { slug, locationId } = input
const result = await prisma.orgEmail.findMany({
where: {
organization: { some: { organization: { slug } } },
locations: { every: { orgLocationId: { not: locationId } } },
},
select: {
id: true,
firstName: true,
lastName: true,
email: true,
published: true,
deleted: true,
description: { select: { tsKey: { select: { text: true } } } },
},
})
const transformed = result.map(({ description, ...record }) => ({
...record,
description: description ? description.tsKey.text : null,
}))
return transformed
} catch (error) {
handleError(error)
}
}
export default getLinkOptions
6 changes: 6 additions & 0 deletions packages/api/router/orgEmail/query.getLinkOptions.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from 'zod'

import { prefixedId } from '~api/schemas/idPrefix'

export const ZGetLinkOptionsSchema = z.object({ slug: z.string(), locationId: prefixedId('orgLocation') })
export type TGetLinkOptionsSchema = z.infer<typeof ZGetLinkOptionsSchema>
2 changes: 2 additions & 0 deletions packages/api/router/orgEmail/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// codegen:start {preset: barrel, include: ./*.schema.ts}
export * from './mutation.create.schema'
export * from './mutation.locationLink.schema'
export * from './mutation.update.schema'
export * from './mutation.upsertMany.schema'
export * from './query.forContactInfo.schema'
export * from './query.forContactInfoEdit.schema'
export * from './query.forEditDrawer.schema'
export * from './query.get.schema'
export * from './query.getLinkOptions.schema'
// codegen:end
18 changes: 18 additions & 0 deletions packages/api/router/orgPhone/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,22 @@ export const orgPhoneRouter = defineRouter({
)
return handler(opts)
}),
getLinkOptions: permissionedProcedure('updatePhone')
.input(schema.ZGetLinkOptionsSchema)
.query(async (opts) => {
const handler = await importHandler(
namespaced('getLinkOptions'),
() => import('./query.getLinkOptions.handler')
)
return handler(opts)
}),
locationLink: permissionedProcedure('updatePhone')
.input(schema.ZLocationLinkSchema)
.mutation(async (opts) => {
const handler = await importHandler(
namespaced('locationLink'),
() => import('./mutation.locationLink.handler')
)
return handler(opts)
}),
})
Loading

0 comments on commit 708456b

Please sign in to comment.