Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ interface IUserToValue {
}

export const mapUserToValue = (
{ id, name, username, avatar, email, defaultScheduleId }: IUserToValue,
{ id, name, username, avatar, email, defaultScheduleId, isOptional }: IUserToValue & { isOptional?: boolean },
pendingString: string
) => ({
value: `${id || ""}`,
label: `${name || email || ""}${!username ? ` (${pendingString})` : ""}`,
avatar,
email,
defaultScheduleId,
isOptional: isOptional || false,
});

const sortByLabel = (a: ReturnType<typeof mapUserToValue>, b: ReturnType<typeof mapUserToValue>) => {
Expand Down
4 changes: 4 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -4631,5 +4631,9 @@
"last_6_months": "Last 6 months",
"last_12_months": "Last 12 months",
"calendar_events_disabled_video_limitation": "When calendar events are disabled, meeting links cannot be generated for apps whose link generation is tied to calendar event creation, for example, Google Meet and Microsoft Teams. Other video apps like Cal Video will continue to work.",
"make_optional": "Make optional",
"make_required": "Make required",
"optional": "Optional",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Duplicate localization keys "optional" and "required" are added even though they already exist earlier in common.json, creating duplicate JSON keys and overriding earlier values. Please reuse the existing keys instead of redefining them here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/public/static/locales/en/common.json, line 4636:

<comment>Duplicate localization keys "optional" and "required" are added even though they already exist earlier in common.json, creating duplicate JSON keys and overriding earlier values. Please reuse the existing keys instead of redefining them here.</comment>

<file context>
@@ -4631,5 +4631,9 @@
   "calendar_events_disabled_video_limitation": "When calendar events are disabled, meeting links cannot be generated for apps whose link generation is tied to calendar event creation, for example, Google Meet and Microsoft Teams. Other video apps like Cal Video will continue to work.",
+  "make_optional": "Make optional",
+  "make_required": "Make required",
+  "optional": "Optional",
+  "required": "Required",
   "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
</file context>
Fix with Cubic

"required": "Required",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const getAggregatedAvailability = (
userAvailability: {
dateRanges: DateRange[];
oooExcludedDateRanges: DateRange[];
user?: { isFixed?: boolean; groupId?: string | null };
user?: { isFixed?: boolean; groupId?: string | null; isOptional?: boolean };
}[],
schedulingType: SchedulingType | null
): DateRange[] => {
Expand All @@ -36,14 +36,14 @@ export const getAggregatedAvailability = (
userAvailability.length > 1;

const fixedHosts = userAvailability.filter(
({ user }) => !schedulingType || schedulingType === SchedulingType.COLLECTIVE || user?.isFixed
({ user }) => (!schedulingType || schedulingType === SchedulingType.COLLECTIVE || user?.isFixed) && !user?.isOptional
);

const fixedDateRanges = mergeOverlappingDateRanges(
intersect(fixedHosts.map((s) => (!isTeamEvent ? s.dateRanges : s.oooExcludedDateRanges)))
);
const dateRangesToIntersect = fixedDateRanges.length ? [fixedDateRanges] : [];
const roundRobinHosts = userAvailability.filter(({ user }) => user?.isFixed !== true);
const roundRobinHosts = userAvailability.filter(({ user }) => user?.isFixed !== true && !user?.isOptional);
if (roundRobinHosts.length) {
// Group round robin hosts by their groupId
const hostsByGroup = roundRobinHosts.reduce(
Expand Down
18 changes: 18 additions & 0 deletions packages/features/eventtypes/components/CheckedTeamSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type CheckedSelectOption = {
priority?: number;
weight?: number;
isFixed?: boolean;
isOptional?: boolean;
disabled?: boolean;
defaultScheduleId?: number | null;
groupId: string | null;
Expand Down Expand Up @@ -136,6 +137,23 @@ export const CheckedTeamSelect = ({
<div className="ml-auto flex items-center">
{option && !option.isFixed ? (
<>
<Tooltip content={t(option.isOptional ? "make_required" : "make_optional")}>
<Button
color="minimal"
onClick={() =>
props.onChange(
value.map((item) =>
item.value === option.value ? { ...item, isOptional: !item.isOptional } : item
)
)
}
className={classNames(
"mr-6 h-2 p-0 text-sm hover:bg-transparent",
option.isOptional ? "text-orange-500" : "text-subtle"
)}>
{option.isOptional ? t("optional") : t("required")}
</Button>
</Tooltip>
<Tooltip content={t("change_priority")}>
<Button
color="minimal"
Expand Down
2 changes: 2 additions & 0 deletions packages/features/eventtypes/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type HostLocation = {

export type Host = {
isFixed: boolean;
isOptional: boolean;
userId: number;
priority: number;
weight: number;
Expand Down Expand Up @@ -269,6 +270,7 @@ export type HostInput = {
userId: number;
profileId?: number | null;
isFixed?: boolean;
isOptional?: boolean;
priority?: number | null;
weight?: number | null;
scheduleId?: number | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,7 @@ export class EventTypeRepository implements IEventTypesRepository {
},
hosts: {
select: {
isOptional: true,
user: {
select: {
email: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Host" ADD COLUMN "isOptional" BOOLEAN NOT NULL DEFAULT false;
1 change: 1 addition & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ model Host {
isFixed Boolean @default(false)
priority Int?
weight Int?
isOptional Boolean @default(false)
// weightAdjustment is deprecated. We not calculate the calibratino value on the spot. Plan to drop this column.
weightAdjustment Int?
schedule Schedule? @relation(fields: [scheduleId], references: [id])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
const hostData: {
userId: number;
isFixed: boolean;
isOptional: boolean;
priority: number;
weight: number;
groupId: string | null | undefined;
Expand All @@ -526,6 +527,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
} = {
userId: host.userId,
isFixed: data.schedulingType === SchedulingType.COLLECTIVE || host.isFixed || false,
isOptional: host.isOptional || false,
priority: host.priority ?? 2,
weight: host.weight ?? 100,
groupId: host.groupId,
Expand All @@ -547,6 +549,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
update: existingHosts.map((host) => {
const updateData: {
isFixed: boolean | undefined;
isOptional: boolean | undefined;
priority: number;
weight: number;
scheduleId: number | null | undefined;
Expand All @@ -571,6 +574,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
};
} = {
isFixed: data.schedulingType === SchedulingType.COLLECTIVE || host.isFixed,
isOptional: host.isOptional,
priority: host.priority ?? 2,
weight: host.weight ?? 100,
scheduleId: host.scheduleId === undefined ? undefined : host.scheduleId,
Expand Down
1 change: 1 addition & 0 deletions packages/trpc/server/routers/viewer/eventTypes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const hostSchema: z.ZodType<HostInput> = z.object({
userId: z.number(),
profileId: z.number().or(z.null()).optional(),
isFixed: z.boolean().optional(),
isOptional: z.boolean().optional(),
priority: z.number().min(0).max(4).optional().nullable(),
weight: z.number().min(0).optional().nullable(),
scheduleId: z.number().optional().nullable(),
Expand Down
3 changes: 2 additions & 1 deletion packages/trpc/server/routers/viewer/slots/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,11 +741,12 @@ export class AvailableSlotsService {
}: {
hosts: {
isFixed?: boolean;
isOptional?: boolean;
groupId?: string | null;
user: GetAvailabilityUserWithDelegationCredentials;
}[];
}) {
return hosts.map(({ isFixed, groupId, user }) => ({ isFixed, groupId, ...user }));
return hosts.map(({ isFixed, isOptional, groupId, user }) => ({ isFixed, isOptional, groupId, ...user }));
}

private getUsersWithCredentials = withReporting(
Expand Down
Loading