Pārlūkot izejas kodu

Check join date when validating timesheet

tags/Baseline_180220205_Frontend
Wayne pirms 11 mēnešiem
vecāks
revīzija
cd1f1dc9ca
7 mainītis faili ar 55 papildinājumiem un 12 dzēšanām
  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 Parādīt failu

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


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


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


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


@@ -149,19 +154,26 @@ export const checkTotalHours = (
return acc + (entry.otHours || 0); return acc + (entry.otHours || 0);
}, 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."; 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 ( } else if (
isFullTime && isFullTime &&
isDayToCheckAfterJoinDate &&
!isHoliday && !isHoliday &&
!dayJsObj.isSame(dayjs(), "day") && !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}}."; 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}}"; return "The daily total hours cannot be more than {{TIMESHEET_DAILY_MAX_HOURS}}";
} }
}; };


+ 15
- 3
src/components/LeaveModal/LeaveCalendar.tsx Parādīt failu

@@ -24,7 +24,7 @@ import {
} from "@/app/api/timesheets/actions"; } from "@/app/api/timesheets/actions";
import { Props as LeaveEditModalProps } from "../LeaveTable/LeaveEditModal"; import { Props as LeaveEditModalProps } from "../LeaveTable/LeaveEditModal";
import LeaveEditModal 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 { checkTotalHours } from "@/app/api/timesheets/utils";
import unionBy from "lodash/unionBy"; import unionBy from "lodash/unionBy";


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


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


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


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


const handleSaveLeave = useCallback( const handleSaveLeave = useCallback(


+ 2
- 0
src/components/LeaveModal/LeaveModal.tsx Parādīt failu

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


+ 10
- 3
src/components/TimeLeaveModal/TimeLeaveModal.tsx Parādīt failu

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


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


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


const onCancel = useCallback(() => { const onCancel = useCallback(() => {


+ 5
- 0
src/components/UserWorkspacePage/UserWorkspacePage.tsx Parādīt failu

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


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


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


+ 1
- 0
src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx Parādīt failu

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


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


+ 4
- 0
src/config/authConfig.ts Parādīt failu

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


Notiek ielāde…
Atcelt
Saglabāt