Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface AdvancedPermissionGroupProps {
resource: Resource;
selectedPermissions: string[];
onChange: (permissions: string[]) => void;
disabled?: boolean;
}

const INTERNAL_DATAACCESS_KEY = "_resource";
Expand All @@ -24,6 +25,7 @@ export function AdvancedPermissionGroup({
resource,
selectedPermissions,
onChange,
disabled,
}: AdvancedPermissionGroupProps) {
const { t } = useLocale();
const { toggleSinglePermission, toggleResourcePermissionLevel } = usePermissions();
Expand All @@ -47,7 +49,9 @@ export function AdvancedPermissionGroup({

const handleToggleAll = (e: React.MouseEvent) => {
e.stopPropagation(); // Stop event from triggering parent click
onChange(toggleResourcePermissionLevel(resource, isAllSelected ? "none" : "all", selectedPermissions));
if (!disabled) {
onChange(toggleResourcePermissionLevel(resource, isAllSelected ? "none" : "all", selectedPermissions));
}
};

// Helper function to check if read permission is auto-enabled
Expand Down Expand Up @@ -77,6 +81,7 @@ export function AdvancedPermissionGroup({
checked={isAllSelected}
onCheckedChange={() => handleToggleAll}
onClick={handleToggleAll}
disabled={disabled}
/>
<span className="text-default text-sm font-medium leading-none">
{t(resourceConfig._resource?.i18nKey || "")}
Expand Down Expand Up @@ -107,9 +112,12 @@ export function AdvancedPermissionGroup({
checked={isChecked}
className="mr-2"
onCheckedChange={(checked) => {
onChange(toggleSinglePermission(permission, !!checked, selectedPermissions));
if (!disabled) {
onChange(toggleSinglePermission(permission, !!checked, selectedPermissions));
}
}}
onClick={(e) => e.stopPropagation()} // Stop checkbox clicks from affecting parent
disabled={disabled}
/>
<div
className="flex items-center gap-2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
export interface RoleColorPickerProps {
value: string;
onChange: (color: string) => void;
disabled?: boolean;
}

export default function RoleColorPicker({ value, onChange }: RoleColorPickerProps) {
export default function RoleColorPicker({ value, onChange, disabled }: RoleColorPickerProps) {
const { t } = useLocale();
const [, setCustomColor] = useState(value);
const colorInputRef = useRef<HTMLInputElement>(null);
Expand All @@ -21,15 +22,18 @@ export default function RoleColorPicker({ value, onChange }: RoleColorPickerProp
};

const handleColorSquareClick = () => {
colorInputRef.current?.click();
if (!disabled) {
colorInputRef.current?.click();
}
};

return (
<div className="mt-6">
<button
type="button"
onClick={handleColorSquareClick}
className="bg-default text-default border-default hover:bg-muted hover:text-emphasis focus:bg-subtle focus-visible:shadow-outline-gray-focused shadow-outline-gray-rested enabled:hover:shadow-outline-gray-hover enabled:active:shadow-outline-gray-active flex h-8 w-8 items-center justify-center gap-2 rounded-[10px] border text-sm transition-shadow duration-200 focus-visible:outline-none focus-visible:ring-0">
disabled={disabled}
className="bg-default text-default border-default hover:bg-muted hover:text-emphasis focus:bg-subtle focus-visible:shadow-outline-gray-focused shadow-outline-gray-rested enabled:hover:shadow-outline-gray-hover enabled:active:shadow-outline-gray-active flex h-8 w-8 items-center justify-center gap-2 rounded-[10px] border text-sm transition-shadow duration-200 focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed disabled:opacity-50">
<div
className="h-5 w-5 rounded border shadow-sm"
style={{ backgroundColor: value }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
const { t } = useLocale();
const router = useRouter();
const isEditing = Boolean(role);
const isSystemRole = role?.type === "SYSTEM";
const [searchQuery, setSearchQuery] = useState("");

const defaultValues = useMemo(
Expand Down Expand Up @@ -171,7 +172,9 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent>
<SheetHeader>
<SheetTitle>{isEditing ? t("edit_role") : t("create_role")}</SheetTitle>
<SheetTitle>
{isSystemRole ? t("view_role") : isEditing ? t("edit_role") : t("create_role")}
</SheetTitle>
</SheetHeader>
<Form form={form} handleSubmit={onSubmit}>
<div className="space-y-4 py-5">
Expand All @@ -181,11 +184,13 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
label={t("role_name")}
{...form.register("name")}
placeholder={t("role_name_placeholder")}
disabled={isSystemRole}
/>
</div>
<RoleColorPicker
value={color}
onChange={(value) => form.setValue("color", value, { shouldDirty: true })}
disabled={isSystemRole}
/>
</div>

Expand All @@ -208,6 +213,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
placeholder={t("search_permissions")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
disabled={isSystemRole}
/>
</div>

Expand All @@ -217,6 +223,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
resource={resource as Resource}
selectedPermissions={permissions}
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
disabled={isSystemRole}
/>
))}
</div>
Expand All @@ -239,6 +246,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
resource={resource}
permissions={permissions}
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
disabled={isSystemRole}
/>
))}
</div>
Expand All @@ -247,18 +255,27 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
</div>
</div>

<SheetFooter>
<Button
type="button"
color="secondary"
onClick={() => onOpenChange(false)}
disabled={createMutation.isPending || updateMutation.isPending}>
{t("cancel")}
</Button>
<Button type="submit" loading={createMutation.isPending || updateMutation.isPending}>
{isEditing ? t("save") : t("create")}
</Button>
</SheetFooter>
{!isSystemRole && (
<SheetFooter>
<Button
type="button"
color="secondary"
onClick={() => onOpenChange(false)}
disabled={createMutation.isPending || updateMutation.isPending}>
{t("cancel")}
</Button>
<Button type="submit" loading={createMutation.isPending || updateMutation.isPending}>
{isEditing ? t("save") : t("create")}
</Button>
</SheetFooter>
)}
{isSystemRole && (
<SheetFooter>
<Button type="button" color="secondary" onClick={() => onOpenChange(false)}>
{t("close")}
</Button>
</SheetFooter>
)}
</Form>
</SheetContent>
</Sheet>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ export function RolesList({
role={role}
key={role.id}
onClick={() => {
// Cant edit system roles
// For system roles, open in view-only mode
if (role.type === "SYSTEM") {
setSelectedRoleId(role.id);
setIsOpen(true);
return;
}

Expand Down Expand Up @@ -136,7 +138,9 @@ function RoleItem({
<div
className={classNames(
"border-subtle flex p-3",
canUpdate && role.type !== "SYSTEM" && "hover:bg-subtle cursor-pointer"
(canUpdate && role.type !== "SYSTEM") || role.type === "SYSTEM"
? "hover:bg-subtle cursor-pointer"
: ""
)}
onClick={onClick}>
<div className="flex w-full items-center gap-3 truncate">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ interface SimplePermissionItemProps {
resource: string;
permissions: string[];
onChange: (permissions: string[]) => void;
disabled?: boolean;
}

export function SimplePermissionItem({ resource, permissions, onChange }: SimplePermissionItemProps) {
export function SimplePermissionItem({
resource,
permissions,
onChange,
disabled,
}: SimplePermissionItemProps) {
const { t } = useLocale();
const { getResourcePermissionLevel, toggleResourcePermissionLevel } = usePermissions();

Expand All @@ -37,10 +43,12 @@ export function SimplePermissionItem({ resource, permissions, onChange }: Simple
</span>
<ToggleGroup
onValueChange={(val) => {
if (val) onChange(toggleResourcePermissionLevel(resource, val as PermissionLevel, permissions));
if (val && !disabled)
onChange(toggleResourcePermissionLevel(resource, val as PermissionLevel, permissions));
}}
value={getResourcePermissionLevel(resource, permissions)}
options={options}
disabled={disabled}
/>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@
"send_invite_email": "Send an invite email",
"role": "Role",
"edit_role": "Edit Role",
"view_role": "View Role",
"edit_team": "Edit team",
"editing_user": "Editing user",
"editing_org": "Editing organization",
Expand Down
Loading