feat: add feed template string of survey

This commit is contained in:
moonrailgun 2024-08-15 01:33:23 +08:00
parent d9862105ed
commit 22fc5f98f8
9 changed files with 110 additions and 19 deletions

View File

@ -504,6 +504,9 @@ importers:
lodash: lodash:
specifier: ^4.17.21 specifier: ^4.17.21
version: 4.17.21 version: 4.17.21
lodash-es:
specifier: ^4.17.21
version: 4.17.21
maxmind: maxmind:
specifier: ^4.3.18 specifier: ^4.3.18
version: 4.3.18 version: 4.3.18
@ -592,6 +595,9 @@ importers:
'@types/lodash': '@types/lodash':
specifier: ^4.14.198 specifier: ^4.14.198
version: 4.14.198 version: 4.14.198
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
'@types/md5': '@types/md5':
specifier: ^2.3.5 specifier: ^2.3.5
version: 2.3.5 version: 2.3.5

View File

@ -18,7 +18,7 @@ import {
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { Input } from '@/components/ui/input'; 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 { zodResolver } from '@hookform/resolvers/zod';
import { generateRandomString } from '@/utils/common'; import { generateRandomString } from '@/utils/common';
import { LuArrowDown, LuArrowUp, LuMinus, LuPlus } from 'react-icons/lu'; import { LuArrowDown, LuArrowUp, LuMinus, LuPlus } from 'react-icons/lu';
@ -48,6 +48,7 @@ const addFormSchema = z.object({
), ),
}), }),
feedChannelIds: z.array(z.string()), feedChannelIds: z.array(z.string()),
feedTemplate: z.string(),
}); });
export type SurveyEditFormValues = z.infer<typeof addFormSchema>; export type SurveyEditFormValues = z.infer<typeof addFormSchema>;
@ -78,9 +79,12 @@ export const SurveyEditForm: React.FC<SurveyEditFormProps> = React.memo(
items: [generateDefaultItem()], items: [generateDefaultItem()],
}, },
feedChannelIds: [], feedChannelIds: [],
feedTemplate: '',
}, },
}); });
const feedChannelIds = form.watch('feedChannelIds');
const [handleSubmit, isLoading] = useEventWithLoading( const [handleSubmit, isLoading] = useEventWithLoading(
async (values: SurveyEditFormValues) => { async (values: SurveyEditFormValues) => {
await props.onSubmit(values); await props.onSubmit(values);
@ -263,6 +267,36 @@ export const SurveyEditForm: React.FC<SurveyEditFormProps> = React.memo(
</FormItem> </FormItem>
)} )}
/> />
{feedChannelIds.length > 0 && (
<FormField
control={form.control}
name="feedTemplate"
render={({ field }) => (
<FormItem>
<FormLabel>{t('Feed Template')}</FormLabel>
<FormControl>
<Input
placeholder="survey {{_surveyName}} receive a new record."
{...field}
/>
</FormControl>
<FormDescription>
<p>
{t(
'Survey Template String, here are available variables:'
)}
</p>
<p>
{'{{_surveyName}} '}
{fields.map((f) => `{{${f.name}}}`).join(' ')}
</p>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
)}
</CardContent> </CardContent>
<CardFooter> <CardFooter>

View File

@ -51,6 +51,7 @@
"isolated-vm": "^4.7.2", "isolated-vm": "^4.7.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"maxmind": "^4.3.18", "maxmind": "^4.3.18",
"md5": "^2.3.0", "md5": "^2.3.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
@ -82,6 +83,7 @@
"@types/fs-extra": "^11.0.3", "@types/fs-extra": "^11.0.3",
"@types/jsonwebtoken": "^9.0.5", "@types/jsonwebtoken": "^9.0.5",
"@types/lodash": "^4.14.198", "@types/lodash": "^4.14.198",
"@types/lodash-es": "^4.17.12",
"@types/md5": "^2.3.5", "@types/md5": "^2.3.5",
"@types/morgan": "^1.9.5", "@types/morgan": "^1.9.5",
"@types/node": "^18.17.12", "@types/node": "^18.17.12",

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Survey" ADD COLUMN "feedTemplate" TEXT NOT NULL DEFAULT '';

View File

@ -459,6 +459,7 @@ model Survey {
/// @zod.custom(imports.SurveyPayloadSchema) /// @zod.custom(imports.SurveyPayloadSchema)
payload Json @db.Json payload Json @db.Json
feedChannelIds String[] @default([]) // send survey result to feed channel feedChannelIds String[] @default([]) // send survey result to feed channel
feedTemplate String @default("") // send survey result to feed channel
createdAt DateTime @default(now()) @db.Timestamptz(6) createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6) updatedAt DateTime @updatedAt @db.Timestamptz(6)

View File

@ -17,6 +17,7 @@ export const SurveyModelSchema = z.object({
*/ */
payload: imports.SurveyPayloadSchema, payload: imports.SurveyPayloadSchema,
feedChannelIds: z.string().array(), feedChannelIds: z.string().array(),
feedTemplate: z.string(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
}) })

View File

@ -20,6 +20,8 @@ import { buildCursorResponseSchema } from '../../utils/schema.js';
import { fetchDataByCursor } from '../../utils/prisma.js'; import { fetchDataByCursor } from '../../utils/prisma.js';
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
import { createFeedEvent } from '../../model/feed/event.js'; import { createFeedEvent } from '../../model/feed/event.js';
import { formatString } from '../../utils/template.js';
import { logger } from '../../utils/logger.js';
export const surveyRouter = router({ export const surveyRouter = router({
all: workspaceProcedure all: workspaceProcedure
@ -185,15 +187,33 @@ export const surveyRouter = router({
Array.isArray(survey.feedChannelIds) && Array.isArray(survey.feedChannelIds) &&
survey.feedChannelIds.length > 0 survey.feedChannelIds.length > 0
) { ) {
const templateStr =
survey.feedTemplate ||
'survey {{_surveyName}} receive a new record.';
survey.feedChannelIds.forEach((channelId) => { survey.feedChannelIds.forEach((channelId) => {
try {
const surveyPayload = SurveyPayloadSchema.parse(survey.payload);
createFeedEvent(workspaceId, { createFeedEvent(workspaceId, {
channelId: channelId, channelId: channelId,
eventName: 'receive', eventName: 'receive',
eventContent: `survey [${survey.name}] receive a new record.`, eventContent: formatString(templateStr, {
_surveyName: survey.name,
...Object.fromEntries(
surveyPayload.items.map((item) => [
item.name,
payload[item.name] ?? '',
])
),
}),
tags: [], tags: [],
source: 'survey', source: 'survey',
important: false, important: false,
}); });
} catch (err) {
logger.error('[surveySubmitSendFeed]', err);
}
}); });
} }
}); });
@ -212,11 +232,13 @@ export const surveyRouter = router({
name: z.string(), name: z.string(),
payload: SurveyPayloadSchema, payload: SurveyPayloadSchema,
feedChannelIds: z.array(z.string()), feedChannelIds: z.array(z.string()),
feedTemplate: z.string(),
}) })
) )
.output(SurveyModelSchema) .output(SurveyModelSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const { workspaceId, name, payload, feedChannelIds } = input; const { workspaceId, name, payload, feedChannelIds, feedTemplate } =
input;
const res = await prisma.survey.create({ const res = await prisma.survey.create({
data: { data: {
@ -224,6 +246,7 @@ export const surveyRouter = router({
name, name,
payload, payload,
feedChannelIds, feedChannelIds,
feedTemplate,
}, },
}); });
@ -242,11 +265,19 @@ export const surveyRouter = router({
name: z.string().optional(), name: z.string().optional(),
payload: SurveyPayloadSchema.optional(), payload: SurveyPayloadSchema.optional(),
feedChannelIds: z.array(z.string()).optional(), feedChannelIds: z.array(z.string()).optional(),
feedTemplate: z.string().optional(),
}) })
) )
.output(SurveyModelSchema) .output(SurveyModelSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const { workspaceId, surveyId, name, payload, feedChannelIds } = input; const {
workspaceId,
surveyId,
name,
payload,
feedChannelIds,
feedTemplate,
} = input;
const res = await prisma.survey.update({ const res = await prisma.survey.update({
where: { where: {
@ -257,6 +288,7 @@ export const surveyRouter = router({
name, name,
payload, payload,
feedChannelIds, feedChannelIds,
feedTemplate,
}, },
}); });

View File

@ -1,8 +1,8 @@
import type { JWTPayload } from '../middleware/auth'; import type { JWTPayload } from '../middleware/auth.ts';
import type { import type {
MonitorStatusPageListSchema, MonitorStatusPageListSchema,
SurveyPayloadSchema, SurveyPayloadSchema,
} from '../prisma/zod/schemas'; } from '../prisma/zod/schemas/index.ts';
declare global { declare global {
namespace Express { namespace Express {

View File

@ -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<string, string>) {
return template(raw)(variable);
}