Переглянути джерело

Check join date when validating timesheet

tags/Baseline_180220205_Frontend
Wayne 11 місяці тому
джерело
коміт
cd1f1dc9ca
7 змінених файлів з 55 додано та 12 видалено
  1. +18
    -6
      src/app/api/timesheets/utils.ts
  2. +15
    -3
      src/components/LeaveModal/LeaveCalendar.tsx
  3. +2
    -0
      src/components/LeaveModal/LeaveModal.tsx
  4. +10
    -3
      src/components/TimeLeaveModal/TimeLeaveModal.tsx
  5. +5
    -0
      src/components/UserWorkspacePage/UserWorkspacePage.tsx
  6. +1
    -0
      src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx
  7. +4
    -0
      src/config/authConfig.ts

+ 18
- 6
src/app/api/timesheets/utils.ts Переглянути файл

@@ -4,6 +4,8 @@ import { LeaveEntry, RecordTimeLeaveInput, TimeEntry } from "./actions";
import { convertDateArrayToString } from "@/app/utils/formatUtil";
import compact from "lodash/compact";
import dayjs, { Dayjs } from "dayjs";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
dayjs.extend(isSameOrAfter);

export type TimeEntryError = {
[field in keyof TimeEntry]?: string;
@@ -84,6 +86,7 @@ export const validateTimeLeaveRecord = (
records: RecordTimeLeaveInput,
companyHolidays: HolidaysResult[],
isFullTime?: boolean,
joinDate?: Dayjs,
): { [date: string]: string } | undefined => {
const errors: { [date: string]: string } = {};

@@ -122,6 +125,7 @@ export const validateTimeLeaveRecord = (
entries.filter((e) => e.type === "leaveEntry") as LeaveEntry[],
isHoliday,
isFullTime,
joinDate,
);

if (totalHourError) {
@@ -138,6 +142,7 @@ export const checkTotalHours = (
leaves: LeaveEntry[],
isHoliday?: boolean,
isFullTime?: boolean,
joinDate?: Dayjs,
): string | undefined => {
const leaveHours = leaves.reduce((acc, entry) => acc + entry.inputHours, 0);

@@ -149,19 +154,26 @@ export const checkTotalHours = (
return acc + (entry.otHours || 0);
}, 0);

if (totalInputHours + leaveHours > DAILY_NORMAL_MAX_HOURS) {
const totalHours = totalInputHours + leaveHours;

const isDayToCheckAfterJoinDate =
joinDate && joinDate.isValid() ? dayJsObj.isSameOrAfter(joinDate) : true;

if (!isDayToCheckAfterJoinDate && totalHours > 0) {
return "Cannot input hours before join date.";
}

if (totalHours > DAILY_NORMAL_MAX_HOURS) {
return "The daily normal hours (timesheet hours + leave hours) cannot be more than {{DAILY_NORMAL_MAX_HOURS}}. Please use other hours for exceeding hours or decrease the leave hours.";
} else if (
isFullTime &&
isDayToCheckAfterJoinDate &&
!isHoliday &&
!dayJsObj.isSame(dayjs(), "day") &&
totalInputHours + leaveHours !== DAILY_NORMAL_MAX_HOURS
totalHours !== DAILY_NORMAL_MAX_HOURS
) {
return "The daily normal hours (timesheet hours + leave hours) for full-time staffs should be {{DAILY_NORMAL_MAX_HOURS}}.";
} else if (
totalInputHours + totalOtHours + leaveHours >
TIMESHEET_DAILY_MAX_HOURS
) {
} else if (totalHours + totalOtHours > TIMESHEET_DAILY_MAX_HOURS) {
return "The daily total hours cannot be more than {{TIMESHEET_DAILY_MAX_HOURS}}";
}
};


+ 15
- 3
src/components/LeaveModal/LeaveCalendar.tsx Переглянути файл

@@ -24,7 +24,7 @@ import {
} from "@/app/api/timesheets/actions";
import { Props as LeaveEditModalProps } from "../LeaveTable/LeaveEditModal";
import LeaveEditModal from "../LeaveTable/LeaveEditModal";
import dayjs from "dayjs";
import dayjs, { Dayjs } from "dayjs";
import { checkTotalHours } from "@/app/api/timesheets/utils";
import unionBy from "lodash/unionBy";

@@ -35,6 +35,7 @@ export interface Props {
leaveRecords: RecordLeaveInput;
timesheetRecords: RecordTimesheetInput;
isFullTime: boolean;
joinDate: Dayjs;
}

interface EventClickArg {
@@ -55,8 +56,12 @@ const LeaveCalendar: React.FC<Props> = ({
timesheetRecords,
leaveRecords,
isFullTime,
joinDate,
}) => {
const { t ,i18n: { language }} = useTranslation(["home", "common"]);
const {
t,
i18n: { language },
} = useTranslation(["home", "common"]);
const locale = language === "zh" ? "zh-tw" : "en";
const theme = useTheme();

@@ -236,11 +241,18 @@ const LeaveCalendar: React.FC<Props> = ({
leavesWithNewEntry,
Boolean(isHoliday),
isFullTime,
joinDate,
);

if (totalHourError) throw Error(totalHourError);
},
[companyHolidays, isFullTime, localLeaveRecords, timesheetRecords],
[
companyHolidays,
isFullTime,
joinDate,
localLeaveRecords,
timesheetRecords,
],
);

const handleSaveLeave = useCallback(


+ 2
- 0
src/components/LeaveModal/LeaveModal.tsx Переглянути файл

@@ -36,6 +36,7 @@ const LeaveModal: React.FC<Props> = ({
leaveRecords,
timesheetRecords,
isFullTime,
joinDate,
}) => {
const { t } = useTranslation("home");
const isMobile = useIsMobile();
@@ -43,6 +44,7 @@ const LeaveModal: React.FC<Props> = ({
const title = t("Record leave");
const content = (
<LeaveCalendar
joinDate={joinDate}
isFullTime={isFullTime}
leaveTypes={leaveTypes}
companyHolidays={companyHolidays}


+ 10
- 3
src/components/TimeLeaveModal/TimeLeaveModal.tsx Переглянути файл

@@ -20,7 +20,7 @@ import {
RecordTimesheetInput,
saveTimeLeave,
} from "@/app/api/timesheets/actions";
import dayjs from "dayjs";
import dayjs, { Dayjs } from "dayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { AssignedProject, ProjectWithTasks } from "@/app/api/projects";
import FullscreenModal from "../FullscreenModal";
@@ -51,6 +51,7 @@ interface Props {
fastEntryEnabled?: boolean;
leaveTypes: LeaveType[];
isFullTime: boolean;
joinDate: Dayjs;
miscTasks: Task[];
}

@@ -77,6 +78,7 @@ const TimeLeaveModal: React.FC<Props> = ({
fastEntryEnabled,
leaveTypes,
isFullTime,
joinDate,
miscTasks,
}) => {
const { t } = useTranslation("home");
@@ -113,7 +115,12 @@ const TimeLeaveModal: React.FC<Props> = ({

const onSubmit = useCallback<SubmitHandler<RecordTimeLeaveInput>>(
async (data) => {
const errors = validateTimeLeaveRecord(data, companyHolidays, isFullTime);
const errors = validateTimeLeaveRecord(
data,
companyHolidays,
isFullTime,
joinDate,
);
if (errors) {
Object.keys(errors).forEach((date) =>
formProps.setError(date, {
@@ -138,7 +145,7 @@ const TimeLeaveModal: React.FC<Props> = ({
formProps.reset(newFormValues);
onClose();
},
[companyHolidays, formProps, onClose, isFullTime],
[companyHolidays, isFullTime, joinDate, formProps, onClose],
);

const onCancel = useCallback(() => {


+ 5
- 0
src/components/UserWorkspacePage/UserWorkspacePage.tsx Переглянути файл

@@ -26,6 +26,7 @@ import { TimesheetAmendmentModal } from "../TimesheetAmendment/TimesheetAmendmen
import TimeLeaveModal from "../TimeLeaveModal/TimeLeaveModal";
import LeaveModal from "../LeaveModal";
import { Task } from "@/app/api/tasks";
import dayjs from "dayjs";

export interface Props {
leaveTypes: LeaveType[];
@@ -40,6 +41,7 @@ export interface Props {
maintainNormalStaffWorkspaceAbility: boolean;
maintainManagementStaffWorkspaceAbility: boolean;
isFullTime: boolean;
joinDate?: number | null;
miscTasks: Task[];
}

@@ -61,6 +63,7 @@ const UserWorkspacePage: React.FC<Props> = ({
maintainNormalStaffWorkspaceAbility,
maintainManagementStaffWorkspaceAbility,
isFullTime,
joinDate,
miscTasks,
}) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
@@ -192,6 +195,7 @@ const UserWorkspacePage: React.FC<Props> = ({
timesheetRecords={defaultTimesheets}
leaveRecords={defaultLeaveRecords}
isFullTime={isFullTime}
joinDate={dayjs(joinDate)}
miscTasks={miscTasks}
/>
<LeaveModal
@@ -203,6 +207,7 @@ const UserWorkspacePage: React.FC<Props> = ({
leaveRecords={defaultLeaveRecords}
timesheetRecords={defaultTimesheets}
isFullTime={isFullTime}
joinDate={dayjs(joinDate)}
/>
{assignedProjects.length > 0 ? (
<AssignedProjects


+ 1
- 0
src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx Переглянути файл

@@ -61,6 +61,7 @@ const UserWorkspaceWrapper: React.FC = async () => {

return (
<UserWorkspacePage
joinDate={userStaff?.joinDate}
isFullTime={isFullTime}
teamLeaves={teamLeaves}
teamTimesheets={teamTimesheets}


+ 4
- 0
src/config/authConfig.ts Переглянути файл

@@ -7,6 +7,10 @@ export interface SessionStaff {
teamId: number;
isTeamLead: boolean;
employType: string | null;
/**
* The join date in milliseconds since epoch
*/
joinDate: number | null;
}
export interface SessionWithTokens extends Session {
staff?: SessionStaff;


Завантаження…
Відмінити
Зберегти