feat(projects): WIP projects service.

This commit is contained in:
a.bouhuolia
2022-10-02 21:33:23 +02:00
parent 900a237a52
commit 41db96d958
21 changed files with 1279 additions and 1252 deletions

2076
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -107,7 +107,11 @@
}, },
"proxy": "http://localhost:3000/", "proxy": "http://localhost:3000/",
"devDependencies": { "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": { "browserslist": {
"production": [ "production": [

View File

@@ -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, ability: ReportsAction.READ_AP_AGING_SUMMARY,
}, },
}, },
{
text: <T id={'project_profitability_summary'} />,
href: '/financial-reports/project-profitability-summary',
type: ISidebarMenuItemType.Link,
},
], ],
}, },
{ {

View File

@@ -1,5 +1,4 @@
// @ts-nocheck // @ts-nocheck
//@ts-nocheck
import React from 'react'; import React from 'react';
import { Overlay, OverlayProps } from '@blueprintjs/core'; import { Overlay, OverlayProps } from '@blueprintjs/core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@@ -19,7 +18,7 @@ export interface ISidebarOverlayProps {
export interface ISidebarOverlayItemProps { export interface ISidebarOverlayItemProps {
text: string | JSX.Element; text: string | JSX.Element;
href?: string; href: string;
onClick?: any; onClick?: any;
} }
@@ -85,7 +84,7 @@ function SidebarOverlayItem({ item }: SidebarOverlayItemProps) {
) : // ) : //
item.type === ISidebarMenuItemType.Link || item.type === ISidebarMenuItemType.Link ||
item.type === ISidebarMenuItemType.Dialog ? ( item.type === ISidebarMenuItemType.Dialog ? (
<SidebarOverlayItemLink text={item.text} onClick={item.onClick} /> <SidebarOverlayItemLink text={item.text} href={item.href} onClick={item.onClick} />
) : null; ) : null;
} }

View File

