2 Commits

4 changed files with 57 additions and 5 deletions
Split View
  1. +37
    -2
      src/app/api/timesheets/utils.ts
  2. +8
    -1
      src/components/DateHoursTable/DateHoursList.tsx
  3. +11
    -1
      src/components/TimeLeaveModal/TimeLeaveModal.tsx
  4. +1
    -1
      src/components/TimesheetTable/TimesheetEditModal.tsx

+ 37
- 2
src/app/api/timesheets/utils.ts View File

@@ -47,7 +47,7 @@ export const validateTimeEntry = (
} else {
if (entry.taskGroupId && !entry.taskId) {
error.taskId = "Required";
} else if (!entry.remark) {
} else if (!entry.taskGroupId && !entry.remark) {
error.remark = "Required for non-billable tasks";
}
}
@@ -133,7 +133,10 @@ export const validateTimeLeaveRecord = (
}
});

return Object.keys(errors).length > 0 ? errors : undefined;
const hasErrors = Object.keys(errors).length > 0;
const temporarilySaveable = isTemporarilySaveable(records, errors, holidays);

return !temporarilySaveable && hasErrors ? errors : undefined;
};

export const checkTotalHours = (
@@ -180,3 +183,35 @@ export const checkTotalHours = (

export const DAILY_NORMAL_MAX_HOURS = 8;
export const TIMESHEET_DAILY_MAX_HOURS = 20;

export const isTemporarilySaveable = (
records: RecordTimeLeaveInput,
errors: { [date: string]: string },
holidays: Set<string>,
): boolean => {
const filledDates = Object.keys(records)
.reduce<{ date: string; hasFilled: boolean }[]>((acc, date) => {
const dayJsObj = dayjs(date);
const isHoliday =
holidays.has(date) || dayJsObj.day() === 0 || dayJsObj.day() === 6;
if (isHoliday) {
return acc;
}
return [...acc, { date, hasFilled: !Boolean(errors[date]) }];
}, [])
.sort((a, b) => dayjs(a.date).diff(dayjs(b.date)));

const isConsecutivelyFilled = filledDates.every((currentDate, index) => {
if (index === 0) {
return true;
}

if (currentDate.hasFilled && !filledDates[index - 1].hasFilled) {
return false;
}

return true;
});

return isConsecutivelyFilled;
};

+ 8
- 1
src/components/DateHoursTable/DateHoursList.tsx View File

@@ -33,6 +33,7 @@ interface Props<EntryComponentProps = object> {
>;
entryComponentProps: EntryComponentProps;
errorComponent?: React.ReactNode;
onSubmit?: () => void;
}

function DateHoursList<EntryTableProps>({
@@ -43,6 +44,7 @@ function DateHoursList<EntryTableProps>({
entryComponentProps,
companyHolidays,
errorComponent,
onSubmit,
}: Props<EntryTableProps>) {
const {
t,
@@ -240,7 +242,12 @@ function DateHoursList<EntryTableProps>({
{t("Done")}
</Button>
) : (
<Button variant="contained" startIcon={<Check />} type="submit">
<Button
variant="contained"
startIcon={<Check />}
type="submit"
onClick={onSubmit}
>
{t("Save")}
</Button>
)}


+ 11
- 1
src/components/TimeLeaveModal/TimeLeaveModal.tsx View File

@@ -237,7 +237,14 @@ const TimeLeaveModal: React.FC<Props> = ({
>
{t("Cancel")}
</Button>
<Button variant="contained" startIcon={<Check />} type="submit">
<Button
variant="contained"
startIcon={<Check />}
type="submit"
onClick={() => {
formProps.clearErrors();
}}
>
{t("Save")}
</Button>
</CardActions>
@@ -277,6 +284,9 @@ const TimeLeaveModal: React.FC<Props> = ({
miscTasks,
}}
errorComponent={errorComponent}
onSubmit={() => {
formProps.clearErrors();
}}
/>
</Box>
</FullscreenModal>


+ 1
- 1
src/components/TimesheetTable/TimesheetEditModal.tsx View File

@@ -304,7 +304,7 @@ const TimesheetEditModal: React.FC<Props> = ({
error={Boolean(formState.errors.remark)}
{...register("remark", {
validate: (value) =>
Boolean(projectId || value) ||
Boolean(projectId || taskGroupId || value) ||
t("Required for non-billable tasks"),
})}
helperText={formState.errors.remark?.message}


Loading…
Cancel
Save