Compare commits

...

3 Commits

Author SHA1 Message Date
moonrailgun
cbd6821def chore: update survey edit form 2024-10-11 00:58:10 +08:00
moonrailgun
8a6a75f3f5 feat: survey add webhook url field which can send webhook when receive any survey 2024-10-11 00:26:34 +08:00
moonrailgun
fd63f2a22e feat: add survey webhook 2024-10-10 23:40:24 +08:00
7 changed files with 65 additions and 15 deletions

View File

@ -1,12 +1,7 @@
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { Button } from '@/components/ui/button';
import { useEvent, useEventWithLoading } from '@/hooks/useEvent';
import { useCurrentWorkspaceId } from '@/store/user';
import { defaultErrorHandler, trpc } from '@/api/trpc';
import { useEventWithLoading } from '@/hooks/useEvent';
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { CommonWrapper } from '@/components/CommonWrapper';
import { routeAuthBeforeLoad } from '@/utils/route';
import { z } from 'zod';
import {
Form,
@ -18,7 +13,7 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useForm, useFieldArray, useWatch } from 'react-hook-form';
import { useForm, useFieldArray } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { generateRandomString } from '@/utils/common';
import { LuArrowDown, LuArrowUp, LuMinus, LuPlus } from 'react-icons/lu';
@ -49,6 +44,7 @@ const addFormSchema = z.object({
}),
feedChannelIds: z.array(z.string()),
feedTemplate: z.string(),
webhookUrl: z.string().url().or(z.literal('')),
});
export type SurveyEditFormValues = z.infer<typeof addFormSchema>;
@ -80,6 +76,7 @@ export const SurveyEditForm: React.FC<SurveyEditFormProps> = React.memo(
},
feedChannelIds: [],
feedTemplate: '',
webhookUrl: '',
},
});
@ -87,7 +84,7 @@ export const SurveyEditForm: React.FC<SurveyEditFormProps> = React.memo(
const [handleSubmit, isLoading] = useEventWithLoading(
async (values: SurveyEditFormValues) => {
await props.onSubmit(values);
await props.onSubmit({ ...values });
form.reset();
}
);
@ -297,6 +294,23 @@ export const SurveyEditForm: React.FC<SurveyEditFormProps> = React.memo(
)}
/>
)}
<FormField
control={form.control}
name="webhookUrl"
render={({ field }) => (
<FormItem>
<FormLabel optional={true}>{t('Webhook Url')}</FormLabel>
<FormControl className="w-full">
<Input {...field} />
</FormControl>
<FormDescription>
{t('Optional, webhook url to send survey payload')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
<CardFooter>

View File

@ -2,7 +2,7 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { useEvent } from '@/hooks/useEvent';
import { useCurrentWorkspaceId } from '@/store/user';
import { trpc } from '@/api/trpc';
import { defaultErrorHandler, trpc } from '@/api/trpc';
import { Card, CardContent } from '@/components/ui/card';
import { CommonWrapper } from '@/components/CommonWrapper';
import { routeAuthBeforeLoad } from '@/utils/route';
@ -25,7 +25,9 @@ function PageComponent() {
const { surveyId } = Route.useParams<{ surveyId: string }>();
const workspaceId = useCurrentWorkspaceId();
const navigate = useNavigate();
const mutation = trpc.survey.update.useMutation();
const mutation = trpc.survey.update.useMutation({
onError: defaultErrorHandler,
});
const { data: survey, isLoading } = trpc.survey.get.useQuery({
workspaceId,
surveyId,

View File

@ -1,3 +1,8 @@
import { version } from '@tianji/shared';
import axios from 'axios';
axios.defaults.headers.common['User-Agent'] = `tianji/${version}`;
(BigInt.prototype as any).toJSON = function () {
const int = Number.parseInt(this.toString());
return int ?? this.toString();

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ import { Prisma } from '@prisma/client';
import { createFeedEvent } from '../../model/feed/event.js';
import { formatString } from '../../utils/template.js';
import { logger } from '../../utils/logger.js';
import axios from 'axios';
export const surveyRouter = router({
all: workspaceProcedure
@ -154,7 +155,7 @@ export const surveyRouter = router({
const sessionId = hashUuid(workspaceId, surveyId, ip, userAgent!);
await prisma.surveyResult.create({
const result = await prisma.surveyResult.create({
data: {
surveyId,
sessionId,
@ -191,11 +192,11 @@ export const surveyRouter = router({
survey.feedTemplate ||
'survey {{_surveyName}} receive a new record.';
survey.feedChannelIds.forEach((channelId) => {
survey.feedChannelIds.forEach(async (channelId) => {
try {
const surveyPayload = SurveyPayloadSchema.parse(survey.payload);
createFeedEvent(workspaceId, {
await createFeedEvent(workspaceId, {
channelId: channelId,
eventName: 'receive',
eventContent: formatString(templateStr, {
@ -216,6 +217,19 @@ export const surveyRouter = router({
}
});
}
if (survey && survey.webhookUrl) {
axios
.post(survey.webhookUrl, {
...result,
})
.then(() => {
logger.info(
`[surveySubmitWebhook] send webhooks to ${survey.webhookUrl} success!`
);
})
.catch((err) => logger.error('[surveySubmitWebhook]', err));
}
});
return 'success';
@ -233,12 +247,19 @@ export const surveyRouter = router({
payload: SurveyPayloadSchema,
feedChannelIds: z.array(z.string()),
feedTemplate: z.string(),
webhookUrl: z.string(),
})
)
.output(SurveyModelSchema)
.mutation(async ({ input }) => {
const { workspaceId, name, payload, feedChannelIds, feedTemplate } =
input;
const {
workspaceId,
name,
payload,
feedChannelIds,
feedTemplate,
webhookUrl,
} = input;
const res = await prisma.survey.create({
data: {
@ -247,6 +268,7 @@ export const surveyRouter = router({
payload,
feedChannelIds,
feedTemplate,
webhookUrl,
},
});
@ -266,6 +288,7 @@ export const surveyRouter = router({
payload: SurveyPayloadSchema.optional(),
feedChannelIds: z.array(z.string()).optional(),
feedTemplate: z.string().optional(),
webhookUrl: z.string().optional(),
})
)
.output(SurveyModelSchema)
@ -277,6 +300,7 @@ export const surveyRouter = router({
payload,
feedChannelIds,
feedTemplate,
webhookUrl,
} = input;
const res = await prisma.survey.update({
@ -289,6 +313,7 @@ export const surveyRouter = router({
payload,
feedChannelIds,
feedTemplate,
webhookUrl,
},
});