diff --git a/packages/features/Segment.test.tsx b/packages/features/Segment.test.tsx
index 2cd26eb143..c26c77e787 100644
--- a/packages/features/Segment.test.tsx
+++ b/packages/features/Segment.test.tsx
@@ -116,7 +116,7 @@ describe("Segment", () => {
await waitFor(() => {
// Query builder container should be present
expect(screen.getByTestId("query-builder-container")).toBeInTheDocument();
- expect(screen.queryByText("loading")).not.toBeInTheDocument();
+ expect(screen.queryByTestId("segment_loading_state")).not.toBeInTheDocument();
});
});
@@ -128,7 +128,7 @@ describe("Segment", () => {
render();
await waitFor(() => {
- expect(screen.getByText("loading")).toBeInTheDocument();
+ expect(screen.getByTestId("segment_loading_state")).toBeInTheDocument();
});
});
diff --git a/packages/features/Segment.tsx b/packages/features/Segment.tsx
index 4c5af7407a..9f2ae2b172 100644
--- a/packages/features/Segment.tsx
+++ b/packages/features/Segment.tsx
@@ -105,14 +105,48 @@ function MatchingTeamMembers({
queryValue: AttributesQueryValue | null;
}) {
const { t } = useLocale();
- const { data: matchingTeamMembersWithResult, isPending } =
- trpc.viewer.attributes.findTeamMembersMatchingAttributeLogic.useQuery({
- teamId,
- attributesQueryValue: queryValue,
- _enablePerf: true,
- });
- if (isPending) return {t("loading")};
+ // Check if queryValue has valid children properties value
+ const hasValidValue = queryValue?.children1
+ ? Object.values(queryValue.children1).some(
+ (child) => child.properties?.value?.[0] !== undefined && child.properties?.value?.[0] !== null
+ )
+ : false;
+
+ const { data: matchingTeamMembersWithResult, isPending } =
+ trpc.viewer.attributes.findTeamMembersMatchingAttributeLogic.useQuery(
+ {
+ teamId,
+ attributesQueryValue: queryValue,
+ _enablePerf: true,
+ },
+ {
+ enabled: hasValidValue,
+ }
+ );
+
+ if (isPending) {
+ return (
+
+
+
+ {[...Array(3)].map((_, index) => (
+ -
+
+
+ ))}
+
+
+ );
+ }
+
if (!matchingTeamMembersWithResult) return {t("something_went_wrong")};
const { result: matchingTeamMembers } = matchingTeamMembersWithResult;
if (!matchingTeamMembers || !queryValue) {
diff --git a/packages/features/eventtypes/components/EditWeightsForAllTeamMembers.tsx b/packages/features/eventtypes/components/EditWeightsForAllTeamMembers.tsx
index bb4401f30f..1c2e05d794 100644
--- a/packages/features/eventtypes/components/EditWeightsForAllTeamMembers.tsx
+++ b/packages/features/eventtypes/components/EditWeightsForAllTeamMembers.tsx
@@ -7,7 +7,8 @@ import { useState, useEffect, useRef, useMemo } from "react";
import type { Host, TeamMember } from "@calcom/features/eventtypes/lib/types";
import { downloadAsCsv } from "@calcom/lib/csvUtils";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import "@calcom/ui";
+import type { AttributesQueryValue } from "@calcom/lib/raqb/types";
+import { trpc } from "@calcom/trpc";
import {
Avatar,
Button,
@@ -25,7 +26,7 @@ import {
} from "@calcom/ui";
type TeamMemberItemProps = {
- member: TeamMember & { weight?: number };
+ member: Omit & { weight?: number };
onWeightChange: (memberId: string, weight: number) => void;
};
@@ -92,29 +93,93 @@ const TeamMemberItem = ({ member, onWeightChange }: TeamMemberItemProps) => {
);
};
+interface UseTeamMembersWithSegmentProps {
+ initialTeamMembers: TeamMember[];
+ assignRRMembersUsingSegment: boolean;
+ teamId?: number;
+ queryValue?: AttributesQueryValue | null;
+ value: Host[];
+}
+
+const useTeamMembersWithSegment = ({
+ initialTeamMembers,
+ assignRRMembersUsingSegment,
+ teamId,
+ queryValue,
+ value,
+}: UseTeamMembersWithSegmentProps) => {
+ const { data: matchingTeamMembersWithResult, isPending } =
+ trpc.viewer.attributes.findTeamMembersMatchingAttributeLogic.useQuery(
+ {
+ teamId: teamId || 0,
+ attributesQueryValue: queryValue as AttributesQueryValue,
+ _enablePerf: true,
+ },
+ {
+ enabled: assignRRMembersUsingSegment && !!queryValue && !!teamId,
+ }
+ );
+
+ const teamMembers = useMemo(() => {
+ if (assignRRMembersUsingSegment && matchingTeamMembersWithResult?.result) {
+ return matchingTeamMembersWithResult.result.map((member) => ({
+ value: member.id.toString(),
+ label: member.name || member.email,
+ email: member.email,
+ avatar: "", // Add avatar with fallback to empty string
+ }));
+ }
+ return initialTeamMembers;
+ }, [assignRRMembersUsingSegment, matchingTeamMembersWithResult, initialTeamMembers]);
+
+ const localWeightsInitialValues = useMemo(
+ () =>
+ teamMembers.reduce>((acc, member) => {
+ const memberInValue = value.find((host) => host.userId === parseInt(member.value, 10));
+ acc[member.value] = memberInValue?.weight ?? 100;
+ return acc;
+ }, {}),
+ [teamMembers, value]
+ );
+
+ return {
+ teamMembers,
+ localWeightsInitialValues,
+ isPending,
+ };
+};
+
interface Props {
teamMembers: TeamMember[];
value: Host[];
onChange: (hosts: Host[]) => void;
assignAllTeamMembers: boolean;
+ assignRRMembersUsingSegment: boolean;
+ teamId?: number;
+ queryValue?: AttributesQueryValue | null;
}
export const EditWeightsForAllTeamMembers = ({
- teamMembers,
+ teamMembers: initialTeamMembers,
value,
onChange,
assignAllTeamMembers,
+ assignRRMembersUsingSegment,
+ teamId,
+ queryValue,
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const { t } = useLocale();
const [searchQuery, setSearchQuery] = useState("");
- const localWeightsInitialValues = teamMembers.reduce>((acc, member) => {
- // When assignAllTeamMembers is false, only include members that exist in value array
- // Find the member in the value array and use its weight if it exists
- const memberInValue = value.find((host) => host.userId === parseInt(member.value, 10));
- acc[member.value] = memberInValue?.weight ?? 100;
- return acc;
- }, {});
+
+ const { teamMembers, localWeightsInitialValues } = useTeamMembersWithSegment({
+ initialTeamMembers,
+ assignRRMembersUsingSegment,
+ teamId,
+ queryValue,
+ value,
+ });
+
const [localWeights, setLocalWeights] = useState>(localWeightsInitialValues);
const [uploadErrors, setUploadErrors] = useState>([]);
const [isErrorsExpanded, setIsErrorsExpanded] = useState(true);
@@ -124,10 +189,21 @@ export const EditWeightsForAllTeamMembers = ({
};
const handleSave = () => {
- const updatedValue = value.map((host) => ({
- ...host,
- weight: localWeights[host.userId.toString()] ?? host.weight ?? 100,
- }));
+ // Create a map of existing hosts for easy lookup
+ const existingHostsMap = new Map(value.map((host) => [host.userId.toString(), host]));
+
+ // Create the updated value by processing all team members
+ const updatedValue = teamMembers.map((member) => {
+ const existingHost = existingHostsMap.get(member.value);
+ return {
+ ...existingHost,
+ userId: parseInt(member.value, 10),
+ isFixed: existingHost?.isFixed ?? false,
+ priority: existingHost?.priority ?? 0,
+ weight: localWeights[member.value] ?? existingHost?.weight ?? 100,
+ };
+ });
+
onChange(updatedValue);
setIsOpen(false);
};
diff --git a/packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx b/packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
index 2cd833a8cc..c69c07d578 100644
--- a/packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
+++ b/packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
@@ -299,6 +299,10 @@ const RoundRobinHosts = ({
control,
name: "isRRWeightsEnabled",
});
+ const rrSegmentQueryValue = useWatch({
+ control,
+ name: "rrSegmentQueryValue",
+ });
return (
@@ -319,40 +323,39 @@ const RoundRobinHosts = ({
- {!assignRRMembersUsingSegment && (
- <>
-
- name="isRRWeightsEnabled"
- render={({ field: { value: isRRWeightsEnabled, onChange } }) => (
- {
- onChange(active);
- const rrHosts = getValues("hosts").filter((host) => !host.isFixed);
- const sortedRRHosts = rrHosts.sort((a, b) => sortHosts(a, b, active));
- setValue("hosts", sortedRRHosts);
- }}>
- {!assignRRMembersUsingSegment ? (
- {
- const sortedRRHosts = hosts.sort((a, b) => sortHosts(a, b, true));
- setValue("hosts", sortedRRHosts, { shouldDirty: true });
- }}
- assignAllTeamMembers={assignAllTeamMembers}
- />
- ) : null}
-
- )}
- />
- >
- )}
+ <>
+
+ name="isRRWeightsEnabled"
+ render={({ field: { value: isRRWeightsEnabled, onChange } }) => (
+ {
+ onChange(active);
+ const rrHosts = getValues("hosts").filter((host) => !host.isFixed);
+ const sortedRRHosts = rrHosts.sort((a, b) => sortHosts(a, b, active));
+ setValue("hosts", sortedRRHosts);
+ }}>
+ {
+ const sortedRRHosts = hosts.sort((a, b) => sortHosts(a, b, true));
+ setValue("hosts", sortedRRHosts, { shouldDirty: true });
+ }}
+ assignAllTeamMembers={assignAllTeamMembers}
+ assignRRMembersUsingSegment={assignRRMembersUsingSegment}
+ teamId={teamId}
+ queryValue={rrSegmentQueryValue}
+ />
+
+ )}
+ />
+ >