Skip to content

Commit

Permalink
feat: add action on leave request (#830)
Browse files Browse the repository at this point in the history
* RM#88008
  • Loading branch information
gca-axelor authored and lme-axelor committed Dec 17, 2024
1 parent e62f164 commit e1dab1d
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 47 deletions.
5 changes: 5 additions & 0 deletions packages/apps/hr/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ export {
export {searchExpenseType as searchExpenseTypeApi} from './expense-type-api';
export {searchKilometricAllowParam as searchKilometricAllowParamApi} from './kilometric-allow-param-api';
export {
cancelLeave as cancelLeaveApi,
deleteLeave as deleteLeaveApi,
fetchLeave as fetchLeaveApi,
fetchLeaveById as fetchLeaveByIdApi,
fetchLeaveReason as fetchLeaveReasonApi,
fetchLeaveToValidate as fetchLeaveToValidateApi,
fetchMissingDuration as fetchMissingDurationApi,
rejectLeave as rejectLeaveApi,
sendLeave as sendLeaveApi,
validateLeave as validateLeaveApi,
} from './leave-api';
export {
searchManufOrder as searchManufOrderApi,
Expand Down
60 changes: 59 additions & 1 deletion packages/apps/hr/src/api/leave-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
import {
createStandardFetch,
createStandardSearch,
getTypes,
getActionApi,
getSearchCriterias,
getTypes,
} from '@axelor/aos-mobile-core';

const createLeaveCriteria = (userId, selectedStatus) => {
Expand Down Expand Up @@ -134,3 +135,60 @@ export async function fetchMissingDuration({
resolve(2);
});
}

export async function sendLeave({leaveRequestId, version}) {
return getActionApi().send({
url: `ws/aos/leave-request/send/${leaveRequestId}`,
method: 'put',
body: {
version,
},
description: 'send leave',
});
}

export async function validateLeave({leaveRequestId, version}) {
return getActionApi().send({
url: `ws/aos/leave-request/validate/${leaveRequestId}`,
method: 'put',
body: {
version,
},
description: 'validate leave',
});
}

export async function cancelLeave({leaveRequestId, version}) {
return getActionApi().send({
url: `ws/aos/leave-request/cancel/${leaveRequestId}`,
method: 'put',
body: {
version,
},
description: 'cancel leave',
});
}

export async function rejectLeave({leaveRequestId, version, groundForRefusal}) {
return getActionApi().send({
url: `ws/aos/leave-request/reject/${leaveRequestId}`,
method: 'put',
body: {
version,
groundForRefusal,
},
description: 'reject leave',
});
}

export async function deleteLeave({leaveRequestId}) {
return getActionApi().send({
url: `ws/rest/com.axelor.apps.hr.db.LeaveRequest/${leaveRequestId}`,
method: 'delete',
description: 'delete leave request',
matchers: {
modelName: 'com.axelor.apps.hr.db.LeaveRequest',
id: leaveRequestId,
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';
import {useTranslator, useTypes} from '@axelor/aos-mobile-core';
import React, {useMemo} from 'react';
import {
usePermitted,
useSelector,
useTranslator,
useTypes,
} from '@axelor/aos-mobile-core';
import {ActionCard} from '@axelor/aos-mobile-ui';
import {LeaveCard} from '../../atoms';
import {Leave} from '../../../types';

interface LeaveActionCardProps {
mode: number;
Expand All @@ -39,26 +43,47 @@ const LeaveActionCard = ({
}: LeaveActionCardProps) => {
const I18n = useTranslator();
const {LeaveRequest} = useTypes();
const {readonly} = usePermitted({
modelName: 'com.axelor.apps.hr.db.LeaveRequest',
});

const {user} = useSelector((state: any) => state.user);

const userCanValidate = useMemo(() => {
return (
(user.employee?.hrManager ||
leave.employee?.managerUser?.id === user.id) &&
leave.statusSelect === LeaveRequest?.statusSelect.WaitingValidation
);
}, [LeaveRequest?.statusSelect.WaitingValidation, leave, user]);

const isDefaultDisplay = useMemo(() => {
return (
readonly ||
(!userCanValidate &&
leave.statusSelect !== LeaveRequest?.statusSelect.Draft)
);
}, [LeaveRequest?.statusSelect, readonly, leave, userCanValidate]);

return (
<ActionCard
translator={I18n.t}
actionList={[
{
iconName: 'send-fill',
helper: I18n.t('Hr_Send'),
onPress: onSend,
hidden:
mode === Leave.mode.leavesToValidate ||
leave.statusSelect !== LeaveRequest?.statusSelect.Draft,
},
{
iconName: 'check-lg',
helper: I18n.t('Hr_Validate'),
onPress: onValidate,
hidden: mode === Leave.mode.myLeaves,
},
]}>
actionList={
!isDefaultDisplay && [
{
iconName: 'send-fill',
helper: I18n.t('Hr_Send'),
onPress: onSend,
hidden: leave.statusSelect !== LeaveRequest?.statusSelect.Draft,
},
{
iconName: 'check-lg',
helper: I18n.t('Hr_Validate'),
onPress: onValidate,
hidden: leave.statusSelect === LeaveRequest?.statusSelect.Draft,
},
]
}>
<LeaveCard
mode={mode}
statusSelect={leave.statusSelect}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,94 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';
import React, {useCallback, useMemo, useState} from 'react';
import {StyleSheet, View} from 'react-native';
import {useTranslator, useTypes} from '@axelor/aos-mobile-core';
import {
useDispatch,
useNavigation,
usePermitted,
useSelector,
useTranslator,
useTypes,
} from '@axelor/aos-mobile-core';
import {Button, useThemeColor} from '@axelor/aos-mobile-ui';
import {
cancelLeave,
deleteLeave,
sendLeave,
validateLeave,
} from '../../../features/leaveSlice';
import {LeaveRefusalPopup} from '../../templates';

interface LeaveDetailsButtonsProps {
statusSelect: number;
leaveRequest: any;
}

const LeaveDetailsButtons = ({statusSelect}: LeaveDetailsButtonsProps) => {
const LeaveDetailsButtons = ({
statusSelect,
leaveRequest,
}: LeaveDetailsButtonsProps) => {
const I18n = useTranslator();
const Colors = useThemeColor();
const navigation = useNavigation();
const dispatch = useDispatch();

const {LeaveRequest} = useTypes();
const {canDelete, readonly} = usePermitted({
modelName: 'com.axelor.apps.hr.db.LeaveRequest',
});

const [refusalPopupIsOpen, setRefusalPopupIsOpen] = useState(false);

const {user} = useSelector(state => state.user);

const leaveRequestParams = useMemo(
() => ({
leaveRequestId: leaveRequest.id,
version: leaveRequest.version,
user: user,
userId: user.id,
}),
[leaveRequest, user],
);

const sendLeaveAPI = useCallback(() => {
dispatch((sendLeave as any)(leaveRequestParams));
}, [dispatch, leaveRequestParams]);

const validateLeaveAPI = useCallback(() => {
dispatch((validateLeave as any)(leaveRequestParams));
}, [dispatch, leaveRequestParams]);

const cancelLeaveAPI = useCallback(() => {
dispatch((cancelLeave as any)(leaveRequestParams));
}, [dispatch, leaveRequestParams]);

const deleteLeaveAPI = useCallback(() => {
dispatch((deleteLeave as any)(leaveRequestParams));
navigation.pop();
}, [dispatch, leaveRequestParams, navigation]);

if (readonly) {
return null;
}

if (statusSelect === LeaveRequest?.statusSelect.Draft) {
return (
<View style={styles.container}>
<Button
title={I18n.t('Hr_Delete')}
onPress={() => console.log('Delete button pressed.')}
width="45%"
color={Colors.errorColor}
iconName="trash3-fill"
/>
{canDelete && (
<Button
title={I18n.t('Hr_Delete')}
onPress={deleteLeaveAPI}
width="45%"
color={Colors.errorColor}
iconName="trash3-fill"
/>
)}
<Button
title={I18n.t('Hr_Send')}
onPress={() => console.log('Send button pressed.')}
onPress={sendLeaveAPI}
width="45%"
iconName="send-fill"
/>
Expand All @@ -51,20 +112,42 @@ const LeaveDetailsButtons = ({statusSelect}: LeaveDetailsButtonsProps) => {
}

if (statusSelect === LeaveRequest?.statusSelect.WaitingValidation) {
if (
user?.employee?.hrManager ||
leaveRequest.employee?.managerUser?.id === user.id
) {
return (
<View style={styles.container}>
<Button
title={I18n.t('Hr_Refuse')}
onPress={() => setRefusalPopupIsOpen(true)}
width="45%"
color={Colors.errorColor}
iconName="x-lg"
/>
<Button
title={I18n.t('Hr_Validate')}
onPress={validateLeaveAPI}
width="45%"
iconName="check-lg"
/>
<LeaveRefusalPopup
isOpen={refusalPopupIsOpen}
leaveId={leaveRequest.id}
leaveVersion={leaveRequest.version}
handleClose={() => setRefusalPopupIsOpen(false)}
/>
</View>
);
}

return (
<View style={styles.container}>
<Button
title={I18n.t('Hr_Refuse')}
onPress={() => console.log('Refuse button pressed.')}
width="45%"
title={I18n.t('Hr_Cancel')}
onPress={cancelLeaveAPI}
color={Colors.errorColor}
iconName="x-lg"
/>
<Button
title={I18n.t('Hr_Validate')}
onPress={() => console.log('Validate button pressed.')}
width="45%"
iconName="check-lg"
iconName="trash3-fill"
/>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2024 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, {useCallback} from 'react';
import {useDispatch, useSelector} from '@axelor/aos-mobile-core';
import {RefusalPopup} from '../../molecules';
import {rejectLeave} from '../../../features/leaveSlice';

interface LeaveRefusalPopupProps {
isOpen: boolean;
leaveId: number;
leaveVersion: number;
handleClose: () => void;
}

const LeaveRefusalPopup = ({
isOpen,
leaveId,
leaveVersion,
handleClose,
}: LeaveRefusalPopupProps) => {
const dispatch = useDispatch();

const {user} = useSelector(state => state.user);

const rejectLeaveAPI = useCallback(
(refusalMessage: string) => {
handleClose();
dispatch(
(rejectLeave as any)({
leaveRequestId: leaveId,
version: leaveVersion,
user: user,
userId: user.id,
groundForRefusal: refusalMessage,
}),
);
},
[dispatch, handleClose, leaveId, leaveVersion, user],
);

return (
<RefusalPopup
isOpen={isOpen}
onCancel={handleClose}
onValidate={rejectLeaveAPI}
/>
);
};

export default LeaveRefusalPopup;
Loading

0 comments on commit e1dab1d

Please sign in to comment.