Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7594964
improvement: redesign org roles page to match project roles UI
mathnogueira Apr 14, 2026
d85ef26
add descriptions and add PermissionActionSelect for better filters
mathnogueira Apr 14, 2026
cfb44a2
fix lint
mathnogueira Apr 14, 2026
88c40ab
remove duplicate blocks
mathnogueira Apr 14, 2026
3e430cd
improve code duplication and organize description imports
mathnogueira Apr 14, 2026
691ab8e
use enums instead of strings
mathnogueira Apr 14, 2026
89faa6a
use enums in other places that were missing
mathnogueira Apr 14, 2026
1124489
make page similar to the project roles page
mathnogueira Apr 14, 2026
4d1bc00
use PermissionActionSelect
mathnogueira Apr 14, 2026
8a3706f
simplify code
mathnogueira Apr 14, 2026
d80427b
use ORG_PERMISSION_OBJECT in SIMPLE_PERMISSION_OPTIONS
mathnogueira Apr 14, 2026
07024ed
redesign options button
mathnogueira Apr 14, 2026
08d6419
fix linter errors
mathnogueira Apr 14, 2026
83b9ecc
fix claude comments
mathnogueira Apr 14, 2026
15a7343
fix review issues
mathnogueira Apr 14, 2026
fec4536
fix lint issue
mathnogueira Apr 14, 2026
1c55062
fix review errors
mathnogueira Apr 15, 2026
a6ea74c
review patches
mathnogueira Apr 15, 2026
c215269
remove dead code
mathnogueira Apr 15, 2026
0e46ce4
use V3 select
mathnogueira Apr 15, 2026
88f5889
fix linter issues
mathnogueira Apr 15, 2026
2d6d717
adapt email domains
mathnogueira Apr 15, 2026
a5dcc68
fix lint issue
mathnogueira Apr 15, 2026
efc0f4a
add "Add policy" button
mathnogueira Apr 15, 2026
549950a
fix issues
mathnogueira Apr 15, 2026
adb1c23
fix existing issues found by claude
mathnogueira Apr 15, 2026
aad1594
fix dropdown options
mathnogueira Apr 15, 2026
f48d8bf
fix linter issues
mathnogueira Apr 15, 2026
3368573
fix type issues
mathnogueira Apr 15, 2026
46465e6
fix lint
mathnogueira Apr 16, 2026
31f6f36
update styles and use v3 components
mathnogueira Apr 17, 2026
f5b006a
refactor components to reuse project components
mathnogueira Apr 20, 2026
c343d3b
better types + validation + coloring
mathnogueira Apr 20, 2026
95f68f3
better types
mathnogueira Apr 20, 2026
20e710f
autofix lint
mathnogueira Apr 20, 2026
1c643ab
add isConditional to fix bug
mathnogueira Apr 20, 2026
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
93 changes: 93 additions & 0 deletions frontend/src/components/v3/platform/PermissionActionSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
components,
GroupBase,
MultiValueProps,
MultiValueRemoveProps,
OptionProps,
Props
} from "react-select";
import { CheckIcon } from "lucide-react";

import { FilterableSelect } from "../generic/ReactSelect";
import { Tooltip, TooltipContent, TooltipTrigger } from "../generic/Tooltip";

export type PermissionActionOption = {
label: string;
value: string;
description?: string;
};

const OptionWithDescription = <T extends PermissionActionOption>(props: OptionProps<T>) => {
const { data, children, isSelected } = props;
return (
<components.Option {...props}>
<div className="flex flex-row items-center justify-between">
<div className="min-w-0 flex-1">
<p className="truncate">{children}</p>
{data.description && (
<p className="truncate text-xs leading-4 text-muted">{data.description}</p>
)}
</div>
{isSelected && <CheckIcon className="ml-2 size-4 shrink-0" />}
</div>
</components.Option>
);
};

const PermissionActionMultiValueRemove = ({ selectProps, ...props }: MultiValueRemoveProps) => {
if (selectProps?.isDisabled) return null;
return <components.MultiValueRemove selectProps={selectProps} {...props} />;
};

