diff --git a/apps/web/modules/ee/contacts/components/contacts-secondary-navigation.tsx b/apps/web/modules/ee/contacts/components/contacts-secondary-navigation.tsx
index 141715185e..31fdb67e09 100644
--- a/apps/web/modules/ee/contacts/components/contacts-secondary-navigation.tsx
+++ b/apps/web/modules/ee/contacts/components/contacts-secondary-navigation.tsx
@@ -30,16 +30,16 @@ export const ContactsSecondaryNavigation = async ({
label: t("common.contacts"),
href: `/environments/${environmentId}/contacts`,
},
- {
- id: "segments",
- label: t("common.segments"),
- href: `/environments/${environmentId}/segments`,
- },
{
id: "attributes",
label: t("common.attributes"),
href: `/environments/${environmentId}/attributes`,
},
+ {
+ id: "segments",
+ label: t("common.segments"),
+ href: `/environments/${environmentId}/segments`,
+ },
];
return ;
diff --git a/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx b/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx
index fb01fa17f5..026e51f726 100644
--- a/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx
+++ b/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx
@@ -133,6 +133,10 @@ export function SegmentSettings({
return true;
}
+ if (segment.filters.length === 0) {
+ return true;
+ }
+
// parse the filters to check if they are valid
const parsedFilters = ZSegmentFilters.safeParse(segment.filters);
if (!parsedFilters.success) {
diff --git a/apps/web/modules/ee/contacts/segments/lib/segment-schema.test.ts b/apps/web/modules/ee/contacts/segments/lib/segment-schema.test.ts
new file mode 100644
index 0000000000..85572bed25
--- /dev/null
+++ b/apps/web/modules/ee/contacts/segments/lib/segment-schema.test.ts
@@ -0,0 +1,73 @@
+import { createId } from "@paralleldrive/cuid2";
+import { describe, expect, test } from "vitest";
+import { ZSegmentCreateInput, ZSegmentFilters, ZSegmentUpdateInput } from "@formbricks/types/segment";
+
+const validFilters = [
+ {
+ id: createId(),
+ connector: null,
+ resource: {
+ id: createId(),
+ root: {
+ type: "attribute" as const,
+ contactAttributeKey: "email",
+ },
+ value: "user@example.com",
+ qualifier: {
+ operator: "equals" as const,
+ },
+ },
+ },
+];
+
+describe("segment schema validation", () => {
+ test("keeps base segment filters compatible with empty arrays", () => {
+ const result = ZSegmentFilters.safeParse([]);
+
+ expect(result.success).toBe(true);
+ });
+
+ test("requires at least one filter when creating a segment", () => {
+ const result = ZSegmentCreateInput.safeParse({
+ environmentId: "environmentId",
+ title: "Power users",
+ description: "Users with a matching email",
+ isPrivate: false,
+ filters: [],
+ surveyId: "surveyId",
+ });
+
+ expect(result.success).toBe(false);
+ expect(result.error?.issues[0]?.message).toBe("At least one filter is required");
+ });
+
+ test("accepts segment creation with a valid filter", () => {
+ const result = ZSegmentCreateInput.safeParse({
+ environmentId: "environmentId",
+ title: "Power users",
+ description: "Users with a matching email",
+ isPrivate: false,
+ filters: validFilters,
+ surveyId: "surveyId",
+ });
+
+ expect(result.success).toBe(true);
+ });
+
+ test("requires at least one filter when updating a segment", () => {
+ const result = ZSegmentUpdateInput.safeParse({
+ filters: [],
+ });
+
+ expect(result.success).toBe(false);
+ expect(result.error?.issues[0]?.message).toBe("At least one filter is required");
+ });
+
+ test("accepts segment updates with a valid filter", () => {
+ const result = ZSegmentUpdateInput.safeParse({
+ filters: validFilters,
+ });
+
+ expect(result.success).toBe(true);
+ });
+});
diff --git a/apps/web/modules/organization/settings/teams/components/invite-member/invite-member-modal.tsx b/apps/web/modules/organization/settings/teams/components/invite-member/invite-member-modal.tsx
index f2ac6ca7c3..1e569f18f1 100644
--- a/apps/web/modules/organization/settings/teams/components/invite-member/invite-member-modal.tsx
+++ b/apps/web/modules/organization/settings/teams/components/invite-member/invite-member-modal.tsx
@@ -89,7 +89,7 @@ export const InviteMemberModal = ({
{t("environments.settings.teams.invite_member_description")}
-
+
{!showTeamAdminRestrictions && (
= z
error: "Invalid filters applied",
});
+const ZRequiredSegmentFilters = ZSegmentFilters.refine((filters) => filters.length > 0, {
+ error: "At least one filter is required",
+});
+
export const ZSegment = z.object({
id: z.string(),
title: z.string(),
@@ -350,7 +354,7 @@ export const ZSegmentCreateInput = z.object({
title: z.string(),
description: z.string().optional(),
isPrivate: z.boolean().prefault(true),
- filters: ZSegmentFilters,
+ filters: ZRequiredSegmentFilters,
surveyId: z.string(),
});
@@ -367,7 +371,7 @@ export const ZSegmentUpdateInput = z
title: z.string(),
description: z.string().nullable(),
isPrivate: z.boolean().prefault(true),
- filters: ZSegmentFilters,
+ filters: ZRequiredSegmentFilters,
surveys: z.array(z.string()),
})
.partial();