@@ -54,12 +54,11 @@ const ProjectProfitabilitySummaryDataTable = styled(ReportDataTable)`
padding-top: 0.32rem; padding-top: 0.32rem;
padding-bottom: 0.32rem; padding-bottom: 0.32rem;
} }
.tr.row_type--total .td { &.row_type--TOTAL .td {
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
font-weight: 500; font-weight: 500;
border-bottom: 3px double #000; border-bottom: 3px double #000;
} }
&:last-of-type .td { &:last-of-type .td {
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
} }

View File

@@ -84,6 +84,7 @@ function ProjectDetailActionsBar({
transactions={projectTranslations} transactions={projectTranslations}
onItemSelect={handleNewTransactionBtnClick} onItemSelect={handleNewTransactionBtnClick}
/> />
<NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'time-24'} iconSize={16} />} icon={<Icon icon={'time-24'} iconSize={16} />}

View File

@@ -21,7 +21,6 @@ export default function ProjectDetailTabs() {
renderActiveTabPanelOnly={true} renderActiveTabPanelOnly={true}
defaultSelectedTabId={'tasks'} defaultSelectedTabId={'tasks'}
> >
<Tab id="overview" title={intl.get('project_details.label.overview')} />
<Tab <Tab
id="tasks" id="tasks"
title={intl.get('project_details.label.tasks')} title={intl.get('project_details.label.tasks')}

View File

@@ -1,15 +1,10 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; 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 { FormatDate } from '@/components';
import { import {
DetailFinancialCard, DetailFinancialCard,
DetailFinancialSection, DetailFinancialSection,
FinancialProgressBar,
FinancialCardText,
} from './components'; } from './components';
import { calculateStatus } from '@/utils'; import { calculateStatus } from '@/utils';
import { useCalculateProject } from './utils'; import { useCalculateProject } from './utils';
@@ -18,20 +13,11 @@ import { useProjectDetailContext } from './ProjectDetailProvider';
/** /**
* Project details header. * Project details header.
* @returns
*/ */
export function ProjectDetailHeader() { export function ProjectDetailHeader() {
const { project } = useProjectDetailContext(); const { project } = useProjectDetailContext();
const { percentageOfInvoice, percentageOfExpense } = useCalculateProject(); 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 ( return (
<DetailFinancialSection> <DetailFinancialSection>
<DetailFinancialCard <DetailFinancialCard
@@ -41,32 +27,25 @@ export function ProjectDetailHeader() {
<DetailFinancialCard <DetailFinancialCard
label={intl.get('project_details.label.invoiced')} label={intl.get('project_details.label.invoiced')}
value={project.total_invoiced_formatted} value={project.total_invoiced_formatted}
> description={intl.get('project_details.label.of_project_estimate', {
<FinancialCardText> value: percentageOfInvoice,
{intl.get('project_details.label.of_project_estimate', { })}
value: percentageOfInvoice, progressValue={calculateStatus(
})} project.total_invoiced,
</FinancialCardText> project.cost_estimate,
<FinancialProgressBar )}
intent={Intent.NONE} />
value={calculateStatus(project.total_invoiced, project.cost_estimate)}
/>
</DetailFinancialCard>
<DetailFinancialCard <DetailFinancialCard
label={intl.get('project_details.label.time_expenses')} label={intl.get('project_details.label.time_expenses')}
value={project.total_expenses_formatted} value={project.total_expenses_formatted}
> description={intl.get('project_details.label.of_project_estimate', {
<FinancialCardText> value: percentageOfExpense,
{intl.get('project_details.label.of_project_estimate', { })}
value: percentageOfExpense, progressValue={calculateStatus(
})} project.total_expenses,
</FinancialCardText> project.cost_estimate,
<FinancialProgressBar )}
intent={Intent.NONE} />
value={calculateStatus(project.total_expenses, project.cost_estimate)}
/>
</DetailFinancialCard>
<DetailFinancialCard <DetailFinancialCard
label={intl.get('project_details.label.to_be_invoiced')} label={intl.get('project_details.label.to_be_invoiced')}
value={project.total_billable_formatted} value={project.total_billable_formatted}
@@ -74,9 +53,8 @@ export function ProjectDetailHeader() {
<DetailFinancialCard <DetailFinancialCard
label={'Deadline'} label={'Deadline'}
value={<FormatDate value={project.deadline_formatted} />} value={<FormatDate value={project.deadline_formatted} />}
> description={'4 days to go'}
<FinancialCardText>4 days to go</FinancialCardText> />
</DetailFinancialCard>
</DetailFinancialSection> </DetailFinancialSection>
); );
} }

View File

@@ -84,5 +84,21 @@ const ProjectTaksDataTable = styled(DataTable)`
display: none; 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;
}
} }
`; `;

View File

