From 1b13b988992ad40c5eb7193feece1838741dc770 Mon Sep 17 00:00:00 2001
From: elforjani13 <39470382+elforjani13@users.noreply.github.com>
Date: Sat, 30 Jul 2022 15:10:23 +0200
Subject: [PATCH] feat: add api project timesheet.
---
.../ProjectAlerts/ProjectTaskDeleteAlert.tsx | 6 +-
.../ProjectTimesheetDeleteAlert.tsx | 78 ++++++++++++
.../containers/ProjectAlerts/index.ts | 4 +
.../ProjectDetailActionsBar.tsx | 1 -
.../ProjectTasks/ProjectTaskProvider.tsx | 2 +-
.../ProjectTimesheetsProvider.tsx | 32 +++++
.../ProjectTimeSheets/index.tsx | 5 +-
.../ProjectTaskFormProvider.tsx | 3 +
.../ProjectTimeEntryForm.schema.tsx | 12 +-
.../ProjectTimeEntryForm.tsx | 26 +++-
.../ProjectTimeEntryFormFields.tsx | 7 +-
.../ProjectTimeEntryFormFloatingActions.tsx | 6 +-
.../ProjectTimeEntryFormProvider.tsx | 25 +++-
src/containers/Projects/hooks/index.ts | 3 +-
.../Projects/hooks/projectTimeEntry.tsx | 120 ++++++++++++++++++
.../Projects/hooks/projectsTask.tsx | 19 ++-
src/containers/Projects/hooks/type.ts | 20 ++-
src/lang/en/index.json | 4 +
18 files changed, 334 insertions(+), 39 deletions(-)
create mode 100644 src/containers/Projects/containers/ProjectAlerts/ProjectTimesheetDeleteAlert.tsx
create mode 100644 src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/ProjectTimesheetsProvider.tsx
create mode 100644 src/containers/Projects/hooks/projectTimeEntry.tsx
diff --git a/src/containers/Projects/containers/ProjectAlerts/ProjectTaskDeleteAlert.tsx b/src/containers/Projects/containers/ProjectAlerts/ProjectTaskDeleteAlert.tsx
index 9a320da76..4cbfa3b21 100644
--- a/src/containers/Projects/containers/ProjectAlerts/ProjectTaskDeleteAlert.tsx
+++ b/src/containers/Projects/containers/ProjectAlerts/ProjectTaskDeleteAlert.tsx
@@ -32,8 +32,8 @@ function ProjectTaskDeleteAlert({
closeAlert(name);
};
- // handleConfirm delete project
- const handleConfirmProjectDelete = () => {
+ // handleConfirm delete project task
+ const handleConfirmProjectTaskDelete = () => {
deleteProjectTaskMutate(taskId)
.then(() => {
AppToaster.show({
@@ -61,7 +61,7 @@ function ProjectTaskDeleteAlert({
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelDeleteAlert}
- onConfirm={handleConfirmProjectDelete}
+ onConfirm={handleConfirmProjectTaskDelete}
loading={isLoading}
>
diff --git a/src/containers/Projects/containers/ProjectAlerts/ProjectTimesheetDeleteAlert.tsx b/src/containers/Projects/containers/ProjectAlerts/ProjectTimesheetDeleteAlert.tsx
new file mode 100644
index 000000000..7d9789270
--- /dev/null
+++ b/src/containers/Projects/containers/ProjectAlerts/ProjectTimesheetDeleteAlert.tsx
@@ -0,0 +1,78 @@
+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 { useDeleteProjectTimeEntry } from '../../hooks';
+
+import withAlertStoreConnect from '@/containers/Alert/withAlertStoreConnect';
+import withAlertActions from '@/containers/Alert/withAlertActions';
+
+import { compose } from '@/utils';
+
+/**
+ * Project timesheet delete alert.
+ * @returns
+ */
+function ProjectTimesheetDeleteAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { timesheetId },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: deleteProjectTimeEntryMutate, isLoading } =
+ useDeleteProjectTimeEntry();
+
+ // handle cancel delete alert.
+ const handleCancelDeleteAlert = () => {
+ closeAlert(name);
+ };
+
+ // handleConfirm delete project time sheet.
+ const handleConfirmProjectTimesheetDelete = () => {
+ deleteProjectTimeEntryMutate(timesheetId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('project_time_entry.alert.delete_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch(
+ ({
+ response: {
+ data: { errors },
+ },
+ }) => {},
+ )
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelDeleteAlert}
+ onConfirm={handleConfirmProjectTimesheetDelete}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(ProjectTimesheetDeleteAlert);
diff --git a/src/containers/Projects/containers/ProjectAlerts/index.ts b/src/containers/Projects/containers/ProjectAlerts/index.ts
index eecc2e6bb..17a6acf14 100644
--- a/src/containers/Projects/containers/ProjectAlerts/index.ts
+++ b/src/containers/Projects/containers/ProjectAlerts/index.ts
@@ -4,6 +4,9 @@ const ProjectDeleteAlert = React.lazy(() => import('./ProjectDeleteAlert'));
const ProjectTaskDeleteAlert = React.lazy(
() => import('./ProjectTaskDeleteAlert'),
);
+const ProjectTimesheetDeleteAlert = React.lazy(
+ () => import('./ProjectTimesheetDeleteAlert'),
+);
/**
* Project alerts.
@@ -11,4 +14,5 @@ const ProjectTaskDeleteAlert = React.lazy(
export default [
{ name: 'project-delete', component: ProjectDeleteAlert },
{ name: 'project-task-delete', component: ProjectTaskDeleteAlert },
+ { name: 'project-timesheet-delete', component: ProjectTimesheetDeleteAlert },
];
diff --git a/src/containers/Projects/containers/ProjectDetails/ProjectDetailActionsBar.tsx b/src/containers/Projects/containers/ProjectDetails/ProjectDetailActionsBar.tsx
index 560ddf68c..be33fc8a3 100644
--- a/src/containers/Projects/containers/ProjectDetails/ProjectDetailActionsBar.tsx
+++ b/src/containers/Projects/containers/ProjectDetails/ProjectDetailActionsBar.tsx
@@ -35,7 +35,6 @@ function ProjectDetailActionsBar({
// #withSettingsActions
addSetting,
}) {
- const history = useHistory();
const { projectId } = useProjectDetailContext();
// Handle new transaction button click.
diff --git a/src/containers/Projects/containers/ProjectDetails/ProjectTasks/ProjectTaskProvider.tsx b/src/containers/Projects/containers/ProjectDetails/ProjectTasks/ProjectTaskProvider.tsx
index 8b30aba57..840ca0804 100644
--- a/src/containers/Projects/containers/ProjectDetails/ProjectTasks/ProjectTaskProvider.tsx
+++ b/src/containers/Projects/containers/ProjectDetails/ProjectTasks/ProjectTaskProvider.tsx
@@ -26,7 +26,7 @@ function ProjectTaskProvider({ ...props }) {
enabled: !!projectId,
});
- console.log(project, 'XX');
+
// provider payload.
const provider = {
project,
diff --git a/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/ProjectTimesheetsProvider.tsx b/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/ProjectTimesheetsProvider.tsx
new file mode 100644
index 000000000..c047782b7
--- /dev/null
+++ b/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/ProjectTimesheetsProvider.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import { useProject } from '../../../hooks';
+
+const ProjectTimesheetContext = React.createContext();
+
+/**
+ * Project timesheets data provider.
+ * @returns
+ */
+function ProjectTimesheetsProvider({ ...props }) {
+ const { id } = useParams();
+ const projectId = parseInt(id, 10);
+
+ // Handle fetch project detail.
+ const { data: project } = useProject(projectId, {
+ enabled: !!projectId,
+ });
+
+ // provider payload.
+ const provider = {
+ projectId,
+ project,
+ };
+
+ return ;
+}
+
+const useProjectTimesheetContext = () =>
+ React.useContext(ProjectTimesheetContext);
+
+export { ProjectTimesheetsProvider, useProjectTimesheetContext };
diff --git a/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/index.tsx b/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/index.tsx
index 1e443e5a8..62b1872e1 100644
--- a/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/index.tsx
+++ b/src/containers/Projects/containers/ProjectDetails/ProjectTimeSheets/index.tsx
@@ -3,6 +3,7 @@ import styled from 'styled-components';
import { ProjectTimesheetsTable } from './ProjectTimesheetsTable';
import { ProjectTimesheetsHeader } from './ProjectTimesheetsHeader';
+import { ProjectTimesheetsProvider } from './ProjectTimesheetsProvider';
/**
* Project Timesheets.
@@ -10,12 +11,12 @@ import { ProjectTimesheetsHeader } from './ProjectTimesheetsHeader';
*/
export default function ProjectTimeSheets() {
return (
-
+
-
+
);
}
diff --git a/src/containers/Projects/containers/ProjectTaskFormDialog/ProjectTaskFormProvider.tsx b/src/containers/Projects/containers/ProjectTaskFormDialog/ProjectTaskFormProvider.tsx
index 8f417f1f3..355c2efd1 100644
--- a/src/containers/Projects/containers/ProjectTaskFormDialog/ProjectTaskFormProvider.tsx
+++ b/src/containers/Projects/containers/ProjectTaskFormDialog/ProjectTaskFormProvider.tsx
@@ -31,6 +31,9 @@ function ProjectTaskFormProvider({
},
);
+ console.log(taskId, 'XX');
+ console.log(projectTask, 'XX');
+
const isNewMode = !taskId;
// State provider.
const provider = {
diff --git a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.schema.tsx b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.schema.tsx
index 8177df2ad..c75f29c08 100644
--- a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.schema.tsx
+++ b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.schema.tsx
@@ -6,12 +6,12 @@ const Schema = Yup.object().shape({
date: Yup.date()
.label(intl.get('project_time_entry.schema.label.date'))
.required(),
- projectId: Yup.string()
- .label(intl.get('project_time_entry.schema.label.project_name'))
- .required(),
- taskId: Yup.string()
- .label(intl.get('project_time_entry.schema.label.task_name'))
- .required(),
+ // projectId: Yup.string()
+ // .label(intl.get('project_time_entry.schema.label.project_name'))
+ // .required(),
+ // taskId: Yup.string()
+ // .label(intl.get('project_time_entry.schema.label.task_name'))
+ // .required(),
description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
duration: Yup.string()
.label(intl.get('project_time_entry.schema.label.duration'))
diff --git a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.tsx b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.tsx
index f81b719e4..679ce35f2 100644
--- a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.tsx
+++ b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryForm.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import moment from 'moment';
import intl from 'react-intl-universal';
+import { Intent } from '@blueprintjs/core';
import { Formik } from 'formik';
import { AppToaster } from '@/components';
@@ -13,7 +14,7 @@ import { compose } from '@/utils';
const defaultInitialValues = {
date: moment(new Date()).format('YYYY-MM-DD'),
- projectId: '',
+ // projectId: '',
taskId: '',
description: '',
duration: '',
@@ -28,7 +29,11 @@ function ProjectTimeEntryForm({
closeDialog,
}) {
// time entry form dialog context.
- const { dialogName } = useProjectTimeEntryFormContext();
+ const {
+ dialogName,
+ createProjectTimeEntryMutate,
+ editProjectTimeEntryMutate,
+ } = useProjectTimeEntryFormContext();
// Initial form values
const initialValues = {
@@ -37,11 +42,21 @@ function ProjectTimeEntryForm({
// Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
- const form = {};
+ const form = {
+ ...values,
+ };
// Handle request response success.
const onSuccess = (response) => {
- AppToaster.show({});
+ AppToaster.show({
+ message: intl.get(
+ true
+ ? 'project_time_entry.success_message'
+ : 'project_time_entry.dialog.edit_success_message',
+ ),
+
+ intent: Intent.SUCCESS,
+ });
closeDialog(dialogName);
};
@@ -53,6 +68,9 @@ function ProjectTimeEntryForm({
}) => {
setSubmitting(false);
};
+ createProjectTimeEntryMutate([values.taskId, form])
+ .then(onSuccess)
+ .catch(onError);
};
return (
diff --git a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFields.tsx b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFields.tsx
index ad12187b1..0ec18a5d4 100644
--- a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFields.tsx
+++ b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFields.tsx
@@ -9,10 +9,10 @@ import {
FInputGroup,
FDateInput,
FTextArea,
- FEditableText,
FieldRequiredHint,
FormattedMessage as T,
} from '@/components';
+import { useProjectTimeEntryFormContext } from './ProjectTimeEntryFormProvider';
import { TaskSelect, ProjectsSelect } from '../../components';
import { momentFormatter } from '@/utils';
@@ -21,6 +21,9 @@ import { momentFormatter } from '@/utils';
* @returns
*/
function ProjectTimeEntryFormFields() {
+ // time entry form dialog context.
+ const { projectTasks } = useProjectTimeEntryFormContext();
+
return (
{/*------------ Project -----------*/}
@@ -45,7 +48,7 @@ function ProjectTimeEntryFormFields() {
>
diff --git a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFloatingActions.tsx b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFloatingActions.tsx
index 7589d406b..cf0cf6bca 100644
--- a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFloatingActions.tsx
+++ b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormFloatingActions.tsx
@@ -18,8 +18,10 @@ function ProjectTimeEntryFormFloatingActions({
const { dialogName } = useProjectTimeEntryFormContext();
// Formik context.
- const { isSubmitting } = useFormikContext();
-
+ const { isSubmitting, values, errors } = useFormikContext();
+ console.log(values, 'XX');
+ console.log(errors, 'XX');
+
// Handle close button click.
const handleCancelBtnClick = () => {
closeDialog(dialogName);
diff --git a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormProvider.tsx b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormProvider.tsx
index 40c4ee5e9..0ce82d96e 100644
--- a/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormProvider.tsx
+++ b/src/containers/Projects/containers/ProjectTimeEntryFormDialog/ProjectTimeEntryFormProvider.tsx
@@ -1,4 +1,9 @@
import React from 'react';
+import {
+ useProjectTasks,
+ useCreateProjectTimeEntry,
+ useEditProjectTimeEntry,
+} from '../../hooks';
import { DialogContent } from '@/components';
const ProjecctTimeEntryFormContext = React.createContext();
@@ -13,12 +18,30 @@ function ProjectTimeEntryFormProvider({
projectId,
...props
}) {
+ // Create and edit project time entry mutations.
+ const { mutateAsync: createProjectTimeEntryMutate } =
+ useCreateProjectTimeEntry();
+ const { mutateAsync: editProjectTimeEntryMutate } = useEditProjectTimeEntry();
+
+ // Handle fetch project tasks.
+ const {
+ data: { projectTasks },
+ isLoading: isProjectTasksLoading,
+ } = useProjectTasks(projectId, {
+ enabled: !!projectId,
+ });
+
+ // provider payload.
const provider = {
dialogName,
+ projectId,
+ projectTasks,
+ createProjectTimeEntryMutate,
+ editProjectTimeEntryMutate,
};
return (
-
+
);
diff --git a/src/containers/Projects/hooks/index.ts b/src/containers/Projects/hooks/index.ts
index 77427adfb..544277616 100644
--- a/src/containers/Projects/hooks/index.ts
+++ b/src/containers/Projects/hooks/index.ts
@@ -1,2 +1,3 @@
export * from './projects'
-export * from './projectsTask'
\ No newline at end of file
+export * from './projectsTask'
+export * from './projectTimeEntry'
\ No newline at end of file
diff --git a/src/containers/Projects/hooks/projectTimeEntry.tsx b/src/containers/Projects/hooks/projectTimeEntry.tsx
new file mode 100644
index 000000000..6499c19ad
--- /dev/null
+++ b/src/containers/Projects/hooks/projectTimeEntry.tsx
@@ -0,0 +1,120 @@
+import { useQueryClient, useMutation } from 'react-query';
+import { useRequestQuery } from '@/hooks/useQueryRequest';
+import useApiRequest from '@/hooks/useRequest';
+import t from './type';
+
+// Common invalidate queries.
+const commonInvalidateQueries = (queryClient) => {
+ // Invalidate projects.
+ queryClient.invalidateQueries(t.PROJECTS);
+ // Invalidate project entries.
+ queryClient.invalidateQueries(t.PROJECT_TIME_ENTRIES);
+};
+
+/**
+ * Create a new project time entry.
+ * @param props
+ * @returns
+ */
+export function useCreateProjectTimeEntry(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) => apiRequest.post(`/projects/tasks/${id}/times`, values),
+ {
+ onSuccess: () => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Edit the given project time entry.
+ * @param props
+ * @returns
+ */
+export function useEditProjectTimeEntry(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) => apiRequest.post(`projects/times/${id}`, values),
+ {
+ onSuccess: (res, [id, values]) => {
+ // Invalidate specific project time entry.
+ queryClient.invalidateQueries([t.PROJECT_TIME_ENTRY, id]);
+
+ commonInvalidateQueries(queryClient);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Delete the given project time entry
+ * @param props
+ */
+export function useDeleteProjectTimeEntry(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation((id) => apiRequest.delete(`projects/times/${id}`), {
+ onSuccess: (res, id) => {
+ // Invalidate specific project task.
+ queryClient.invalidateQueries([t.PROJECT_TASK, id]);
+
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrive the given project time entry.
+ * @param timeId
+ * @param props
+ * @param requestProps
+ * @returns
+ */
+export function useProjectTimeEntry(timeId, props, requestProps) {
+ return useRequestQuery(
+ [t.PROJECT_TIME_ENTRY, timeId],
+ { method: 'get', url: `projects/times/${timeId}`, ...requestProps },
+ {
+ select: (res) => res.data.time,
+ defaultData: {},
+ ...props,
+ },
+ );
+}
+
+const transformProjectTimeEntries = (res) => ({
+ projectTasks: res.data.times,
+});
+
+/**
+ *
+ * @param taskId - Task id.
+ * @param props
+ * @param requestProps
+ * @returns
+ */
+export function useProjectTimeEntries(taskId, props, requestProps) {
+ return useRequestQuery(
+ [t.PROJECT_TIME_ENTRIES, taskId],
+ { method: 'get', url: `projects/tasks/${taskId}/times`, ...requestProps },
+ {
+ select: transformProjectTimeEntries,
+ defaultData: {
+ projectTimeEntries: [],
+ },
+ ...props,
+ },
+ );
+}
diff --git a/src/containers/Projects/hooks/projectsTask.tsx b/src/containers/Projects/hooks/projectsTask.tsx
index 64ee669bc..1acfcbc5e 100644
--- a/src/containers/Projects/hooks/projectsTask.tsx
+++ b/src/containers/Projects/hooks/projectsTask.tsx
@@ -40,18 +40,15 @@ export function useEditProjectTask(props) {
const queryClient = useQueryClient();
const apiRequest = useApiRequest();
- return useMutation(
- ([id, values]) => apiRequest.post(`tasks/${id}`, values),
- {
- onSuccess: (res, [id, values]) => {
- // Invalidate specific project task.
- queryClient.invalidateQueries([t.PROJECT_TASK, id]);
+ return useMutation(([id, values]) => apiRequest.post(`tasks/${id}`, values), {
+ onSuccess: (res, [id, values]) => {
+ // Invalidate specific project task.
+ queryClient.invalidateQueries([t.PROJECT_TASK, id]);
- commonInvalidateQueries(queryClient);
- },
- ...props,
+ commonInvalidateQueries(queryClient);
},
- );
+ ...props,
+ });
}
/**
@@ -83,7 +80,7 @@ export function useDeleteProjectTask(props) {
*/
export function useProjectTask(taskId, props, requestProps) {
return useRequestQuery(
- [t.PROJECT, taskId],
+ [t.PROJECT_TASK, taskId],
{ method: 'get', url: `tasks/${taskId}`, ...requestProps },
{
select: (res) => res.data.task,
diff --git a/src/containers/Projects/hooks/type.ts b/src/containers/Projects/hooks/type.ts
index 40ccea949..72f12914c 100644
--- a/src/containers/Projects/hooks/type.ts
+++ b/src/containers/Projects/hooks/type.ts
@@ -8,9 +8,19 @@ const PROJECTS = {
PROJECTS: 'PROJECTS',
};
-const PROJECT_TASKS ={
- PROJECT_TASKS:'PROJECT_TASKS',
- PROJECT_TASK:'PROJECT_TASK',
-}
+const PROJECT_TASKS = {
+ PROJECT_TASKS: 'PROJECT_TASKS',
+ PROJECT_TASK: 'PROJECT_TASK',
+};
-export default { ...PROJECTS, ...CUSTOMERS,...PROJECT_TASKS };
+const PROJECT_TIME_ENTRIES = {
+ PROJECT_TIME_ENTRIES: 'PROJECT_TIME_ENTRIES',
+ PROJECT_TIME_ENTRY: 'PROJECT_TIME_ENTRY',
+};
+
+export default {
+ ...PROJECTS,
+ ...CUSTOMERS,
+ ...PROJECT_TASKS,
+ ...PROJECT_TIME_ENTRIES,
+};
diff --git a/src/lang/en/index.json b/src/lang/en/index.json
index 2ecb5a4dc..84e9c54aa 100644
--- a/src/lang/en/index.json
+++ b/src/lang/en/index.json
@@ -2124,6 +2124,10 @@
"project_time_entry.schema.label.task_name": "Task name",
"project_time_entry.schema.label.duration": "Duration",
"project_time_entry.schema.label.date": "Date",
+ "project_time_entry.success_message": "The time entry has been created successfully.",
+ "project_time_entry.edit_success_message": "The time entry has been edited successfully.",
+ "project_time_entry.alert.delete_message": "The deleted time entry has been deleted successfully.",
+ "project_time_entry.alert.once_delete_this_project": "Once you delete this time entry, you won't be able to restore it later. Are you sure you want to delete this time entry?",
"find_or_choose_a_project": "Find or choose a project",
"choose_a_task": "Choose a task",
"project_expense.dialog.label": "New Expense",