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'),
);
const ProjectStatusAlert = React.lazy(() => import('./ProjectStatusAlert'));
/**
* Project alerts.
*/
@@ -15,4 +17,5 @@ export default [
{ name: 'project-delete', component: ProjectDeleteAlert },
{ name: 'project-task-delete', component: ProjectTaskDeleteAlert },
{ 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={'Deadline'}
value={<FormatDate value={'2022-06-08T22:00:00.000Z'} />}
value={<FormatDate value={project.deadline_formatted} />}
>
<FinancialCardText>4 days to go</FinancialCardText>
</DetailFinancialCard>

View File

@@ -1,9 +1,9 @@
import intl from 'react-intl-universal';
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',
},
];

View File

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

View File

@@ -10,21 +10,27 @@ import {
Intent,
ProgressBar,
} 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';
/**
* project status.
*/
export function ProjectStatus({ project }) {
export function ProjectStatus({ row }) {
return (
<ProjectStatusRoot>
<ProjectStatusTaskAmount>{project.task_amount}</ProjectStatusTaskAmount>
<ProjectStatusTaskAmount>{row.cost_estimate}</ProjectStatusTaskAmount>
<ProjectProgressBar
animate={false}
stripes={false}
// intent={Intent.PRIMARY}
value={calculateStatus(project.task_amount, project.cost_estimate)}
intent={Intent.PRIMARY}
value={calculateStatus(100, row.cost_estimate)}
/>
</ProjectStatusRoot>
);
@@ -33,22 +39,22 @@ export function ProjectStatus({ project }) {
/**
* status accessor.
*/
export const StatusAccessor = (project) => {
export const StatusAccessor = (row) => {
return (
<Choose>
<Choose.When condition={project.is_process}>
<ProjectStatus project={project} />
<Choose.When condition={row.status_formatted === 'InProgress'}>
<ProjectStatus row={row} />
</Choose.When>
<Choose.When condition={project.is_closed}>
<Choose.When condition={row.status_formatted === 'Closed'}>
<StatusTag minimal={true} intent={Intent.SUCCESS} round={true}>
<T id={'closed'} />
{row.status_formatted}
</StatusTag>
</Choose.When>
<Choose.When condition={project.is_draft}>
<StatusTag round={true} minimal={true}>
<Choose.Otherwise>
<StatusTag minimal={true} round={true}>
<T id={'draft'} />
</StatusTag>
</Choose.When>
</Choose.Otherwise>
</Choose>
);
};
@@ -67,7 +73,7 @@ export const AvatarCell = ({ row: { original }, size }) => (
*/
export const ActionsMenu = ({
row: { original },
payload: { onEdit, onDelete, onViewDetails, onNewTask },
payload: { onEdit, onDelete, onViewDetails, onNewTask, onStatus },
}) => (
<Menu>
<MenuItem
@@ -86,6 +92,19 @@ export const ActionsMenu = ({
text={intl.get('projects.action.new_task')}
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 />
<MenuItem
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();
return {

View File

@@ -66,7 +66,7 @@ export default function useApiRequest() {
if (lockedError) {
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 });
setLogout();
}
@@ -97,6 +97,10 @@ export default function useApiRequest() {
return http.put(`/api/${resource}`, params);
},
patch(resource, params, config) {
return http.patch(`/api/${resource}`, params, config);
},
delete(resource, params) {
return http.delete(`/api/${resource}`, params);
},

View File

@@ -2064,6 +2064,8 @@
"projects.dialog.edit_project": "Edit Project",
"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.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.description": "",
"projects.empty_status.action": "New Project",
@@ -2078,7 +2080,7 @@
"project_task.dialog.fixed_price": "Fixed price",
"project_task.dialog.non_chargeable": "Non-chargeable",
"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.delete_task": "Delete Task",
"project_task.rate": "{rate} /hour",
@@ -2106,15 +2108,17 @@
"project_details.label.purchases": "Purchases",
"project_details.label.sales": "Sales",
"project_details.label.journals": "Journals",
"project_details.new_expenses": "New Expenses",
"project_details.new_estimated_expenses": "New Estimated Expenses",
"timesheets.actions.delete_timesheet": "Delete",
"project_details.new_expense": "New Expense",
"project_details.new_estimated_expense": "New Estimated Expense",
"timesheets.action.delete_timesheet": "Delete",
"timesheets.action.edit_timesheet": "Edit Timesheet",
"timesheets.column.date": "Date",
"timesheets.column.task": "Task",
"timesheets.column.user": "User",
"timesheets.column.time": "Time",
"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.task": "Task",
"project_time_entry.dialog.description": "Description",