feat: add projects list.

This commit is contained in:
elforjani13
2022-06-11 00:36:18 +02:00
parent 01038136f2
commit 928d4d3f00
11 changed files with 439 additions and 0 deletions

View File

@@ -538,6 +538,27 @@ export const SidebarMenu = [
},
],
},
// ---------------------
// # Projects Management
// ---------------------
{
text: 'Projects',
type: ISidebarMenuItemType.Overlay,
overlayId: ISidebarMenuOverlayIds.Projects,
children: [
{
text: 'Projects Management',
type: ISidebarMenuItemType.Group,
children: [
{
text: 'Projects',
href: '/projects',
type: ISidebarMenuItemType.Link,
},
],
},
],
},
// ---------------
// # Reports
// ---------------

View File

@@ -69,6 +69,7 @@ export enum ISidebarMenuOverlayIds {
Contacts = 'Contacts',
Cashflow = 'Cashflow',
Expenses = 'Expenses',
Projects = 'Projects',
}
export enum ISidebarSubscriptionAbility {

View File

@@ -0,0 +1,60 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Menu, MenuDivider, MenuItem, Intent } from '@blueprintjs/core';
import { Icon } from 'components';
import { safeCallback } from 'utils';
/**
* Table actions cell.
*/
export const ActionsMenu = ({
row: { original },
payload: { onEdit, onDelete, onViewDetails, onNewTask },
}) => (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={intl.get('view_details')}
onClick={safeCallback(onViewDetails, original)}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={'Edit Project'}
onClick={safeCallback(onEdit, original)}
/>
<MenuItem
icon={<Icon icon="plus" />}
text={'New Task'}
onClick={safeCallback(onNewTask, original)}
/>
<MenuDivider />
<MenuItem
text={'Delete Project'}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER}
onClick={safeCallback(onDelete, original)}
/>
</Menu>
);
/**
* Retrieve projects list columns columns.
*/
export const useProjectsListColumns = () => {
return React.useMemo(
() => [
{
id: 'name',
Header: 'Project Name',
accessor: 'name',
width: 100,
className: 'name',
clickable: true,
},
],
[],
);
};

View File

@@ -0,0 +1,126 @@
import React from 'react';
import {
Button,
NavbarGroup,
Classes,
NavbarDivider,
Alignment,
} from '@blueprintjs/core';
import {
Icon,
AdvancedFilterPopover,
DashboardActionViewsList,
DashboardFilterButton,
DashboardRowsHeightButton,
FormattedMessage as T,
} from 'components';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import withProjects from './withProjects';
import withProjectsActions from './withProjectsActions';
import withSettings from '../../Settings/withSettings';
import withSettingsActions from '../../Settings/withSettingsActions';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
/**
* Projects actions bar.
* @returns
*/
function ProjectsActionsBar({
// #withDialogActions
openDialog,
// #withProjects
projectsFilterRoles,
// #withProjectsActions
setProjectsTableState,
// #withSettingsActions
addSetting,
}) {
// Handle tab change.
const handleTabChange = (view) => {
setProjectsTableState({
viewSlug: view ? view.slug : null,
});
};
// Handle click a refresh projects list.
const handleRefreshBtnClick = () => {};
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('projects', 'tableSize', size);
};
// Handle new project button click.
const handleNewProjectBtnClick = () => {
openDialog('project-form');
};
return (
<DashboardActionsBar>
<NavbarGroup>
<DashboardActionViewsList
resourceName={'projects'}
allMenuItem={true}
allMenuItemText={<T id={'all'} />}
views={[]}
onChange={handleTabChange}
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon="plus" />}
text={'New Project'}
onClick={handleNewProjectBtnClick}
/>
{/* AdvancedFilterPopover */}
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'print-16'} iconSize={'16'} />}
text={<T id={'print'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'file-import-16'} />}
text={<T id={'import'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'file-export-16'} iconSize={'16'} />}
text={<T id={'export'} />}
/>
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={'medium'}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />}
onClick={handleRefreshBtnClick}
/>
</NavbarGroup>
</DashboardActionsBar>
);
}
export default compose(
withDialogActions,
withProjectsActions,
withSettingsActions,
withProjects(({ projectsTableState }) => ({
projectsFilterRoles: projectsTableState?.filterRoles,
})),
)(ProjectsActionsBar);

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import { useProjectsListContext } from './ProjectsListProvider';
import { useProjectsListColumns, ActionsMenu } from '../components';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
const projects = [
{
id: 1,
name: 'Project 1',
description: 'Project 1 description',
status: 'Active',
},
];
/**
* Projects list datatable.
* @returns
*/
function ProjectsDataTable({
// #withDial
openDialog,
}) {
// Retrieve projects table columns.
const columns = useProjectsListColumns();
// Handle cell click.
const handleCellClick = (cell, event) => {};
// Handle new task button click.
const handleNewTaskButtonClick = () => {
openDialog('task-form');
};
return (
<DataTable
columns={columns}
data={projects}
// loading={}
// headerLoading={}
// progressBarLoading={}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
ContextMenu={ActionsMenu}
onCellClick={handleCellClick}
payload={{
onNewTask: handleNewTaskButtonClick,
}}
/>
);
}
export default compose(withDialogActions)(ProjectsDataTable);

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { DashboardPageContent, DashboardContentTable } from 'components';
import ProjectsActionsBar from './ProjectsActionsBar';
// import ProjectsViewTabs from './ProjectsViewTabs';
import ProjectsDataTable from './ProjectsDataTable';
import withProjects from './withProjects';
import withProjectsActions from './withProjectsActions';
import { ProjectsListProvider } from './ProjectsListProvider';
import { compose, transformTableStateToQuery } from 'utils';
/**
* Projects list.
* @returns {React.TSX}
*/
function ProjectsList({
// #withProjects
projectsTableState,
projectsTableStateChanged,
// #withProjectsActions
resetProjectsTableState,
}) {
// Resets the projects table state once the page unmount.
React.useEffect(
() => () => {
resetProjectsTableState();
},
[resetProjectsTableState],
);
return (
<ProjectsListProvider
query={transformTableStateToQuery(projectsTableState)}
tableStateChanged={projectsTableStateChanged}
>
<ProjectsActionsBar />
<DashboardPageContent>
{/* <ProjectsViewTabs /> */}
<DashboardContentTable>
<ProjectsDataTable />
</DashboardContentTable>
</DashboardPageContent>
</ProjectsListProvider>
);
}
export default compose(
withProjects(({ projectsTableState, projectsTableStateChanged }) => ({
projectsTableState,
projectsTableStateChanged,
})),
withProjectsActions,
)(ProjectsList);

