diff --git a/packages/api/router/orgEmail/mutation.create.handler.ts b/packages/api/router/orgEmail/mutation.create.handler.ts index 3d00abb838..c712342f58 100644 --- a/packages/api/router/orgEmail/mutation.create.handler.ts +++ b/packages/api/router/orgEmail/mutation.create.handler.ts @@ -1,3 +1,4 @@ +import { addSingleKey } from '@weareinreach/crowdin/api' import { getAuditedClient } from '@weareinreach/db' import { type TRPCHandlerParams } from '~api/types/handler' @@ -5,10 +6,22 @@ import { type TCreateSchema } from './mutation.create.schema' const create = async ({ ctx, input }: TRPCHandlerParams) => { const prisma = getAuditedClient(ctx.actorId) - const newEmail = await prisma.orgEmail.create({ - data: input, - select: { id: true }, + + const result = await prisma.$transaction(async (tx) => { + if (input.description) { + const crowdinId = await addSingleKey({ + isDatabaseString: true, + key: input.description.create.tsKey.create.key, + text: input.description.create.tsKey.create.text, + }) + input.description.create.tsKey.create.crowdinId = crowdinId.id + } + const newEmail = await tx.orgEmail.create({ + data: input, + select: { id: true }, + }) + return newEmail }) - return newEmail + return result } export default create diff --git a/packages/api/router/orgEmail/mutation.create.schema.ts b/packages/api/router/orgEmail/mutation.create.schema.ts index 8966359eba..0b139eddb4 100644 --- a/packages/api/router/orgEmail/mutation.create.schema.ts +++ b/packages/api/router/orgEmail/mutation.create.schema.ts @@ -26,27 +26,33 @@ export const ZCreateSchema = z .transform(({ orgId, data, title, titleId, description }) => { const id = generateId('orgEmail') + const handleTitle = () => { + if (title) { + return { + create: { + title, + key: { + create: { + text: title, + key: slug(title), + namespace: { connect: { name: namespace.userTitle } }, + }, + }, + }, + } + } + if (titleId) { + return { connect: { id: titleId } } + } + return undefined + } + return Prisma.validator()({ ...data, description: description ? generateNestedFreeText({ orgId, itemId: id, text: description, type: 'emailDesc' }) : undefined, - title: title - ? { - create: { - title, - key: { - create: { - text: title, - key: slug(title), - namespace: { connect: { name: namespace.userTitle } }, - }, - }, - }, - } - : titleId - ? { connect: { id: titleId } } - : undefined, + title: handleTitle(), }) }) export type TCreateSchema = z.infer diff --git a/packages/api/router/orgEmail/mutation.update.handler.ts b/packages/api/router/orgEmail/mutation.update.handler.ts index c0c5405d06..5e4bb688ec 100644 --- a/packages/api/router/orgEmail/mutation.update.handler.ts +++ b/packages/api/router/orgEmail/mutation.update.handler.ts @@ -1,8 +1,23 @@ -import { generateNestedFreeText, generateNestedFreeTextUpsert, getAuditedClient } from '@weareinreach/db' +import { upsertSingleKey } from '@weareinreach/crowdin/api' +import { generateNestedFreeTextUpsert, getAuditedClient } from '@weareinreach/db' import { type TRPCHandlerParams } from '~api/types/handler' import { type TUpdateSchema } from './mutation.update.schema' +const select = { + id: true, + deleted: true, + description: { select: { tsKey: { select: { text: true, key: true, ns: true } } } }, + descriptionId: true, + email: true, + firstName: true, + lastName: true, + locationOnly: true, + primary: true, + published: true, + serviceOnly: true, + titleId: true, +} as const const update = async ({ ctx, input }: TRPCHandlerParams) => { const prisma = getAuditedClient(ctx.actorId) const { id, orgId, description, descriptionId, titleId, email, linkLocationId, ...record } = input @@ -17,58 +32,49 @@ const update = async ({ ctx, input }: TRPCHandlerParams { + if (updateDescriptionText) { + const crowdin = await upsertSingleKey({ + isDatabaseString: true, + key: updateDescriptionText.upsert.create.tsKey.create.key, + text: updateDescriptionText.upsert.create.tsKey.create.text, }) - const { description: updatedDescription, ...rest } = updated + if (crowdin.id) { + updateDescriptionText.upsert.create.tsKey.create.crowdinId = crowdin.id + } + } + const updated = email + ? await tx.orgEmail.upsert({ + where: { id }, + create: { + id, + email, + ...record, + description: updateDescriptionText?.upsert, + ...(linkLocationId && { + locations: { createMany: { data: [{ orgLocationId: linkLocationId }], skipDuplicates: true } }, + }), + }, + update: { + ...record, + description: updateDescriptionText, + title: titleId ? { connect: { id: titleId } } : undefined, + }, + select, + }) + : await tx.orgEmail.update({ + where: { id }, + data: { + ...record, + description: updateDescriptionText, + title: titleId ? { connect: { id: titleId } } : undefined, + }, + select, + }) + return updated + }) + + const { description: updatedDescription, ...rest } = result const reformatted = { ...rest, diff --git a/packages/api/router/orgEmail/mutation.upsertMany.handler.ts b/packages/api/router/orgEmail/mutation.upsertMany.handler.ts index e7fb59e200..cbeaa13c92 100644 --- a/packages/api/router/orgEmail/mutation.upsertMany.handler.ts +++ b/packages/api/router/orgEmail/mutation.upsertMany.handler.ts @@ -1,6 +1,7 @@ import compact from 'just-compact' -import { generateNestedFreeText, getAuditedClient } from '@weareinreach/db' +import { upsertSingleKey } from '@weareinreach/crowdin/api' +import { generateId, generateNestedFreeTextUpsert, getAuditedClient } from '@weareinreach/db' import { connectOneId, connectOrDisconnectId, @@ -21,53 +22,71 @@ const upsertMany = async ({ ctx, input }: TRPCHandlerParams { - const before = passedId ? existing.find(({ id: existingId }) => existingId === passedId) : undefined - const servicesBefore = before?.services?.map(({ serviceId }) => ({ serviceId })) ?? [] - const locationsBefore = before?.locations?.map(({ orgLocationId }) => ({ orgLocationId })) ?? [] - const id = passedId ?? ctx.generateId('orgEmail') - const services = servicesArr.map((serviceId) => ({ serviceId })) - const locations = locationsArr.map((orgLocationId) => ({ orgLocationId })) + const results: Array<{ id: string }> = [] - return prisma.orgEmail.upsert({ - where: { id }, - create: { - id, - ...record, - title: connectOneId(title), - services: createManyOptional(services), - locations: createManyOptional(locations), - description: description - ? generateNestedFreeText({ orgId, text: description, type: 'emailDesc', itemId: id }) - : undefined, - }, - update: { - id, - ...record, - title: connectOrDisconnectId(title), - services: diffConnectionsMtoN(services, servicesBefore, 'serviceId'), - locations: diffConnectionsMtoN(locations, locationsBefore, 'orgLocationId'), - description: description - ? { - upsert: { - ...generateNestedFreeText({ - orgId, - text: description, - type: 'emailDesc', - itemId: id, - }), - update: { tsKey: { update: { text: description } } }, - }, - } - : undefined, - }, + const upserts = await prisma.$transaction(async (tx) => { + for (const { + title, + services: servicesArr, + locations: locationsArr, + description, + id: passedId, + ...record + } of data) { + const before = passedId ? existing.find(({ id: existingId }) => existingId === passedId) : undefined + const servicesBefore = before?.services?.map(({ serviceId }) => ({ serviceId })) ?? [] + const locationsBefore = before?.locations?.map(({ orgLocationId }) => ({ orgLocationId })) ?? [] + const id = passedId ?? ctx.generateId('orgEmail') + + const services = servicesArr.map((serviceId) => ({ serviceId })) + const locations = locationsArr.map((orgLocationId) => ({ orgLocationId })) + + const descriptionText = description + ? generateNestedFreeTextUpsert({ + orgId, + text: description, + type: 'emailDesc', + itemId: id, + freeTextId: generateId('freeText'), + }) + : undefined + + if (descriptionText) { + const crowdin = await upsertSingleKey({ + isDatabaseString: true, + key: descriptionText.upsert.create.tsKey.create.key, + text: descriptionText.upsert.create.tsKey.create.text, }) + if (crowdin.id) { + descriptionText.upsert.create.tsKey.create.crowdinId = crowdin.id + } } - ) - ) + + const txnResult = await tx.orgEmail.upsert({ + where: { id }, + create: { + id, + ...record, + title: connectOneId(title), + services: createManyOptional(services), + locations: createManyOptional(locations), + description: descriptionText?.upsert, + }, + update: { + id, + ...record, + title: connectOrDisconnectId(title), + services: diffConnectionsMtoN(services, servicesBefore, 'serviceId'), + locations: diffConnectionsMtoN(locations, locationsBefore, 'orgLocationId'), + description: descriptionText, + }, + select: { id: true }, + }) + results.push(txnResult) + } + return results + }) return upserts } export default upsertMany diff --git a/packages/api/router/orgPhone/mutation.create.handler.ts b/packages/api/router/orgPhone/mutation.create.handler.ts index e4ef57ca98..273f59745d 100644 --- a/packages/api/router/orgPhone/mutation.create.handler.ts +++ b/packages/api/router/orgPhone/mutation.create.handler.ts @@ -1,3 +1,6 @@ +import invariant from 'tiny-invariant' + +import { addSingleKey } from '@weareinreach/crowdin/api' import { getAuditedClient } from '@weareinreach/db' import { type TRPCHandlerParams } from '~api/types/handler' @@ -5,10 +8,35 @@ import { type TCreateSchema } from './mutation.create.schema' const create = async ({ ctx, input }: TRPCHandlerParams) => { const prisma = getAuditedClient(ctx.actorId) - const newPhone = await prisma.orgPhone.create({ - data: input, - select: { id: true }, + + const result = await prisma.$transaction(async (tx) => { + if (input.description) { + const crowdinDesc = await addSingleKey({ + isDatabaseString: true, + key: input.description.create.tsKey.create.key, + text: input.description.create.tsKey.create.text, + }) + input.description.create.tsKey.create.crowdinId = crowdinDesc.id + } + if (input.phoneType?.create) { + invariant( + input.phoneType.create.key?.create && input.phoneType.create.key.create.namespace?.connect?.name + ) + const crowdinPhoneType = await addSingleKey({ + isDatabaseString: false, + key: input.phoneType.create.key.create.key, + text: input.phoneType.create.key.create.text, + ns: input.phoneType.create.key.create.namespace.connect.name as 'phone-type', + }) + input.phoneType.create.key.create.crowdinId = crowdinPhoneType.id + } + + const newPhone = await tx.orgPhone.create({ + data: input, + select: { id: true }, + }) + return newPhone }) - return newPhone + return result } export default create diff --git a/packages/api/router/orgPhone/mutation.create.schema.ts b/packages/api/router/orgPhone/mutation.create.schema.ts index 907c6b4225..0885a88dbc 100644 --- a/packages/api/router/orgPhone/mutation.create.schema.ts +++ b/packages/api/router/orgPhone/mutation.create.schema.ts @@ -24,22 +24,28 @@ export const ZCreateSchema = z const description = data.description ? generateNestedFreeText({ orgId, itemId: id, text: data.description, type: 'phoneDesc' }) : undefined - const phoneType = data.phoneTypeId - ? { connect: { id: data.phoneTypeId } } - : data.phoneTypeNew - ? { - create: { - type: data.phoneTypeNew, - key: { - create: { - key: slug(data.phoneTypeNew), - text: data.phoneTypeNew, - namespace: { connect: { name: namespace.phoneType } }, - }, + const handlePhoneType = (): Prisma.PhoneTypeCreateNestedOneWithoutAttachedPhonesInput | undefined => { + if (data.phoneTypeId) { + return { connect: { id: data.phoneTypeId } } + } + if (data.phoneTypeNew) { + return { + create: { + type: data.phoneTypeNew, + key: { + create: { + key: slug(data.phoneTypeNew), + text: data.phoneTypeNew, + namespace: { connect: { name: namespace.phoneType } }, }, }, - } - : undefined + }, + } + } + return undefined + } + + const phoneType = handlePhoneType() const { number, ext, locationOnly, primary, published } = data return Prisma.validator()({ @@ -49,9 +55,9 @@ export const ZCreateSchema = z locationOnly, primary, published, - country: { connect: { id: data.countryId } }, description, phoneType, + country: { connect: { id: data.countryId } }, }) }) export type TCreateSchema = z.infer