@@ -3,7 +3,7 @@ import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import styled from 'styled-components'; import styled from 'styled-components';
import { Icon, If, Choose, FormattedMessage as T } from '@/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'; import { safeCallback } from '@/utils';
/** /**
@@ -70,25 +70,72 @@ export function TaskAccessor(task) {
); );
} }
const TaskRoot = styled.div` export function TaskTimeAccessor(task) {
margin-left: 12px; 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` const TaskHeader = styled.div`
display: flex; display: flex;
align-items: baseline; align-items: baseline;
flex-flow: wrap; flex-flow: wrap;
`; `;
const TaskTitle = styled.span` const TaskTitle = styled.span`
font-weight: 500; font-weight: 600;
line-height: 1.5rem;
`; `;
const TaskContent = styled.div` const TaskContent = styled.div`
display: block; display: block;
white-space: nowrap; white-space: nowrap;
font-size: 13px; font-size: 13px;
opacity: 0.75; opacity: 0.75;
margin-bottom: 0.1rem; margin-top: 0.25rem;
line-height: 1.2rem;
`; `;
const TaskDescription = styled.span` const TaskDescription = styled.span`
margin: 0.3rem; margin: 0.3rem;

View File

@@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { TaskAccessor } from './components'; import { TaskAccessor, TaskTimeAccessor } from './components';
/** /**
* Retrieve project tasks list columns. * Retrieve project tasks list columns.
@@ -17,6 +17,15 @@ export function useProjectTaskColumns() {
clickable: true, clickable: true,
textOverview: true, textOverview: true,
}, },
{
id: 'actions',
Header: 'Header',
accessor: TaskTimeAccessor,
width: 100,
className: 'name',
clickable: true,
textOverview: true,
}
], ],
[], [],
); );

View File

@@ -1,28 +1,37 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { isUndefined } from 'lodash';
import styled from 'styled-components'; import styled from 'styled-components';
import { ProgressBar } from '@blueprintjs/core'; import { Intent, ProgressBar } from '@blueprintjs/core';
export function DetailFinancialSection({ children }) { export function DetailFinancialSection({ children }) {
return <FinancialSectionWrap>{children}</FinancialSectionWrap>; 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 ( return (
<React.Fragment> <FinancialSectionCard>
<FinancialSectionCard> <FinancialCardTitle>{label}</FinancialCardTitle>
<FinancialSectionCardContent> <FinancialCardValue>{value}</FinancialCardValue>
<FinancialCardTitle>{label}</FinancialCardTitle> {description && <FinancialCartDesc>{description}</FinancialCartDesc>}
<FinancialCardValue>{value}</FinancialCardValue> {!isUndefined(progressValue) && (
{children} <FinancialProgressBar intent={Intent.NONE} value={progressValue} />
</FinancialSectionCardContent> )}
</FinancialSectionCard> </FinancialSectionCard>
</React.Fragment>
); );
} }
export const FinancialDescription = ({ childern }) => {
return <FinancialCardText>{childern}</FinancialCardText>;
};
export const FinancialProgressBar = ({ ...rest }) => { export const FinancialProgressBar = ({ ...rest }) => {
return <FinancialCardProgressBar animate={false} stripes={false} {...rest} />; return <FinancialCardProgressBar animate={false} stripes={false} {...rest} />;
@@ -41,34 +50,30 @@ const FinancialSectionCard = styled.div`
border-radius: 3px; border-radius: 3px;
width: 230px; width: 230px;
height: 116px; height: 116px;
padding: 16px;
background-color: #fff; 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` const FinancialCardTitle = styled.div`
font-size: 15px; font-size: 14px;
color: #000; color: #203252;
white-space: nowrap; white-space: nowrap;
font-weight: 400; font-weight: 400;
line-height: 1.5rem;
`; `;
const FinancialCardValue = styled.div` const FinancialCardValue = styled.div`
font-size: 21px; font-size: 20px;
line-height: 2rem; font-weight: 600;
font-weight: 700;
`; `;
const FinancialCardStatus = styled.div``; const FinancialCartDesc = styled.div`
font-size: 12px;
`;
export const FinancialCardText = styled.div` export const FinancialCardText = styled.div`
font-size: 13px; font-size: 13px;
line-height: 1.5rem; color: #7b8195;
`; `;
export const FinancialCardProgressBar = styled(ProgressBar)` export const FinancialCardProgressBar = styled(ProgressBar)`
&.bp3-progress-bar { &.bp3-progress-bar {

View File

@@ -6,9 +6,6 @@ const Schema = Yup.object().shape({
name: Yup.string() name: Yup.string()
.label(intl.get('project_task.schema.label.task_name')) .label(intl.get('project_task.schema.label.task_name'))
.required(), .required(),
charge_type: Yup.string()
.label(intl.get('project_task.schema.label.charge_type'))
.required(),
rate: Yup.number() rate: Yup.number()
.label(intl.get('project_task.schema.label.rate')) .label(intl.get('project_task.schema.label.rate'))
.required(), .required(),

View File

@@ -12,7 +12,6 @@ import withDialogActions from '@/containers/Dialog/withDialogActions';
const defaultInitialValues = { const defaultInitialValues = {
name: '', name: '',
charge_type: 'TIME',
estimate_hours: '', estimate_hours: '',
rate: '0.00', rate: '0.00',
}; };

View File

@@ -8,10 +8,9 @@ import {
Col, Col,
Row, Row,
FormattedMessage as T, FormattedMessage as T,
InputPrependText,
} from '@/components'; } from '@/components';
import { EstimateAmount } from './utils'; import { EstimateAmount } from './utils';
import { taskChargeOptions } from '../common/modalChargeOptions';
import { ProjectTaskChargeTypeSelect } from '../../components';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization'; import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import { compose } from '@/utils'; import { compose } from '@/utils';
@@ -48,17 +47,12 @@ function ProjectTaskFormFields({
{/*------------ Charge -----------*/} {/*------------ Charge -----------*/}
<Col xs={8}> <Col xs={8}>
<FFormGroup <FFormGroup
name={'charge_type'} name={'rate'}
className={'form-group--select-list'} className={'form-group--select-list'}
label={<T id={'project_task.dialog.charge'} />} label={<T id={'project_task.dialog.charge'} />}
> >
<ControlGroup> <ControlGroup>
<ProjectTaskChargeTypeSelect <InputPrependText text={'Hourly Price'} />
name="charge_type"
items={taskChargeOptions}
popoverProps={{ minimal: true }}
filterable={false}
/>
<FInputGroup <FInputGroup
name="rate" name="rate"
disabled={values?.charge_type === 'non_chargable'} disabled={values?.charge_type === 'non_chargable'}

