mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
feat(projects): WIP projects service.
This commit is contained in:
2076
package-lock.json
generated
2076
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,11 @@
|
||||
},
|
||||
"proxy": "http://localhost:3000/",
|
||||
"devDependencies": {
|
||||
"@types/react-dom": "^16.9.16"
|
||||
"@types/react-dom": "^16.9.16",
|
||||
"react-error-overlay": "^6.0.9"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -583,6 +583,17 @@ export const SidebarMenu = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: <T id={'Reports'} />,
|
||||
type: ISidebarMenuItemType.Group,
|
||||
children: [
|
||||
{
|
||||
text: <T id={'project_profitability_summary'} />,
|
||||
href: '/financial-reports/project-profitability-summary',
|
||||
type: ISidebarMenuItemType.Link,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// ---------------
|
||||
@@ -669,11 +680,6 @@ export const SidebarMenu = [
|
||||
ability: ReportsAction.READ_AP_AGING_SUMMARY,
|
||||
},
|
||||
},
|
||||
{
|
||||
text: <T id={'project_profitability_summary'} />,
|
||||
href: '/financial-reports/project-profitability-summary',
|
||||
type: ISidebarMenuItemType.Link,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
//@ts-nocheck
|
||||
import React from 'react';
|
||||
import { Overlay, OverlayProps } from '@blueprintjs/core';
|
||||
import { Link } from 'react-router-dom';
|
||||
@@ -19,7 +18,7 @@ export interface ISidebarOverlayProps {
|
||||
|
||||
export interface ISidebarOverlayItemProps {
|
||||
text: string | JSX.Element;
|
||||
href?: string;
|
||||
href: string;
|
||||
onClick?: any;
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ function SidebarOverlayItem({ item }: SidebarOverlayItemProps) {
|
||||
) : //
|
||||
item.type === ISidebarMenuItemType.Link ||
|
||||
item.type === ISidebarMenuItemType.Dialog ? (
|
||||
<SidebarOverlayItemLink text={item.text} onClick={item.onClick} />
|
||||
<SidebarOverlayItemLink text={item.text} href={item.href} onClick={item.onClick} />
|
||||
) : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,12 +54,11 @@ const ProjectProfitabilitySummaryDataTable = styled(ReportDataTable)`
|
||||
padding-top: 0.32rem;
|
||||
padding-bottom: 0.32rem;
|
||||
}
|
||||
.tr.row_type--total .td {
|
||||
&.row_type--TOTAL .td {
|
||||
border-top: 1px solid #bbb;
|
||||
font-weight: 500;
|
||||
border-bottom: 3px double #000;
|
||||
}
|
||||
|
||||
&:last-of-type .td {
|
||||
border-bottom: 1px solid #bbb;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ function ProjectDetailActionsBar({
|
||||
transactions={projectTranslations}
|
||||
onItemSelect={handleNewTransactionBtnClick}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'time-24'} iconSize={16} />}
|
||||
|
||||
@@ -21,7 +21,6 @@ export default function ProjectDetailTabs() {
|
||||
renderActiveTabPanelOnly={true}
|
||||
defaultSelectedTabId={'tasks'}
|
||||
>
|
||||
<Tab id="overview" title={intl.get('project_details.label.overview')} />
|
||||
<Tab
|
||||
id="tasks"
|
||||
title={intl.get('project_details.label.tasks')}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import moment from 'moment';
|
||||
import styled from 'styled-components';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { FormatDate } from '@/components';
|
||||
import {
|
||||
DetailFinancialCard,
|
||||
DetailFinancialSection,
|
||||
FinancialProgressBar,
|
||||
FinancialCardText,
|
||||
} from './components';
|
||||
import { calculateStatus } from '@/utils';
|
||||
import { useCalculateProject } from './utils';
|
||||
@@ -18,20 +13,11 @@ import { useProjectDetailContext } from './ProjectDetailProvider';
|
||||
|
||||
/**
|
||||
* Project details header.
|
||||
* @returns
|
||||
*/
|
||||
export function ProjectDetailHeader() {
|
||||
const { project } = useProjectDetailContext();
|
||||
|
||||
const { percentageOfInvoice, percentageOfExpense } = useCalculateProject();
|
||||
|
||||
// function getDiff() {
|
||||
let start = moment(new Date());
|
||||
let end = moment(project.deadline);
|
||||
let duration = moment.duration(start.diff(end, 'days'));
|
||||
|
||||
console.log(duration, 'XX');
|
||||
|
||||
return (
|
||||
<DetailFinancialSection>
|
||||
<DetailFinancialCard
|
||||
@@ -41,32 +27,25 @@ export function ProjectDetailHeader() {
|
||||
<DetailFinancialCard
|
||||
label={intl.get('project_details.label.invoiced')}
|
||||
value={project.total_invoiced_formatted}
|
||||
>
|
||||
<FinancialCardText>
|
||||
{intl.get('project_details.label.of_project_estimate', {
|
||||
value: percentageOfInvoice,
|
||||
})}
|
||||
</FinancialCardText>
|
||||
<FinancialProgressBar
|
||||
intent={Intent.NONE}
|
||||
value={calculateStatus(project.total_invoiced, project.cost_estimate)}
|
||||
/>
|
||||
</DetailFinancialCard>
|
||||
description={intl.get('project_details.label.of_project_estimate', {
|
||||
value: percentageOfInvoice,
|
||||
})}
|
||||
progressValue={calculateStatus(
|
||||
project.total_invoiced,
|
||||
project.cost_estimate,
|
||||
)}
|
||||
/>
|
||||
<DetailFinancialCard
|
||||
label={intl.get('project_details.label.time_expenses')}
|
||||
value={project.total_expenses_formatted}
|
||||
>
|
||||
<FinancialCardText>
|
||||
{intl.get('project_details.label.of_project_estimate', {
|
||||
value: percentageOfExpense,
|
||||
})}
|
||||
</FinancialCardText>
|
||||
<FinancialProgressBar
|
||||
intent={Intent.NONE}
|
||||
value={calculateStatus(project.total_expenses, project.cost_estimate)}
|
||||
/>
|
||||
</DetailFinancialCard>
|
||||
|
||||
description={intl.get('project_details.label.of_project_estimate', {
|
||||
value: percentageOfExpense,
|
||||
})}
|
||||
progressValue={calculateStatus(
|
||||
project.total_expenses,
|
||||
project.cost_estimate,
|
||||
)}
|
||||
/>
|
||||
<DetailFinancialCard
|
||||
label={intl.get('project_details.label.to_be_invoiced')}
|
||||
value={project.total_billable_formatted}
|
||||
@@ -74,9 +53,8 @@ export function ProjectDetailHeader() {
|
||||
<DetailFinancialCard
|
||||
label={'Deadline'}
|
||||
value={<FormatDate value={project.deadline_formatted} />}
|
||||
>
|
||||
<FinancialCardText>4 days to go</FinancialCardText>
|
||||
</DetailFinancialCard>
|
||||
description={'4 days to go'}
|
||||
/>
|
||||
</DetailFinancialSection>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,5 +84,21 @@ const ProjectTaksDataTable = styled(DataTable)`
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody .tr .td{
|
||||
padding-top: 0.7rem;
|
||||
padding-bottom: 0.7rem;
|
||||
|
||||
&:first-of-type{
|
||||
padding-left: 1rem;
|
||||
}
|
||||
&.td-actions{
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tbody .tr:last-of-type .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import { Icon, If, Choose, FormattedMessage as T } from '@/components';
|
||||
import { Menu, MenuItem, Intent } from '@blueprintjs/core';
|
||||
import { Menu, MenuItem, Intent, ProgressBar } from '@blueprintjs/core';
|
||||
import { safeCallback } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -70,25 +70,72 @@ export function TaskAccessor(task) {
|
||||
);
|
||||
}
|
||||
|
||||
const TaskRoot = styled.div`
|
||||
margin-left: 12px;
|
||||
export function TaskTimeAccessor(task) {
|
||||
return (
|
||||
<TaskTimeRoot>
|
||||
<TaskTimeMinutesRoot>
|
||||
<TaskTimeMinutes>00:00</TaskTimeMinutes>
|
||||
<TaskTimeFull>17h 30m</TaskTimeFull>
|
||||
</TaskTimeMinutesRoot>
|
||||
|
||||
<TaskProgressBar
|
||||
animate={false}
|
||||
stripes={false}
|
||||
intent={Intent.NONE}
|
||||
value={100}
|
||||
/>
|
||||
</TaskTimeRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const TaskTimeRoot = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
`;
|
||||
|
||||
const TaskTimeMinutesRoot = styled.div`
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
`;
|
||||
|
||||
const TaskTimeMinutes = styled.div``;
|
||||
const TaskTimeFull = styled.div`
|
||||
font-size: 12px;
|
||||
color: #5b5c62;
|
||||
`;
|
||||
|
||||
const TaskProgressBar = styled(ProgressBar)`
|
||||
&.bp3-progress-bar {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
height: 4px;
|
||||
max-width: 150px;
|
||||
&,
|
||||
.bp3-progress-meter {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TaskRoot = styled.div``;
|
||||
const TaskHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-flow: wrap;
|
||||
`;
|
||||
const TaskTitle = styled.span`
|
||||
font-weight: 500;
|
||||
line-height: 1.5rem;
|
||||
font-weight: 600;
|
||||
`;
|
||||
const TaskContent = styled.div`
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
opacity: 0.75;
|
||||
margin-bottom: 0.1rem;
|
||||
line-height: 1.2rem;
|
||||
margin-top: 0.25rem;
|
||||
`;
|
||||
const TaskDescription = styled.span`
|
||||
margin: 0.3rem;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { TaskAccessor } from './components';
|
||||
import { TaskAccessor, TaskTimeAccessor } from './components';
|
||||
|
||||
/**
|
||||
* Retrieve project tasks list columns.
|
||||
@@ -17,6 +17,15 @@ export function useProjectTaskColumns() {
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
Header: 'Header',
|
||||
accessor: TaskTimeAccessor,
|
||||
width: 100,
|
||||
className: 'name',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
}
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { isUndefined } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
import { ProgressBar } from '@blueprintjs/core';
|
||||
import { Intent, ProgressBar } from '@blueprintjs/core';
|
||||
|
||||
export function DetailFinancialSection({ children }) {
|
||||
return <FinancialSectionWrap>{children}</FinancialSectionWrap>;
|
||||
}
|
||||
|
||||
export function DetailFinancialCard({ label, value, children }) {
|
||||
interface DetailFinancialCardProps {
|
||||
label: string;
|
||||
value: number;
|
||||
description: string | JSX.Element;
|
||||
progressValue: number;
|
||||
}
|
||||
|
||||
export function DetailFinancialCard({
|
||||
label,
|
||||
value,
|
||||
description,
|
||||
progressValue,
|
||||
}: DetailFinancialCardProps) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FinancialSectionCard>
|
||||
<FinancialSectionCardContent>
|
||||
<FinancialCardTitle>{label}</FinancialCardTitle>
|
||||
<FinancialCardValue>{value}</FinancialCardValue>
|
||||
{children}
|
||||
</FinancialSectionCardContent>
|
||||
</FinancialSectionCard>
|
||||
</React.Fragment>
|
||||
<FinancialSectionCard>
|
||||
<FinancialCardTitle>{label}</FinancialCardTitle>
|
||||
<FinancialCardValue>{value}</FinancialCardValue>
|
||||
{description && <FinancialCartDesc>{description}</FinancialCartDesc>}
|
||||
{!isUndefined(progressValue) && (
|
||||
<FinancialProgressBar intent={Intent.NONE} value={progressValue} />
|
||||
)}
|
||||
</FinancialSectionCard>
|
||||
);
|
||||
}
|
||||
export const FinancialDescription = ({ childern }) => {
|
||||
return <FinancialCardText>{childern}</FinancialCardText>;
|
||||
};
|
||||
|
||||
export const FinancialProgressBar = ({ ...rest }) => {
|
||||
return <FinancialCardProgressBar animate={false} stripes={false} {...rest} />;
|
||||
@@ -41,34 +50,30 @@ const FinancialSectionCard = styled.div`
|
||||
border-radius: 3px;
|
||||
width: 230px;
|
||||
height: 116px;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #c8cad0; // #000a1e33 #f0f0f0
|
||||
border: 1px solid #c8cad0;
|
||||
gap: 6px;
|
||||
`;
|
||||
|
||||
const FinancialSectionCardContent = styled.div`
|
||||
margin: 16px;
|
||||
`;
|
||||
|
||||
const FinancialCardWrap = styled.div``;
|
||||
|
||||
const FinancialCardTitle = styled.div`
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
color: #203252;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
`;
|
||||
const FinancialCardValue = styled.div`
|
||||
font-size: 21px;
|
||||
line-height: 2rem;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const FinancialCardStatus = styled.div``;
|
||||
const FinancialCartDesc = styled.div`
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
export const FinancialCardText = styled.div`
|
||||
font-size: 13px;
|
||||
line-height: 1.5rem;
|
||||
color: #7b8195;
|
||||
`;
|
||||
export const FinancialCardProgressBar = styled(ProgressBar)`
|
||||
&.bp3-progress-bar {
|
||||
|
||||
@@ -6,9 +6,6 @@ const Schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.label(intl.get('project_task.schema.label.task_name'))
|
||||
.required(),
|
||||
charge_type: Yup.string()
|
||||
.label(intl.get('project_task.schema.label.charge_type'))
|
||||
.required(),
|
||||
rate: Yup.number()
|
||||
.label(intl.get('project_task.schema.label.rate'))
|
||||
.required(),
|
||||
|
||||
@@ -12,7 +12,6 @@ import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
|
||||
const defaultInitialValues = {
|
||||
name: '',
|
||||
charge_type: 'TIME',
|
||||
estimate_hours: '',
|
||||
rate: '0.00',
|
||||
};
|
||||
|
||||
@@ -8,10 +8,9 @@ import {
|
||||
Col,
|
||||
Row,
|
||||
FormattedMessage as T,
|
||||
InputPrependText,
|
||||
} from '@/components';
|
||||
import { EstimateAmount } from './utils';
|
||||
import { taskChargeOptions } from '../common/modalChargeOptions';
|
||||
import { ProjectTaskChargeTypeSelect } from '../../components';
|
||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||
import { compose } from '@/utils';
|
||||
|
||||
@@ -48,17 +47,12 @@ function ProjectTaskFormFields({
|
||||
{/*------------ Charge -----------*/}
|
||||
<Col xs={8}>
|
||||
<FFormGroup
|
||||
name={'charge_type'}
|
||||
name={'rate'}
|
||||
className={'form-group--select-list'}
|
||||
label={<T id={'project_task.dialog.charge'} />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<ProjectTaskChargeTypeSelect
|
||||
name="charge_type"
|
||||
items={taskChargeOptions}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
/>
|
||||
<InputPrependText text={'Hourly Price'} />
|
||||
<FInputGroup
|
||||
name="rate"
|
||||
disabled={values?.charge_type === 'non_chargable'}
|
||||
|
||||
@@ -35,7 +35,7 @@ function ProjectTaskFormFloatingActions({
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '75px' }}
|
||||
style={{ minWidth: '85px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'save'} />}
|
||||
|
||||
@@ -14,26 +14,12 @@ export function EstimateAmount({ baseCurrency }) {
|
||||
return (
|
||||
<EstimatedAmountBase>
|
||||
<EstimatedAmountContent>
|
||||
<Choose>
|
||||
<Choose.When condition={values?.charge_type === 'TIME'}>
|
||||
<T id={'project_task.dialog.estimated_amount'} />
|
||||
<EstimatedAmount>
|
||||
<Money amount={estimatedAmount} currency={baseCurrency} />
|
||||
</EstimatedAmount>
|
||||
</Choose.When>
|
||||
<Choose.When condition={values?.charge_type === 'FIXED'}>
|
||||
<T id={'project_task.dialog.total'} />
|
||||
<EstimatedAmount>
|
||||
<Money amount={values.rate} currency={baseCurrency} />
|
||||
</EstimatedAmount>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<T id={'project_task.dialog.total'} />
|
||||
<EstimatedAmount>
|
||||
<Money amount={0.0} currency={baseCurrency} />
|
||||
</EstimatedAmount>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
<EstimatedText>
|
||||
<T id={'project_task.dialog.estimated_amount'} />
|
||||
</EstimatedText>
|
||||
<EstimatedAmount>
|
||||
<Money amount={estimatedAmount} currency={baseCurrency} />
|
||||
</EstimatedAmount>
|
||||
</EstimatedAmountContent>
|
||||
</EstimatedAmountBase>
|
||||
);
|
||||
@@ -42,9 +28,7 @@ export function EstimateAmount({ baseCurrency }) {
|
||||
const EstimatedAmountBase = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: 14px;
|
||||
line-height: 1.5rem;
|
||||
opacity: 0.75;
|
||||
margin-bottom: 2rem;
|
||||
`;
|
||||
|
||||
const EstimatedAmountContent = styled.span`
|
||||
@@ -53,7 +37,11 @@ const EstimatedAmountContent = styled.span`
|
||||
`;
|
||||
|
||||
const EstimatedAmount = styled.span`
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const EstimatedText = styled.span`
|
||||
color: #607090;
|
||||
`;
|
||||
|
||||
@@ -10,9 +10,7 @@ import {
|
||||
import {
|
||||
Icon,
|
||||
Can,
|
||||
AdvancedFilterPopover,
|
||||
DashboardActionViewsList,
|
||||
DashboardFilterButton,
|
||||
DashboardRowsHeightButton,
|
||||
FormattedMessage as T,
|
||||
DashboardActionsBar,
|
||||
@@ -86,8 +84,6 @@ function ProjectsActionsBar({
|
||||
onClick={handleNewProjectBtnClick}
|
||||
/>
|
||||
</Can>
|
||||
{/* AdvancedFilterPopover */}
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'print-16'} iconSize={'16'} />}
|
||||
|
||||
@@ -66,14 +66,12 @@ function ProjectsDataTable({
|
||||
action: 'edit',
|
||||
});
|
||||
};
|
||||
|
||||
// Handle new task button click.
|
||||
const handleNewTaskButtonClick = (project) => {
|
||||
openDialog('project-task-form', {
|
||||
projectId: project.id,
|
||||
});
|
||||
};
|
||||
|
||||
// Local storage memorizing columns widths.
|
||||
const [initialColumnsWidths, , handleColumnResizing] =
|
||||
useMemorizedColumnsWidths(TABLES.PROJECTS);
|
||||
@@ -132,32 +130,7 @@ export default compose(
|
||||
const ProjectsTable = styled(DataTable)`
|
||||
.tbody {
|
||||
.tr .td {
|
||||
padding: 0.5rem 0.8rem;
|
||||
}
|
||||
.avatar.td {
|
||||
.cell-inner {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
padding: 0.75rem 0.8rem;
|
||||
}
|
||||
}
|
||||
.table-size--small {
|
||||
|
||||
@@ -40,6 +40,7 @@ function ProjectsList({
|
||||
<ProjectsActionsBar />
|
||||
<DashboardPageContent>
|
||||
<ProjectsViewTabs />
|
||||
|
||||
<DashboardContentTable>
|
||||
<ProjectsDataTable />
|
||||
</DashboardContentTable>
|
||||
|
||||
@@ -28,12 +28,12 @@ import { safeCallback, firstLettersArgs, calculateStatus } from '@/utils';
|
||||
export function ProjectStatus({ row }) {
|
||||
return (
|
||||
<ProjectStatusRoot>
|
||||
<ProjectStatusTaskAmount>{row.cost_estimate}</ProjectStatusTaskAmount>
|
||||
<ProjectStatusTaskAmount>{row.total_expenses_formatted}</ProjectStatusTaskAmount>
|
||||
<ProjectProgressBar
|
||||
animate={false}
|
||||
stripes={false}
|
||||
intent={Intent.PRIMARY}
|
||||
value={calculateStatus(100, row.cost_estimate)}
|
||||
value={calculateStatus(row.total_expenses, row.cost_estimate)}
|
||||
/>
|
||||
</ProjectStatusRoot>
|
||||
);
|
||||
@@ -44,37 +44,10 @@ export function ProjectStatus({ row }) {
|
||||
*/
|
||||
export const StatusAccessor = (row) => {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={row.status_formatted === 'InProgress'}>
|
||||
<ProjectStatus row={row} />
|
||||
</Choose.When>
|
||||
<Choose.When condition={row.status_formatted === 'Closed'}>
|
||||
<StatusTagWrap>
|
||||
<Tag minimal={true} intent={Intent.SUCCESS} round={true}>
|
||||
{row.status_formatted}
|
||||
</Tag>
|
||||
</StatusTagWrap>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTagWrap>
|
||||
<Tag minimal={true} round={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</StatusTagWrap>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
<ProjectStatus row={row} />
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Avatar cell.
|
||||
*/
|
||||
export const AvatarCell = ({ row: { original }, size }) => (
|
||||
<span className="avatar" data-size={size}>
|
||||
{firstLettersArgs(original?.contact_display_name, original?.name)}
|
||||
</span>
|
||||
);
|
||||
|
||||
/**
|
||||
* Table actions cell.
|
||||
*/
|
||||
@@ -135,20 +108,27 @@ export const ActionsMenu = ({
|
||||
* Projects accessor.
|
||||
*/
|
||||
export const ProjectsAccessor = (row) => (
|
||||
<ProjectItemsWrap>
|
||||
<ProjectItemsHeader>
|
||||
<ProjectItemContactName>
|
||||
{row.contact_display_name}
|
||||
</ProjectItemContactName>
|
||||
<ProjectItemProjectName>{row.name}</ProjectItemProjectName>
|
||||
</ProjectItemsHeader>
|
||||
<ProjectItemDescription>
|
||||
<FormatDate value={row.deadline_formatted} />
|
||||
{intl.get('projects.label.cost_estimate', {
|
||||
value: row.cost_estimate_formatted,
|
||||
})}
|
||||
</ProjectItemDescription>
|
||||
</ProjectItemsWrap>
|
||||
<ProjectName>
|
||||
<ProjectAvatar data-size="medium">
|
||||
{firstLettersArgs(row?.contact_display_name, row?.name)}
|
||||
</ProjectAvatar>
|
||||
|
||||
<ProjectItemsWrap>
|
||||
<ProjectItemsHeader>
|
||||
<ProjectItemContactName>
|
||||
{row.contact_display_name}
|
||||
</ProjectItemContactName>
|
||||
<ProjectItemProjectName>{row.name}</ProjectItemProjectName>
|
||||
</ProjectItemsHeader>
|
||||
|
||||
<ProjectItemDescription>
|
||||
<FormatDate value={row.deadline_formatted} />
|
||||
{intl.get('projects.label.cost_estimate', {
|
||||
value: row.cost_estimate_formatted,
|
||||
})}
|
||||
</ProjectItemDescription>
|
||||
</ProjectItemsWrap>
|
||||
</ProjectName>
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -157,16 +137,6 @@ export const ProjectsAccessor = (row) => (
|
||||
export const useProjectsListColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'avatar',
|
||||
Header: '',
|
||||
Cell: AvatarCell,
|
||||
className: 'avatar',
|
||||
width: 45,
|
||||
disableResizing: true,
|
||||
disableSortBy: true,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
Header: '',
|
||||
@@ -196,16 +166,18 @@ const ProjectItemsHeader = styled.div`
|
||||
`;
|
||||
|
||||
const ProjectItemContactName = styled.div`
|
||||
font-weight: 500;
|
||||
padding-right: 4px;
|
||||
font-weight: 600;
|
||||
padding-right: 10px;
|
||||
`;
|
||||
const ProjectItemProjectName = styled.div`
|
||||
color: #595b66;
|
||||
`;
|
||||
const ProjectItemProjectName = styled.div``;
|
||||
|
||||
const ProjectItemDescription = styled.div`
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
opacity: 0.75;
|
||||
margin-top: 0.2rem;
|
||||
margin-top: 0.3rem;
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
@@ -218,8 +190,7 @@ const ProjectStatusRoot = styled.div`
|
||||
|
||||
const ProjectStatusTaskAmount = styled.div`
|
||||
text-align: right;
|
||||
font-weight: 400;
|
||||
line-height: 1.5rem;
|
||||
font-size: 15px;
|
||||
margin-left: 20px;
|
||||
`;
|
||||
|
||||
@@ -244,3 +215,36 @@ const StatusTagWrap = styled.div`
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProjectName = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 15px;
|
||||
`;
|
||||
|
||||
export const Avatar = styled.div`
|
||||
display: inline-block;
|
||||
background: #adbcc9;
|
||||
border-radius: 8%;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
|
||||
&[data-size='medium'] {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
&[data-size='small'] {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
line-height: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProjectAvatar = styled(Avatar)`
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
`;
|
||||
Reference in New Issue
Block a user