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

View File

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