Skip to content

Commit

Permalink
Merge branch 'main' into ms2/spaces-view
Browse files Browse the repository at this point in the history
  • Loading branch information
FelipeTrost authored Dec 9, 2024
2 parents dbb1e34 + 01eca25 commit 3443719
Show file tree
Hide file tree
Showing 23 changed files with 1,206 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const DeploymentsModal = ({
});

const openFolder = async (id: string) => {
const folder = await getFolder(id);
const folder = await getFolder(environment.spaceId, id);
if ('error' in folder) {
throw new Error('Failed to fetch folder');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { getCurrentEnvironment } from '@/components/auth';
import Content from '@/components/content';
import { getRoleById } from '@/lib/data/DTOs';
import { getRoleById } from '@/lib/data/legacy/iam/roles';
import UnauthorizedFallback from '@/components/unauthorized-fallback';
import { toCaslResource } from '@/lib/ability/caslAbility';
import RoleId from './role-id-page';
import { getMembers } from '@/lib/data/DTOs';
import { getUserById } from '@/lib/data/DTOs';
import { Button, Card, Space, Tabs } from 'antd';
import { LeftOutlined } from '@ant-design/icons';
import RoleGeneralData from './roleGeneralData';
import RolePermissions from './rolePermissions';
import RoleMembers from './role-members';
import { AuthenticatedUser } from '@/lib/data/user-schema';
import SpaceLink from '@/components/space-link';
import { getFolderById } from '@/lib/data/legacy/folders';

const Page = async ({
params: { roleId, environmentId },
Expand All @@ -15,6 +21,7 @@ const Page = async ({
}) => {
const { ability, activeEnvironment } = await getCurrentEnvironment(environmentId);
const role = await getRoleById(roleId, ability);
if (role && !ability.can('manage', toCaslResource('Role', role))) return <UnauthorizedFallback />;

if (!role)
return (
Expand All @@ -35,9 +42,52 @@ const Page = async ({
.map((user) => getUserById(user.userId)),
)) as AuthenticatedUser[];

if (!ability.can('manage', toCaslResource('Role', role))) return <UnauthorizedFallback />;
const roleParentFolder = role.parentId ? await getFolderById(role.parentId, ability) : undefined;

return <RoleId role={role} usersNotInRole={usersNotInRole} usersInRole={usersInRole} />;
return (
<Content
title={
<Space>
<SpaceLink href={`/iam/roles`}>
<Button icon={<LeftOutlined />} type="text">
Roles
</Button>
</SpaceLink>
{role?.name}
</Space>
}
>
<div style={{ maxWidth: '800px', margin: 'auto' }}>
<Card>
<Tabs
items={[
{
key: 'generalData',
label: 'General Data',
children: <RoleGeneralData role={role} roleParentFolder={roleParentFolder} />,
},
{
key: 'permissions',
label: 'Permissions',
children: <RolePermissions role={role} />,
},
{
key: 'members',
label: 'Manage Members',
children: (
<RoleMembers
role={role}
usersNotInRole={usersNotInRole}
usersInRole={usersInRole}
/>
),
},
]}
/>
</Card>
</div>
</Content>
);
};

export default Page;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,42 +1,106 @@
'use client';

import { toCaslResource } from '@/lib/ability/caslAbility';
import { Alert, App, Button, DatePicker, Form, Input } from 'antd';
import { FC, useEffect, useState } from 'react';
import { Alert, App, Button, DatePicker, Form, Input, Modal, Space } from 'antd';
import { FC, useState } from 'react';
import dayjs from 'dayjs';
import germanLocale from 'antd/es/date-picker/locale/de_DE';
import { useAbilityStore } from '@/lib/abilityStore';
import { updateRole } from '@/lib/data/roles';
import { useRouter } from 'next/navigation';
import { Role } from '@/lib/data/role-schema';
import { Role, RoleInputSchema } from '@/lib/data/role-schema';
import { useEnvironment } from '@/components/auth-can';
import useParseZodErrors, { antDesignInputProps } from '@/lib/useParseZodErrors';
import FormSubmitButton from '@/components/form-submit-button';
import { FolderTree } from '@/components/FolderTree';
import { ProcessListItemIcon } from '@/components/process-list';
import { Folder } from '@/lib/data/folder-schema';
import { wrapServerCall } from '@/lib/wrap-server-call';

const RoleGeneralData: FC<{ role: Role }> = ({ role: _role }) => {
const InputSchema = RoleInputSchema.omit({ environmentId: true, permissions: true });

const FolderInput = ({
onChange,
defaultFolder,
}: {
value?: string;
onChange?: (id: Role['parentId']) => void;
defaultFolder?: Folder;
}) => {
const [modalOpen, setModalOpen] = useState(false);
const [selectedFolder, setSelectedFolder] = useState<{ type: string; name: string } | undefined>(
() =>
defaultFolder && {
type: 'folder',
name: defaultFolder.parentId ? defaultFolder.name : '< root >',
},
);

return (
<>
<Modal
title="Choose a folder"
open={modalOpen}
onCancel={() => setModalOpen(false)}
closeIcon={null}
>
<Space direction="vertical" style={{ maxWidth: '100%' }}>
<Button
onClick={() => {
onChange?.(undefined);
setSelectedFolder(undefined);
setModalOpen(false);
}}
type="default"
danger
>
Clear folder
</Button>
<FolderTree
newChildrenHook={(nodes) => nodes.filter((node) => node.element.type === 'folder')}
treeProps={{
onSelect(_, info) {
const element = info.node.element;
if (element.type !== 'folder') return;

onChange?.(element.id);
setSelectedFolder(element);
setModalOpen(false);
},
}}
showRootAsFolder
/>
</Space>
</Modal>
<Button onClick={() => setModalOpen(true)}>
{selectedFolder ? (
<>
<ProcessListItemIcon item={selectedFolder as any} /> {selectedFolder.name}
</>
) : (
'Select folder'
)}
</Button>
</>
);
};

const RoleGeneralData: FC<{ role: Role; roleParentFolder?: Folder }> = ({
role: _role,
roleParentFolder,
}) => {
const app = App.useApp();
const ability = useAbilityStore((store) => store.ability);
const [form] = Form.useForm();
const router = useRouter();
const environment = useEnvironment();

const [submittable, setSubmittable] = useState(false);
const values = Form.useWatch('name', form);

useEffect(() => {
form.validateFields({ validateOnly: true }).then(
() => {
setSubmittable(true);
},
() => {
setSubmittable(false);
},
);
}, [form, values]);
const [errors, parseInput] = useParseZodErrors(InputSchema);

const role = toCaslResource('Role', _role);

async function submitChanges(values: Record<string, any>) {
if (typeof values.expirationDayJs === 'object') {
if (typeof values?.expirationDayJs === 'object') {
values.expiration = (values.expirationDayJs as dayjs.Dayjs).toISOString();
delete values.expirationDayJs;
}
Expand All @@ -60,19 +124,18 @@ const RoleGeneralData: FC<{ role: Role }> = ({ role: _role }) => {
</>
)}

<Form.Item
label="Name"
name="name"
rules={[{ required: true, message: 'this field is required' }]}
required
>
<Form.Item label="Name" name="name" {...antDesignInputProps(errors, 'name')} required>
<Input
placeholder="input placeholder"
disabled={!ability.can('update', role, { field: 'name' })}
/>
</Form.Item>

<Form.Item label="Description" name="description">
<Form.Item
label="Description"
name="description"
{...antDesignInputProps(errors, 'description')}
>
<Input.TextArea
placeholder="input placeholder"
disabled={!ability.can('update', role, { field: 'description' })}
Expand All @@ -89,10 +152,12 @@ const RoleGeneralData: FC<{ role: Role }> = ({ role: _role }) => {
/>
</Form.Item>

<Form.Item label="Folder" name="parentId">
<FolderInput defaultFolder={roleParentFolder} />
</Form.Item>

<Form.Item>
<Button type="primary" htmlType="submit" disabled={!submittable}>
Update Role
</Button>
<FormSubmitButton submitText="Update Role" isValidData={(values) => !!parseInput(values)} />
</Form.Item>
</Form>
);
Expand Down
36 changes: 36 additions & 0 deletions src/management-system-v2/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,42 @@
}
}

.proceed-robot-icon::before {
font-family: var(--custom-icon-font);
content: 'a';
width: 1em;
}

.proceed-screen-icon::before {
font-family: var(--custom-icon-font);
content: 'b';
width: 1em;
}

.proceed-user-icon::before {
font-family: var(--custom-icon-font);
content: 'c';
width: 1em;
}

.proceed-server-icon::before {
font-family: var(--custom-icon-font);
content: 'd';
width: 1em;
}

.proceed-laptop-icon::before {
font-family: var(--custom-icon-font);
content: 'e';
width: 1em;
}

.proceed-server-with-screen-icon::before {
font-family: var(--custom-icon-font);
content: 'f';
width: 1em;
}

.orange:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: orange !important;
}
Expand Down
7 changes: 6 additions & 1 deletion src/management-system-v2/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import 'antd/dist/reset.css';
import '@/public/antd.min.css';
import './globals.css';
import { Inter } from 'next/font/google';
import localFont from 'next/font/local';
import { FC, PropsWithChildren } from 'react';
import App from '@/components/app';
import { ConfigProvider } from 'antd';

import classNames from 'classnames';

const inter = Inter({ subsets: ['latin'], variable: '--inter' });

const myFont = localFont({ src: './performer-icons.woff', variable: '--custom-icon-font' });

export const metadata = {
title: 'PROCEED',
description: 'Next Gen Business Processes',
Expand All @@ -18,7 +23,7 @@ type RootLayoutProps = PropsWithChildren;
const RootLayout: FC<RootLayoutProps> = ({ children }) => {
return (
<html lang="en">
<body className={inter.variable}>
<body className={classNames(inter.variable, myFont.variable)}>
<App>{children}</App>
</body>
</html>
Expand Down
Binary file not shown.
Loading

0 comments on commit 3443719

Please sign in to comment.