mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
fix: project form.
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import { MenuItem, Button } from '@blueprintjs/core';
|
||||||
|
import { FSelect, FFormGroup } from 'components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} query
|
||||||
|
* @param {*} project
|
||||||
|
* @param {*} _index
|
||||||
|
* @param {*} exactMatch
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const projectItemPredicate = (query, project, _index, exactMatch) => {
|
||||||
|
const normalizedTitle = project.name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return `${project.name}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} project
|
||||||
|
* @param {*} param1
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const projectItemRenderer = (project, { handleClick, modifiers, query }) => {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
active={modifiers.active}
|
||||||
|
disabled={modifiers.disabled}
|
||||||
|
key={project.id}
|
||||||
|
onClick={handleClick}
|
||||||
|
text={project.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectSelectProps = {
|
||||||
|
itemPredicate: projectItemPredicate,
|
||||||
|
itemRenderer: projectItemRenderer,
|
||||||
|
valueAccessor: 'id',
|
||||||
|
labelAccessor: 'name',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProjectSelect({ projects, ...rest }) {
|
||||||
|
return (
|
||||||
|
<FSelect
|
||||||
|
items={projects}
|
||||||
|
{...projectSelectProps}
|
||||||
|
{...rest}
|
||||||
|
input={ProjectSelectButton}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProjectSelectButton({ label }) {
|
||||||
|
return <Button text={label ? label : intl.get('find_or_choose_a_project')} />;
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ function ProjectFormFields() {
|
|||||||
<FastField name={'contact'}>
|
<FastField name={'contact'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={intl.get('projects.label.contact')}
|
label={intl.get('projects.dialog.contact')}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
>
|
>
|
||||||
@@ -56,14 +56,14 @@ function ProjectFormFields() {
|
|||||||
</FastField>
|
</FastField>
|
||||||
{/*------------ Project Name -----------*/}
|
{/*------------ Project Name -----------*/}
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
label={intl.get('projects.label.project_name')}
|
label={intl.get('projects.dialog.project_name')}
|
||||||
name={'projectName'}
|
name={'projectName'}
|
||||||
>
|
>
|
||||||
<FInputGroup name="projectName" />
|
<FInputGroup name="projectName" />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
{/*------------ DeadLine -----------*/}
|
{/*------------ DeadLine -----------*/}
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
label={intl.get('projects.label.deadline')}
|
label={intl.get('projects.dialog.deadline')}
|
||||||
name={'projectDeadline'}
|
name={'projectDeadline'}
|
||||||
className={classNames(CLASSES.FILL, 'form-group--date')}
|
className={classNames(CLASSES.FILL, 'form-group--date')}
|
||||||
>
|
>
|
||||||
@@ -82,13 +82,13 @@ function ProjectFormFields() {
|
|||||||
<FFormGroup name={'projectState'}>
|
<FFormGroup name={'projectState'}>
|
||||||
<FCheckbox
|
<FCheckbox
|
||||||
name="projectState"
|
name="projectState"
|
||||||
label={intl.get('projects.label.calculator_expenses')}
|
label={intl.get('projects.dialog.calculator_expenses')}
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
{/*------------ Cost Estimate -----------*/}
|
{/*------------ Cost Estimate -----------*/}
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'projectCost'}
|
name={'projectCost'}
|
||||||
label={intl.get('projects.label.cost_estimate')}
|
label={intl.get('projects.dialog.cost_estimate')}
|
||||||
>
|
>
|
||||||
<ControlGroup>
|
<ControlGroup>
|
||||||
<InputPrependText text={'USD'} />
|
<InputPrependText text={'USD'} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { DataTable } from 'components';
|
import { DataTable } from 'components';
|
||||||
import { TABLES } from 'common/tables';
|
import { TABLES } from 'common/tables';
|
||||||
@@ -16,21 +17,21 @@ import { compose } from 'utils';
|
|||||||
const projects = [
|
const projects = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Project 1',
|
name: 'Maroon Bronze',
|
||||||
description: 'Project 1 description',
|
deadline: '2022-06-08T22:00:00.000Z',
|
||||||
status: 'Active',
|
display_name: 'Kyrie Rearden',
|
||||||
|
cost_estimate: '4000',
|
||||||
|
task_amount: '1000',
|
||||||
|
is_in_process: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Project 2',
|
name: 'Project Sherwood',
|
||||||
description: 'Project 2 description',
|
deadline: '2022-06-08T22:00:00.000Z',
|
||||||
status: 'Active',
|
display_name: 'Ella-Grace Miller',
|
||||||
},
|
cost_estimate: '0',
|
||||||
{
|
task_amount: '1000',
|
||||||
id: 3,
|
is_in_process: true,
|
||||||
name: 'Project 3',
|
|
||||||
description: 'Project 3 description',
|
|
||||||
status: 'Active',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -81,16 +82,16 @@ function ProjectsDataTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<ProjectsTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={projects}
|
data={projects}
|
||||||
// loading={}
|
// loading={}
|
||||||
// headerLoading={}
|
// headerLoading={}
|
||||||
// progressBarLoading={}
|
// progressBarLoading={}
|
||||||
manualSortBy={true}
|
manualSortBy={true}
|
||||||
selectionColumn={true}
|
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
|
hideTableHeader={true}
|
||||||
TableLoadingRenderer={TableSkeletonRows}
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
ContextMenu={ActionsMenu}
|
ContextMenu={ActionsMenu}
|
||||||
@@ -114,3 +115,40 @@ export default compose(
|
|||||||
projectsTableSize: projectSettings?.tableSize,
|
projectsTableSize: projectSettings?.tableSize,
|
||||||
})),
|
})),
|
||||||
)(ProjectsDataTable);
|
)(ProjectsDataTable);
|
||||||
|
|
||||||
|
const ProjectsTable = styled(DataTable)`
|
||||||
|
.tbody {
|
||||||
|
.tr .td {
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar.td {
|
||||||
|
.avatar {
|
||||||
|
display: inline-block;
|
||||||
|
background: #adbcc9;
|
||||||
|
border-radius: 8%;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&[data-size='medium'] {
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
&[data-size='small'] {
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table-size--small {
|
||||||
|
.tbody .tr {
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuDivider,
|
||||||
|
MenuItem,
|
||||||
|
Tag,
|
||||||
|
Intent,
|
||||||
|
ProgressBar,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { Icon, FormatDate, Choose, FormattedMessage as T } from 'components';
|
||||||
|
import { safeCallback, firstLettersArgs, calculateStatus } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* project status.
|
||||||
|
*/
|
||||||
|
export function ProjectStatus({ project }) {
|
||||||
|
return (
|
||||||
|
<ProjectStatusRoot>
|
||||||
|
{project.task_amount}
|
||||||
|
<ProjectProgressBar
|
||||||
|
animate={false}
|
||||||
|
intent={Intent.NONE}
|
||||||
|
value={calculateStatus(project.task_amount, project.cost_estimate)}
|
||||||
|
/>
|
||||||
|
</ProjectStatusRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* project status accessor.
|
||||||
|
*/
|
||||||
|
export const ProjectStatusAccessor = (row) => {
|
||||||
|
return (
|
||||||
|
<Choose>
|
||||||
|
<Choose.When condition={row.is_in_process}>
|
||||||
|
<ProjectStatus project={row} />
|
||||||
|
</Choose.When>
|
||||||
|
<Choose.Otherwise>
|
||||||
|
<Tag round={true} minimal={true}>
|
||||||
|
<T id={'draft'} />
|
||||||
|
</Tag>
|
||||||
|
</Choose.Otherwise>
|
||||||
|
</Choose>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avatar cell.
|
||||||
|
*/
|
||||||
|
export const AvatarCell = ({ row: { original }, size }) => (
|
||||||
|
<span className="avatar" data-size={size}>
|
||||||
|
{firstLettersArgs(original?.display_name, original?.name)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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={intl.get('projects.action.edit_project')}
|
||||||
|
onClick={safeCallback(onEdit, original)}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
icon={<Icon icon="plus" />}
|
||||||
|
text={intl.get('projects.action.new_task')}
|
||||||
|
onClick={safeCallback(onNewTask, original)}
|
||||||
|
/>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem
|
||||||
|
text={intl.get('projects.action.delete_project')}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={safeCallback(onDelete, original)}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Projects accessor.
|
||||||
|
*/
|
||||||
|
export const ProjectsAccessor = (row) => (
|
||||||
|
<ProjectItemsWrap>
|
||||||
|
<ProjectItemsHeader>
|
||||||
|
<ProjectItemContactName>{row.display_name}</ProjectItemContactName>
|
||||||
|
<ProjectItemProjectName>{row.name}</ProjectItemProjectName>
|
||||||
|
</ProjectItemsHeader>
|
||||||
|
<ProjectItemDescription>
|
||||||
|
<FormatDate value={row.deadline} />
|
||||||
|
{intl.get('projects.label.cost_estimate', {
|
||||||
|
value: row.cost_estimate,
|
||||||
|
})}
|
||||||
|
</ProjectItemDescription>
|
||||||
|
</ProjectItemsWrap>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve projects list columns columns.
|
||||||
|
*/
|
||||||
|
export const useProjectsListColumns = () => {
|
||||||
|
return React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'avatar',
|
||||||
|
Header: '',
|
||||||
|
Cell: AvatarCell,
|
||||||
|
className: 'avatar',
|
||||||
|
width: 45,
|
||||||
|
disableResizing: true,
|
||||||
|
disableSortBy: true,
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'name',
|
||||||
|
Header: 'Project Name',
|
||||||
|
accessor: ProjectsAccessor,
|
||||||
|
width: 240,
|
||||||
|
className: 'name',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'status',
|
||||||
|
Header: 'status',
|
||||||
|
accessor: ProjectStatusAccessor,
|
||||||
|
width: 80,
|
||||||
|
className: 'status',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProjectItemsWrap = styled.div``;
|
||||||
|
|
||||||
|
const ProjectItemsHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectItemContactName = styled.div`
|
||||||
|
font-weight: 600;
|
||||||
|
padding-right: 4px;
|
||||||
|
`;
|
||||||
|
const ProjectItemProjectName = styled.div``;
|
||||||
|
|
||||||
|
const ProjectItemDescription = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0.75;
|
||||||
|
margin-top: 4px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectStatusRoot = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
margin: 0 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectProgressBar = styled(ProgressBar)`
|
||||||
|
&.bp3-progress-bar {
|
||||||
|
margin-right: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 3px;
|
||||||
|
max-width: 130px;
|
||||||
|
|
||||||
|
&,
|
||||||
|
.bp3-progress-meter {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
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={intl.get('projects.action.edit_project')}
|
|
||||||
onClick={safeCallback(onEdit, original)}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
icon={<Icon icon="plus" />}
|
|
||||||
text={intl.get('projects.action.new_task')}
|
|
||||||
onClick={safeCallback(onNewTask, original)}
|
|
||||||
/>
|
|
||||||
<MenuDivider />
|
|
||||||
<MenuItem
|
|
||||||
text={intl.get('projects.action.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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -2050,12 +2050,13 @@
|
|||||||
"projects.action.new_task": "New Task",
|
"projects.action.new_task": "New Task",
|
||||||
"projects.action.delete_project": "Delete Project",
|
"projects.action.delete_project": "Delete Project",
|
||||||
"projects.label.new_project": "New Project",
|
"projects.label.new_project": "New Project",
|
||||||
"projects.label.contact": "Contact",
|
"projects.dialog.contact": "Contact",
|
||||||
"projects.label.project_name": "Project Name",
|
"projects.dialog.project_name": "Project Name",
|
||||||
"projects.label.deadline": "Deadline",
|
"projects.dialog.deadline": "Deadline",
|
||||||
"projects.label.calculator_expenses": "Calculator from tasks & estimated expenses",
|
"projects.dialog.calculator_expenses": "Calculator from tasks & estimated expenses",
|
||||||
"projects.label.cost_estimate": "Cost Estimate",
|
"projects.dialog.cost_estimate": "Cost Estimate",
|
||||||
"projects.label.create": "Create",
|
"projects.label.create": "Create",
|
||||||
|
"projects.label.cost_estimate":" • Estimate {value}",
|
||||||
"task.label.new_task": "New Task",
|
"task.label.new_task": "New Task",
|
||||||
"task.label.task_name": "Task Name",
|
"task.label.task_name": "Task Name",
|
||||||
"task.label.estimated_hours": "Task Name",
|
"task.label.estimated_hours": "Task Name",
|
||||||
|
|||||||
Reference in New Issue
Block a user