-
-
-
-
-
+
+
+
+
+ {
+ onAdd("notify");
+ setOpen(false);
+ }}
+ >
+ {t("alertingActionNotify")}
+
+ {
+ onAdd("webhook");
+ setOpen(false);
+ }}
+ >
+ {t("alertingActionWebhook")}
+
+
+
+
);
@@ -325,15 +313,10 @@ export function ActionBlock({
if (nt === "notify") {
form.setValue(`actions.${index}`, {
type: "notify",
- userIds: [],
- roleIds: [],
+ userTags: [],
+ roleTags: [],
emailTags: []
});
- } else if (nt === "sms") {
- form.setValue(`actions.${index}`, {
- type: "sms",
- phoneTags: []
- });
} else {
form.setValue(`actions.${index}`, {
type: "webhook",
@@ -354,9 +337,6 @@ export function ActionBlock({
{t("alertingActionNotify")}
-
- {t("alertingActionSms")}
-
{t("alertingActionWebhook")}
@@ -373,9 +353,6 @@ export function ActionBlock({
form={form}
/>
)}
- {type === "sms" && (
-
- )}
{type === "webhook" && (
;
}) {
const t = useTranslations();
+
const [emailActiveIdx, setEmailActiveIdx] = useState(null);
- const userIds = form.watch(`actions.${index}.userIds`) ?? [];
- const roleIds = form.watch(`actions.${index}.roleIds`) ?? [];
- const emailTags = form.watch(`actions.${index}.emailTags`) ?? [];
+ const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<
+ number | null
+ >(null);
+ const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
+ number | null
+ >(null);
+
+ const { data: orgUsers = [] } = useQuery(orgQueries.users({ orgId }));
+ const { data: orgRoles = [] } = useQuery(orgQueries.roles({ orgId }));
+
+ const allUsers = useMemo(
+ () =>
+ orgUsers.map((u) => ({
+ id: String(u.id),
+ text: getUserDisplayName({
+ email: u.email,
+ name: u.name,
+ username: u.username
+ })
+ })),
+ [orgUsers]
+ );
+
+ const allRoles = useMemo(
+ () =>
+ orgRoles
+ .map((r) => ({ id: String(r.roleId), text: r.name }))
+ .filter((r) => r.text !== "Admin"),
+ [orgRoles]
+ );
+
+ const userTags = (form.watch(`actions.${index}.userTags`) ?? []) as Tag[];
+ const roleTags = (form.watch(`actions.${index}.roleTags`) ?? []) as Tag[];
+ const emailTags = (form.watch(`actions.${index}.emailTags`) ?? []) as Tag[];
return (
-
- {t("alertingNotifyUsers")}
-
- form.setValue(`actions.${index}.userIds`, ids)
- }
- />
-
-
- {t("alertingNotifyRoles")}
-
- form.setValue(`actions.${index}.roleIds`, ids)
- }
- />
-
+
(
+
+ {t("alertingNotifyUsers")}
+
+ {
+ const next =
+ typeof newTags === "function"
+ ? newTags(userTags)
+ : newTags;
+ form.setValue(
+ `actions.${index}.userTags`,
+ next as Tag[]
+ );
+ }}
+ enableAutocomplete={true}
+ autocompleteOptions={allUsers}
+ allowDuplicates={false}
+ restrictTagsToAutocompleteOptions={true}
+ sortTags={true}
+ />
+
+
+
+ )}
+ />
+ (
+
+ {t("alertingNotifyRoles")}
+
+ {
+ const next =
+ typeof newTags === "function"
+ ? newTags(roleTags)
+ : newTags;
+ form.setValue(
+ `actions.${index}.roleTags`,
+ next as Tag[]
+ );
+ }}
+ enableAutocomplete={true}
+ autocompleteOptions={allRoles}
+ allowDuplicates={false}
+ restrictTagsToAutocompleteOptions={true}
+ sortTags={true}
+ />
+
+
+
+ )}
+ />
(
-
+ render={({ field }) => (
+
{t("alertingNotifyEmails")}
{
const next =
@@ -442,12 +502,18 @@ function NotifyActionFields({
: updater;
form.setValue(
`actions.${index}.emailTags`,
- next
+ next as Tag[]
);
}}
activeTagIndex={emailActiveIdx}
setActiveTagIndex={setEmailActiveIdx}
placeholder={t("alertingEmailPlaceholder")}
+ size="sm"
+ allowDuplicates={false}
+ sortTags={true}
+ validateTag={(tag) =>
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(tag)
+ }
delimiterList={[",", "Enter"]}
/>
@@ -459,51 +525,6 @@ function NotifyActionFields({
);
}
-function SmsActionFields({
- index,
- control,
- form
-}: {
- index: number;
- control: Control;
- form: UseFormReturn;
-}) {
- const t = useTranslations();
- const [phoneActiveIdx, setPhoneActiveIdx] = useState(null);
- const phoneTags = form.watch(`actions.${index}.phoneTags`) ?? [];
- return (
- (
-
- {t("alertingSmsNumbers")}
-
- {
- const next =
- typeof updater === "function"
- ? updater(phoneTags)
- : updater;
- form.setValue(
- `actions.${index}.phoneTags`,
- next
- );
- }}
- activeTagIndex={phoneActiveIdx}
- setActiveTagIndex={setPhoneActiveIdx}
- placeholder={t("alertingSmsPlaceholder")}
- delimiterList={[",", "Enter"]}
- />
-
-
-
- )}
- />
- );
-}
-
function WebhookActionFields({
index,
control,
@@ -663,160 +684,6 @@ function WebhookHeadersField({
);
}
-function UserMultiSelect({
- orgId,
- value,
- onChange
-}: {
- orgId: string;
- value: string[];
- onChange: (v: string[]) => void;
-}) {
- const t = useTranslations();
- const [open, setOpen] = useState(false);
- const [q, setQ] = useState("");
- const [debounced] = useDebounce(q, 150);
- const { data: users = [] } = useQuery(orgQueries.users({ orgId }));
- const shown = useMemo(() => {
- const qq = debounced.trim().toLowerCase();
- if (!qq) return users.slice(0, 200);
- return users
- .filter((u) => {
- const label = getUserDisplayName({
- email: u.email,
- name: u.name,
- username: u.username
- }).toLowerCase();
- return (
- label.includes(qq) ||
- (u.email ?? "").toLowerCase().includes(qq)
- );
- })
- .slice(0, 200);
- }, [users, debounced]);
- const toggle = (id: string) => {
- if (value.includes(id)) {
- onChange(value.filter((x) => x !== id));
- } else {
- onChange([...value, id]);
- }
- };
- const summary =
- value.length === 0
- ? t("alertingSelectUsers")
- : t("alertingUsersSelected", { count: value.length });
- return (
-
-
-
-
-
-
-
-
- {t("noResults")}
-
- {shown.map((u) => {
- const uid = String(u.id);
- return (
- toggle(uid)}
- className="cursor-pointer"
- >
-
- {getUserDisplayName({
- email: u.email,
- name: u.name,
- username: u.username
- })}
-
- );
- })}
-
-
-
-
-
- );
-}
-
-function RoleMultiSelect({
- orgId,
- value,
- onChange
-}: {
- orgId: string;
- value: number[];
- onChange: (v: number[]) => void;
-}) {
- const t = useTranslations();
- const [open, setOpen] = useState(false);
- const { data: roles = [] } = useQuery(orgQueries.roles({ orgId }));
- const toggle = (id: number) => {
- if (value.includes(id)) {
- onChange(value.filter((x) => x !== id));
- } else {
- onChange([...value, id]);
- }
- };
- const summary =
- value.length === 0
- ? t("alertingSelectRoles")
- : t("alertingRolesSelected", { count: value.length });
- return (
-
-
-
-
-
-
-
-
- {roles.map((r) => (
- toggle(r.roleId)}
- className="cursor-pointer"
- >
-
- {r.name}
-
- ))}
-
-
-
-
-
- );
-}
-
export function AlertRuleSourceFields({
orgId,
control
@@ -838,7 +705,8 @@ export function AlertRuleSourceFields({