Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl committed Aug 9, 2024
1 parent 1b414ac commit 8da5349
Show file tree
Hide file tree
Showing 18 changed files with 634 additions and 175 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,4 @@ scripts/automatic_extraction_rules.py

playwright_dump_*.html
playwright_dump_*.png
ldap_generated.ldif
10 changes: 9 additions & 1 deletion keep-ui/app/settings/auth/groups-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useRoles } from "utils/hooks/useRoles";
import { useUsers } from "utils/hooks/useUsers";
import { getApiURL } from "utils/apiUrl";
import { useSession } from "next-auth/react";
import "./multiselect.css";

interface GroupSidebarProps {
isOpen: boolean;
Expand All @@ -28,7 +29,7 @@ const GroupsSidebar = ({ isOpen, toggle, group, isNewGroup, mutateGroups, access

const { data: session } = useSession();
const { data: roles = [] } = useRoles();
const { data: users = [] } = useUsers();
const { data: users = [], mutate: mutateUsers } = useUsers();
const [isSubmitting, setIsSubmitting] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -66,6 +67,7 @@ const GroupsSidebar = ({ isOpen, toggle, group, isNewGroup, mutateGroups, access

if (response.ok) {
await mutateGroups();
await mutateUsers();
handleClose();
} else {
const errorData = await response.json();
Expand Down Expand Up @@ -141,6 +143,10 @@ const GroupsSidebar = ({ isOpen, toggle, group, isNewGroup, mutateGroups, access
{...field}
error={!!errors.name}
errorMessage={errors.name?.message}
disabled={!isNewGroup}
className={`${
isNewGroup ? "" : "bg-gray-200"
}`}
/>
)}
/>
Expand All @@ -157,6 +163,7 @@ const GroupsSidebar = ({ isOpen, toggle, group, isNewGroup, mutateGroups, access
{...field}
onValueChange={(value) => field.onChange(value)}
value={field.value as string[]}
className="custom-multiselect"
>
{users.map((user) => (
<MultiSelectItem key={user.email} value={user.email}>
Expand All @@ -179,6 +186,7 @@ const GroupsSidebar = ({ isOpen, toggle, group, isNewGroup, mutateGroups, access
{...field}
onValueChange={(value) => field.onChange(value)}
value={field.value as string[]}
className="custom-multiselect"
>
{roles.map((role) => (
<MultiSelectItem key={role.id} value={role.name}>
Expand Down
13 changes: 8 additions & 5 deletions keep-ui/app/settings/auth/groups-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import { useState, useEffect, useMemo } from "react";
import GroupsSidebar from "./groups-sidebar";
import { getApiURL } from "utils/apiUrl";
import { TrashIcon } from "@heroicons/react/24/outline";
import { MdGroupAdd } from "react-icons/md";

interface Props {
accessToken: string;
}

export default function GroupsTab({ accessToken }: Props) {
const { data: groups = [], isLoading: groupsLoading, mutate: mutateGroups } = useGroups();
const { data: users = [], isLoading: usersLoading } = useUsers();
const { data: users = [], isLoading: usersLoading, mutate: mutateUsers } = useUsers();
const { data: roles = [] } = useRoles();

const [groupStates, setGroupStates] = useState<{ [key: string]: { members: string[], roles: string[] } }>({});
Expand Down Expand Up @@ -70,11 +71,11 @@ export default function GroupsTab({ accessToken }: Props) {
setIsSidebarOpen(true);
};

const handleDeleteGroup = async (groupId: string, event: React.MouseEvent) => {
const handleDeleteGroup = async (groupName: string, event: React.MouseEvent) => {
event.stopPropagation();
if (window.confirm("Are you sure you want to delete this group?")) {
try {
const url = `${getApiURL()}/auth/groups/${groupId}`;
const url = `${getApiURL()}/auth/groups/${groupName}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
Expand All @@ -84,6 +85,7 @@ export default function GroupsTab({ accessToken }: Props) {

if (response.ok) {
await mutateGroups();
await mutateUsers();
} else {
console.error("Failed to delete group");
}
Expand All @@ -105,8 +107,9 @@ export default function GroupsTab({ accessToken }: Props) {
color="orange"
size="md"
onClick={handleAddGroupClick}
icon={MdGroupAdd}
>
Create Group
Add Group
</Button>
</div>
</div>
Expand Down Expand Up @@ -169,7 +172,7 @@ export default function GroupsTab({ accessToken }: Props) {
variant="light"
color="orange"
className="opacity-0 group-hover:opacity-100 transition-opacity"
onClick={(e) => handleDeleteGroup(group.id, e)}
onClick={(e) => handleDeleteGroup(group.name, e)}
/>
</TableCell>
</TableRow>
Expand Down
92 changes: 92 additions & 0 deletions keep-ui/app/settings/auth/permissions-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Fragment, useEffect } from "react";
import { Dialog, Transition } from "@headlessui/react";
import { Text, Button, Badge } from "@tremor/react";
import { IoMdClose } from "react-icons/io";
import { MultiSelect, MultiSelectItem } from "@tremor/react";
import { Permission } from "app/settings/models"; // Adjust the import as necessary

interface PermissionSidebarProps {
isOpen: boolean;
toggle: VoidFunction;
accessToken: string;
preset: any;
permissions: Permission[];
selectedPermissions: { [key: string]: string[] };
onPermissionChange: (presetId: string, newPermissions: string[]) => void;
}

const PermissionSidebar = ({
isOpen,
toggle,
accessToken,
preset,
permissions,
selectedPermissions,
onPermissionChange,
}: PermissionSidebarProps) => {
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog onClose={toggle}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black/30 z-20" aria-hidden="true" />
</Transition.Child>
<Transition.Child
as={Fragment}
enter="transition ease-in-out duration-300 transform"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<Dialog.Panel className="fixed right-0 inset-y-0 w-3/4 bg-white z-30 p-6 overflow-auto flex flex-col">
<div className="flex justify-between mb-4">
<Dialog.Title className="text-3xl font-bold" as={Text}>
Permissions Details
</Dialog.Title>
<Button onClick={toggle} variant="light">
<IoMdClose className="h-6 w-6 text-gray-500" />
</Button>
</div>
{preset && (
<div className="flex flex-col space-y-4">
<div>
<Text className="text-lg font-medium">Resource Name</Text>
<Text>{preset.name}</Text>
</div>
<div>
<Text className="text-lg font-medium">Resource Type</Text>
<Text>{preset.type}</Text>
</div>
<div>
<Text className="text-lg font-medium">Permissions</Text>
<MultiSelect
placeholder="Select permissions"
value={selectedPermissions[preset.id] || []}
onValueChange={(value) => onPermissionChange(preset.id, value)}
>
{permissions.map((permission) => (
<MultiSelectItem key={permission.id} value={permission.id}>
{permission.name} ({permission.type})
</MultiSelectItem>
))}
</MultiSelect>
</div>
</div>
)}
</Dialog.Panel>
</Transition.Child>
</Dialog>
</Transition>
);
};

export default PermissionSidebar;
98 changes: 78 additions & 20 deletions keep-ui/app/settings/auth/permissions-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import {
TableBody,
TableCell,
TextInput,
MultiSelect,
MultiSelectItem,
Badge,
Button,
} from "@tremor/react";
import Loading from "app/loading";
Expand All @@ -21,7 +20,10 @@ import { useGroups } from "utils/hooks/useGroups";
import { useUsers } from "utils/hooks/useUsers";
import { usePermissions } from "utils/hooks/usePermissions";
import { getApiURL } from "utils/apiUrl";
import { TrashIcon } from "@heroicons/react/24/outline";
import PermissionSidebar from "./permissions-sidebar";
import "./multiselect.css";

interface Props {
accessToken: string;
}
Expand All @@ -32,6 +34,8 @@ export default function PermissionsTab({ accessToken }: Props) {
const [selectedPermissions, setSelectedPermissions] = useState<{ [key: string]: string[] }>({});
const [initialPermissions, setInitialPermissions] = useState<{ [key: string]: string[] }>({});
const [filter, setFilter] = useState("");
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [selectedPreset, setSelectedPreset] = useState<any>(null);

const { useAllPresets } = usePresets();
const { data: presets = [], error: presetsError, isValidating: presetsLoading } = useAllPresets();
Expand Down Expand Up @@ -123,12 +127,39 @@ export default function PermissionsTab({ accessToken }: Props) {
preset.name.toLowerCase().includes(filter.toLowerCase())
);

const handleRowClick = (preset: any) => {
setSelectedPreset(preset);
setIsSidebarOpen(true);
};

const handleDeletePermission = async (presetId: string, event: React.MouseEvent) => {
event.stopPropagation();
if (window.confirm("Are you sure you want to delete this permission?")) {
try {
const response = await fetch(`${apiUrl}/auth/permissions/${presetId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (response.ok) {
// Reload permissions
} else {
console.error("Failed to delete permission");
}
} catch (error) {
console.error("Error deleting permission:", error);
}
}
};

return (
<div className="h-full w-full flex flex-col">
<div className="flex justify-between items-center mb-4">
<div>
<Title>Permissions Management</Title>
<Subtitle>Manage permissions for presets</Subtitle>
<Subtitle>Manage permissions for Keep resources</Subtitle>
</div>
<Button
color="orange"
Expand All @@ -139,7 +170,7 @@ export default function PermissionsTab({ accessToken }: Props) {
</Button>
</div>
<TextInput
placeholder="Search presets"
placeholder="Search resource"
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="mb-4"
Expand All @@ -148,33 +179,60 @@ export default function PermissionsTab({ accessToken }: Props) {
<Table className="h-full">
<TableHead>
<TableRow>
<TableHeaderCell className="w-1/3">Preset Name</TableHeaderCell>
<TableHeaderCell className="w-2/3">Permissions</TableHeaderCell>
<TableHeaderCell className="w-6/24">Resource Name</TableHeaderCell>
<TableHeaderCell className="w-6/24">Resource Type</TableHeaderCell>
<TableHeaderCell className="w-11/24">Permissions</TableHeaderCell>
<TableHeaderCell className="w-1/24"></TableHeaderCell>
</TableRow>
</TableHead>
<TableBody className="overflow-auto">
{filteredPresets.map((preset) => (
<TableRow key={preset.id}>
<TableCell className="w-1/3">{preset.name}</TableCell>
<TableCell className="w-2/3">
<MultiSelect
placeholder="Select permissions"
className="custom-multiselect"
value={selectedPermissions[preset.id] || []}
onValueChange={(value) => handlePermissionChange(preset.id, value)}
>
{displayPermissions.map((permission) => (
<MultiSelectItem key={permission.id} value={permission.id}>
{permission.name} ({permission.type})
</MultiSelectItem>
<TableRow
key={preset.id}
className="hover:bg-gray-50 transition-colors duration-200 cursor-pointer group"
onClick={() => handleRowClick(preset)}
>
<TableCell className="w-6/24">{preset.name}</TableCell>
<TableCell className="w-6/24"> <Badge color="orange" className="text-xs">preset</Badge></TableCell>
<TableCell className="w-11/24">
<div className="flex flex-wrap gap-1">
{selectedPermissions[preset.id]?.slice(0, 5).map((permId, index) => (
<Badge key={index} color="orange" className="text-xs">
{displayPermissions.find(p => p.id === permId)?.name}
</Badge>
))}
</MultiSelect>
{selectedPermissions[preset.id]?.length > 5 && (
<Badge color="orange" className="text-xs">
+{selectedPermissions[preset.id].length - 5} more
</Badge>
)}
</div>
</TableCell>
<TableCell className="w-1/24">
<div className="flex justify-end">
<Button
icon={TrashIcon}
variant="light"
color="orange"
className="opacity-0 group-hover:opacity-100 transition-opacity"
onClick={(e) => handleDeletePermission(preset.id, e)}
/>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Card>
<PermissionSidebar
isOpen={isSidebarOpen}
toggle={() => setIsSidebarOpen(false)}
accessToken={accessToken}
preset={selectedPreset}
permissions={displayPermissions}
selectedPermissions={selectedPermissions}
onPermissionChange={handlePermissionChange}
/>
</div>
);
}
2 changes: 1 addition & 1 deletion keep-ui/app/settings/auth/roles-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function RolesTab({ accessToken }: Props) {
<div className="flex justify-between mb-4">
<div className="flex flex-col">
<Title>Roles Management</Title>
<Subtitle>Manage user roles</Subtitle>
<Subtitle>Manage roles</Subtitle>
</div>
<div className="flex space-x-2">
<Button
Expand Down
Loading

0 comments on commit 8da5349

Please sign in to comment.