From 22fc5f98f8b646d6505a7a518074f5ce3f40215f Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Thu, 15 Aug 2024 01:33:23 +0800 Subject: [PATCH] feat: add feed template string of survey --- pnpm-lock.yaml | 6 +++ .../components/survey/SurveyEditForm.tsx | 36 ++++++++++++- src/server/package.json | 2 + .../migration.sql | 2 + src/server/prisma/schema.prisma | 13 ++--- src/server/prisma/zod/survey.ts | 1 + src/server/trpc/routers/survey.ts | 52 +++++++++++++++---- src/server/types/global.d.ts | 4 +- src/server/utils/template.ts | 13 +++++ 9 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 src/server/prisma/migrations/20240814145845_add_survey_feed_template/migration.sql create mode 100644 src/server/utils/template.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb99a22..c77d2ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -504,6 +504,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 maxmind: specifier: ^4.3.18 version: 4.3.18 @@ -592,6 +595,9 @@ importers: '@types/lodash': specifier: ^4.14.198 version: 4.14.198 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 '@types/md5': specifier: ^2.3.5 version: 2.3.5 diff --git a/src/client/components/survey/SurveyEditForm.tsx b/src/client/components/survey/SurveyEditForm.tsx index dcf1a79..0f7a3fb 100644 --- a/src/client/components/survey/SurveyEditForm.tsx +++ b/src/client/components/survey/SurveyEditForm.tsx @@ -18,7 +18,7 @@ import { FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { useForm, useFieldArray } from 'react-hook-form'; +import { useForm, useFieldArray, useWatch } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { generateRandomString } from '@/utils/common'; import { LuArrowDown, LuArrowUp, LuMinus, LuPlus } from 'react-icons/lu'; @@ -48,6 +48,7 @@ const addFormSchema = z.object({ ), }), feedChannelIds: z.array(z.string()), + feedTemplate: z.string(), }); export type SurveyEditFormValues = z.infer; @@ -78,9 +79,12 @@ export const SurveyEditForm: React.FC = React.memo( items: [generateDefaultItem()], }, feedChannelIds: [], + feedTemplate: '', }, }); + const feedChannelIds = form.watch('feedChannelIds'); + const [handleSubmit, isLoading] = useEventWithLoading( async (values: SurveyEditFormValues) => { await props.onSubmit(values); @@ -263,6 +267,36 @@ export const SurveyEditForm: React.FC = React.memo( )} /> + + {feedChannelIds.length > 0 && ( + ( + + {t('Feed Template')} + + + + +

+ {t( + 'Survey Template String, here are available variables:' + )} +

+

+ {'{{_surveyName}} '} + {fields.map((f) => `{{${f.name}}}`).join(' ')} +

+
+ +
+ )} + /> + )} diff --git a/src/server/package.json b/src/server/package.json index d1ece0d..0159f3e 100644 --- a/src/server/package.json +++ b/src/server/package.json @@ -51,6 +51,7 @@ "isolated-vm": "^4.7.2", "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "maxmind": "^4.3.18", "md5": "^2.3.0", "morgan": "^1.10.0", @@ -82,6 +83,7 @@ "@types/fs-extra": "^11.0.3", "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.14.198", + "@types/lodash-es": "^4.17.12", "@types/md5": "^2.3.5", "@types/morgan": "^1.9.5", "@types/node": "^18.17.12", diff --git a/src/server/prisma/migrations/20240814145845_add_survey_feed_template/migration.sql b/src/server/prisma/migrations/20240814145845_add_survey_feed_template/migration.sql new file mode 100644 index 0000000..e080f58 --- /dev/null +++ b/src/server/prisma/migrations/20240814145845_add_survey_feed_template/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Survey" ADD COLUMN "feedTemplate" TEXT NOT NULL DEFAULT ''; diff --git a/src/server/prisma/schema.prisma b/src/server/prisma/schema.prisma index 8550ebb..860d385 100644 --- a/src/server/prisma/schema.prisma +++ b/src/server/prisma/schema.prisma @@ -452,15 +452,16 @@ enum WorkspaceAuditLogType { } model Survey { - id String @id @default(cuid()) @db.VarChar(30) - workspaceId String @db.VarChar(30) - name String + id String @id @default(cuid()) @db.VarChar(30) + workspaceId String @db.VarChar(30) + name String /// [SurveyPayload] /// @zod.custom(imports.SurveyPayloadSchema) - payload Json @db.Json + payload Json @db.Json feedChannelIds String[] @default([]) // send survey result to feed channel - createdAt DateTime @default(now()) @db.Timestamptz(6) - updatedAt DateTime @updatedAt @db.Timestamptz(6) + feedTemplate String @default("") // send survey result to feed channel + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @updatedAt @db.Timestamptz(6) workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade) surveyResultList SurveyResult[] diff --git a/src/server/prisma/zod/survey.ts b/src/server/prisma/zod/survey.ts index 7b7d64f..c97e49c 100644 --- a/src/server/prisma/zod/survey.ts +++ b/src/server/prisma/zod/survey.ts @@ -17,6 +17,7 @@ export const SurveyModelSchema = z.object({ */ payload: imports.SurveyPayloadSchema, feedChannelIds: z.string().array(), + feedTemplate: z.string(), createdAt: z.date(), updatedAt: z.date(), }) diff --git a/src/server/trpc/routers/survey.ts b/src/server/trpc/routers/survey.ts index d7f914f..167fff8 100644 --- a/src/server/trpc/routers/survey.ts +++ b/src/server/trpc/routers/survey.ts @@ -20,6 +20,8 @@ import { buildCursorResponseSchema } from '../../utils/schema.js'; import { fetchDataByCursor } from '../../utils/prisma.js'; import { Prisma } from '@prisma/client'; import { createFeedEvent } from '../../model/feed/event.js'; +import { formatString } from '../../utils/template.js'; +import { logger } from '../../utils/logger.js'; export const surveyRouter = router({ all: workspaceProcedure @@ -185,15 +187,33 @@ export const surveyRouter = router({ Array.isArray(survey.feedChannelIds) && survey.feedChannelIds.length > 0 ) { + const templateStr = + survey.feedTemplate || + 'survey {{_surveyName}} receive a new record.'; + survey.feedChannelIds.forEach((channelId) => { - createFeedEvent(workspaceId, { - channelId: channelId, - eventName: 'receive', - eventContent: `survey [${survey.name}] receive a new record.`, - tags: [], - source: 'survey', - important: false, - }); + try { + const surveyPayload = SurveyPayloadSchema.parse(survey.payload); + + createFeedEvent(workspaceId, { + channelId: channelId, + eventName: 'receive', + eventContent: formatString(templateStr, { + _surveyName: survey.name, + ...Object.fromEntries( + surveyPayload.items.map((item) => [ + item.name, + payload[item.name] ?? '', + ]) + ), + }), + tags: [], + source: 'survey', + important: false, + }); + } catch (err) { + logger.error('[surveySubmitSendFeed]', err); + } }); } }); @@ -212,11 +232,13 @@ export const surveyRouter = router({ name: z.string(), payload: SurveyPayloadSchema, feedChannelIds: z.array(z.string()), + feedTemplate: z.string(), }) ) .output(SurveyModelSchema) .mutation(async ({ input }) => { - const { workspaceId, name, payload, feedChannelIds } = input; + const { workspaceId, name, payload, feedChannelIds, feedTemplate } = + input; const res = await prisma.survey.create({ data: { @@ -224,6 +246,7 @@ export const surveyRouter = router({ name, payload, feedChannelIds, + feedTemplate, }, }); @@ -242,11 +265,19 @@ export const surveyRouter = router({ name: z.string().optional(), payload: SurveyPayloadSchema.optional(), feedChannelIds: z.array(z.string()).optional(), + feedTemplate: z.string().optional(), }) ) .output(SurveyModelSchema) .mutation(async ({ input }) => { - const { workspaceId, surveyId, name, payload, feedChannelIds } = input; + const { + workspaceId, + surveyId, + name, + payload, + feedChannelIds, + feedTemplate, + } = input; const res = await prisma.survey.update({ where: { @@ -257,6 +288,7 @@ export const surveyRouter = router({ name, payload, feedChannelIds, + feedTemplate, }, }); diff --git a/src/server/types/global.d.ts b/src/server/types/global.d.ts index 8895df7..8f45a21 100644 --- a/src/server/types/global.d.ts +++ b/src/server/types/global.d.ts @@ -1,8 +1,8 @@ -import type { JWTPayload } from '../middleware/auth'; +import type { JWTPayload } from '../middleware/auth.ts'; import type { MonitorStatusPageListSchema, SurveyPayloadSchema, -} from '../prisma/zod/schemas'; +} from '../prisma/zod/schemas/index.ts'; declare global { namespace Express { diff --git a/src/server/utils/template.ts b/src/server/utils/template.ts new file mode 100644 index 0000000..9946dad --- /dev/null +++ b/src/server/utils/template.ts @@ -0,0 +1,13 @@ +import { template, templateSettings } from 'lodash-es'; + +templateSettings.interpolate = /{{([\s\S]+?)}}/g; + +/** + * @example + * + * buildTemplate('hello {{ user }}!', { user: 'mustache' }) + * => 'hello mustache!' + */ +export function formatString(raw: string, variable: Record) { + return template(raw)(variable); +}