chore: added date transform for cached services (#1753)

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Dhruwang Jariwala
2023-12-11 19:23:54 +05:30
committed by GitHub
parent d8b6b95ed5
commit 3103760611
23 changed files with 267 additions and 244 deletions
@@ -98,7 +98,6 @@ export const leaveTeamAction = async (teamId: string) => {
export const createInviteTokenAction = async (inviteId: string) => {
const { email } = await getInvite(inviteId);
const inviteToken = createInviteToken(inviteId, email, {
expiresIn: "7d",
});
@@ -74,7 +74,6 @@ export default function MemberActions({ team, member, invite, showDeleteButton }
const handleShareInvite = async () => {
try {
if (!invite) return;
const { inviteToken } = await createInviteTokenAction(invite.id);
setShareInviteToken(inviteToken);
setShowShareInviteModal(true);
+7 -21
View File
@@ -2,7 +2,7 @@ import "server-only";
import { prisma } from "@formbricks/database";
import { TActionClassType } from "@formbricks/types/actionClasses";
import { TAction, TActionInput, ZActionInput } from "@formbricks/types/actions";
import { TAction, TActionInput, ZAction, ZActionInput } from "@formbricks/types/actions";
import { ZOptionalNumber } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/environment";
import { DatabaseError } from "@formbricks/types/errors";
@@ -14,6 +14,7 @@ import { createActionClass, getActionClassByEnvironmentIdAndName } from "../acti
import { validateInputs } from "../utils/validate";
import { actionCache } from "./cache";
import { createPerson, getPersonByUserId } from "../person/service";
import { formatDateFields } from "../utils/datetime";
export const getLatestActionByEnvironmentId = async (environmentId: string): Promise<TAction | null> => {
const action = await unstable_cache(
@@ -62,12 +63,7 @@ export const getLatestActionByEnvironmentId = async (environmentId: string): Pro
// since the unstable_cache function does not support deserialization of dates, we need to manually deserialize them
// https://github.com/vercel/next.js/issues/51613
return action
? {
...action,
createdAt: new Date(action.createdAt),
}
: action;
return action ? formatDateFields(action, ZAction) : null;
};
export const getLatestActionByPersonId = async (personId: string): Promise<TAction | null> => {
@@ -116,12 +112,7 @@ export const getLatestActionByPersonId = async (personId: string): Promise<TActi
// since the unstable_cache function does not support deserialization of dates, we need to manually deserialize them
// https://github.com/vercel/next.js/issues/51613
return action
? {
...action,
createdAt: new Date(action.createdAt),
}
: action;
return action ? formatDateFields(action, ZAction) : null;
};
export const getActionsByPersonId = async (personId: string, page?: number): Promise<TAction[]> => {
@@ -167,10 +158,7 @@ export const getActionsByPersonId = async (personId: string, page?: number): Pro
)();
// Deserialize dates if caching does not support deserialization
return actions.map((action) => ({
...action,
createdAt: new Date(action.createdAt),
}));
return actions.map((action) => formatDateFields(action, ZAction));
};
export const getActionsByEnvironmentId = async (environmentId: string, page?: number): Promise<TAction[]> => {
@@ -224,10 +212,8 @@ export const getActionsByEnvironmentId = async (environmentId: string, page?: nu
// since the unstable_cache function does not support deserialization of dates, we need to manually deserialize them
// https://github.com/vercel/next.js/issues/51613
return actions.map((action) => ({
...action,
createdAt: new Date(action.createdAt),
}));
return actions.map((action) => formatDateFields(action, ZAction));
};
export const createAction = async (data: TActionInput): Promise<TAction> => {
+20 -9
View File
@@ -2,7 +2,12 @@
import "server-only";
import { prisma } from "@formbricks/database";
import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/actionClasses";
import {
TActionClass,
TActionClassInput,
ZActionClass,
ZActionClassInput,
} from "@formbricks/types/actionClasses";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/environment";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
@@ -11,6 +16,7 @@ import { unstable_cache } from "next/cache";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { validateInputs } from "../utils/validate";
import { actionClassCache } from "./cache";
import { formatDateFields } from "../utils/datetime";
const select = {
id: true,
@@ -23,8 +29,8 @@ const select = {
environmentId: true,
};
export const getActionClasses = (environmentId: string, page?: number): Promise<TActionClass[]> =>
unstable_cache(
export const getActionClasses = async (environmentId: string, page?: number): Promise<TActionClass[]> => {
const actionClasses = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
@@ -40,8 +46,7 @@ export const getActionClasses = (environmentId: string, page?: number): Promise<
createdAt: "asc",
},
});
return actionClasses;
return actionClasses.map((actionClass) => formatDateFields(actionClass, ZActionClass));
} catch (error) {
throw new DatabaseError(`Database error when fetching actions for environment ${environmentId}`);
}
@@ -52,12 +57,14 @@ export const getActionClasses = (environmentId: string, page?: number): Promise<
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return actionClasses.map((actionClass) => formatDateFields(actionClass, ZActionClass));
};
export const getActionClassByEnvironmentIdAndName = async (
environmentId: string,
name: string
): Promise<TActionClass | null> =>
unstable_cache(
): Promise<TActionClass | null> => {
const actionClass = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [name, ZString]);
@@ -81,9 +88,11 @@ export const getActionClassByEnvironmentIdAndName = async (
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return actionClass ? formatDateFields(actionClass, ZActionClass) : null;
};
export const getActionClass = async (actionClassId: string): Promise<TActionClass | null> =>
unstable_cache(
export const getActionClass = async (actionClassId: string): Promise<TActionClass | null> => {
const actionClass = await unstable_cache(
async () => {
validateInputs([actionClassId, ZId]);
@@ -106,6 +115,8 @@ export const getActionClass = async (actionClassId: string): Promise<TActionClas
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return actionClass ? formatDateFields(actionClass, ZActionClass) : null;
};
export const deleteActionClass = async (
environmentId: string,
+13 -11
View File
@@ -1,20 +1,21 @@
import "server-only";
import { prisma } from "@formbricks/database";
import { TApiKey, TApiKeyCreateInput, ZApiKeyCreateInput } from "@formbricks/types/apiKeys";
import { TApiKey, TApiKeyCreateInput, ZApiKey, ZApiKeyCreateInput } from "@formbricks/types/apiKeys";
import { Prisma } from "@prisma/client";
import { getHash } from "../crypto";
import { createHash, randomBytes } from "crypto";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { DatabaseError, InvalidInputError } from "@formbricks/types/errors";
import { validateInputs } from "../utils/validate";
import { ZId } from "@formbricks/types/environment";
import { ZString, ZOptionalNumber } from "@formbricks/types/common";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { unstable_cache } from "next/cache";
import { apiKeyCache } from "./cache";
import { formatDateFields } from "../utils/datetime";
export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> =>
unstable_cache(
export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> => {
const apiKey = await unstable_cache(
async () => {
validateInputs([apiKeyId, ZString]);
@@ -29,10 +30,6 @@ export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> =>
},
});
if (!apiKeyData) {
throw new ResourceNotFoundError("API Key from ID", apiKeyId);
}
return apiKeyData;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -48,9 +45,11 @@ export const getApiKey = async (apiKeyId: string): Promise<TApiKey | null> =>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return apiKey ? formatDateFields(apiKey, ZApiKey) : null;
};
export const getApiKeys = async (environmentId: string, page?: number): Promise<TApiKey[]> =>
unstable_cache(
export const getApiKeys = async (environmentId: string, page?: number): Promise<TApiKey[]> => {
const apiKeys = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
@@ -77,6 +76,8 @@ export const getApiKeys = async (environmentId: string, page?: number): Promise<
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return apiKeys.map((apiKey) => formatDateFields(apiKey, ZApiKey));
};
export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex");
@@ -112,7 +113,7 @@ export async function createApiKey(environmentId: string, apiKeyData: TApiKeyCre
export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null> => {
const hashedKey = getHash(apiKey);
return unstable_cache(
const apiKeyData = await unstable_cache(
async () => {
validateInputs([apiKey, ZString]);
@@ -142,6 +143,7 @@ export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return apiKeyData ? formatDateFields(apiKeyData, ZApiKey) : null;
};
export const deleteApiKey = async (id: string): Promise<TApiKey | null> => {
+11 -11
View File
@@ -8,6 +8,7 @@ import {
ZAttributeClassUpdateInput,
TAttributeClassType,
ZAttributeClassType,
ZAttributeClass,
} from "@formbricks/types/attributeClasses";
import { ZId } from "@formbricks/types/environment";
import { validateInputs } from "../utils/validate";
@@ -16,7 +17,7 @@ import { unstable_cache } from "next/cache";
import { SERVICES_REVALIDATION_INTERVAL, ITEMS_PER_PAGE } from "../constants";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { attributeClassCache } from "./cache";
import { formatAttributeClassDateFields } from "./util";
import { formatDateFields } from "../utils/datetime";
export const getAttributeClass = async (attributeClassId: string): Promise<TAttributeClass | null> => {
const attributeClass = await unstable_cache(
@@ -40,11 +41,7 @@ export const getAttributeClass = async (attributeClassId: string): Promise<TAttr
}
)();
if (!attributeClass) {
return null;
}
return formatAttributeClassDateFields(attributeClass);
return attributeClass ? formatDateFields(attributeClass, ZAttributeClass) : null;
};
export const getAttributeClasses = async (
@@ -80,8 +77,7 @@ export const getAttributeClasses = async (
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return attributeClasses.map(formatAttributeClassDateFields);
return attributeClasses.map((attributeClass) => formatDateFields(attributeClass, ZAttributeClass));
};
export const updateAttributeClass = async (
@@ -113,17 +109,19 @@ export const updateAttributeClass = async (
}
};
export const getAttributeClassByName = async (environmentId: string, name: string) =>
await unstable_cache(
export const getAttributeClassByName = async (environmentId: string, name: string) => {
const attributeClass = await unstable_cache(
async (): Promise<TAttributeClass | null> => {
validateInputs([environmentId, ZId], [name, ZString]);
return await prisma.attributeClass.findFirst({
const attributeClass = await prisma.attributeClass.findFirst({
where: {
environmentId,
name,
},
});
return attributeClass;
},
[`getAttributeClassByName-${environmentId}-${name}`],
{
@@ -131,6 +129,8 @@ export const getAttributeClassByName = async (environmentId: string, name: strin
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return attributeClass ? formatDateFields(attributeClass, ZAttributeClass) : null;
};
export const createAttributeClass = async (
environmentId: string,
+9 -11
View File
@@ -8,6 +8,7 @@ import {
TDisplayLegacyCreateInput,
TDisplayLegacyUpdateInput,
TDisplayUpdateInput,
ZDisplay,
ZDisplayCreateInput,
ZDisplayLegacyCreateInput,
ZDisplayLegacyUpdateInput,
@@ -21,8 +22,8 @@ import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { createPerson, getPersonByUserId } from "../person/service";
import { validateInputs } from "../utils/validate";
import { displayCache } from "./cache";
import { formatDisplaysDateFields } from "./util";
import { TPerson } from "@formbricks/types/people";
import { formatDateFields } from "../utils/datetime";
const selectDisplay = {
id: true,
@@ -33,20 +34,20 @@ const selectDisplay = {
personId: true,
};
export const getDisplay = async (displayId: string): Promise<TDisplay | null> =>
await unstable_cache(
export const getDisplay = async (displayId: string): Promise<TDisplay | null> => {
const display = await unstable_cache(
async () => {
validateInputs([displayId, ZId]);
try {
const responsePrisma = await prisma.response.findUnique({
const display = await prisma.response.findUnique({
where: {
id: displayId,
},
select: selectDisplay,
});
return responsePrisma;
return display;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -61,6 +62,8 @@ export const getDisplay = async (displayId: string): Promise<TDisplay | null> =>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return display ? formatDateFields(display, ZDisplay) : null;
};
export const updateDisplay = async (
displayId: string,
@@ -295,10 +298,6 @@ export const getDisplaysByPersonId = async (personId: string, page?: number): Pr
},
});
if (!displays) {
throw new ResourceNotFoundError("Display from PersonId", personId);
}
return displays;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -314,8 +313,7 @@ export const getDisplaysByPersonId = async (personId: string, page?: number): Pr
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return formatDisplaysDateFields(displays);
return displays.map((display) => formatDateFields(display, ZDisplay));
};
export const deleteDisplayByResponseId = async (responseId: string, surveyId: string): Promise<TDisplay> => {
-9
View File
@@ -1,9 +0,0 @@
import { TDisplay } from "@formbricks/types/displays";
export const formatDisplaysDateFields = (displays: TDisplay[]): TDisplay[] => {
return displays.map((display) => ({
...display,
createdAt: new Date(display.createdAt),
updatedAt: new Date(display.updatedAt),
}));
};
+14 -8
View File
@@ -20,19 +20,20 @@ import { z } from "zod";
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { validateInputs } from "../utils/validate";
import { environmentCache } from "./cache";
import { formatEnvironmentDateFields } from "./util";
import { formatDateFields } from "../utils/datetime";
export const getEnvironment = (environmentId: string): Promise<TEnvironment | null> =>
unstable_cache(
export const getEnvironment = async (environmentId: string): Promise<TEnvironment | null> => {
const environment = await unstable_cache(
async () => {
validateInputs([environmentId, ZId]);
try {
return await prisma.environment.findUnique({
const environment = await prisma.environment.findUnique({
where: {
id: environmentId,
},
});
return environment;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
console.error(error);
@@ -48,9 +49,11 @@ export const getEnvironment = (environmentId: string): Promise<TEnvironment | nu
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return environment ? formatDateFields(environment, ZEnvironment) : null;
};
export const getEnvironments = async (productId: string): Promise<TEnvironment[]> =>
unstable_cache(
export const getEnvironments = async (productId: string): Promise<TEnvironment[]> => {
const environments = await unstable_cache(
async (): Promise<TEnvironment[]> => {
validateInputs([productId, ZId]);
let productPrisma;
@@ -95,6 +98,8 @@ export const getEnvironments = async (productId: string): Promise<TEnvironment[]
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return environments.map((environment) => formatDateFields(environment, ZEnvironment));
};
export const updateEnvironment = async (
environmentId: string,
@@ -130,7 +135,7 @@ export const getFirstEnvironmentByUserId = async (userId: string): Promise<TEnvi
async () => {
validateInputs([userId, ZId]);
try {
return await prisma.environment.findFirst({
const environment = await prisma.environment.findFirst({
where: {
type: "production",
product: {
@@ -144,6 +149,7 @@ export const getFirstEnvironmentByUserId = async (userId: string): Promise<TEnvi
},
},
});
return environment;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -159,7 +165,7 @@ export const getFirstEnvironmentByUserId = async (userId: string): Promise<TEnvi
}
)();
return environment ? formatEnvironmentDateFields(environment) : environment;
return environment ? formatDateFields(environment, ZEnvironment) : null;
};
export const createEnvironment = async (
-14
View File
@@ -1,14 +0,0 @@
import "server-only";
import { TEnvironment } from "@formbricks/types/environment";
export const formatEnvironmentDateFields = (environemt: TEnvironment): TEnvironment => {
if (typeof environemt.createdAt === "string") {
environemt.createdAt = new Date(environemt.createdAt);
}
if (typeof environemt.updatedAt === "string") {
environemt.updatedAt = new Date(environemt.updatedAt);
}
return environemt;
};
+31 -20
View File
@@ -1,15 +1,21 @@
import "server-only";
import { prisma } from "@formbricks/database";
import { Prisma } from "@prisma/client";
import { DatabaseError } from "@formbricks/types/errors";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/environment";
import { TIntegration, TIntegrationInput, ZIntegrationType } from "@formbricks/types/integration";
import { validateInputs } from "../utils/validate";
import { ZString, ZOptionalNumber } from "@formbricks/types/common";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { integrationCache } from "./cache";
import { DatabaseError } from "@formbricks/types/errors";
import {
TIntegration,
TIntegrationInput,
ZIntegration,
ZIntegrationType,
} from "@formbricks/types/integration";
import { Prisma } from "@prisma/client";
import { unstable_cache } from "next/cache";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { formatDateFields } from "../utils/datetime";
import { validateInputs } from "../utils/validate";
import { integrationCache } from "./cache";
export async function createOrUpdateIntegration(
environmentId: string,
@@ -48,20 +54,20 @@ export async function createOrUpdateIntegration(
}
}
export const getIntegrations = async (environmentId: string, page?: number): Promise<TIntegration[]> =>
unstable_cache(
export const getIntegrations = async (environmentId: string, page?: number): Promise<TIntegration[]> => {
const integrations = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
try {
const result = await prisma.integration.findMany({
const integrations = await prisma.integration.findMany({
where: {
environmentId,
},
take: page ? ITEMS_PER_PAGE : undefined,
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
});
return result;
return integrations;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -75,17 +81,19 @@ export const getIntegrations = async (environmentId: string, page?: number): Pro
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return integrations.map((integration) => formatDateFields(integration, ZIntegration));
};
export const getIntegration = async (integrationId: string): Promise<TIntegration | null> =>
unstable_cache(
export const getIntegration = async (integrationId: string): Promise<TIntegration | null> => {
const integration = await unstable_cache(
async () => {
try {
const result = await prisma.integration.findUnique({
const integration = await prisma.integration.findUnique({
where: {
id: integrationId,
},
});
return result;
return integration;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -96,17 +104,19 @@ export const getIntegration = async (integrationId: string): Promise<TIntegratio
[`getIntegration-${integrationId}`],
{ tags: [integrationCache.tag.byId(integrationId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
return integration ? formatDateFields(integration, ZIntegration) : null;
};
export const getIntegrationByType = async (
environmentId: string,
type: TIntegrationInput["type"]
): Promise<TIntegration | null> =>
unstable_cache(
): Promise<TIntegration | null> => {
const integration = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [type, ZIntegrationType]);
try {
const result = await prisma.integration.findUnique({
const integration = await prisma.integration.findUnique({
where: {
type_environmentId: {
environmentId,
@@ -114,8 +124,7 @@ export const getIntegrationByType = async (
},
},
});
return result;
return integration;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
@@ -129,6 +138,8 @@ export const getIntegrationByType = async (
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return integration ? formatDateFields(integration, ZIntegration) : null;
};
export const deleteIntegration = async (integrationId: string): Promise<TIntegration> => {
validateInputs([integrationId, ZString]);
+17 -10
View File
@@ -10,6 +10,7 @@ import {
ZInviteUpdateInput,
ZCurrentUser,
TCurrentUser,
ZInvite,
} from "@formbricks/types/invites";
import { ResourceNotFoundError, ValidationError, DatabaseError } from "@formbricks/types/errors";
import { ZString, ZOptionalNumber } from "@formbricks/types/common";
@@ -18,8 +19,8 @@ import { validateInputs } from "../utils/validate";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { unstable_cache } from "next/cache";
import { inviteCache } from "./cache";
import { formatInviteDateFields } from "./util";
import { getMembershipByUserIdTeamId } from "../membership/service";
import { formatDateFields } from "../utils/datetime";
const inviteSelect = {
id: true,
@@ -33,8 +34,13 @@ const inviteSelect = {
expiresAt: true,
role: true,
};
export const getInvitesByTeamId = async (teamId: string, page?: number): Promise<TInvite[] | null> => {
interface InviteWithCreator extends TInvite {
creator: {
name: string | null;
email: string;
};
}
export const getInvitesByTeamId = async (teamId: string, page?: number): Promise<TInvite[]> => {
const invites = await unstable_cache(
async () => {
validateInputs([teamId, ZString], [page, ZOptionalNumber]);
@@ -52,8 +58,7 @@ export const getInvitesByTeamId = async (teamId: string, page?: number): Promise
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return invites.map(formatInviteDateFields);
return invites.map((invite: TInvite) => formatDateFields(invite, ZInvite));
};
export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise<TInvite | null> => {
@@ -114,10 +119,8 @@ export const deleteInvite = async (inviteId: string): Promise<TInvite> => {
}
};
export const getInvite = async (
inviteId: string
): Promise<TInvite & { creator: { name: string | null; email: string } }> =>
unstable_cache(
export const getInvite = async (inviteId: string): Promise<InviteWithCreator> => {
const invite = await unstable_cache(
async () => {
validateInputs([inviteId, ZString]);
@@ -138,12 +141,16 @@ export const getInvite = async (
if (!invite) {
throw new ResourceNotFoundError("Invite", inviteId);
}
return invite;
},
[`getInvite-${inviteId}`],
{ tags: [inviteCache.tag.byId(inviteId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
return {
...formatDateFields(invite, ZInvite),
creator: invite.creator,
};
};
export const resendInvite = async (inviteId: string): Promise<TInvite> => {
validateInputs([inviteId, ZString]);
+8 -13
View File
@@ -4,13 +4,14 @@ import { prisma } from "@formbricks/database";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/environment";
import { DatabaseError } from "@formbricks/types/errors";
import { TPerson, TPersonUpdateInput, ZPersonUpdateInput } from "@formbricks/types/people";
import { TPerson, TPersonUpdateInput, ZPerson, ZPersonUpdateInput } from "@formbricks/types/people";
import { Prisma } from "@prisma/client";
import { unstable_cache } from "next/cache";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { validateInputs } from "../utils/validate";
import { personCache } from "./cache";
import { createAttributeClass, getAttributeClassByName } from "../attributeClass/service";
import { formatDateFields } from "../utils/datetime";
export const selectPerson = {
id: true,
@@ -90,11 +91,7 @@ export const getPerson = async (personId: string): Promise<TPerson | null> => {
{ tags: [personCache.tag.byId(personId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
if (!prismaPerson) {
return null;
}
return transformPrismaPerson(prismaPerson);
return prismaPerson ? formatDateFields(transformPrismaPerson(prismaPerson), ZPerson) : null;
};
export const getPeople = async (environmentId: string, page?: number): Promise<TPerson[]> => {
@@ -126,12 +123,8 @@ export const getPeople = async (environmentId: string, page?: number): Promise<T
}
)();
if (!peoplePrisma || peoplePrisma.length === 0) {
return [];
}
return peoplePrisma
.map(transformPrismaPerson)
.map((prismaPerson) => formatDateFields(transformPrismaPerson(prismaPerson), ZPerson))
.filter((person: TPerson | null): person is TPerson => person !== null);
};
@@ -309,8 +302,8 @@ export const updatePerson = async (personId: string, personInput: TPersonUpdateI
}
};
export const getPersonByUserId = async (environmentId: string, userId: string): Promise<TPerson | null> =>
await unstable_cache(
export const getPersonByUserId = async (environmentId: string, userId: string): Promise<TPerson | null> => {
const person = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [userId, ZString]);
@@ -378,6 +371,8 @@ export const getPersonByUserId = async (environmentId: string, userId: string):
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return person ? formatDateFields(person, ZPerson) : null;
};
/**
* @deprecated This function is deprecated and only used in legacy endpoints. Use updatePerson instead.
+13 -11
View File
@@ -15,6 +15,7 @@ import { environmentCache } from "../environment/cache";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { deleteLocalFilesByEnvironmentId, deleteS3FilesByEnvironmentId } from "../storage/service";
import { productCache } from "./cache";
import { formatDateFields } from "../utils/datetime";
const selectProduct = {
id: true,
@@ -33,8 +34,8 @@ const selectProduct = {
environments: true,
};
export const getProducts = async (teamId: string, page?: number): Promise<TProduct[]> =>
unstable_cache(
export const getProducts = async (teamId: string, page?: number): Promise<TProduct[]> => {
const products = await unstable_cache(
async () => {
validateInputs([teamId, ZId], [page, ZOptionalNumber]);
@@ -47,7 +48,6 @@ export const getProducts = async (teamId: string, page?: number): Promise<TProdu
take: page ? ITEMS_PER_PAGE : undefined,
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
});
return products;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -63,9 +63,11 @@ export const getProducts = async (teamId: string, page?: number): Promise<TProdu
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return products.map((product) => formatDateFields(product, ZProduct));
};
export const getProductByEnvironmentId = async (environmentId: string): Promise<TProduct | null> =>
unstable_cache(
export const getProductByEnvironmentId = async (environmentId: string): Promise<TProduct | null> => {
const product = await unstable_cache(
async () => {
validateInputs([environmentId, ZId]);
@@ -83,10 +85,6 @@ export const getProductByEnvironmentId = async (environmentId: string): Promise<
select: selectProduct,
});
if (!productPrisma) {
return null;
}
return productPrisma;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -102,6 +100,8 @@ export const getProductByEnvironmentId = async (environmentId: string): Promise<
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return product ? formatDateFields(product, ZProduct) : null;
};
export const updateProduct = async (
productId: string,
@@ -154,8 +154,8 @@ export const updateProduct = async (
}
};
export const getProduct = async (productId: string): Promise<TProduct | null> =>
unstable_cache(
export const getProduct = async (productId: string): Promise<TProduct | null> => {
const product = await unstable_cache(
async () => {
let productPrisma;
try {
@@ -180,6 +180,8 @@ export const getProduct = async (productId: string): Promise<TProduct | null> =>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return product ? formatDateFields(product, ZProduct) : null;
};
export const deleteProduct = async (productId: string): Promise<TProduct> => {
const product = await prisma.product.delete({
+14 -21
View File
@@ -8,6 +8,7 @@ import {
TProfile,
TProfileCreateInput,
TProfileUpdateInput,
ZProfile,
ZProfileUpdateInput,
} from "@formbricks/types/profile";
import { Prisma } from "@prisma/client";
@@ -16,6 +17,7 @@ import { z } from "zod";
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { updateMembership } from "../membership/service";
import { deleteTeam } from "../team/service";
import { formatDateFields } from "../utils/datetime";
import { validateInputs } from "../utils/validate";
import { profileCache } from "./cache";
import { formatProfileDateFields } from "./util";
@@ -51,7 +53,6 @@ export const getProfile = async (id: string): Promise<TProfile | null> => {
if (!profile) {
return null;
}
return profile;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -68,14 +69,12 @@ export const getProfile = async (id: string): Promise<TProfile | null> => {
}
)();
if (!profile) {
return null;
}
return {
...profile,
...formatProfileDateFields(profile),
} as TProfile;
return profile
? {
...profile,
...formatDateFields(profile, ZProfile),
}
: null;
};
export const getProfileByEmail = async (email: string): Promise<TProfile | null> => {
@@ -91,10 +90,6 @@ export const getProfileByEmail = async (email: string): Promise<TProfile | null>
select: responseSelection,
});
if (!profile) {
return null;
}
return profile;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -111,14 +106,12 @@ export const getProfileByEmail = async (email: string): Promise<TProfile | null>
}
)();
if (!profile) {
return null;
}
return {
...profile,
...formatProfileDateFields(profile),
} as TProfile;
return profile
? {
...profile,
...formatProfileDateFields(profile),
}
: null;
};
const getAdminMemberships = (memberships: TMembership[]): TMembership[] =>
+18 -21
View File
@@ -10,8 +10,10 @@ import {
TResponseInput,
TResponseLegacyInput,
TResponseUpdateInput,
ZResponse,
ZResponseInput,
ZResponseLegacyInput,
ZResponseNote,
ZResponseUpdateInput,
} from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
@@ -20,10 +22,11 @@ import { unstable_cache } from "next/cache";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { deleteDisplayByResponseId } from "../display/service";
import { createPerson, getPerson, getPersonByUserId, transformPrismaPerson } from "../person/service";
import { calculateTtcTotal, formatResponseDateFields } from "../response/util";
import { calculateTtcTotal } from "../response/util";
import { responseNoteCache } from "../responseNote/cache";
import { getResponseNotes } from "../responseNote/service";
import { captureTelemetry } from "../telemetry";
import { formatDateFields } from "../utils/datetime";
import { validateInputs } from "../utils/validate";
import { responseCache } from "./cache";
@@ -137,8 +140,8 @@ export const getResponsesByPersonId = async (
)();
return responses.map((response) => ({
...response,
...formatResponseDateFields(response),
...formatDateFields(response, ZResponse),
notes: response.notes.map((note) => formatDateFields(note, ZResponseNote)),
}));
};
@@ -184,14 +187,12 @@ export const getResponseBySingleUseId = async (
}
)();
if (!response) {
return null;
}
return {
...response,
...formatResponseDateFields(response),
};
return response
? {
...formatDateFields(response, ZResponse),
notes: response.notes.map((note) => formatDateFields(note, ZResponseNote)),
}
: null;
};
export const createResponse = async (responseInput: TResponseInput): Promise<TResponse> => {
@@ -369,13 +370,9 @@ export const getResponse = async (responseId: string): Promise<TResponse | null>
}
)();
if (!response) {
return null;
}
return {
...response,
...formatResponseDateFields(response),
...formatDateFields(response, ZResponse),
notes: response.notes.map((note) => formatDateFields(note, ZResponseNote)),
} as TResponse;
};
@@ -426,8 +423,8 @@ export const getResponses = async (surveyId: string, page?: number): Promise<TRe
)();
return responses.map((response) => ({
...response,
...formatResponseDateFields(response),
...formatDateFields(response, ZResponse),
notes: response.notes.map((note) => formatDateFields(note, ZResponseNote)),
}));
};
@@ -483,8 +480,8 @@ export const getResponsesByEnvironmentId = async (
)();
return responses.map((response) => ({
...response,
...formatResponseDateFields(response),
...formatDateFields(response, ZResponse),
notes: response.notes.map((note) => formatDateFields(note, ZResponseNote)),
}));
};
+14 -7
View File
@@ -2,8 +2,8 @@ import "server-only";
import { prisma } from "@formbricks/database";
import { DatabaseError } from "@formbricks/types/errors";
import { TResponseNote } from "@formbricks/types/responses";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TResponseNote, ZResponseNote } from "@formbricks/types/responses";
import { Prisma } from "@prisma/client";
import { responseCache } from "../response/cache";
import { validateInputs } from "../utils/validate";
@@ -12,6 +12,7 @@ import { ZString } from "@formbricks/types/common";
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { unstable_cache } from "next/cache";
import { responseNoteCache } from "./cache";
import { formatDateFields } from "../utils/datetime";
const select = {
id: true,
@@ -60,7 +61,6 @@ export const createResponseNote = async (
id: responseNote.id,
responseId: responseNote.response.id,
});
return responseNote;
} catch (error) {
console.error(error);
@@ -72,8 +72,8 @@ export const createResponseNote = async (
}
};
export const getResponseNote = async (responseNoteId: string): Promise<TResponseNote | null> =>
unstable_cache(
export const getResponseNote = async (responseNoteId: string): Promise<TResponseNote | null> => {
const responseNote = await unstable_cache(
async () => {
try {
const responseNote = await prisma.responseNote.findUnique({
@@ -95,9 +95,11 @@ export const getResponseNote = async (responseNoteId: string): Promise<TResponse
[`getResponseNote-${responseNoteId}`],
{ tags: [responseNoteCache.tag.byId(responseNoteId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
return responseNote ? formatDateFields(responseNote, ZResponseNote) : null;
};
export const getResponseNotes = async (responseId: string): Promise<TResponseNote[]> =>
unstable_cache(
export const getResponseNotes = async (responseId: string): Promise<TResponseNote[]> => {
const responseNotes = await unstable_cache(
async () => {
try {
validateInputs([responseId, ZId]);
@@ -108,6 +110,9 @@ export const getResponseNotes = async (responseId: string): Promise<TResponseNot
},
select,
});
if (!responseNotes) {
throw new ResourceNotFoundError("Response Notes by ResponseId", responseId);
}
return responseNotes;
} catch (error) {
console.error(error);
@@ -121,6 +126,8 @@ export const getResponseNotes = async (responseId: string): Promise<TResponseNot
[`getResponseNotes-${responseId}`],
{ tags: [responseNoteCache.tag.byResponseId(responseId)], revalidate: SERVICES_REVALIDATION_INTERVAL }
)();
return responseNotes.map((responseNote) => formatDateFields(responseNote, ZResponseNote));
};
export const updateResponseNote = async (responseNoteId: string, text: string): Promise<TResponseNote> => {
validateInputs([responseNoteId, ZString], [text, ZString]);
+11 -27
View File
@@ -19,10 +19,9 @@ import { productCache } from "../product/cache";
import { getProductByEnvironmentId } from "../product/service";
import { responseCache } from "../response/cache";
import { captureTelemetry } from "../telemetry";
import { diffInDays } from "../utils/datetime";
import { diffInDays, formatDateFields } from "../utils/datetime";
import { validateInputs } from "../utils/validate";
import { surveyCache } from "./cache";
import { formatSurveyDateFields } from "./util";
export const selectSurvey = {
id: true,
@@ -126,7 +125,6 @@ export const getSurvey = async (surveyId: string): Promise<TSurvey | null> => {
...surveyPrisma,
triggers: surveyPrisma.triggers.map((trigger) => trigger.actionClass.name),
};
return transformedSurvey;
},
[`getSurvey-${surveyId}`],
@@ -136,16 +134,9 @@ export const getSurvey = async (surveyId: string): Promise<TSurvey | null> => {
}
)();
if (!survey) {
return null;
}
// since the unstable_cache function does not support deserialization of dates, we need to manually deserialize them
// https://github.com/vercel/next.js/issues/51613
return {
...survey,
...formatSurveyDateFields(survey),
};
return survey ? formatDateFields(survey, ZSurvey) : null;
};
export const getSurveysByAttributeClassId = async (
@@ -187,11 +178,7 @@ export const getSurveysByAttributeClassId = async (
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return surveys.map((survey) => ({
...survey,
...formatSurveyDateFields(survey),
}));
return surveys.map((survey) => formatDateFields(survey, ZSurvey));
};
export const getSurveysByActionClassId = async (actionClassId: string, page?: number): Promise<TSurvey[]> => {
@@ -232,11 +219,7 @@ export const getSurveysByActionClassId = async (actionClassId: string, page?: nu
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return surveys.map((survey) => ({
...survey,
...formatSurveyDateFields(survey),
}));
return surveys.map((survey) => formatDateFields(survey, ZSurvey));
};
export const getSurveys = async (environmentId: string, page?: number): Promise<TSurvey[]> => {
@@ -282,10 +265,7 @@ export const getSurveys = async (environmentId: string, page?: number): Promise<
// since the unstable_cache function does not support deserialization of dates, we need to manually deserialize them
// https://github.com/vercel/next.js/issues/51613
return surveys.map((survey) => ({
...survey,
...formatSurveyDateFields(survey),
}));
return surveys.map((survey) => formatDateFields(survey, ZSurvey));
};
export const updateSurvey = async (updatedSurvey: TSurvey): Promise<TSurvey> => {
@@ -609,10 +589,10 @@ export const duplicateSurvey = async (environmentId: string, surveyId: string) =
return newSurvey;
};
export const getSyncSurveys = (environmentId: string, person: TPerson): Promise<TSurvey[]> => {
export const getSyncSurveys = async (environmentId: string, person: TPerson): Promise<TSurvey[]> => {
validateInputs([environmentId, ZId]);
return unstable_cache(
const surveys = await unstable_cache(
async () => {
const product = await getProductByEnvironmentId(environmentId);
@@ -689,6 +669,9 @@ export const getSyncSurveys = (environmentId: string, person: TPerson): Promise<
}
});
if (!surveys) {
throw new ResourceNotFoundError("Survey", environmentId);
}
return surveys;
},
[`getSyncSurveys-${environmentId}-${person.userId}`],
@@ -702,4 +685,5 @@ export const getSyncSurveys = (environmentId: string, person: TPerson): Promise<
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return surveys.map((survey) => formatDateFields(survey, ZSurvey));
};
+18 -10
View File
@@ -4,7 +4,7 @@ import { prisma } from "@formbricks/database";
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { ZId } from "@formbricks/types/environment";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TTeam, TTeamBilling, TTeamUpdateInput, ZTeamUpdateInput } from "@formbricks/types/teams";
import { TTeam, TTeamBilling, TTeamUpdateInput, ZTeam, ZTeamUpdateInput } from "@formbricks/types/teams";
import { Prisma } from "@prisma/client";
import { unstable_cache } from "next/cache";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
@@ -12,6 +12,7 @@ import { environmentCache } from "../environment/cache";
import { getProducts } from "../product/service";
import { validateInputs } from "../utils/validate";
import { teamCache } from "./cache";
import { formatDateFields } from "../utils/datetime";
export const select = {
id: true,
@@ -25,8 +26,8 @@ export const getTeamsTag = (teamId: string) => `teams-${teamId}`;
export const getTeamsByUserIdCacheTag = (userId: string) => `users-${userId}-teams`;
export const getTeamByEnvironmentIdCacheTag = (environmentId: string) => `environments-${environmentId}-team`;
export const getTeamsByUserId = async (userId: string, page?: number): Promise<TTeam[]> =>
unstable_cache(
export const getTeamsByUserId = async (userId: string, page?: number): Promise<TTeam[]> => {
const teams = await unstable_cache(
async () => {
validateInputs([userId, ZString], [page, ZOptionalNumber]);
@@ -43,7 +44,9 @@ export const getTeamsByUserId = async (userId: string, page?: number): Promise<T
take: page ? ITEMS_PER_PAGE : undefined,
skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined,
});
if (!teams) {
throw new ResourceNotFoundError("Teams by UserId", userId);
}
return teams;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -59,9 +62,11 @@ export const getTeamsByUserId = async (userId: string, page?: number): Promise<T
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return teams.map((team) => formatDateFields(team, ZTeam));
};
export const getTeamByEnvironmentId = async (environmentId: string): Promise<TTeam | null> =>
unstable_cache(
export const getTeamByEnvironmentId = async (environmentId: string): Promise<TTeam | null> => {
const team = await unstable_cache(
async () => {
validateInputs([environmentId, ZId]);
@@ -97,9 +102,11 @@ export const getTeamByEnvironmentId = async (environmentId: string): Promise<TTe
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return team ? formatDateFields(team, ZTeam) : null;
};
export const getTeam = async (teamId: string): Promise<TTeam | null> =>
unstable_cache(
export const getTeam = async (teamId: string): Promise<TTeam | null> => {
const team = await unstable_cache(
async () => {
validateInputs([teamId, ZString]);
@@ -110,7 +117,6 @@ export const getTeam = async (teamId: string): Promise<TTeam | null> =>
},
select,
});
return team;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
@@ -126,6 +132,8 @@ export const getTeam = async (teamId: string): Promise<TTeam | null> =>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return team ? formatDateFields(team, ZTeam) : null;
};
export const createTeam = async (teamInput: TTeamUpdateInput): Promise<TTeam> => {
try {
@@ -281,7 +289,7 @@ export const getTeamsWithPaidPlan = async (): Promise<TTeam[]> => {
}
)();
return teams;
return teams.map((team) => formatDateFields(team, ZTeam));
};
export const getMonthlyActiveTeamPeopleCount = async (teamId: string): Promise<number> =>
+37 -1
View File
@@ -1,4 +1,4 @@
// utility functions for date and time
import z from "zod";
// Helper function to calculate difference in days between two dates
export const diffInDays = (date1: Date, date2: Date) => {
@@ -6,6 +6,42 @@ export const diffInDays = (date1: Date, date2: Date) => {
return Math.floor(diffTime / (1000 * 60 * 60 * 24));
};
function isZodDate(schema: z.ZodTypeAny): boolean {
// Check if the field is a ZodDate
if (schema instanceof z.ZodDate) {
return true;
}
// Check if the field is a nullable type and the inner type is ZodDate
if (schema instanceof z.ZodNullable && schema._def.innerType instanceof z.ZodDate) {
return true;
}
return false;
}
export function formatDateFields<T extends z.ZodRawShape>(
object: z.infer<z.ZodObject<T>>,
zodSchema: z.ZodObject<T>
): z.infer<typeof zodSchema> {
const schemaFields = zodSchema.shape;
const formattedObject = { ...object };
for (const key in schemaFields) {
if (Object.prototype.hasOwnProperty.call(schemaFields, key) && isZodDate(schemaFields[key])) {
const dateStr = (formattedObject as any)[key];
try {
if (typeof dateStr === "string") {
(formattedObject as any)[key] = new Date(dateStr);
}
} catch (error) {
console.error(`Error parsing date for key ${key}:`, error);
}
}
}
return formattedObject as z.infer<typeof zodSchema>;
}
export const formatDateWithOrdinal = (date: Date): string => {
const getOrdinalSuffix = (day: number) => {
const suffixes = ["th", "st", "nd", "rd"];
+10 -5
View File
@@ -1,6 +1,6 @@
import "server-only";
import { TWebhook, TWebhookInput, ZWebhookInput } from "@formbricks/types/webhooks";
import { TWebhook, TWebhookInput, ZWebhook, ZWebhookInput } from "@formbricks/types/webhooks";
import { prisma } from "@formbricks/database";
import { Prisma } from "@prisma/client";
import { validateInputs } from "../utils/validate";
@@ -10,9 +10,10 @@ import { ZOptionalNumber } from "@formbricks/types/common";
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { webhookCache } from "./cache";
import { unstable_cache } from "next/cache";
import { formatDateFields } from "../utils/datetime";
export const getWebhooks = async (environmentId: string, page?: number): Promise<TWebhook[]> =>
unstable_cache(
export const getWebhooks = async (environmentId: string, page?: number): Promise<TWebhook[]> => {
const webhooks = await unstable_cache(
async () => {
validateInputs([environmentId, ZId], [page, ZOptionalNumber]);
@@ -35,6 +36,8 @@ export const getWebhooks = async (environmentId: string, page?: number): Promise
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return webhooks.map((webhook) => formatDateFields(webhook, ZWebhook));
};
export const getWebhookCountBySource = async (
environmentId: string,
@@ -63,8 +66,8 @@ export const getWebhookCountBySource = async (
}
)();
export const getWebhook = async (id: string): Promise<TWebhook | null> =>
unstable_cache(
export const getWebhook = async (id: string): Promise<TWebhook | null> => {
const webhook = await unstable_cache(
async () => {
validateInputs([id, ZId]);
@@ -81,6 +84,8 @@ export const getWebhook = async (id: string): Promise<TWebhook | null> =>
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();
return webhook ? formatDateFields(webhook, ZWebhook) : null;
};
export const createWebhook = async (
environmentId: string,
+1 -1
View File
@@ -1,7 +1,7 @@
import z from "zod";
import { ZMembershipRole } from "./memberships";
const ZInvite = z.object({
export const ZInvite = z.object({
id: z.string(),
email: z.string(),
name: z.string().nullish(),
+1 -1
View File
@@ -22,7 +22,7 @@ export const ZResponseNoteUser = z.object({
export type TResponseNoteUser = z.infer<typeof ZResponseNoteUser>;
const ZResponseNote = z.object({
export const ZResponseNote = z.object({
updatedAt: z.date(),
createdAt: z.date(),
id: z.string(),