fix: add projects status

This commit is contained in:
elforjani13
2022-08-06 12:37:40 +02:00
parent 93da3ed41d
commit 3753097ea6
9 changed files with 157 additions and 24 deletions

View File

@@ -0,0 +1,75 @@
import React from 'react';
import intl from 'react-intl-universal';
import { FormattedMessage as T, FormattedHTMLMessage } from '@/components';
import { Intent, Alert } from '@blueprintjs/core';
import { AppToaster } from '@/components';
import { useProjectStatus } from '../../hooks';
import withAlertStoreConnect from '@/containers/Alert/withAlertStoreConnect';
import withAlertActions from '@/containers/Alert/withAlertActions';
import { compose } from '@/utils';
/**
* Project status alert.
* @returns
*/
function ProjectStatusAlert({
name,
isOpen,
payload: { projectId, status },
// #withAlertActions
closeAlert,
}) {
const { mutateAsync: statusProjectMutate, isLoading } = useProjectStatus();
// handle cancel alert.
const handleCancelAlert = () => {
closeAlert(name);
};
// handle confirm alert.
const handleConfirmAlert = () => {
const values = {
status: status !== 'InProgress' ? 'InProgress' : 'Closed',
};
statusProjectMutate([projectId, values])
.then(() => {
AppToaster.show({
message: intl.get('projects.alert.status_message'),
intent: Intent.SUCCESS,
});
})
.catch(
({
response: {
data: { errors },
},
}) => {},
)
.finally(() => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'save'} />}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancelAlert}
onConfirm={handleConfirmAlert}
loading={isLoading}
>
<FormattedHTMLMessage id="projects.alert.are_you_sure_you_want" />
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(ProjectStatusAlert);

View File

@@ -8,6 +8,8 @@ const ProjectTimesheetDeleteAlert = React.lazy(
() => import('./ProjectTimesheetDeleteAlert'), () => import('./ProjectTimesheetDeleteAlert'),
); );
const ProjectStatusAlert = React.lazy(() => import('./ProjectStatusAlert'));
/** /**
* Project alerts. * Project alerts.
*/ */
@@ -15,4 +17,5 @@ export default [
{ name: 'project-delete', component: ProjectDeleteAlert }, { name: 'project-delete', component: ProjectDeleteAlert },
{ name: 'project-task-delete', component: ProjectTaskDeleteAlert }, { name: 'project-task-delete', component: ProjectTaskDeleteAlert },
{ name: 'project-timesheet-delete', component: ProjectTimesheetDeleteAlert }, { name: 'project-timesheet-delete', component: ProjectTimesheetDeleteAlert },
{ name: 'project-status', component: ProjectStatusAlert },
]; ];

View File

@@ -35,7 +35,7 @@ export function ProjectTasksHeader() {
<DetailFinancialCard label={'To be invoiced'} value={'3.14'} /> <DetailFinancialCard label={'To be invoiced'} value={'3.14'} />
<DetailFinancialCard <DetailFinancialCard
label={'Deadline'} label={'Deadline'}
value={<FormatDate value={'2022-06-08T22:00:00.000Z'} />} value={<FormatDate value={project.deadline_formatted} />}
> >
<FinancialCardText>4 days to go</FinancialCardText> <FinancialCardText>4 days to go</FinancialCardText>
</DetailFinancialCard> </DetailFinancialCard>

View File

@@ -1,9 +1,9 @@
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
export const projectTranslations = [ export const projectTranslations = [
{ name: intl.get('project_details.new_expenses'), path: 'expense' }, { name: intl.get('project_details.new_expense'), path: 'expense' },
{ {
name: intl.get('project_details.new_estimated_expenses'), name: intl.get('project_details.new_estimated_expense'),
path: 'estimated_expense', path: 'estimated_expense',
}, },
]; ];

View File

@@ -43,6 +43,10 @@ function ProjectsDataTable({
openAlert('project-delete', { projectId: id }); openAlert('project-delete', { projectId: id });
}; };
const handleProjectStatus = ({ id, status_formatted }) => {
openAlert('project-status', { projectId: id, status: status_formatted });
};
// Retrieve projects table columns. // Retrieve projects table columns.
const columns = useProjectsListColumns(); const columns = useProjectsListColumns();
@@ -109,6 +113,7 @@ function ProjectsDataTable({
onEdit: handleEditProject, onEdit: handleEditProject,
onDelete: handleDeleteProject, onDelete: handleDeleteProject,
onNewTask: handleNewTaskButtonClick, onNewTask: handleNewTaskButtonClick,
onStatus: handleProjectStatus,
}} }}
/> />
); );

