dont filter admin role in role selector for alerts

This commit is contained in:
miloschwartz
2026-04-22 17:52:31 -07:00
parent f651ca84fa
commit 8481b0a073
2 changed files with 88 additions and 57 deletions

View File

@@ -97,10 +97,7 @@ export default function UptimeAlertSection({
); );
const allRoles = useMemo( const allRoles = useMemo(
() => () => orgRoles.map((r) => ({ id: String(r.roleId), text: r.name })),
orgRoles
.map((r) => ({ id: String(r.roleId), text: r.name }))
.filter((r) => r.text !== "Admin"),
[orgRoles] [orgRoles]
); );

View File

@@ -30,10 +30,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue SelectValue
} from "@app/components/ui/select"; } from "@app/components/ui/select";
import { import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group";
RadioGroup,
RadioGroupItem
} from "@app/components/ui/radio-group";
import { Label } from "@app/components/ui/label"; import { Label } from "@app/components/ui/label";
import { StrategySelect } from "@app/components/StrategySelect"; import { StrategySelect } from "@app/components/StrategySelect";
import { TagInput, type Tag } from "@app/components/tags/tag-input"; import { TagInput, type Tag } from "@app/components/tags/tag-input";
@@ -59,7 +56,6 @@ export function AddActionPanel({
}) { }) {
const t = useTranslations(); const t = useTranslations();
const EXTERNAL_INTEGRATIONS = [ const EXTERNAL_INTEGRATIONS = [
{ {
id: "pagerduty", id: "pagerduty",
@@ -247,9 +243,7 @@ function HealthCheckMultiSelect({
const shown = useMemo(() => { const shown = useMemo(() => {
const query = debounced.trim().toLowerCase(); const query = debounced.trim().toLowerCase();
const base = query const base = query
? healthChecks.filter((hc) => ? healthChecks.filter((hc) => hc.name.toLowerCase().includes(query))
hc.name.toLowerCase().includes(query)
)
: healthChecks; : healthChecks;
// Always keep already-selected items visible even if they fall outside the search // Always keep already-selected items visible even if they fall outside the search
if (query && value.length > 0) { if (query && value.length > 0) {
@@ -323,9 +317,7 @@ function HealthCheckMultiSelect({
aria-hidden aria-hidden
tabIndex={-1} tabIndex={-1}
/> />
<span className="truncate"> <span className="truncate">{hc.name}</span>
{hc.name}
</span>
</CommandItem> </CommandItem>
))} ))}
</CommandGroup> </CommandGroup>
@@ -510,8 +502,12 @@ function NotifyActionFields({
number | null number | null
>(null); >(null);
const { data: orgUsers = [], isLoading: isLoadingUsers } = useQuery(orgQueries.users({ orgId })); const { data: orgUsers = [], isLoading: isLoadingUsers } = useQuery(
const { data: orgRoles = [], isLoading: isLoadingRoles } = useQuery(orgQueries.roles({ orgId })); orgQueries.users({ orgId })
);
const { data: orgRoles = [], isLoading: isLoadingRoles } = useQuery(
orgQueries.roles({ orgId })
);
const allUsers = useMemo( const allUsers = useMemo(
() => () =>
@@ -527,10 +523,7 @@ function NotifyActionFields({
); );
const allRoles = useMemo( const allRoles = useMemo(
() => () => orgRoles.map((r) => ({ id: String(r.roleId), text: r.name })),
orgRoles
.map((r) => ({ id: String(r.roleId), text: r.name }))
.filter((r) => r.text !== "Admin"),
[orgRoles] [orgRoles]
); );
@@ -578,9 +571,18 @@ function NotifyActionFields({
hasResolvedTagsRef.current = true; hasResolvedTagsRef.current = true;
}, [isLoadingUsers, isLoadingRoles, allUsers, allRoles]); }, [isLoadingUsers, isLoadingRoles, allUsers, allRoles]);
const userTags = (useWatch({ control, name: `actions.${index}.userTags` }) ?? []) as Tag[]; const userTags = (useWatch({
const roleTags = (useWatch({ control, name: `actions.${index}.roleTags` }) ?? []) as Tag[]; control,
const emailTags = (useWatch({ control, name: `actions.${index}.emailTags` }) ?? []) as Tag[]; name: `actions.${index}.userTags`
}) ?? []) as Tag[];
const roleTags = (useWatch({
control,
name: `actions.${index}.roleTags`
}) ?? []) as Tag[];
const emailTags = (useWatch({
control,
name: `actions.${index}.emailTags`
}) ?? []) as Tag[];
return ( return (
<div className="space-y-3 pt-1"> <div className="space-y-3 pt-1">
@@ -788,7 +790,9 @@ function WebhookActionFields({
{t("httpDestAuthNoneTitle")} {t("httpDestAuthNoneTitle")}
</Label> </Label>
<p className="text-xs text-muted-foreground mt-0.5"> <p className="text-xs text-muted-foreground mt-0.5">
{t("httpDestAuthNoneDescription")} {t(
"httpDestAuthNoneDescription"
)}
</p> </p>
</div> </div>
</div> </div>
@@ -806,10 +810,14 @@ function WebhookActionFields({
htmlFor={`auth-bearer-${index}`} htmlFor={`auth-bearer-${index}`}
className="cursor-pointer font-medium" className="cursor-pointer font-medium"
> >
{t("httpDestAuthBearerTitle")} {t(
"httpDestAuthBearerTitle"
)}
</Label> </Label>
<p className="text-xs text-muted-foreground mt-0.5"> <p className="text-xs text-muted-foreground mt-0.5">
{t("httpDestAuthBearerDescription")} {t(
"httpDestAuthBearerDescription"
)}
</p> </p>
</div> </div>
{field.value === "bearer" && ( {field.value === "bearer" && (
@@ -821,7 +829,9 @@ function WebhookActionFields({
<FormControl> <FormControl>
<Input <Input
{...f} {...f}
placeholder={t("httpDestAuthBearerPlaceholder")} placeholder={t(
"httpDestAuthBearerPlaceholder"
)}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -845,10 +855,14 @@ function WebhookActionFields({
htmlFor={`auth-basic-${index}`} htmlFor={`auth-basic-${index}`}
className="cursor-pointer font-medium" className="cursor-pointer font-medium"
> >
{t("httpDestAuthBasicTitle")} {t(
"httpDestAuthBasicTitle"
)}
</Label> </Label>
<p className="text-xs text-muted-foreground mt-0.5"> <p className="text-xs text-muted-foreground mt-0.5">
{t("httpDestAuthBasicDescription")} {t(
"httpDestAuthBasicDescription"
)}
</p> </p>
</div> </div>
{field.value === "basic" && ( {field.value === "basic" && (
@@ -860,7 +874,9 @@ function WebhookActionFields({
<FormControl> <FormControl>
<Input <Input
{...f} {...f}
placeholder={t("httpDestAuthBasicPlaceholder")} placeholder={t(
"httpDestAuthBasicPlaceholder"
)}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -884,10 +900,14 @@ function WebhookActionFields({
htmlFor={`auth-custom-${index}`} htmlFor={`auth-custom-${index}`}
className="cursor-pointer font-medium" className="cursor-pointer font-medium"
> >
{t("httpDestAuthCustomTitle")} {t(
"httpDestAuthCustomTitle"
)}
</Label> </Label>
<p className="text-xs text-muted-foreground mt-0.5"> <p className="text-xs text-muted-foreground mt-0.5">
{t("httpDestAuthCustomDescription")} {t(
"httpDestAuthCustomDescription"
)}
</p> </p>
</div> </div>
{field.value === "custom" && ( {field.value === "custom" && (
@@ -895,12 +915,16 @@ function WebhookActionFields({
<FormField <FormField
control={control} control={control}
name={`actions.${index}.customHeaderName`} name={`actions.${index}.customHeaderName`}
render={({ field: f }) => ( render={({
field: f
}) => (
<FormItem className="flex-1"> <FormItem className="flex-1">
<FormControl> <FormControl>
<Input <Input
{...f} {...f}
placeholder={t("httpDestAuthCustomHeaderNamePlaceholder")} placeholder={t(
"httpDestAuthCustomHeaderNamePlaceholder"
)}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -910,12 +934,16 @@ function WebhookActionFields({
<FormField <FormField
control={control} control={control}
name={`actions.${index}.customHeaderValue`} name={`actions.${index}.customHeaderValue`}
render={({ field: f }) => ( render={({
field: f
}) => (
<FormItem className="flex-1"> <FormItem className="flex-1">
<FormControl> <FormControl>
<Input <Input
{...f} {...f}
placeholder={t("httpDestAuthCustomHeaderValuePlaceholder")} placeholder={t(
"httpDestAuthCustomHeaderValuePlaceholder"
)}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -949,7 +977,7 @@ function WebhookHeadersField({
}) { }) {
const t = useTranslations(); const t = useTranslations();
const headers = const headers =
(useWatch({ control, name: `actions.${index}.headers` as const }) ?? []); useWatch({ control, name: `actions.${index}.headers` as const }) ?? [];
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<FormLabel>{t("alertingWebhookHeaders")}</FormLabel> <FormLabel>{t("alertingWebhookHeaders")}</FormLabel>
@@ -961,7 +989,12 @@ function WebhookHeadersField({
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex-1"> <FormItem className="flex-1">
<FormControl> <FormControl>
<Input {...field} placeholder={t("webhookHeaderKeyPlaceholder")} /> <Input
{...field}
placeholder={t(
"webhookHeaderKeyPlaceholder"
)}
/>
</FormControl> </FormControl>
</FormItem> </FormItem>
)} )}
@@ -972,7 +1005,12 @@ function WebhookHeadersField({
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex-1"> <FormItem className="flex-1">
<FormControl> <FormControl>
<Input {...field} placeholder={t("webhookHeaderValuePlaceholder")} /> <Input
{...field}
placeholder={t(
"webhookHeaderValuePlaceholder"
)}
/>
</FormControl> </FormControl>
</FormItem> </FormItem>
)} )}
@@ -984,9 +1022,8 @@ function WebhookHeadersField({
className="shrink-0" className="shrink-0"
onClick={() => { onClick={() => {
const cur = const cur =
form.getValues( form.getValues(`actions.${index}.headers`) ??
`actions.${index}.headers` [];
) ?? [];
form.setValue( form.setValue(
`actions.${index}.headers`, `actions.${index}.headers`,
cur.filter((__, i) => i !== hi), cur.filter((__, i) => i !== hi),
@@ -1005,10 +1042,11 @@ function WebhookHeadersField({
onClick={() => { onClick={() => {
const cur = const cur =
form.getValues(`actions.${index}.headers`) ?? []; form.getValues(`actions.${index}.headers`) ?? [];
form.setValue(`actions.${index}.headers`, [ form.setValue(
...cur, `actions.${index}.headers`,
{ key: "", value: "" } [...cur, { key: "", value: "" }],
], { shouldDirty: true }); { shouldDirty: true }
);
}} }}
> >
<Plus className="h-4 w-4 mr-1" /> <Plus className="h-4 w-4 mr-1" />
@@ -1111,22 +1149,18 @@ export function AlertRuleSourceFields({
curTrigger !== "resource_unhealthy" && curTrigger !== "resource_unhealthy" &&
curTrigger !== "resource_toggle" curTrigger !== "resource_toggle"
) { ) {
setValue( setValue("trigger", "resource_toggle", {
"trigger", shouldValidate: true
"resource_toggle", });
{ shouldValidate: true }
);
} }
} else if ( } else if (
curTrigger !== "health_check_healthy" && curTrigger !== "health_check_healthy" &&
curTrigger !== "health_check_unhealthy" && curTrigger !== "health_check_unhealthy" &&
curTrigger !== "health_check_toggle" curTrigger !== "health_check_toggle"
) { ) {
setValue( setValue("trigger", "health_check_toggle", {
"trigger", shouldValidate: true
"health_check_toggle", });
{ shouldValidate: true }
);
} }
}} }}
> >