make team members page work

This commit is contained in:
Simon Larsen
2022-08-03 12:29:12 +01:00
parent 7062ba675c
commit e0cd5bf227
9 changed files with 432 additions and 10 deletions

View File

@@ -51,6 +51,10 @@ module.exports = {
test: /\.s[ac]ss$/i,
use: ['style-loader', 'css-loader', "sass-loader"]
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: 'file-loader'

View File

@@ -3,8 +3,6 @@ import {
Entity,
Index,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
} from 'typeorm';
import BaseModel from './BaseModel';
@@ -18,7 +16,6 @@ import TableColumn from '../Types/Database/TableColumn';
import ColumnType from '../Types/Database/ColumnType';
import ObjectID from '../Types/ObjectID';
import ColumnLength from '../Types/Database/ColumnLength';
import TeamPermission from './TeamPermission';
import TableAccessControl from '../Types/Database/AccessControl/TableAccessControl';
import Permission from '../Types/Permission';
import ColumnAccessControl from '../Types/Database/AccessControl/ColumnAccessControl';
@@ -230,12 +227,6 @@ export default class Team extends BaseModel {
})
public deletedByUserId?: ObjectID = undefined;
@ManyToMany(() => {
return TeamPermission;
})
@JoinTable()
public permissions?: Array<TeamPermission> = undefined;
@ColumnAccessControl({
create: [],
read: [

View File

@@ -130,6 +130,12 @@ export class PermissionHelper {
description: 'Owner of this project, manages billing, inviting other admins to this project, and can delete this project.',
isAssignableToProject: true
},
{
permission: Permission.ProjectMember,
title: 'Project Member',
description: 'Member of this project. Can view most resources unless restricted.',
isAssignableToProject: true
},
{
permission: Permission.ProjectAdmin,
title: 'Project Admin',

View File

@@ -26,6 +26,8 @@ import SettingsApiKeys from './Pages/Settings/APIKeys';
import SettingsApiKeyView from './Pages/Settings/APIKeyView';
import SettingLabels from './Pages/Settings/Labels';
import SettingCustomSMTP from './Pages/Settings/CustomSMTP';
import SettingsTeams from './Pages/Settings/Teams';
import SettingsTeamView from './Pages/Settings/TeamView';
// Import CSS
import 'CommonUI/src/Styles/theme.scss';
@@ -223,6 +225,30 @@ const App: FunctionComponent = () => {
}
/>
<PageRoute
path={RouteMap[PageMap.SETTINGS_TEAMS]?.toString()}
element={
<SettingsTeams
pageRoute={
RouteMap[PageMap.SETTINGS_TEAMS] as Route
}
currentProject={selectedProject}
/>
}
/>
<PageRoute
path={RouteMap[PageMap.SETTINGS_TEAM_VIEW]?.toString()}
element={
<SettingsTeamView
pageRoute={
RouteMap[PageMap.SETTINGS_TEAM_VIEW] as Route
}
currentProject={selectedProject}
/>
}
/>
{/* Misc Routes */}
<PageRoute
path={RouteMap[PageMap.LOGOUT]?.toString()}

View File

@@ -64,7 +64,9 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => {
<SideMenuItem
link={{
title: 'Teams and Members',
to: new Route('/:projectSlug/home'),
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_TEAMS] as Route
),
}}
icon={IconProp.User}
/>

View File

@@ -0,0 +1,279 @@
import Route from 'Common/Types/API/Route';
import Page from 'CommonUI/src/Components/Page/Page';
import React, { FunctionComponent, ReactElement } from 'react';
import PageMap from '../../Utils/PageMap';
import RouteMap from '../../Utils/RouteMap';
import PageComponentProps from '../PageComponentProps';
import DashboardSideMenu from './SideMenu';
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
import TableColumnType from 'CommonUI/src/Components/Table/Types/TableColumnType';
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
import { IconProp } from 'CommonUI/src/Components/Icon/Icon';
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
import Team from 'Common/Models/Team';
import TeamMember from 'Common/Models/TeamMember';
import Navigation from 'CommonUI/src/Utils/Navigation';
import PermissionUtil from 'CommonUI/src/Utils/Permission';
import Label from 'Common/Models/Label';
import { JSONObject } from 'Common/Types/JSON';
import Permission, { PermissionHelper } from 'Common/Types/Permission';
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
import ObjectID from 'Common/Types/ObjectID';
import Pill from 'CommonUI/src/Components/Pill/Pill';
import Color from 'Common/Types/Color';
import TeamPermission from 'Common/Models/TeamPermission';
const TeamView: FunctionComponent<PageComponentProps> = (
props: PageComponentProps
): ReactElement => {
return (
<Page
title={'Project Settings'}
breadcrumbLinks={[
{
title: 'Project',
to: RouteMap[PageMap.HOME] as Route,
},
{
title: 'Settings',
to: RouteMap[PageMap.SETTINGS] as Route,
},
{
title: 'Teams',
to: RouteMap[PageMap.SETTINGS_TEAMS] as Route,
},
{
title: 'View Team',
to: RouteMap[PageMap.SETTINGS_TEAM_VIEW] as Route,
},
]}
sideMenu={<DashboardSideMenu />}
>
{/* API Key View */}
<CardModelDetail
cardProps={{
title: "Team Details",
description: "Here's more details on this team.",
icon: IconProp.User,
}
}
isEditable={true}
formFields={[
{
field: {
name: true,
},
title: 'Name',
fieldType: FormFieldSchemaType.Text,
required: true,
placeholder: 'Team Name',
validation: {
noSpaces: true,
minLength: 2,
},
},
{
field: {
description: true,
},
title: 'Description',
fieldType: FormFieldSchemaType.LongText,
required: true,
placeholder:
'Team Description',
},
]}
modelDetailProps={
{
type: Team,
model: new Team(),
id: "model-detail-team",
fields: [
{
field: {
name: true
},
title: "Name",
},
{
field: {
description: true
},
title: "Description",
}
],
modelId: Navigation.getLastParam(),
}
}
/>
{/* Team Members Table */}
<ModelTable<TeamMember>
type={TeamMember}
model={new TeamMember()}
id="table-team-member"
isDeleteable={true}
isEditable={true}
isCreateable={true}
isViewable={false}
cardProps={{
icon: IconProp.User,
title: 'Team Members',
description:
'See a list of members or invite them to this team. ',
}}
noItemsMessage={'No members found for this team.'}
formFields={[
{
field: {
user: true,
},
title: 'User Email',
description: 'Please enter the email of the user you would like to invite.',
fieldType: FormFieldSchemaType.Email,
required: false,
placeholder:
'member@company.com',
}
]}
showRefreshButton={true}
showFilterButton={true}
currentPageRoute={props.pageRoute}
columns={[
{
field: {
user: true,
},
title: 'User',
type: TableColumnType.Text,
getColumnElement: (item: JSONObject): ReactElement => {
if (item["user"]) {
if (item["user"] && (item["user"] as JSONObject)['name'] && (item["user"] as JSONObject)['name']) {
return <p>item["user"] as JSONObject)['name']</p>;
}
}
return <></>;
},
},
]}
/>
{/* Team Permisison Table */}
<ModelTable<TeamPermission>
type={TeamPermission}
model={new TeamPermission()}
id="table-team-permission"
isDeleteable={true}
isEditable={true}
isCreateable={true}
isViewable={false}
cardProps={{
icon: IconProp.Lock,
title: 'Team Permissions',
description:
'Add different permisisons to this team to make it more granular.',
}}
noItemsMessage={'No permisisons created for this team so far.'}
formFields={[
{
field: {
permission: true,
},
title: 'Permission',
fieldType: FormFieldSchemaType.Dropdown,
required: true,
placeholder: 'Permission',
dropdownOptions: PermissionUtil.projectPermissionsAsDropdownOptions()
},
{
field: {
labels: true,
},
title: 'Labels (Optional)',
description: 'Labels on which this permissions will apply on. This is optional and an advanced feature.',
fieldType: FormFieldSchemaType.MultiSelectDropdown,
dropdownModal: {
type: Label,
labelField: "name",
valueField: "_id"
},
required: false,
placeholder:
'Labels',
}
]}
showRefreshButton={true}
showFilterButton={true}
currentPageRoute={props.pageRoute}
columns={[
{
field: {
permission: true,
},
title: 'Permission',
type: TableColumnType.Text,
isFilterable: true,
getColumnElement: (item: JSONObject): ReactElement => {
return (
<p>{PermissionHelper.getTitle(item["permission"] as Permission)}</p>
);
},
},
{
field: {
labels: true,
},
title: 'Labels',
type: TableColumnType.Text,
getColumnElement: (item: JSONObject): ReactElement => {
const returnElements = [];
if (item["labels"] && Array.isArray(item["labels"])) {
let counter = 0;
for (const label of item["labels"])
if (label && (label as JSONObject)['color'] && (label as JSONObject)['name']) {
returnElements.push(
<Pill
key={counter}
color={(label as JSONObject)['color'] as Color}
text={(label as JSONObject)['name'] as string}
style={{
marginRight: "5px"
}}
/>
);
counter++;
}
}
return <>{returnElements}</>;
},
},
]}
/>
<ModelDelete
type={Team}
modelId={new ObjectID(Navigation.getLastParam()?.toString() || '')}
onDeleteSuccess={() => {
Navigation.navigate(RouteMap[PageMap.SETTINGS_TEAMS] as Route);
}}
/>
</Page>
);
};
export default TeamView;

View File

@@ -0,0 +1,101 @@
import Route from 'Common/Types/API/Route';
import Page from 'CommonUI/src/Components/Page/Page';
import React, { FunctionComponent, ReactElement } from 'react';
import PageMap from '../../Utils/PageMap';
import RouteMap from '../../Utils/RouteMap';
import PageComponentProps from '../PageComponentProps';
import DashboardSideMenu from './SideMenu';
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
import Team from 'Common/Models/Team';
import TableColumnType from 'CommonUI/src/Components/Table/Types/TableColumnType';
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
import { IconProp } from 'CommonUI/src/Components/Icon/Icon';
const Teams: FunctionComponent<PageComponentProps> = (
props: PageComponentProps
): ReactElement => {
return (
<Page
title={'Project Settings'}
breadcrumbLinks={[
{
title: 'Project',
to: RouteMap[PageMap.HOME] as Route,
},
{
title: 'Settings',
to: RouteMap[PageMap.SETTINGS] as Route,
},
{
title: 'Teams',
to: RouteMap[PageMap.SETTINGS_TEAMS] as Route,
},
]}
sideMenu={<DashboardSideMenu />}
>
<ModelTable<Team>
type={Team}
model={new Team()}
id="teams-table"
isDeleteable={false}
isEditable={true}
isCreateable={true}
isViewable={true}
cardProps={{
icon: IconProp.User,
title: 'Teams',
description:
'Here is a list of all the teams in this project.',
}}
noItemsMessage={'No teams created for this project so far.'}
formFields={[
{
field: {
name: true,
},
title: 'Name',
fieldType: FormFieldSchemaType.Text,
required: true,
placeholder: 'Team Name',
validation: {
minLength: 2,
},
},
{
field: {
description: true,
},
title: 'Description',
fieldType: FormFieldSchemaType.LongText,
required: true,
placeholder:
'Team Description',
}
]}
showRefreshButton={true}
showFilterButton={true}
currentPageRoute={props.pageRoute}
columns={[
{
field: {
name: true,
},
title: 'Name',
type: TableColumnType.Text,
isFilterable: true,
},
{
field: {
description: true,
},
title: 'Description',
type: TableColumnType.Text,
isFilterable: true,
}
]}
/>
</Page>
);
};
export default Teams;

View File

@@ -20,6 +20,11 @@ enum PageMap {
SETTINGS_APIKEY_VIEW = 'SETTINGS_APIKEY_VIEW',
SETTINGS_CUSTOM_SMTP = 'SETTINGS_CUSTOM_SMTP',
// Team
SETTINGS_TEAMS = 'SETTINGS_TEAMS',
SETTINGS_TEAM_VIEW = 'SETTINGS_TEAM_VIEW',
// Labels.
SETTINGS_LABELS = 'SETTINGS_LABELS',

View File

@@ -53,6 +53,14 @@ const RouteMap: Dictionary<Route> = {
`/dashboard/${RouteParams.ProjectID}/settings/custom-smtp`
),
[PageMap.SETTINGS_TEAMS]: new Route(
`/dashboard/${RouteParams.ProjectID}/settings/teams`
),
[PageMap.SETTINGS_TEAM_VIEW]: new Route(
`/dashboard/${RouteParams.ProjectID}/settings/teams/${RouteParams.ModelID}`
),
// labels.
[PageMap.SETTINGS_LABELS]: new Route(
`/dashboard/${RouteParams.ProjectID}/settings/labels`