diff --git a/packages/core/src/auth/components/molecules/ProcessHistoryCard/ProcessHistoryCard.tsx b/packages/core/src/auth/components/molecules/ProcessHistoryCard/ProcessHistoryCard.tsx new file mode 100644 index 0000000000..ad215e5383 --- /dev/null +++ b/packages/core/src/auth/components/molecules/ProcessHistoryCard/ProcessHistoryCard.tsx @@ -0,0 +1,95 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * 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 . + */ + +import React, {useCallback, useMemo} from 'react'; +import {StyleSheet} from 'react-native'; +import {useThemeColor, ObjectCard} from '@axelor/aos-mobile-ui'; +import {useTranslator} from '../../../../i18n'; +import {ProcessStatus} from '../../../../components'; +import {ProcessHistory} from '../../../types'; + +interface ProccessHistoryCardProps { + style?: any; + status: ProcessStatus; + name: string; + startedDate: string; + completedDate: string; + failedDate: string; + successCallBack: () => void; + errorCallBack: () => void; +} + +const ProccessHistoryCard = ({ + style, + status, + name, + startedDate, + completedDate, + failedDate, + successCallBack, + errorCallBack, +}: ProccessHistoryCardProps) => { + const Colors = useThemeColor(); + const I18n = useTranslator(); + + const borderStyle = useMemo(() => { + return getStyles(ProcessHistory.getStatusColor(status, Colors).background) + ?.border; + }, [Colors, status]); + + const handleOnProcess = useCallback(() => { + status === ProcessStatus.COMPLETED ? successCallBack() : errorCallBack(); + }, [status, successCallBack, errorCallBack]); + + return ( + + ); +}; + +const getStyles = (color: string) => + StyleSheet.create({ + border: {borderLeftWidth: 7, borderLeftColor: color}, + }); + +export default ProccessHistoryCard; diff --git a/packages/core/src/auth/components/molecules/index.js b/packages/core/src/auth/components/molecules/index.js index 8fa0bfc338..57b14fbbc5 100644 --- a/packages/core/src/auth/components/molecules/index.js +++ b/packages/core/src/auth/components/molecules/index.js @@ -17,3 +17,4 @@ */ export {default as TranslationsButton} from './TranslationsButton/TranslationsButton'; +export {default as ProcessHistoryCard} from './ProcessHistoryCard/ProcessHistoryCard'; diff --git a/packages/core/src/auth/hooks/use-auth-headers.js b/packages/core/src/auth/hooks/use-auth-headers.js index 2db786e250..eb37e62356 100644 --- a/packages/core/src/auth/hooks/use-auth-headers.js +++ b/packages/core/src/auth/hooks/use-auth-headers.js @@ -41,8 +41,17 @@ const useUserProfileActions = () => { headerActionsProvider.registerModel('auth_user_profile', { actions: [ { - key: 'settings', + key: 'processes', order: 10, + iconName: 'clock-history', + iconColor: Colors.primaryColor.background, + title: I18n.t('User_ProcessHistroy'), + onPress: () => navigation.navigate('ProcessesHistroyListScreen'), + showInHeader: true, + }, + { + key: 'settings', + order: 20, iconName: 'gear-fill', iconColor: Colors.primaryColor.background, title: I18n.t('User_Settings'), diff --git a/packages/core/src/auth/i18n/en.json b/packages/core/src/auth/i18n/en.json index ffbea12ed4..fe5b392064 100644 --- a/packages/core/src/auth/i18n/en.json +++ b/packages/core/src/auth/i18n/en.json @@ -20,6 +20,15 @@ "User_Language": "Language", "User_Theme": "Theme", "User_NoAppUser": "The user could not be fetched. Please verify that you have the necessary permissions or contact your administrator.", + "User_ProcessHistory": "Process history", + "User_ProcessHistory_Today": "Today", + "User_ProcessHistory_All": "All", + "User_ProcessHistory_Status_Running": "Running", + "User_ProcessHistory_Status_Completed": "Completed", + "User_ProcessHistory_Status_Failed": "Failed", + "User_ProcessHistory_StartedOn": "Started on", + "User_ProcessHistory_CompletedOn": "Completed on", + "User_ProcessHistory_FailedOn": "Failed on", "Auth_SliceAction_FetchLanguages": "fetch languages", "Auth_SliceAction_FetchCompanies": "fetch companies", "Auth_SliceAction_FetchActiveUser": "fetch active user", diff --git a/packages/core/src/auth/i18n/fr.json b/packages/core/src/auth/i18n/fr.json index fcad2ba59a..4ce423c71d 100644 --- a/packages/core/src/auth/i18n/fr.json +++ b/packages/core/src/auth/i18n/fr.json @@ -20,6 +20,15 @@ "User_Language": "Langage", "User_Theme": "Thème", "User_NoAppUser": "L'utilisateur n'a pas pu être récupéré. Veuillez vérifier que vous avez les permissions nécessaires ou contactez votre administrateur.", + "User_ProcessHistory": "Historique du processus", + "User_ProcessHistory_Today": "Aujourd'hui", + "User_ProcessHistory_All": "Tous", + "User_ProcessHistory_Status_Running": "En cours", + "User_ProcessHistory_Status_Completed": "Terminé", + "User_ProcessHistory_Status_Failed": "Échoué", + "User_ProcessHistory_StartedOn": "Démarré le", + "User_ProcessHistory_CompletedOn": "Terminé le", + "User_ProcessHistory_FailedOn": "Échoué le", "Auth_SliceAction_FetchLanguages": "récupération des langages", "Auth_SliceAction_FetchCompanies": "récupération des sociétés", "Auth_SliceAction_FetchActiveUser": "récupération de l'utilisateur actif", diff --git a/packages/core/src/auth/index.ts b/packages/core/src/auth/index.ts index dbefdea674..910d966496 100644 --- a/packages/core/src/auth/index.ts +++ b/packages/core/src/auth/index.ts @@ -20,7 +20,11 @@ import {Module} from '../app/Module'; import enTranslations from './i18n/en.json'; import frTranslations from './i18n/fr.json'; import * as authReducers from './features'; -import {SettingsScreen, UserScreen} from './screens'; +import { + ProcessesHistroyListScreen, + SettingsScreen, + UserScreen, +} from './screens'; import {auth_modelAPI} from './models'; import {useAuthHeaders} from './hooks/use-auth-headers'; @@ -47,6 +51,10 @@ export const authModule: Module = { title: 'User_Settings', component: SettingsScreen, }, + ProcessesHistroyListScreen: { + title: 'User_ProcessHistory', + component: ProcessesHistroyListScreen, + }, }, translations: { en: enTranslations, diff --git a/packages/core/src/auth/screens/ProcessHistoryListScreen.js b/packages/core/src/auth/screens/ProcessHistoryListScreen.js new file mode 100644 index 0000000000..bb432bf0fe --- /dev/null +++ b/packages/core/src/auth/screens/ProcessHistoryListScreen.js @@ -0,0 +1,121 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * 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 . + */ +import React, {useCallback, useEffect, useState} from 'react'; +import {StyleSheet} from 'react-native'; +import { + ChipSelect, + HeaderContainer, + Screen, + ScrollList, + ToggleSwitch, + useThemeColor, +} from '@axelor/aos-mobile-ui'; +import {filterChip} from '../../utils'; +import {ProcessStatus} from '../../components'; +import {useTranslator} from '../../i18n'; +import {useLoader} from '../../components'; +import {ProcessHistoryCard} from '../components'; +import {ProcessHistory} from '../types'; + +const ProcessHistoryListScreen = () => { + const I18n = useTranslator(); + const Colors = useThemeColor(); + const [tabSwitch, setTabSwitch] = useState(false); + + const {processList} = useLoader(); + + const [filteredList, setFilteredList] = useState(processList); + const [selectedStatus, setSelectedStatus] = useState([]); + + const filterOnStatus = useCallback( + list => { + return filterChip(list, selectedStatus, 'status'); + }, + [selectedStatus], + ); + + useEffect(() => { + setFilteredList(filterOnStatus(processList)); + }, [filterOnStatus, processList]); + + return ( + + setTabSwitch(!tabSwitch)} + /> + } + chipComponent={ + setSelectedStatus(chiplist)} + selectionItems={[ + { + title: I18n.t('User_ProcessHistory_Status_Running'), + color: ProcessHistory.getStatusColor( + ProcessStatus.RUNNING, + Colors, + ), + key: ProcessStatus.RUNNING, + }, + { + title: I18n.t('User_ProcessHistory_Status_Completed'), + color: ProcessHistory.getStatusColor( + ProcessStatus.COMPLETED, + Colors, + ), + key: ProcessStatus.COMPLETED, + }, + { + title: I18n.t('User_ProcessHistory_Status_Failed'), + color: ProcessHistory.getStatusColor( + ProcessStatus.FAILED, + Colors, + ), + key: ProcessStatus.FAILED, + }, + ]} + /> + } + expandableFilter={false} + /> + ( + + )} + moreLoading={false} + isListEnd={true} + translator={I18n.t} + /> + + ); +}; + +const styles = StyleSheet.create({ + item: { + marginHorizontal: 12, + marginVertical: 4, + }, +}); + +export default ProcessHistoryListScreen; diff --git a/packages/core/src/auth/screens/index.js b/packages/core/src/auth/screens/index.js index 239ce83123..ff0ecfe1b8 100644 --- a/packages/core/src/auth/screens/index.js +++ b/packages/core/src/auth/screens/index.js @@ -16,5 +16,6 @@ * along with this program. If not, see . */ +export {default as ProcessesHistroyListScreen} from './ProcessHistoryListScreen'; export {default as SettingsScreen} from './SettingsScreen'; export {default as UserScreen} from './UserScreen'; diff --git a/packages/core/src/auth/types/index.ts b/packages/core/src/auth/types/index.ts new file mode 100644 index 0000000000..60c5438580 --- /dev/null +++ b/packages/core/src/auth/types/index.ts @@ -0,0 +1,19 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * 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 . + */ + +export {default as ProcessHistory} from './process-history'; diff --git a/packages/core/src/auth/types/process-history.tsx b/packages/core/src/auth/types/process-history.tsx new file mode 100644 index 0000000000..2e9e745c99 --- /dev/null +++ b/packages/core/src/auth/types/process-history.tsx @@ -0,0 +1,43 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * 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 . + */ + +import {Color, ThemeColors} from '@axelor/aos-mobile-ui'; +import {ProcessStatus} from '../../components'; + +class ProcessHistory { + static getStatusColor = ( + status: ProcessStatus, + Colors: ThemeColors, + ): Color => { + switch (status) { + case ProcessStatus.RUNNING: + return Colors.cautionColor; + case ProcessStatus.COMPLETED: + return Colors.successColor; + case ProcessStatus.FAILED: + return Colors.errorColor; + default: + console.warn( + `Status provided with value ${status} is not supported by process history`, + ); + return null; + } + }; +} + +export default ProcessHistory; diff --git a/packages/core/src/components/templates/Loader/LoaderContext.tsx b/packages/core/src/components/templates/Loader/LoaderContext.tsx index 3da8ce4b33..712feb4fdb 100644 --- a/packages/core/src/components/templates/Loader/LoaderContext.tsx +++ b/packages/core/src/components/templates/Loader/LoaderContext.tsx @@ -24,16 +24,31 @@ import React, { useReducer, } from 'react'; -export type LoaderStatus = 'ok' | 'error' | undefined; +export enum ProcessStatus { + RUNNING, + COMPLETED, + FAILED, +} type callBack = () => void; +export type ProcessHistoryItem = { + status: ProcessStatus; + name: string; + startedDate: string; + completedDate: string; + failedDate: string; + successCallBack: () => void; + errorCallBack: () => void; +}; + interface LoaderContextState { + processList: ProcessHistoryItem[]; numberProcesses: number; loading: boolean; notifyMe: boolean; showPopup: boolean; - status: LoaderStatus; + status: ProcessStatus; message: string; finished: boolean; disabled: boolean; @@ -42,7 +57,7 @@ interface LoaderContextState { setLoading: (option: boolean) => void; setNotifyMe: (option: boolean) => void; setShowPopup: (option: boolean) => void; - setStatus: (option: LoaderStatus) => void; + setStatus: (option: ProcessStatus) => void; setMessage: (message: string) => void; setFinished: (option: boolean) => void; setDisabled: (option: boolean) => void; @@ -54,10 +69,51 @@ interface LoaderContextState { interface LoaderAction { type: string; - payload?: boolean | string | callBack | LoaderStatus; + payload?: boolean | string | callBack | ProcessStatus; } const defaultLoaderContext = { + processList: [ + { + status: ProcessStatus.RUNNING, + name: 'Process 1', + startedDate: '2024-02-06T10:00:00Z', + completedDate: '', + failedDate: '', + successCallBack: () => { + console.log('Process 1 completed successfully'); + }, + errorCallBack: () => { + console.log('Error occurred in Process 1'); + }, + }, + { + status: ProcessStatus.FAILED, + name: 'Process 2', + startedDate: '2024-02-05T15:30:00Z', + completedDate: '', + failedDate: '2024-02-05T16:00:00Z', + successCallBack: () => { + console.log('Process 2 completed successfully'); + }, + errorCallBack: () => { + console.log('Error occurred in Process 2'); + }, + }, + { + status: ProcessStatus.COMPLETED, + name: 'Process 3', + startedDate: '2024-02-06T08:45:00Z', + completedDate: '2024-02-06T11:30:00Z', + failedDate: '', + successCallBack: () => { + console.log('Process 3 completed successfully'); + }, + errorCallBack: () => { + console.log('Error occurred in Process 3'); + }, + }, + ], numberProcesses: 0, loading: false, notifyMe: false, @@ -153,7 +209,7 @@ const LoaderReducer = ( case actionTypes.setStatus: { return { ...state, - status: action.payload as LoaderStatus, + status: action.payload as ProcessStatus, }; } case actionTypes.setMessage: { diff --git a/packages/core/src/components/templates/Loader/LoaderToastNotifier.tsx b/packages/core/src/components/templates/Loader/LoaderToastNotifier.tsx index 766f18b052..fb9f19e8a0 100644 --- a/packages/core/src/components/templates/Loader/LoaderToastNotifier.tsx +++ b/packages/core/src/components/templates/Loader/LoaderToastNotifier.tsx @@ -19,7 +19,7 @@ import {useEffect} from 'react'; import {useTranslator} from '../../../i18n'; import {showToastMessage} from '../../../utils/show-toast-message'; -import {useLoader} from './LoaderContext'; +import {ProcessStatus, useLoader} from './LoaderContext'; const LoaderToastNotifier = () => { const I18n = useTranslator(); @@ -38,15 +38,17 @@ const LoaderToastNotifier = () => { useEffect(() => { if (finished) { if (!notifyMe) { - status === 'ok' ? onSuccessCallBack() : onErrorCallBack(); + status === ProcessStatus.COMPLETED + ? onSuccessCallBack() + : onErrorCallBack(); } else { - if (status === 'ok') { + if (status === ProcessStatus.COMPLETED) { showToastMessage({ type: 'success', position: 'top', topOffset: 30, text1: I18n.t('Base_Success'), - text2: message || I18n.t('Base_Loader_ProccessSuccessMessage'), + text2: message || I18n.t('Base_Loader_ProccessCompletedMessage'), onPress: () => !disabled && onSuccessCallBack(), }); } else { @@ -55,7 +57,7 @@ const LoaderToastNotifier = () => { position: 'top', topOffset: 30, text1: I18n.t('Base_Error'), - text2: message || I18n.t('Base_Loader_ProccessErrorMessage'), + text2: message || I18n.t('Base_Loader_ProccessFailedMessage'), onPress: () => !disabled && onErrorCallBack(), }); } diff --git a/packages/core/src/components/templates/Loader/use-loader-listener.ts b/packages/core/src/components/templates/Loader/use-loader-listener.ts index 5506ace7f7..0b31b461fb 100644 --- a/packages/core/src/components/templates/Loader/use-loader-listener.ts +++ b/packages/core/src/components/templates/Loader/use-loader-listener.ts @@ -17,11 +17,11 @@ */ import {useCallback, useEffect, useMemo, useState} from 'react'; -import {LoaderStatus, useLoader} from './LoaderContext'; +import {ProcessStatus, useLoader} from './LoaderContext'; interface LoaderListenerProps { process: () => Promise; - onFinish?: (status: LoaderStatus) => void; + onFinish?: (status: ProcessStatus) => void; onSuccess?: () => void; onError?: () => void; disabled?: boolean; @@ -51,7 +51,7 @@ const useLoaderListner = ({ } = useLoader(); const onFinishCallBack = useCallback( - (status: LoaderStatus, response: any) => { + (status: ProcessStatus, response: any) => { setLoading(false); setShowPopup(false); setStatus(status); @@ -77,9 +77,9 @@ const useLoaderListner = ({ try { const response = await process(); - onFinishCallBack('ok', response); + onFinishCallBack(ProcessStatus.COMPLETED, response); } catch (error) { - onFinishCallBack('error', error); + onFinishCallBack(ProcessStatus.FAILED, error); } }, [process, setLoading, onFinishCallBack]); diff --git a/packages/core/src/i18n/translations/en.json b/packages/core/src/i18n/translations/en.json index d34d637ef6..f065cd0d3c 100644 --- a/packages/core/src/i18n/translations/en.json +++ b/packages/core/src/i18n/translations/en.json @@ -158,8 +158,8 @@ "Base_Dashboard": "Dashboard", "Base_Dashboard_Misconfigured": "This dashboard is misconfigured, please contact your administrator.", "Base_Dashboard_RefreshConfig": "Refresh dashboard", - "Base_Loader_ProccessSuccessMessage": "Process successfully completed.", - "Base_Loader_ProccessErrorMessage": "An error has occurred.", + "Base_Loader_ProccessCompletedMessage": "Process successfully completed.", + "Base_Loader_ProccessFailedMessage": "An error has occurred.", "Base_Loader_DoNotCloseTheApp": "Do not close the application until the process is done.", "Base_Loader_NotifyMe": "Notify me when it's ready", "Base_Loader_LoadingInProgress": "Loading in progress", diff --git a/packages/core/src/i18n/translations/fr.json b/packages/core/src/i18n/translations/fr.json index 39a55eebc8..5499950807 100644 --- a/packages/core/src/i18n/translations/fr.json +++ b/packages/core/src/i18n/translations/fr.json @@ -158,8 +158,8 @@ "Base_Dashboard": "Tableau de bord", "Base_Dashboard_Misconfigured": "Ce tableau de bord est mal configuré, veuillez contacter votre administrateur.", "Base_Dashboard_RefreshConfig": "Actualiser le tableau de bord", - "Base_Loader_ProccessSuccessMessage": "Le processus s'est terminé avec succès.", - "Base_Loader_ProccessErrorMessage": "Une erreur s'est produite.", + "Base_Loader_ProccessCompletedMessage": "Le processus s'est terminé avec succès.", + "Base_Loader_ProccessFailedMessage": "Une erreur s'est produite.", "Base_Loader_DoNotCloseTheApp": "Veuillez ne pas fermer l'application avant que le processus soit terminé.", "Base_Loader_NotifyMe": "M'avertir lorsqu'il est prêt", "Base_Loader_LoadingInProgress": "Chargement en cours",