♻️ replace roles tag with roles selector in role config fields

This commit is contained in:
Fred KISSIE
2026-04-30 22:01:46 +02:00
parent 39bf64bc35
commit b4906ec9ba
3 changed files with 129 additions and 89 deletions

View File

@@ -212,6 +212,11 @@ export default function GeneralPage() {
const detectedRoleMappingConfig =
detectRoleMappingConfig(roleMapping);
console.log({
detectedRoleMappingConfig,
roleMapping
});
// Extract tenant ID from Azure URLs if present
let tenantId = "";
if (idpVariant === "azure" && data.idpOidcConfig?.authUrl) {

View File

@@ -16,6 +16,9 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { build } from "@server/build";
import { RolesSelector } from "./roles-selector";
import { useOrgContext } from "@app/hooks/useOrgContext";
import { useParams } from "next/navigation";
export type RoleMappingRoleOption = {
roleId: number;
@@ -58,9 +61,8 @@ export default function RoleMappingConfigFields({
const t = useTranslations();
const { env } = useEnvContext();
const { isPaidUser } = usePaidStatus();
const [activeFixedRoleTagIndex, setActiveFixedRoleTagIndex] = useState<
number | null
>(null);
const { orgId } = useParams();
const supportsMultipleRolesPerUser = isPaidUser(tierMatrix.fullRbac);
const showSingleRoleDisclaimer =
@@ -160,23 +162,16 @@ export default function RoleMappingConfigFields({
{roleMappingMode === "fixedRoles" && (
<div className="space-y-2 min-w-0 max-w-full">
<TagInput
tags={fixedRoleNames.map((name) => ({
<RolesSelector
selectedRoles={fixedRoleNames.map((name) => ({
id: name,
text: name
}))}
setTags={(nextTags) => {
const prevTags = fixedRoleNames.map((name) => ({
id: name,
text: name
}));
const next =
typeof nextTags === "function"
? nextTags(prevTags)
: nextTags;
mapRolesByName
orgId={orgId as string}
onSelectRoles={(nextTags) => {
let names = [
...new Set(next.map((tag) => tag.text))
...new Set(nextTags.map((tag) => tag.text))
];
if (!supportsMultipleRolesPerUser) {
@@ -198,19 +193,6 @@ export default function RoleMappingConfigFields({
onFixedRoleNamesChange(names);
}}
activeTagIndex={activeFixedRoleTagIndex}
setActiveTagIndex={setActiveFixedRoleTagIndex}
placeholder={
restrictToOrgRoles
? t("roleMappingFixedRolesPlaceholderSelect")
: t("roleMappingFixedRolesPlaceholderFreeform")
}
enableAutocomplete={restrictToOrgRoles}
autocompleteOptions={roleOptions}
restrictTagsToAutocompleteOptions={restrictToOrgRoles}
allowDuplicates={false}
sortTags={true}
size="sm"
/>
<FormDescription>
{showFreeformRoleNamesHint
@@ -352,6 +334,7 @@ function BuilderRuleRow({
}) {
const t = useTranslations();
const [activeTagIndex, setActiveTagIndex] = useState<number | null>(null);
const { orgId } = useParams();
return (
<div
@@ -378,67 +361,109 @@ function BuilderRuleRow({
{t("roleMappingAssignRoles")}
</FormLabel>
<div className="min-w-0 max-w-full">
<TagInput
tags={rule.roleNames.map((name) => ({
id: name,
text: name
}))}
setTags={(nextTags) => {
const prevRoleTags = rule.roleNames.map((name) => ({
{restrictToOrgRoles ? (
<RolesSelector
selectedRoles={rule.roleNames.map((name) => ({
id: name,
text: name
}));
const next =
typeof nextTags === "function"
? nextTags(prevRoleTags)
: nextTags;
}))}
buttonText={t("roleMappingAssignRoles")}
mapRolesByName
orgId={orgId as string}
onSelectRoles={(nextTags) => {
let names = [
...new Set(nextTags.map((tag) => tag.text))
];
let names = [
...new Set(next.map((tag) => tag.text))
];
if (!supportsMultipleRolesPerUser) {
if (
names.length === 0 &&
rule.roleNames.length > 0
) {
onChange({
...rule,
roleNames: [
rule.roleNames[
rule.roleNames.length - 1
]!
]
});
return;
if (!supportsMultipleRolesPerUser) {
if (
names.length === 0 &&
rule.roleNames.length > 0
) {
onChange({
...rule,
roleNames: [
rule.roleNames[
rule.roleNames.length - 1
]!
]
});
return;
}
if (names.length > 1) {
names = [names[names.length - 1]!];
}
}
if (names.length > 1) {
names = [names[names.length - 1]!];
}
}
onChange({
...rule,
roleNames: names
});
}}
activeTagIndex={activeTagIndex}
setActiveTagIndex={setActiveTagIndex}
placeholder={
restrictToOrgRoles
? t("roleMappingAssignRoles")
: t("roleMappingAssignRolesPlaceholderFreeform")
}
enableAutocomplete={restrictToOrgRoles}
autocompleteOptions={roleOptions}
restrictTagsToAutocompleteOptions={restrictToOrgRoles}
allowDuplicates={false}
sortTags={true}
size="sm"
styleClasses={{
inlineTagsContainer: "min-w-0 max-w-full"
}}
/>
onChange({
...rule,
roleNames: names
});
}}
/>
) : (
<TagInput
tags={rule.roleNames.map((name) => ({
id: name,
text: name
}))}
setTags={(nextTags) => {
const prevRoleTags = rule.roleNames.map(
(name) => ({
id: name,
text: name
})
);
const next =
typeof nextTags === "function"
? nextTags(prevRoleTags)
: nextTags;
let names = [
...new Set(next.map((tag) => tag.text))
];
if (!supportsMultipleRolesPerUser) {
if (
names.length === 0 &&
rule.roleNames.length > 0
) {
onChange({
...rule,
roleNames: [
rule.roleNames[
rule.roleNames.length - 1
]!
]
});
return;
}
if (names.length > 1) {
names = [names[names.length - 1]!];
}
}
onChange({
...rule,
roleNames: names
});
}}
activeTagIndex={activeTagIndex}
setActiveTagIndex={setActiveTagIndex}
placeholder={t(
"roleMappingAssignRolesPlaceholderFreeform"
)}
enableAutocomplete={false}
autocompleteOptions={roleOptions}
restrictTagsToAutocompleteOptions={false}
allowDuplicates={false}
sortTags={true}
size="sm"
styleClasses={{
inlineTagsContainer: "min-w-0 max-w-full"
}}
/>
)}
</div>
{showFreeformRoleNamesHint && (
<p className="text-sm text-muted-foreground">

View File

@@ -14,6 +14,8 @@ export type RolesSelectorProps = {
onSelectRoles: (roles: SelectedRole[]) => void;
disabled?: boolean;
restrictAdminRole?: boolean;
mapRolesByName?: boolean;
buttonText?: string;
};
export function RolesSelector({
@@ -21,7 +23,9 @@ export function RolesSelector({
selectedRoles = [],
onSelectRoles,
disabled,
restrictAdminRole
restrictAdminRole,
mapRolesByName,
buttonText
}: RolesSelectorProps) {
const t = useTranslations();
const [roleSearchQuery, setRoleSearchQuery] = useState("");
@@ -36,7 +40,7 @@ export function RolesSelector({
const rolesShown = useMemo(() => {
let allRoles: Array<SelectedRole & { isAdmin?: boolean }> = roles.map(
(r) => ({
id: r.roleId.toString(),
id: mapRolesByName ? r.name : r.roleId.toString(),
text: r.name,
isAdmin: Boolean(r.isAdmin)
})
@@ -55,11 +59,17 @@ export function RolesSelector({
}
return allRoles;
}, [roles, selectedRoles, debouncedValue, restrictAdminRole]);
}, [
roles,
selectedRoles,
debouncedValue,
restrictAdminRole,
mapRolesByName
]);
return (
<MultiSelectTagInput
buttonText={t("alertingSelectRoles")}
buttonText={buttonText ?? t("alertingSelectRoles")}
searchQuery={roleSearchQuery}
onSearch={setRoleSearchQuery}
options={rolesShown}