const MultiValueWithTooltip = <T extends PermissionActionOption>(props: MultiValueProps<T>) => {
const { data } = props;
if (!data.description) return <components.MultiValue {...props} />;
return (
<Tooltip>
<TooltipTrigger asChild>
<div>
<components.MultiValue {...props} />
</div>
</TooltipTrigger>
<TooltipContent>{data.description}</TooltipContent>
</Tooltip>
);
};

type PermissionActionSelectProps<T extends PermissionActionOption> = Omit<
Props<T, boolean, GroupBase<T>>,
"isMulti"
> & {
groupBy?: string | null;
getGroupHeaderLabel?: ((groupValue: unknown) => string) | null;
isError?: boolean;
};

export const PermissionActionSelect = <T extends PermissionActionOption>({
components: customComponents,
...props
}: PermissionActionSelectProps<T>) => {
return (
<FilterableSelect<T>
isMulti
filterOption={(option, inputValue) => {
if (!inputValue) return true;
const lowerInput = inputValue.toLowerCase();
const data = option.data as T;
return (
option.label.toLowerCase().includes(lowerInput) ||
Boolean(data.description?.toLowerCase().includes(lowerInput))
);
}}
components={
{
Option: OptionWithDescription,
MultiValueRemove: PermissionActionMultiValueRemove,
MultiValue: MultiValueWithTooltip,
...customComponents
} as any
}
{...props}
/>
);
};
1 change: 1 addition & 0 deletions frontend/src/components/v3/platform/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./DocumentationLinkBadge";
export * from "./PermissionActionSelect";
export * from "./ScopeIcons";
37 changes: 22 additions & 15 deletions frontend/src/pages/organization/RoleByIDPage/RoleByIDPage.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { faChevronLeft, faCopy, faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { ChevronLeftIcon, CopyIcon, EllipsisIcon, PencilIcon, TrashIcon } from "lucide-react";
import { twMerge } from "tailwind-merge";

import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { DeleteActionModal, PageHeader } from "@app/components/v2";
import {
Button,
DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
PageHeader
} from "@app/components/v2";
DropdownMenuTrigger
} from "@app/components/v3";
import { ROUTE_PATHS } from "@app/const/routes";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { useDeleteOrgRole, useGetOrgRole } from "@app/hooks/api";
Expand Down Expand Up @@ -76,9 +75,9 @@ export const Page = () => {
search={{
selectedTab: OrgAccessControlTabSections.Roles
}}
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
className="mb-4 flex items-center gap-x-2 text-sm text-muted"
>
<FontAwesomeIcon icon={faChevronLeft} />
<ChevronLeftIcon className="size-4" />
Roles
</Link>
<PageHeader
Expand All @@ -93,14 +92,12 @@ export const Page = () => {
{isCustomRole && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
colorSchema="secondary"
rightIcon={<FontAwesomeIcon icon={faEllipsisV} className="ml-2" />}
>
<Button variant="outline">
Options
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" sideOffset={2} className="p-1">
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
navigator.clipboard.writeText(data.id);
Expand All @@ -110,8 +107,8 @@ export const Page = () => {
type: "info"
});
}}
icon={<FontAwesomeIcon icon={faCopy} />}
>
<CopyIcon />
Copy ID
</DropdownMenuItem>
<DropdownMenuItem
Expand All @@ -123,44 +120,54 @@ export const Page = () => {
type: "info"
});
}}
icon={<FontAwesomeIcon icon={faCopy} />}
>
<CopyIcon />
Copy Slug
</DropdownMenuItem>
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Role}>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={() => {
handlePopUpOpen("role", {
roleId
});
}}
isDisabled={!isAllowed}
>
<PencilIcon />
Edit Role
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Role}>
{(isAllowed) => (
<DropdownMenuItem
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={() => {
handlePopUpOpen("duplicateRole");
}}
isDisabled={!isAllowed}
>
<CopyIcon />
Duplicate Role
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan I={OrgPermissionActions.Delete} a={OrgPermissionSubjects.Role}>
{(isAllowed) => (
<DropdownMenuItem
variant="danger"
onClick={() => {
handlePopUpOpen("deleteOrgRole");
}}
isDisabled={!isAllowed}
>
<TrashIcon />
Delete Role
</DropdownMenuItem>
)}
Expand Down
Loading
Loading