View File

@@ -35,7 +35,7 @@ function ProjectTaskFormFloatingActions({
<Button <Button
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
loading={isSubmitting} loading={isSubmitting}
style={{ minWidth: '75px' }} style={{ minWidth: '85px' }}
type="submit" type="submit"
> >
{<T id={'save'} />} {<T id={'save'} />}

View File

@@ -14,26 +14,12 @@ export function EstimateAmount({ baseCurrency }) {
return ( return (
<EstimatedAmountBase> <EstimatedAmountBase>
<EstimatedAmountContent> <EstimatedAmountContent>
<Choose> <EstimatedText>
<Choose.When condition={values?.charge_type === 'TIME'}> <T id={'project_task.dialog.estimated_amount'} />
<T id={'project_task.dialog.estimated_amount'} /> </EstimatedText>
<EstimatedAmount> <EstimatedAmount>
<Money amount={estimatedAmount} currency={baseCurrency} /> <Money amount={estimatedAmount} currency={baseCurrency} />
</EstimatedAmount> </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>
</EstimatedAmountContent> </EstimatedAmountContent>
</EstimatedAmountBase> </EstimatedAmountBase>
); );
@@ -42,9 +28,7 @@ export function EstimateAmount({ baseCurrency }) {
const EstimatedAmountBase = styled.div` const EstimatedAmountBase = styled.div`
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
font-size: 14px; margin-bottom: 2rem;
line-height: 1.5rem;
opacity: 0.75;
`; `;
const EstimatedAmountContent = styled.span` const EstimatedAmountContent = styled.span`
@@ -53,7 +37,11 @@ const EstimatedAmountContent = styled.span`
`; `;
const EstimatedAmount = styled.span` const EstimatedAmount = styled.span`
font-size: 15px; font-size: 18px;
font-weight: 700; font-weight: 600;
margin-left: 10px; margin-left: 10px;
`; `;
const EstimatedText = styled.span`
color: #607090;
`;

View File

@@ -10,9 +10,7 @@ import {
import { import {
Icon, Icon,
Can, Can,
AdvancedFilterPopover,
DashboardActionViewsList, DashboardActionViewsList,
DashboardFilterButton,
DashboardRowsHeightButton, DashboardRowsHeightButton,
FormattedMessage as T, FormattedMessage as T,
DashboardActionsBar, DashboardActionsBar,
@@ -86,8 +84,6 @@ function ProjectsActionsBar({
onClick={handleNewProjectBtnClick} onClick={handleNewProjectBtnClick}
/> />
</Can> </Can>
{/* AdvancedFilterPopover */}
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'print-16'} iconSize={'16'} />} icon={<Icon icon={'print-16'} iconSize={'16'} />}

View File

@@ -66,14 +66,12 @@ function ProjectsDataTable({
action: 'edit', action: 'edit',
}); });
}; };
// Handle new task button click. // Handle new task button click.
const handleNewTaskButtonClick = (project) => { const handleNewTaskButtonClick = (project) => {
openDialog('project-task-form', { openDialog('project-task-form', {
projectId: project.id, projectId: project.id,
}); });
}; };
// Local storage memorizing columns widths. // Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] = const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.PROJECTS); useMemorizedColumnsWidths(TABLES.PROJECTS);
@@ -132,32 +130,7 @@ export default compose(
const ProjectsTable = styled(DataTable)` const ProjectsTable = styled(DataTable)`
.tbody { .tbody {
.tr .td { .tr .td {
padding: 0.5rem 0.8rem; padding: 0.75rem 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;
}
}
}
} }
} }
.table-size--small { .table-size--small {

View File

@@ -40,6 +40,7 @@ function ProjectsList({
<ProjectsActionsBar /> <ProjectsActionsBar />
<DashboardPageContent> <DashboardPageContent>
<ProjectsViewTabs /> <ProjectsViewTabs />
<DashboardContentTable> <DashboardContentTable>
<ProjectsDataTable /> <ProjectsDataTable />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -28,12 +28,12 @@ import { safeCallback, firstLettersArgs, calculateStatus } from '@/utils';
export function ProjectStatus({ row }) { export function ProjectStatus({ row }) {
return ( return (
<ProjectStatusRoot> <ProjectStatusRoot>
<ProjectStatusTaskAmount>{row.cost_estimate}</ProjectStatusTaskAmount> <ProjectStatusTaskAmount>{row.total_expenses_formatted}</ProjectStatusTaskAmount>
<ProjectProgressBar <ProjectProgressBar
animate={false} animate={false}
stripes={false} stripes={false}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
value={calculateStatus(100, row.cost_estimate)} value={calculateStatus(row.total_expenses, row.cost_estimate)}
/> />
</ProjectStatusRoot> </ProjectStatusRoot>
); );
@@ -44,37 +44,10 @@ export function ProjectStatus({ row }) {
*/ */
export const StatusAccessor = (row) => { export const StatusAccessor = (row) => {
return ( return (
<Choose> <ProjectStatus row={row} />
<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>
); );
}; };
/**
* 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. * Table actions cell.
*/ */
@@ -135,20 +108,27 @@ export const ActionsMenu = ({
* Projects accessor. * Projects accessor.
*/ */
export const ProjectsAccessor = (row) => ( export const ProjectsAccessor = (row) => (
<ProjectItemsWrap> <ProjectName>
<ProjectItemsHeader> <ProjectAvatar data-size="medium">
<ProjectItemContactName> {firstLettersArgs(row?.contact_display_name, row?.name)}
{row.contact_display_name} </ProjectAvatar>
</ProjectItemContactName>
<ProjectItemProjectName>{row.name}</ProjectItemProjectName> <ProjectItemsWrap>
</ProjectItemsHeader> <ProjectItemsHeader>
<ProjectItemDescription> <ProjectItemContactName>
<FormatDate value={row.deadline_formatted} /> {row.contact_display_name}
{intl.get('projects.label.cost_estimate', { </ProjectItemContactName>
value: row.cost_estimate_formatted, <ProjectItemProjectName>{row.name}</ProjectItemProjectName>
})} </ProjectItemsHeader>
</ProjectItemDescription>
</ProjectItemsWrap> <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 = () => { export const useProjectsListColumns = () => {
return React.useMemo( return React.useMemo(
() => [ () => [
{
id: 'avatar',
Header: '',
Cell: AvatarCell,
className: 'avatar',
width: 45,
disableResizing: true,
disableSortBy: true,
clickable: true,
},
{ {
id: 'name', id: 'name',
Header: '', Header: '',
@@ -196,16 +166,18 @@ const ProjectItemsHeader = styled.div`
`; `;
const ProjectItemContactName = styled.div` const ProjectItemContactName = styled.div`
font-weight: 500; font-weight: 600;
padding-right: 4px; padding-right: 10px;
`;
const ProjectItemProjectName = styled.div`
color: #595b66;
`; `;
const ProjectItemProjectName = styled.div``;
const ProjectItemDescription = styled.div` const ProjectItemDescription = styled.div`
display: inline-block; display: inline-block;
font-size: 13px; font-size: 13px;
opacity: 0.75; opacity: 0.75;
margin-top: 0.2rem; margin-top: 0.3rem;
line-height: 1; line-height: 1;
`; `;
@@ -218,8 +190,7 @@ const ProjectStatusRoot = styled.div`
const ProjectStatusTaskAmount = styled.div` const ProjectStatusTaskAmount = styled.div`
text-align: right; text-align: right;
font-weight: 400; font-size: 15px;
line-height: 1.5rem;
margin-left: 20px; margin-left: 20px;
`; `;
@@ -244,3 +215,36 @@ const StatusTagWrap = styled.div`
text-align: center; 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;
`;