View File

@@ -0,0 +1,28 @@
import React from 'react';
import DashboardInsider from '../../../components/Dashboard/DashboardInsider';
const ProjectsListContext = React.createContext({});
/**
* Projects list data provider.
* @returns
*/
function ProjectsListProvider({ query, tableStateChanged, ...props }) {
// provider payload.
const provider = {};
return (
<DashboardInsider
// loading={}
name={'projects'}
>
<ProjectsListContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useProjectsListContext = () => React.useContext(ProjectsListContext);
export { ProjectsListProvider, useProjectsListContext };

View File

@@ -0,0 +1,49 @@
import React from 'react';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
import { DashboardViewsTabs } from 'components';
import withProjects from './withProjects';
import withProjectsActions from './withProjectsActions';
import { compose, transfromViewsToTabs } from 'utils';
/**
* Projects views tabs.
* @returns {React.TSX}
*/
function ProjectsViewTabs({
// #withProjects
projectsCurrentView,
// #withProjectsActions
setProjectsTableState,
}) {
// Projects views.
const tabs = transfromViewsToTabs([]);
// Handle tab change.
const handleTabsChange = (viewSlug) => {
setProjectsTableState({ viewSlug: viewSlug || null });
};
return (
<Navbar className={'navbar--dashboard-views'}>
<NavbarGroup align={Alignment.LEFT}>
<DashboardViewsTabs
currentViewSlug={projectsCurrentView}
resourceName={''}
tabs={tabs}
onChange={handleTabsChange}
/>
</NavbarGroup>
</Navbar>
);
}
export default compose(
withProjects(({ projectsTableState }) => ({
projectsCurrentView: projectsTableState?.viewSlug,
})),
withProjectsActions,
)();

View File

@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import {
getProjectsTableStateFactory,
isProjectsTableStateChangedFactory,
} from '../../../store/Project/projects.selectors';
export default (mapState) => {
const getProjectsTableState = getProjectsTableStateFactory();
const isProjectsTableStateChanged = isProjectsTableStateChangedFactory();
const mapStateToProps = (state, props) => {
const mapped = {
projectsTableState: getProjectsTableState(state, props),
projectsTableStateChanged: isProjectsTableStateChanged(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -0,0 +1,14 @@
import { connect } from 'react-redux';
// import type { Dispatch } from 'redux';
import {
setProjectsTableState,
resetProjectsTableState,
} from '../../../store/Project/projects.actions';
const mapDispatchToProps = (dispatch) => ({
setProjectsTableState: (state) => dispatch(setProjectsTableState(state)),
resetProjectsTableState: () => dispatch(resetProjectsTableState()),
});
export default connect(null, mapDispatchToProps);

View File