mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-27 11:00:14 -06:00
chore: moved insights model to database package (#4575)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,9 +35,6 @@ yarn-error.log*
|
||||
!packages/database/.env
|
||||
!apps/web/.env
|
||||
|
||||
# Prisma generated files
|
||||
packages/database/zod
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@ import { INSIGHTS_PER_PAGE } from "@formbricks/lib/constants";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TInsight } from "@formbricks/types/insights";
|
||||
import { TSurveyQuestionId, ZSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
TSurveyQuestionId,
|
||||
TSurveyQuestionSummaryOpenText,
|
||||
ZSurveyQuestionId,
|
||||
} from "@formbricks/types/surveys/types";
|
||||
|
||||
export const getInsightsBySurveyIdQuestionId = reactCache(
|
||||
async (
|
||||
@@ -16,7 +19,7 @@ export const getInsightsBySurveyIdQuestionId = reactCache(
|
||||
questionId: TSurveyQuestionId,
|
||||
limit?: number,
|
||||
offset?: number
|
||||
): Promise<TInsight[]> =>
|
||||
): Promise<TSurveyQuestionSummaryOpenText["insights"]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([surveyId, ZId], [questionId, ZSurveyQuestionId]);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createDocument } from "@/app/api/(internal)/insights/lib/document";
|
||||
import { doesResponseHasAnyOpenTextAnswer } from "@/app/api/(internal)/insights/lib/utils";
|
||||
import { documentCache } from "@/lib/cache/document";
|
||||
import { insightCache } from "@/lib/cache/insight";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { Insight, InsightCategory, Prisma } from "@prisma/client";
|
||||
import { embed } from "ai";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { embeddingsModel } from "@formbricks/lib/aiModels";
|
||||
@@ -12,12 +12,6 @@ import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { TCreatedDocument } from "@formbricks/types/documents";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import {
|
||||
TInsight,
|
||||
TInsightCategory,
|
||||
TInsightCreateInput,
|
||||
ZInsightCreateInput,
|
||||
} from "@formbricks/types/insights";
|
||||
import {
|
||||
TSurvey,
|
||||
TSurveyQuestionId,
|
||||
@@ -25,6 +19,7 @@ import {
|
||||
ZSurveyQuestions,
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { getContactAttributes } from "./contact-attribute";
|
||||
import { TInsightCreateInput, TNearestInsights, ZInsightCreateInput } from "./types";
|
||||
|
||||
export const generateInsightsForSurveyResponsesConcept = async (
|
||||
survey: Pick<TSurvey, "id" | "name" | "environmentId" | "questions">
|
||||
@@ -328,23 +323,16 @@ export const getQuestionResponseReferenceId = (surveyId: string, questionId: TSu
|
||||
return `${surveyId}-${questionId}`;
|
||||
};
|
||||
|
||||
export const createInsight = async (insightGroupInput: TInsightCreateInput): Promise<TInsight> => {
|
||||
export const createInsight = async (insightGroupInput: TInsightCreateInput): Promise<Insight> => {
|
||||
validateInputs([insightGroupInput, ZInsightCreateInput]);
|
||||
|
||||
try {
|
||||
// create document
|
||||
const { vector, ...data } = insightGroupInput;
|
||||
const prismaInsight = await prisma.insight.create({
|
||||
const insight = await prisma.insight.create({
|
||||
data,
|
||||
});
|
||||
|
||||
const insight = {
|
||||
...prismaInsight,
|
||||
_count: {
|
||||
documentInsights: 0,
|
||||
},
|
||||
};
|
||||
|
||||
// update document vector with the embedding
|
||||
const vectorString = `[${insightGroupInput.vector.join(",")}]`;
|
||||
await prisma.$executeRaw`
|
||||
@@ -373,7 +361,7 @@ export const handleInsightAssignments = async (
|
||||
insight: {
|
||||
title: string;
|
||||
description: string;
|
||||
category: TInsightCategory;
|
||||
category: InsightCategory;
|
||||
}
|
||||
) => {
|
||||
try {
|
||||
@@ -427,21 +415,15 @@ export const findNearestInsights = async (
|
||||
vector: number[],
|
||||
limit: number = 5,
|
||||
threshold: number = 0.5
|
||||
): Promise<TInsight[]> => {
|
||||
): Promise<TNearestInsights[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
// Convert the embedding array to a JSON-like string representation
|
||||
const vectorString = `[${vector.join(",")}]`;
|
||||
|
||||
// Execute raw SQL query to find nearest neighbors and exclude the vector column
|
||||
const insights: TInsight[] = await prisma.$queryRaw`
|
||||
const insights: TNearestInsights[] = await prisma.$queryRaw`
|
||||
SELECT
|
||||
id,
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
"environmentId"
|
||||
id
|
||||
FROM "Insight" d
|
||||
WHERE d."environmentId" = ${environmentId}
|
||||
AND d."vector" <=> ${vectorString}::vector(512) <= ${threshold}
|
||||
|
||||
16
apps/web/app/api/(internal)/insights/lib/types.ts
Normal file
16
apps/web/app/api/(internal)/insights/lib/types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Insight } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
|
||||
export const ZInsightCreateInput = ZInsight.pick({
|
||||
environmentId: true,
|
||||
title: true,
|
||||
description: true,
|
||||
category: true,
|
||||
}).extend({
|
||||
vector: z.array(z.number()).length(512),
|
||||
});
|
||||
|
||||
export type TInsightCreateInput = z.infer<typeof ZInsightCreateInput>;
|
||||
|
||||
export type TNearestInsights = Pick<Insight, "id">;
|
||||
@@ -4,6 +4,7 @@ import { Prisma } from "@prisma/client";
|
||||
import { embed, generateObject } from "ai";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
import { embeddingsModel, llmModel } from "@formbricks/lib/aiModels";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import {
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
ZDocumentSentiment,
|
||||
} from "@formbricks/types/documents";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { ZInsightCategory } from "@formbricks/types/insights";
|
||||
|
||||
export const createDocumentAndAssignInsight = async (
|
||||
surveyName: string,
|
||||
@@ -38,7 +38,7 @@ export const createDocumentAndAssignInsight = async (
|
||||
z.object({
|
||||
title: z.string().describe("insight title, very specific"),
|
||||
description: z.string().describe("very brief insight description"),
|
||||
category: ZInsightCategory,
|
||||
category: ZInsight.shape.category,
|
||||
})
|
||||
),
|
||||
isSpam: z.boolean(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { TInsightWithDocumentCount } from "@/modules/ee/insights/experience/types/insights";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Card, CardContent, CardFooter } from "@/modules/ui/components/card";
|
||||
import {
|
||||
@@ -16,7 +17,6 @@ import { useDeferredValue, useEffect, useState } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TDocument, TDocumentFilterCriteria } from "@formbricks/types/documents";
|
||||
import { TInsight } from "@formbricks/types/insights";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import CategoryBadge from "../../experience/components/category-select";
|
||||
import SentimentSelect from "../sentiment-select";
|
||||
@@ -25,7 +25,7 @@ import { getDocumentsByInsightIdAction, getDocumentsByInsightIdSurveyIdQuestionI
|
||||
interface InsightSheetProps {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
insight: TInsight | null;
|
||||
insight: TInsightWithDocumentCount | null;
|
||||
surveyId?: string;
|
||||
questionId?: string;
|
||||
handleFeedback: (feedback: "positive" | "negative") => void;
|
||||
|
||||
@@ -4,18 +4,19 @@ import { InsightSheet } from "@/modules/ee/insights/components/insight-sheet";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/modules/ui/components/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/modules/ui/components/tabs";
|
||||
import { Insight, InsightCategory } from "@prisma/client";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import formbricks from "@formbricks/js";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TDocumentFilterCriteria } from "@formbricks/types/documents";
|
||||
import { TInsight, TInsightCategory } from "@formbricks/types/insights";
|
||||
import { TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import CategoryBadge from "../experience/components/category-select";
|
||||
|
||||
interface InsightViewProps {
|
||||
insights: TInsight[];
|
||||
insights: TSurveyQuestionSummaryOpenText["insights"];
|
||||
questionId?: string;
|
||||
surveyId?: string;
|
||||
documentsFilter?: TDocumentFilterCriteria;
|
||||
@@ -35,8 +36,10 @@ export const InsightView = ({
|
||||
}: InsightViewProps) => {
|
||||
const t = useTranslations();
|
||||
const [isInsightSheetOpen, setIsInsightSheetOpen] = useState(true);
|
||||
const [localInsights, setLocalInsights] = useState<TInsight[]>(insights);
|
||||
const [currentInsight, setCurrentInsight] = useState<TInsight | null>(null);
|
||||
const [localInsights, setLocalInsights] = useState<TSurveyQuestionSummaryOpenText["insights"]>(insights);
|
||||
const [currentInsight, setCurrentInsight] = useState<
|
||||
TSurveyQuestionSummaryOpenText["insights"][number] | null
|
||||
>(null);
|
||||
const [activeTab, setActiveTab] = useState<string>("all");
|
||||
const [visibleInsights, setVisibleInsights] = useState(10);
|
||||
|
||||
@@ -61,9 +64,7 @@ export const InsightView = ({
|
||||
if (filterValue === "all") {
|
||||
setLocalInsights(insights);
|
||||
} else {
|
||||
setLocalInsights(
|
||||
insights.filter((insight) => insight.category === (filterValue as TInsightCategory))
|
||||
);
|
||||
setLocalInsights(insights.filter((insight) => insight.category === (filterValue as InsightCategory)));
|
||||
}
|
||||
},
|
||||
[insights]
|
||||
@@ -88,13 +89,13 @@ export const InsightView = ({
|
||||
setVisibleInsights((prevVisibleInsights) => Math.min(prevVisibleInsights + 10, insights.length));
|
||||
};
|
||||
|
||||
const updateLocalInsight = (insightId: string, updates: Partial<TInsight>) => {
|
||||
const updateLocalInsight = (insightId: string, updates: Partial<Insight>) => {
|
||||
setLocalInsights((prevInsights) =>
|
||||
prevInsights.map((insight) => (insight.id === insightId ? { ...insight, ...updates } : insight))
|
||||
);
|
||||
};
|
||||
|
||||
const onCategoryChange = async (insightId: string, newCategory: TInsight["category"]) => {
|
||||
const onCategoryChange = async (insightId: string, newCategory: InsightCategory) => {
|
||||
updateLocalInsight(insightId, { category: newCategory });
|
||||
};
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ import {
|
||||
getProjectIdFromInsightId,
|
||||
} from "@/lib/utils/helper";
|
||||
import { checkAIPermission } from "@/modules/ee/insights/actions";
|
||||
import { ZInsightFilterCriteria } from "@/modules/ee/insights/experience/types/insights";
|
||||
import { z } from "zod";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { ZInsight, ZInsightFilterCriteria } from "@formbricks/types/insights";
|
||||
import { getInsights, updateInsight } from "./lib/insights";
|
||||
import { getStats } from "./lib/stats";
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { BadgeSelect, TBadgeSelectOption } from "@/modules/ui/components/badge-select";
|
||||
import { InsightCategory } from "@prisma/client";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { TInsight } from "@formbricks/types/insights";
|
||||
import { updateInsightAction } from "../actions";
|
||||
|
||||
interface CategoryBadgeProps {
|
||||
category: TInsight["category"];
|
||||
category: InsightCategory;
|
||||
insightId: string;
|
||||
onCategoryChange?: (insightId: string, category: TInsight["category"]) => void;
|
||||
onCategoryChange?: (insightId: string, category: InsightCategory) => void;
|
||||
}
|
||||
|
||||
const categoryOptions: TBadgeSelectOption[] = [
|
||||
@@ -18,14 +18,14 @@ const categoryOptions: TBadgeSelectOption[] = [
|
||||
{ text: "Other", type: "gray" },
|
||||
];
|
||||
|
||||
const categoryMapping: Record<string, TInsight["category"]> = {
|
||||
const categoryMapping: Record<string, InsightCategory> = {
|
||||
Complaint: "complaint",
|
||||
Request: "featureRequest",
|
||||
Praise: "praise",
|
||||
Other: "other",
|
||||
};
|
||||
|
||||
const getCategoryIndex = (category: TInsight["category"]) => {
|
||||
const getCategoryIndex = (category: InsightCategory) => {
|
||||
switch (category) {
|
||||
case "complaint":
|
||||
return 0;
|
||||
@@ -41,7 +41,7 @@ const getCategoryIndex = (category: TInsight["category"]) => {
|
||||
const CategoryBadge = ({ category, insightId, onCategoryChange }: CategoryBadgeProps) => {
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const t = useTranslations();
|
||||
const handleUpdateCategory = async (newCategory: TInsight["category"]) => {
|
||||
const handleUpdateCategory = async (newCategory: InsightCategory) => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
await updateInsightAction({ insightId, data: { category: newCategory } });
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { InsightSheet } from "@/modules/ee/insights/components/insight-sheet";
|
||||
import {
|
||||
TInsightFilterCriteria,
|
||||
TInsightWithDocumentCount,
|
||||
} from "@/modules/ee/insights/experience/types/insights";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/modules/ui/components/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/modules/ui/components/tabs";
|
||||
import { InsightCategory } from "@prisma/client";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import formbricks from "@formbricks/js";
|
||||
import { TDocumentFilterCriteria } from "@formbricks/types/documents";
|
||||
import { TInsight, TInsightFilterCriteria } from "@formbricks/types/insights";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { getEnvironmentInsightsAction } from "../actions";
|
||||
import CategoryBadge from "./category-select";
|
||||
@@ -31,11 +35,11 @@ export const InsightView = ({
|
||||
locale,
|
||||
}: InsightViewProps) => {
|
||||
const t = useTranslations();
|
||||
const [insights, setInsights] = useState<TInsight[]>([]);
|
||||
const [insights, setInsights] = useState<TInsightWithDocumentCount[]>([]);
|
||||
const [hasMore, setHasMore] = useState<boolean>(true);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [isInsightSheetOpen, setIsInsightSheetOpen] = useState(false);
|
||||
const [currentInsight, setCurrentInsight] = useState<TInsight | null>(null);
|
||||
const [currentInsight, setCurrentInsight] = useState<TInsightWithDocumentCount | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<string>("featureRequest");
|
||||
|
||||
const handleFeedback = (feedback: "positive" | "negative") => {
|
||||
@@ -56,7 +60,7 @@ export const InsightView = ({
|
||||
documentCreatedAt: {
|
||||
min: statsFrom,
|
||||
},
|
||||
category: activeTab === "all" ? undefined : (activeTab as TInsight["category"]),
|
||||
category: activeTab === "all" ? undefined : (activeTab as InsightCategory),
|
||||
}),
|
||||
[statsFrom, activeTab]
|
||||
);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { insightCache } from "@/lib/cache/insight";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import {
|
||||
TInsightFilterCriteria,
|
||||
TInsightWithDocumentCount,
|
||||
ZInsightFilterCriteria,
|
||||
} from "@/modules/ee/insights/experience/types/insights";
|
||||
import { Insight, Prisma } from "@prisma/client";
|
||||
import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { cache } from "@formbricks/lib/cache";
|
||||
@@ -8,7 +13,6 @@ import { responseCache } from "@formbricks/lib/response/cache";
|
||||
import { validateInputs } from "@formbricks/lib/utils/validate";
|
||||
import { ZId, ZOptionalNumber } from "@formbricks/types/common";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { TInsight, TInsightFilterCriteria, ZInsightFilterCriteria } from "@formbricks/types/insights";
|
||||
|
||||
export const getInsights = reactCache(
|
||||
async (
|
||||
@@ -16,7 +20,7 @@ export const getInsights = reactCache(
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
filterCriteria?: TInsightFilterCriteria
|
||||
): Promise<TInsight[]> =>
|
||||
): Promise<TInsightWithDocumentCount[]> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs(
|
||||
@@ -84,7 +88,7 @@ export const getInsights = reactCache(
|
||||
)()
|
||||
);
|
||||
|
||||
export const updateInsight = async (insightId: string, updates: Partial<TInsight>): Promise<void> => {
|
||||
export const updateInsight = async (insightId: string, updates: Partial<Insight>): Promise<void> => {
|
||||
try {
|
||||
const updatedInsight = await prisma.insight.update({
|
||||
where: { id: insightId },
|
||||
|
||||
21
apps/web/modules/ee/insights/experience/types/insights.ts
Normal file
21
apps/web/modules/ee/insights/experience/types/insights.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Insight } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
|
||||
export const ZInsightFilterCriteria = z.object({
|
||||
documentCreatedAt: z
|
||||
.object({
|
||||
min: z.date().optional(),
|
||||
max: z.date().optional(),
|
||||
})
|
||||
.optional(),
|
||||
category: ZInsight.shape.category.optional(),
|
||||
});
|
||||
|
||||
export type TInsightFilterCriteria = z.infer<typeof ZInsightFilterCriteria>;
|
||||
|
||||
export interface TInsightWithDocumentCount extends Insight {
|
||||
_count: {
|
||||
documentInsights: number;
|
||||
};
|
||||
}
|
||||
@@ -4,9 +4,7 @@
|
||||
"version": "0.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"zod",
|
||||
"zod-utils.ts"
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf .turbo node_modules",
|
||||
@@ -37,7 +35,6 @@
|
||||
"prisma-dbml-generator": "0.12.0",
|
||||
"prisma-json-types-generator": "3.1.1",
|
||||
"ts-node": "10.9.2",
|
||||
"zod": "3.24.1",
|
||||
"zod-prisma": "0.5.4"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,6 @@ generator client {
|
||||
// provider = "prisma-dbml-generator"
|
||||
// }
|
||||
|
||||
generator zod {
|
||||
provider = "zod-prisma"
|
||||
output = "./zod"
|
||||
imports = "./zod-utils"
|
||||
relationModel = "default"
|
||||
}
|
||||
|
||||
generator json {
|
||||
provider = "prisma-json-types-generator"
|
||||
}
|
||||
@@ -117,20 +110,15 @@ model Response {
|
||||
contactId String?
|
||||
endingId String?
|
||||
notes ResponseNote[]
|
||||
/// @zod.custom(imports.ZResponseData)
|
||||
/// [ResponseData]
|
||||
data Json @default("{}")
|
||||
/// @zod.custom(imports.ZResponseVariables)
|
||||
/// [ResponseVariables]
|
||||
variables Json @default("{}")
|
||||
/// @zod.custom(imports.ZResponseTtc)
|
||||
/// [ResponseTtc]
|
||||
ttc Json @default("{}")
|
||||
/// @zod.custom(imports.ZResponseMeta)
|
||||
/// [ResponseMeta]
|
||||
meta Json @default("{}")
|
||||
tags TagsOnResponses[]
|
||||
/// @zod.custom(imports.ZResponseContactAttributes)
|
||||
/// [ResponseContactAttributes]
|
||||
contactAttributes Json?
|
||||
// singleUseId, used to prevent multiple responses
|
||||
@@ -273,20 +261,15 @@ model Survey {
|
||||
creator User? @relation(fields: [createdBy], references: [id])
|
||||
createdBy String?
|
||||
status SurveyStatus @default(draft)
|
||||
/// @zod.custom(imports.ZSurveyWelcomeCard)
|
||||
/// [SurveyWelcomeCard]
|
||||
welcomeCard Json @default("{\"enabled\": false}")
|
||||
/// @zod.custom(imports.ZSurveyQuestions)
|
||||
/// [SurveyQuestions]
|
||||
questions Json @default("[]")
|
||||
/// @zod.custom(imports.ZSurveyEnding)
|
||||
/// [SurveyEnding]
|
||||
endings Json[] @default([])
|
||||
thankYouCard Json? //deprecated
|
||||
/// @zod.custom(imports.ZSurveyHiddenFields)
|
||||
/// [SurveyHiddenFields]
|
||||
hiddenFields Json @default("{\"enabled\": false}")
|
||||
/// @zod.custom(imports.ZSurveyVariables)
|
||||
/// [SurveyVariables]
|
||||
variables Json @default("[]")
|
||||
responses Response[]
|
||||
@@ -294,7 +277,6 @@ model Survey {
|
||||
recontactDays Int?
|
||||
displayLimit Int?
|
||||
triggers SurveyTrigger[]
|
||||
/// @zod.custom(imports.ZSurveyInlineTriggers)
|
||||
/// [SurveyInlineTriggers]
|
||||
inlineTriggers Json?
|
||||
attributeFilters SurveyAttributeFilter[]
|
||||
@@ -304,25 +286,20 @@ model Survey {
|
||||
delay Int @default(0)
|
||||
runOnDate DateTime?
|
||||
closeOnDate DateTime?
|
||||
/// @zod.custom(imports.ZSurveyClosedMessage)
|
||||
/// [SurveyClosedMessage]
|
||||
surveyClosedMessage Json?
|
||||
segmentId String?
|
||||
segment Segment? @relation(fields: [segmentId], references: [id])
|
||||
|
||||
/// @zod.custom(imports.ZSurveyProjectOverwrites)
|
||||
/// [SurveyProjectOverwrites]
|
||||
projectOverwrites Json?
|
||||
|
||||
/// @zod.custom(imports.ZSurveyStyling)
|
||||
/// [SurveyStyling]
|
||||
styling Json?
|
||||
|
||||
/// @zod.custom(imports.ZSurveySingleUse)
|
||||
/// [SurveySingleUse]
|
||||
singleUse Json? @default("{\"enabled\": false, \"isEncrypted\": true}")
|
||||
|
||||
/// @zod.custom(imports.ZSurveyVerifyEmail)
|
||||
/// [SurveyVerifyEmail]
|
||||
verifyEmail Json? // deprecated
|
||||
isVerifyEmailEnabled Boolean @default(false)
|
||||
@@ -347,10 +324,8 @@ model SurveyFollowUp {
|
||||
surveyId String
|
||||
name String
|
||||
/// [SurveyFollowUpTrigger]
|
||||
/// @zod.custom(imports.ZSurveyFollowUpTrigger)
|
||||
trigger Json
|
||||
/// [SurveyFollowUpAction]
|
||||
/// @zod.custom(imports.ZSurveyFollowUpAction)
|
||||
action Json
|
||||
}
|
||||
|
||||
@@ -368,7 +343,6 @@ model ActionClass {
|
||||
description String?
|
||||
type ActionType
|
||||
key String?
|
||||
/// @zod.custom(imports.ZActionClassNoCodeConfig)
|
||||
/// [ActionClassNoCodeConfig]
|
||||
noCodeConfig Json?
|
||||
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
|
||||
@@ -396,7 +370,6 @@ model Integration {
|
||||
id String @id @default(cuid())
|
||||
type IntegrationType
|
||||
environmentId String
|
||||
/// @zod.custom(imports.ZIntegrationConfig)
|
||||
/// [IntegrationConfig]
|
||||
config Json
|
||||
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
|
||||
@@ -461,10 +434,8 @@ model Project {
|
||||
environments Environment[]
|
||||
brandColor String? // deprecated; use styling.brandColor instead
|
||||
highlightBorderColor String? // deprecated
|
||||
/// @zod.custom(imports.ZProjectStyling)
|
||||
/// [Styling]
|
||||
styling Json @default("{\"allowStyleOverwrite\":true}")
|
||||
/// @zod.custom(imports.ZProjectConfig)
|
||||
/// [ProjectConfig]
|
||||
config Json @default("{}")
|
||||
recontactDays Int @default(7)
|
||||
@@ -474,7 +445,6 @@ model Project {
|
||||
clickOutsideClose Boolean @default(true)
|
||||
darkOverlay Boolean @default(false)
|
||||
languages Language[]
|
||||
/// @zod.custom(imports.ZLogo)
|
||||
/// [Logo]
|
||||
logo Json?
|
||||
projectTeams ProjectTeam[]
|
||||
@@ -490,10 +460,8 @@ model Organization {
|
||||
name String
|
||||
memberships Membership[]
|
||||
projects Project[]
|
||||
/// @zod.custom(imports.ZOrganizationBilling)
|
||||
/// [OrganizationBilling]
|
||||
billing Json
|
||||
/// @zod.custom(imports.ZOrganizationWhitelabel)
|
||||
/// [OrganizationWhitelabel]
|
||||
whitelabel Json @default("{}")
|
||||
invites Invite[]
|
||||
@@ -640,11 +608,8 @@ model User {
|
||||
invitesAccepted Invite[] @relation("inviteAcceptedBy")
|
||||
role Role?
|
||||
objective Objective?
|
||||
/// @zod.custom(imports.ZUserNotificationSettings)
|
||||
/// @zod.custom(imports.ZUserNotificationSettings)
|
||||
/// [UserNotificationSettings]
|
||||
notificationSettings Json @default("{}")
|
||||
/// @zod.custom(imports.ZUserLocale)
|
||||
/// [Locale]
|
||||
locale String @default("en-US")
|
||||
surveys Survey[]
|
||||
@@ -667,7 +632,6 @@ model Segment {
|
||||
title String
|
||||
description String?
|
||||
isPrivate Boolean @default(true)
|
||||
/// @zod.custom(imports.ZSegmentFilters)
|
||||
/// [SegmentFilter]
|
||||
filters Json @default("[]")
|
||||
environmentId String
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"exclude": ["node_modules", "dist", "zod"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"extends": "@formbricks/config-typescript/node16.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "tsup.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/* eslint-disable import/no-relative-packages -- required for importing types */
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZActionProperties = z.record(z.string());
|
||||
export { ZActionClassNoCodeConfig } from "../types/action-classes";
|
||||
export { ZIntegrationConfig } from "../types/integration";
|
||||
|
||||
export { ZResponseData, ZResponseMeta, ZResponseTtc } from "../types/responses";
|
||||
|
||||
export {
|
||||
ZSurveyWelcomeCard,
|
||||
ZSurveyQuestions,
|
||||
ZSurveyHiddenFields,
|
||||
ZSurveyVariables,
|
||||
ZSurveyClosedMessage,
|
||||
ZSurveyProjectOverwrites,
|
||||
ZSurveyStyling,
|
||||
ZSurveySingleUse,
|
||||
ZSurveyInlineTriggers,
|
||||
ZSurveyEnding,
|
||||
} from "../types/surveys/types";
|
||||
|
||||
export { ZSurveyFollowUpAction, ZSurveyFollowUpTrigger } from "./types/survey-follow-up";
|
||||
|
||||
export { ZSegmentFilters } from "../types/segment";
|
||||
export { ZOrganizationBilling, ZOrganizationWhitelabel } from "../types/organizations";
|
||||
export { ZUserNotificationSettings } from "../types/user";
|
||||
12
packages/database/zod/insights.ts
Normal file
12
packages/database/zod/insights.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { type Insight } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZInsight = z.object({
|
||||
id: z.string().cuid2(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
environmentId: z.string().cuid2(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
category: z.enum(["featureRequest", "complaint", "praise", "other"]),
|
||||
}) satisfies z.ZodType<Insight>;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
import { ZId } from "./common";
|
||||
import { ZInsightCategory } from "./insights";
|
||||
import { ZSurveyQuestionId } from "./surveys/types";
|
||||
|
||||
export const ZDocumentSentiment = z.enum(["positive", "negative", "neutral"]);
|
||||
@@ -47,7 +47,7 @@ export const ZGenerateDocumentObjectSchema = z.object({
|
||||
z.object({
|
||||
title: z.string().describe("insight title, very specific"),
|
||||
description: z.string().describe("very brief insight description"),
|
||||
category: ZInsightCategory,
|
||||
category: ZInsight.shape.category,
|
||||
})
|
||||
),
|
||||
isSpam: z.boolean(),
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { z } from "zod";
|
||||
import { ZId } from "./common";
|
||||
|
||||
export const ZInsightCategory = z.enum(["featureRequest", "complaint", "praise", "other"]);
|
||||
|
||||
export type TInsightCategory = z.infer<typeof ZInsightCategory>;
|
||||
|
||||
export const ZInsight = z.object({
|
||||
id: ZId,
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
environmentId: ZId,
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
category: ZInsightCategory,
|
||||
_count: z.object({
|
||||
documentInsights: z.number(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type TInsight = z.infer<typeof ZInsight>;
|
||||
|
||||
export const ZInsightCreateInput = z.object({
|
||||
environmentId: ZId,
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
category: ZInsightCategory,
|
||||
vector: z.array(z.number()).length(512),
|
||||
});
|
||||
|
||||
export type TInsightCreateInput = z.infer<typeof ZInsightCreateInput>;
|
||||
|
||||
export const ZInsightFilterCriteria = z.object({
|
||||
documentCreatedAt: z
|
||||
.object({
|
||||
min: z.date().optional(),
|
||||
max: z.date().optional(),
|
||||
})
|
||||
.optional(),
|
||||
category: ZInsightCategory.optional(),
|
||||
});
|
||||
|
||||
export type TInsightFilterCriteria = z.infer<typeof ZInsightFilterCriteria>;
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable no-new -- required for error */
|
||||
import { type ZodIssue, z } from "zod";
|
||||
import { ZSurveyFollowUp } from "@formbricks/database/types/survey-follow-up";
|
||||
import { ZInsight } from "@formbricks/database/zod/insights";
|
||||
import { ZActionClass, ZActionClassNoCodeConfig } from "../action-classes";
|
||||
import { ZAllowedFileExtension, ZColor, ZId, ZPlacement, getZSafeUrl } from "../common";
|
||||
import { ZContactAttributes } from "../contact-attribute";
|
||||
import { ZInsight } from "../insights";
|
||||
import { ZLanguage } from "../project";
|
||||
import { ZSegment } from "../segment";
|
||||
import { ZBaseStyling } from "../styling";
|
||||
@@ -2371,7 +2371,13 @@ export const ZSurveyQuestionSummaryOpenText = z.object({
|
||||
contactAttributes: ZContactAttributes.nullable(),
|
||||
})
|
||||
),
|
||||
insights: z.array(ZInsight),
|
||||
insights: z.array(
|
||||
ZInsight.extend({
|
||||
_count: z.object({
|
||||
documentInsights: z.number(),
|
||||
}),
|
||||
})
|
||||
),
|
||||
insightsEnabled: z.boolean().optional(),
|
||||
});
|
||||
|
||||
|
||||
79
pnpm-lock.yaml
generated
79
pnpm-lock.yaml
generated
@@ -831,9 +831,6 @@ importers:
|
||||
zod:
|
||||
specifier: 3.24.1
|
||||
version: 3.24.1
|
||||
zod-prisma:
|
||||
specifier: 0.5.4
|
||||
version: 0.5.4(decimal.js@10.4.3)(prisma@6.0.1)(zod@3.24.1)
|
||||
|
||||
packages/js:
|
||||
dependencies:
|
||||
@@ -3707,9 +3704,6 @@ packages:
|
||||
prisma:
|
||||
optional: true
|
||||
|
||||
'@prisma/debug@3.8.1':
|
||||
resolution: {integrity: sha512-ft4VPTYME1UBJ7trfrBuF2w9jX1ipDy786T9fAEskNGb+y26gPDqz5fiEWc2kgHNeVdz/qTI/V3wXILRyEcgxQ==}
|
||||
|
||||
'@prisma/debug@5.0.0':
|
||||
resolution: {integrity: sha512-3q/M/KqlQ01/HJXifU/zCNOHkoTWu24kGelMF/IBrRxm7njPqTTbwfnT1dh4JK+nuWM5/Dg1Lv00u2c0l7AHxg==}
|
||||
|
||||
@@ -3740,9 +3734,6 @@ packages:
|
||||
'@prisma/fetch-engine@6.0.1':
|
||||
resolution: {integrity: sha512-T36bWFVGeGYYSyYOj9d+O9G3sBC+pAyMC+jc45iSL63/Haq1GrYjQPgPMxrEj9m739taXrupoysRedQ+VyvM/Q==}
|
||||
|
||||
'@prisma/generator-helper@3.8.1':
|
||||
resolution: {integrity: sha512-3zSy+XTEjmjLj6NO+/YPN1Cu7or3xA11TOoOnLRJ9G4pTT67RJXjK0L9Xy5n+3I0Xlb7xrWCgo8MvQQLMWzxPA==}
|
||||
|
||||
'@prisma/generator-helper@5.0.0':
|
||||
resolution: {integrity: sha512-pufQ1mhoH6WzKNtzL79HZDoW4Ql3Lf8QEKVmBoW8e3Tdb50bxpYBYue5LBqp9vNW1xd1pgZO53cNiRfLX2d4Zg==}
|
||||
|
||||
@@ -5401,9 +5392,6 @@ packages:
|
||||
svelte:
|
||||
optional: true
|
||||
|
||||
'@ts-morph/common@0.12.3':
|
||||
resolution: {integrity: sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==}
|
||||
|
||||
'@tsconfig/node10@1.0.11':
|
||||
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
|
||||
|
||||
@@ -5449,9 +5437,6 @@ packages:
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
'@types/debug@4.1.7':
|
||||
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
|
||||
|
||||
'@types/debug@4.1.8':
|
||||
resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==}
|
||||
|
||||
@@ -6857,9 +6842,6 @@ packages:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
code-block-writer@11.0.3:
|
||||
resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==}
|
||||
|
||||
codepage@1.15.0:
|
||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||
engines: {node: '>=0.8'}
|
||||
@@ -10567,9 +10549,6 @@ packages:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
parenthesis@3.1.8:
|
||||
resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==}
|
||||
|
||||
parse-asn1@5.1.7:
|
||||
resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@@ -12501,9 +12480,6 @@ packages:
|
||||
ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
|
||||
ts-morph@13.0.3:
|
||||
resolution: {integrity: sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==}
|
||||
|
||||
ts-node@10.9.2:
|
||||
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
|
||||
hasBin: true
|
||||
@@ -13349,18 +13325,6 @@ packages:
|
||||
resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
zod-prisma@0.5.4:
|
||||
resolution: {integrity: sha512-5Ca4Qd1a1jy1T/NqCEpbr0c+EsbjJfJ/7euEHob3zDvtUK2rTuD1Rc/vfzH8q8PtaR2TZbysD88NHmrLwpv3Xg==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
decimal.js: ^10.0.0
|
||||
prisma: ^3.0.0
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
decimal.js:
|
||||
optional: true
|
||||
|
||||
zod-to-json-schema@3.24.1:
|
||||
resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==}
|
||||
peerDependencies:
|
||||
@@ -16886,12 +16850,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
prisma: 6.0.1
|
||||
|
||||
'@prisma/debug@3.8.1':
|
||||
dependencies:
|
||||
'@types/debug': 4.1.7
|
||||
ms: 2.1.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
'@prisma/debug@5.0.0':
|
||||
dependencies:
|
||||
'@types/debug': 4.1.8
|
||||
@@ -16948,13 +16906,6 @@ snapshots:
|
||||
'@prisma/engines-version': 5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e
|
||||
'@prisma/get-platform': 6.0.1
|
||||
|
||||
'@prisma/generator-helper@3.8.1':
|
||||
dependencies:
|
||||
'@prisma/debug': 3.8.1
|
||||
'@types/cross-spawn': 6.0.2
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
|
||||
'@prisma/generator-helper@5.0.0':
|
||||
dependencies:
|
||||
'@prisma/debug': 5.0.0
|
||||
@@ -19265,13 +19216,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ts-morph/common@0.12.3':
|
||||
dependencies:
|
||||
fast-glob: 3.3.2
|
||||
minimatch: 3.1.2
|
||||
mkdirp: 1.0.4
|
||||
path-browserify: 1.0.1
|
||||
|
||||
'@tsconfig/node10@1.0.11': {}
|
||||
|
||||
'@tsconfig/node12@1.0.11': {}
|
||||
@@ -19323,10 +19267,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/ms': 0.7.34
|
||||
|
||||
'@types/debug@4.1.7':
|
||||
dependencies:
|
||||
'@types/ms': 0.7.34
|
||||
|
||||
'@types/debug@4.1.8':
|
||||
dependencies:
|
||||
'@types/ms': 0.7.34
|
||||
@@ -21023,8 +20963,6 @@ snapshots:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
code-block-writer@11.0.3: {}
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
collapse-white-space@2.1.0: {}
|
||||
@@ -25719,8 +25657,6 @@ snapshots:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
||||
parenthesis@3.1.8: {}
|
||||
|
||||
parse-asn1@5.1.7:
|
||||
dependencies:
|
||||
asn1.js: 4.10.1
|
||||
@@ -27967,11 +27903,6 @@ snapshots:
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
ts-morph@13.0.3:
|
||||
dependencies:
|
||||
'@ts-morph/common': 0.12.3
|
||||
code-block-writer: 11.0.3
|
||||
|
||||
ts-node@10.9.2(@types/node@22.10.2)(typescript@5.7.2):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
@@ -28820,16 +28751,6 @@ snapshots:
|
||||
compress-commons: 4.1.2
|
||||
readable-stream: 3.6.2
|
||||
|
||||
zod-prisma@0.5.4(decimal.js@10.4.3)(prisma@6.0.1)(zod@3.24.1):
|
||||
dependencies:
|
||||
'@prisma/generator-helper': 3.8.1
|
||||
parenthesis: 3.1.8
|
||||
prisma: 6.0.1
|
||||
ts-morph: 13.0.3
|
||||
zod: 3.24.1
|
||||
optionalDependencies:
|
||||
decimal.js: 10.4.3
|
||||
|
||||
zod-to-json-schema@3.24.1(zod@3.24.1):
|
||||
dependencies:
|
||||
zod: 3.24.1
|
||||
|
||||
Reference in New Issue
Block a user