View File

@@ -10,21 +10,27 @@ import {
Intent, Intent,
ProgressBar, ProgressBar,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { Icon, FormatDate, Choose, FormattedMessage as T } from '@/components'; import {
Icon,
FormatDate,
Choose,
If,
FormattedMessage as T,
} from '@/components';
import { safeCallback, firstLettersArgs, calculateStatus } from '@/utils'; import { safeCallback, firstLettersArgs, calculateStatus } from '@/utils';
/** /**
* project status. * project status.
*/ */
export function ProjectStatus({ project }) { export function ProjectStatus({ row }) {
return ( return (
<ProjectStatusRoot> <ProjectStatusRoot>
<ProjectStatusTaskAmount>{project.task_amount}</ProjectStatusTaskAmount> <ProjectStatusTaskAmount>{row.cost_estimate}</ProjectStatusTaskAmount>
<ProjectProgressBar <ProjectProgressBar
animate={false} animate={false}
stripes={false} stripes={false}
// intent={Intent.PRIMARY} intent={Intent.PRIMARY}
value={calculateStatus(project.task_amount, project.cost_estimate)} value={calculateStatus(100, row.cost_estimate)}
/> />
</ProjectStatusRoot> </ProjectStatusRoot>
); );
@@ -33,22 +39,22 @@ export function ProjectStatus({ project }) {
/** /**
* status accessor. * status accessor.
*/ */
export const StatusAccessor = (project) => { export const StatusAccessor = (row) => {
return ( return (
<Choose> <Choose>
<Choose.When condition={project.is_process}> <Choose.When condition={row.status_formatted === 'InProgress'}>
<ProjectStatus project={project} /> <ProjectStatus row={row} />
</Choose.When> </Choose.When>
<Choose.When condition={project.is_closed}> <Choose.When condition={row.status_formatted === 'Closed'}>
<StatusTag minimal={true} intent={Intent.SUCCESS} round={true}> <StatusTag minimal={true} intent={Intent.SUCCESS} round={true}>
<T id={'closed'} /> {row.status_formatted}
</StatusTag> </StatusTag>
</Choose.When> </Choose.When>
<Choose.When condition={project.is_draft}> <Choose.Otherwise>
<StatusTag round={true} minimal={true}> <StatusTag minimal={true} round={true}>
<T id={'draft'} /> <T id={'draft'} />
</StatusTag> </StatusTag>
</Choose.When> </Choose.Otherwise>
</Choose> </Choose>
); );
}; };
@@ -67,7 +73,7 @@ export const AvatarCell = ({ row: { original }, size }) => (
*/ */
export const ActionsMenu = ({ export const ActionsMenu = ({
row: { original }, row: { original },
payload: { onEdit, onDelete, onViewDetails, onNewTask }, payload: { onEdit, onDelete, onViewDetails, onNewTask, onStatus },
}) => ( }) => (
<Menu> <Menu>
<MenuItem <MenuItem
@@ -86,6 +92,19 @@ export const ActionsMenu = ({
text={intl.get('projects.action.new_task')} text={intl.get('projects.action.new_task')}
onClick={safeCallback(onNewTask, original)} onClick={safeCallback(onNewTask, original)}
/> />
<MenuItem text={'Status'} icon={<Icon icon="plus" />}>
<If condition={original.status !== 'InProgress'}>
<MenuItem
text={'InProgress'}
onClick={safeCallback(onStatus, original)}
/>
</If>
<If condition={original.status !== 'Closed'}>
<MenuItem text={'Closed'} onClick={safeCallback(onStatus, original)} />
</If>
</MenuItem>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
text={intl.get('projects.action.delete_project')} text={intl.get('projects.action.delete_project')}

View File

@@ -113,7 +113,30 @@ export function useProjects(query, props) {
); );
} }
export function useRefreshInvoices() { /**
*
* @param props
* @returns
*/
export function useProjectStatus(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
return useMutation(
([id, values]) => apiRequest.patch(`projects/${id}/status`, values),
{
onSuccess: (res, [id, values]) => {
// Invalidate specific project.
queryClient.invalidateQueries([t.PROJECT, id]);
commonInvalidateQueries(queryClient);
},
...props,
},
);
}
export function useRefreshProjects() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return { return {

View File

@@ -66,7 +66,7 @@ export default function useApiRequest() {
if (lockedError) { if (lockedError) {
setGlobalErrors({ transactionsLocked: { ...lockedError.data } }); setGlobalErrors({ transactionsLocked: { ...lockedError.data } });
} }
if (data.errors.find(e => e.type === 'USER_INACTIVE')) { if (data.errors.find((e) => e.type === 'USER_INACTIVE')) {
setGlobalErrors({ userInactive: true }); setGlobalErrors({ userInactive: true });
setLogout(); setLogout();
} }
@@ -97,6 +97,10 @@ export default function useApiRequest() {
return http.put(`/api/${resource}`, params); return http.put(`/api/${resource}`, params);
}, },
patch(resource, params, config) {
return http.patch(`/api/${resource}`, params, config);
},
delete(resource, params) { delete(resource, params) {
return http.delete(`/api/${resource}`, params); return http.delete(`/api/${resource}`, params);
}, },

View File

@@ -2064,6 +2064,8 @@
"projects.dialog.edit_project": "Edit Project", "projects.dialog.edit_project": "Edit Project",
"projects.alert.delete_message": "The deleted project has been deleted successfully.", "projects.alert.delete_message": "The deleted project has been deleted successfully.",
"projects.alert.once_delete_this_project": "Once you delete this project, you won't be able to restore it later. Are you sure you want to delete this project?", "projects.alert.once_delete_this_project": "Once you delete this project, you won't be able to restore it later. Are you sure you want to delete this project?",
"projects.alert.status_message": "The project has been edited successfully.",
"projects.alert.are_you_sure_you_want":"Are you sure you want to edit this project?",
"projects.empty_status.title": "", "projects.empty_status.title": "",
"projects.empty_status.description": "", "projects.empty_status.description": "",
"projects.empty_status.action": "New Project", "projects.empty_status.action": "New Project",
@@ -2078,7 +2080,7 @@
"project_task.dialog.fixed_price": "Fixed price", "project_task.dialog.fixed_price": "Fixed price",
"project_task.dialog.non_chargeable": "Non-chargeable", "project_task.dialog.non_chargeable": "Non-chargeable",
"project_task.dialog.success_message": "The task has been created successfully.", "project_task.dialog.success_message": "The task has been created successfully.",
"project_task.dialog.edit_success_message": "The task has been created successfully.", "project_task.dialog.edit_success_message": "The task has been edited successfully.",
"project_task.action.edit_task": "Edit Task", "project_task.action.edit_task": "Edit Task",
"project_task.action.delete_task": "Delete Task", "project_task.action.delete_task": "Delete Task",
"project_task.rate": "{rate} /hour", "project_task.rate": "{rate} /hour",
@@ -2106,15 +2108,17 @@
"project_details.label.purchases": "Purchases", "project_details.label.purchases": "Purchases",
"project_details.label.sales": "Sales", "project_details.label.sales": "Sales",
"project_details.label.journals": "Journals", "project_details.label.journals": "Journals",
"project_details.new_expenses": "New Expenses", "project_details.new_expense": "New Expense",
"project_details.new_estimated_expenses": "New Estimated Expenses", "project_details.new_estimated_expense": "New Estimated Expense",
"timesheets.actions.delete_timesheet": "Delete", "timesheets.action.delete_timesheet": "Delete",
"timesheets.action.edit_timesheet": "Edit Timesheet",
"timesheets.column.date": "Date", "timesheets.column.date": "Date",
"timesheets.column.task": "Task", "timesheets.column.task": "Task",
"timesheets.column.user": "User", "timesheets.column.user": "User",
"timesheets.column.time": "Time", "timesheets.column.time": "Time",
"timesheets.column.billing_status": "Billing Status", "timesheets.column.billing_status": "Billing Status",
"project_time_entry.dialog.label": "New Time Entry", "project_time_entry.dialog.new_time_entry": "New Time Entry",
"project_time_entry.dialog.edit_time_entry": "Edit Time Entry",
"project_time_entry.dialog.project": "Project", "project_time_entry.dialog.project": "Project",
"project_time_entry.dialog.task": "Task", "project_time_entry.dialog.task": "Task",
"project_time_entry.dialog.description": "Description", "project_time_entry.dialog.description": "Description",