Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) POC - Offline ready jobs #823

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useCallback, useEffect, useState } from 'react';
import { ModalBody, ModalFooter, ModalHeader, Button, InlineLoading } from '@carbon/react';
import { useTranslation } from 'react-i18next';
import { getCurrentOfflineMode, showToast } from '@openmrs/esm-framework';

export interface OfflineActionsProgressModalProps {
items?: Array<any>;
closeModal: (active: boolean) => void;
}

const OfflineReadyModal: React.FC<OfflineActionsProgressModalProps> = ({ closeModal, items }) => {
const { t } = useTranslation();
const [isRunning, setIsRunning] = useState(true);
const [abortController, setAbortController] = useState(() => new AbortController());

async function dispatchOfflineEvent() {
window.dispatchEvent(
new CustomEvent(`openmrs:offline-enabled`, {
detail: getCurrentOfflineMode(),
}),
);

setIsRunning(false);
}

useEffect(() => {
dispatchOfflineEvent();
}, [abortController, items]);

const handleClose = useCallback(() => {
if (isRunning) {
abortController.abort();

showToast({
critical: true,
kind: 'warning',
description: t('unavailableOfflineFeatures', 'Some features may not be available offline.'),
title: t('offlinePreparationCanceled', 'Offline preparation canceled'),
});
closeModal(false);
} else {
showToast({
critical: true,
kind: 'success',
description: t('offlineModeIsReady', 'Offline mode is ready'),
title: t('offline', 'Offline'),
});
closeModal(true);
}
}, [abortController, closeModal, isRunning, t]);

return (
<>
<ModalHeader title={t('preparingOfflineMode', 'Preparing for offline mode')} closeModal={handleClose} />
<ModalBody>
{isRunning && (
<InlineLoading
// className={styles.loader}
description={t('loading', 'Loading') + '...'}
/>
)}
</ModalBody>
<ModalFooter>
<Button kind="danger" onClick={handleClose} disabled={!isRunning}>
{t('cancel', 'Cancel')}
</Button>
<Button kind="primary" onClick={handleClose} disabled={isRunning}>
{t('confirm', 'Confirm')}
</Button>
</ModalFooter>
</>
);
};

export default OfflineReadyModal;
12 changes: 10 additions & 2 deletions packages/apps/esm-offline-tools-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { defineConfigSchema, getSyncLifecycle, registerBreadcrumbs } from '@openmrs/esm-framework';
import {
defineConfigSchema,
getSyncLifecycle,
registerBreadcrumbs,
registerOfflineHandler,
} from '@openmrs/esm-framework';
import { routes } from './constants';
import { createDashboardLink } from './createDashboardLink';
import { dashboardMeta } from './dashboard.meta';
Expand All @@ -8,6 +13,7 @@ import offlineToolsComponent from './root.component';
import offlineToolsLinkComponent from './offline-tools-app-menu-link.component';
import offlineToolsNavItemsComponent from './nav/offline-tools-nav-menu.component';
import offlineToolsConfirmationModalComponent from './components/confirmation-modal.component';
import offlineToolsOfflineReadyModalComponent from './components/offline-ready-modal.component';
import offlineToolsPatientsCardComponent from './offline-patients/patients-overview-card.component';
import offlineToolsActionsCardComponent from './offline-actions/offline-actions-overview-card.component';
import offlineToolsActionsComponent from './offline-actions/offline-actions.component';
Expand Down Expand Up @@ -36,6 +42,8 @@ export const offlineToolsNavItems = getSyncLifecycle(offlineToolsNavItemsCompone

export const offlineToolsConfirmationModal = getSyncLifecycle(offlineToolsConfirmationModalComponent, options);

export const offlineToolsOfflineReadyModal = getSyncLifecycle(offlineToolsOfflineReadyModalComponent, options);

export const offlineToolsPatientsCard = getSyncLifecycle(offlineToolsPatientsCardComponent, options);

export const offlineToolsActionsCard = getSyncLifecycle(offlineToolsActionsCardComponent, options);
Expand Down Expand Up @@ -85,7 +93,7 @@ export const offlineToolsOptInButton = getSyncLifecycle(offlineToolsOptInButtonC

export function startupApp() {
defineConfigSchema(moduleName, {});
setupOffline();
registerOfflineHandler(setupOffline);
setupSynchronizingOfflineActionsNotifications();

registerBreadcrumbs([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import React from 'react';
import React, { useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Toggle } from '@carbon/react';
import { Button } from '@carbon/react';
import { Network_3 } from '@carbon/react/icons';
import { getCurrentOfflineMode, setCurrentOfflineMode } from '@openmrs/esm-framework/src/internal';
import {
getCurrentOfflineMode,
setCurrentOfflineMode,
showModal,
useConnectivity,
} from '@openmrs/esm-framework/src/internal';
import styles from './offline-actions-mode-button.scss';
import { SwitcherItem } from '@carbon/react';

Expand All @@ -12,25 +17,41 @@ function doNotCloseMenu(ev: React.SyntheticEvent) {

const OfflineActionsModeButton: React.FC = () => {
const { t } = useTranslation();
const [active, setActive] = React.useState(() => getCurrentOfflineMode().active);
const toggle = React.useCallback(() => {
setActive((value) => {
const active = !value;
setCurrentOfflineMode(active ? 'on' : 'off');
return active;
const isOnline = useConnectivity();
const [active, setActive] = useState(() => getCurrentOfflineMode().active);

const toggle = useCallback(() => {
const dispose = showModal('offline-tools-offline-ready-modal', {
closeModal: (result) => {
setActive(result);
setCurrentOfflineMode(result ? 'on' : 'off');
dispose();
},
});
}, []);
}, [setActive]);

const handleRefresh = useCallback(() => {
toggle();
}, [toggle]);

return (
<SwitcherItem className={styles.panelItemContainer} aria-label="Offline mode">
<div>
<Network_3 size={20} />
<p onClick={doNotCloseMenu} role="none">
{t('offlineReady', 'Offline Ready')}
</p>
</div>
<Toggle className={styles.toggle} id="offlineModeSwitch" toggled={active} onToggle={toggle} />
</SwitcherItem>
isOnline && (
<SwitcherItem aria-label="Offline Ready" className={styles.panelItemContainer}>
<div>
<Network_3 size={20} />
<p>{t('offlineReady', 'Offline Ready')}</p>
</div>
{active ? (
<Button kind="ghost" onClick={handleRefresh}>
{t('refresh', 'Refresh')}
</Button>
) : (
<Button kind="ghost" id="offlineModeSwitch" onClick={toggle}>
{t('turnOn', 'Turn On')}
</Button>
)}
</SwitcherItem>
)
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,8 @@

.panelItemContainer a {
display: flex;
justify-content: space-between;
align-items: center;

:global(.cds--toggle-input__label .cds--toggle__switch) {
margin-top: 0 !important;
}

:global(.cds--toggle) {
/* setting the width prevents the toggle from changing size when the text changes */
width: 76px;
margin: 0 1rem;
}

:global(.cds--toggle__text) {
color: $ui-02;
}
justify-content: space-between;
}

.panelItemContainer div {
Expand Down
6 changes: 6 additions & 0 deletions packages/apps/esm-offline-tools-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
"online": true,
"offline": true
},
{
"name": "offline-tools-offline-ready-modal",
"component": "offlineToolsOfflineReadyModal",
"online": true,
"offline": true
},
{
"name": "offline-tools-dashboard-patients-card",
"slot": "offline-tools-dashboard-cards",
Expand Down
11 changes: 11 additions & 0 deletions packages/apps/esm-offline-tools-app/translations/am.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"emptyStateText": "There are no {{displayText}} to display",
"home": "Home",
"homeHeader": "Offline home",
Expand All @@ -9,6 +11,10 @@
"homeOverviewCardPatientsHeader": "Patients",
"homeOverviewCardPatientsNewlyRegistered": "Newly registered",
"homeOverviewCardView": "View",
"lastRun": "Last Run",
"loading": "Loading",
"never": "Never",
"offline": "Offline",
"offlineActions": "Offline actions",
"offlineActionsDeleteConfirmationModalCancel": "Cancel",
"offlineActionsDeleteConfirmationModalConfirm": "Delete forever",
Expand All @@ -25,6 +31,7 @@
"offlineActionsTableError": "Error",
"offlineActionsTablePatient": "Patient",
"offlineActionsUpdateOfflinePatients": "Update offline patients",
"offlineModeIsReady": "Offline mode is ready",
"offlinePatients": "Offline patients",
"offlinePatients_lower": "offline patients",
"offlinePatientsHeader": "Offline patients",
Expand Down Expand Up @@ -52,8 +59,12 @@
"offlinePatientSyncDetailsFailedHeader": "There was an error downloading the following items",
"offlinePatientSyncDetailsFallbackErrorMessage": "Unknown error.",
"offlinePatientSyncDetailsHeader": "Offline patient details",
"offlinePreparationCanceled": "Offline preparation canceled",
"offlineReady": "Offline Ready",
"offlineToolsAppMenuLink": "Offline tools",
"preparingOfflineMode": "Preparing for offline mode",
"refresh": "Refresh",
"unavailableOfflineFeatures": "Some features may not be available offline.",
"offlineActionsSynchronizationNotificationTitle": "Upload",
"offlineActionsSynchronizationNotificationSynchronized": "The offline action synchronization has finished.",
"offlineActionsSynchronizationNotificationCanceling": "Canceling...",
Expand Down
11 changes: 11 additions & 0 deletions packages/apps/esm-offline-tools-app/translations/ar.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"emptyStateText": "لا يوجد {{displayText}} لعرضه",
"home": "الرئيسية",
"homeHeader": "الصفحة الرئيسية بدون اتصال",
Expand All @@ -9,6 +11,10 @@
"homeOverviewCardPatientsHeader": "المرضى",
"homeOverviewCardPatientsNewlyRegistered": "مُسجل حديثًا",
"homeOverviewCardView": "عرض",
"lastRun": "Last Run",
"loading": "Loading",
"never": "Never",
"offline": "Offline",
"offlineActions": "الأنشطة بدون اتصال",
"offlineActionsDeleteConfirmationModalCancel": "إلغاء",
"offlineActionsDeleteConfirmationModalConfirm": "حذف نهائي",
Expand All @@ -29,6 +35,7 @@
"offlineActionsTableError": "خطأ",
"offlineActionsTablePatient": "المريض",
"offlineActionsUpdateOfflinePatients": "تحديث المرضى بدون اتصال",
"offlineModeIsReady": "Offline mode is ready",
"offlinePatients": "المرضى بدون اتصال",
"offlinePatients_lower": "المرضى بدون اتصال",
"offlinePatientsHeader": "المرضى بدون اتصال",
Expand Down Expand Up @@ -56,8 +63,12 @@
"offlinePatientSyncDetailsFailedHeader": "حدث خطأ أثناء تحميل العناصر التالية",
"offlinePatientSyncDetailsFallbackErrorMessage": "خطأ غير معروف.",
"offlinePatientSyncDetailsHeader": "تفاصيل المريض بدون اتصال",
"offlinePreparationCanceled": "Offline preparation canceled",
"offlineReady": "جاهز للعمل بدون اتصال",
"offlineToolsAppMenuLink": "أدوات العمل بدون اتصال",
"preparingOfflineMode": "Preparing for offline mode",
"refresh": "Refresh",
"unavailableOfflineFeatures": "Some features may not be available offline.",
"offlineActionsSynchronizationNotificationTitle": "تحميل",
"offlineActionsSynchronizationNotificationSynchronized": "انتهت مزامنة الإجراءات غير المتصلة.",
"offlineActionsSynchronizationNotificationCanceling": "إلغاء...",
Expand Down
11 changes: 11 additions & 0 deletions packages/apps/esm-offline-tools-app/translations/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"emptyStateText": "There are no {{displayText}} to display",
"home": "Home",
"homeHeader": "Offline home",
Expand All @@ -9,6 +11,10 @@
"homeOverviewCardPatientsHeader": "Patients",
"homeOverviewCardPatientsNewlyRegistered": "Newly registered",
"homeOverviewCardView": "View",
"lastRun": "Last Run",
"loading": "Loading",
"never": "Never",
"offline": "Offline",
"offlineActions": "Offline Actions",
"offlineActionsDeleteConfirmationModalCancel": "Cancel",
"offlineActionsDeleteConfirmationModalConfirm": "Delete forever",
Expand All @@ -25,6 +31,7 @@
"offlineActionsTableError": "Error",
"offlineActionsTablePatient": "Patient",
"offlineActionsUpdateOfflinePatients": "Update offline patients",
"offlineModeIsReady": "Offline mode is ready",
"offlinePatients": "Offline patients",
"offlinePatients_lower": "offline patients",
"offlinePatientsHeader": "Offline patients",
Expand Down Expand Up @@ -52,8 +59,12 @@
"offlinePatientSyncDetailsFailedHeader": "There was an error downloading the following items",
"offlinePatientSyncDetailsFallbackErrorMessage": "Unknown error.",
"offlinePatientSyncDetailsHeader": "Offline patient details",
"offlinePreparationCanceled": "Offline preparation canceled",
"offlineReady": "Offline Ready",
"offlineToolsAppMenuLink": "Offline tools",
"preparingOfflineMode": "Preparing for offline mode",
"refresh": "Refresh",
"unavailableOfflineFeatures": "Some features may not be available offline.",
"offlineActionsSynchronizationNotificationTitle": "Upload",
"offlineActionsSynchronizationNotificationSynchronized": "The offline action synchronization has finished.",
"offlineActionsSynchronizationNotificationCanceling": "Canceling...",
Expand Down
11 changes: 11 additions & 0 deletions packages/apps/esm-offline-tools-app/translations/es.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"cancel": "Cancelar",
"confirm": "Confirmar",
"emptyStateText": "No hay {{displayText}} para mostrar",
"home": "Inicio",
"homeHeader": "Inicio offline",
Expand All @@ -9,6 +11,10 @@
"homeOverviewCardPatientsHeader": "Pacientes",
"homeOverviewCardPatientsNewlyRegistered": "Recién registrados",
"homeOverviewCardView": "Ver",
"lastRun": "Última carrera",
"loading": "Loading",
"never": "Nunca",
"offline": "Offline",
"offlineActions": "Acciones offline",
"offlineActionsDeleteConfirmationModalCancel": "Cancelar",
"offlineActionsDeleteConfirmationModalConfirm": "Eliminar para siempre",
Expand All @@ -26,6 +32,7 @@
"offlineActionsTableError": "Error",
"offlineActionsTablePatient": "Paciente",
"offlineActionsUpdateOfflinePatients": "Actualizar pacientes offline",
"offlineModeIsReady": "Offline mode is ready",
"offlinePatients": "Pacientes offline",
"offlinePatients_lower": "pacientes offline",
"offlinePatientsHeader": "Pacientes offline",
Expand Down Expand Up @@ -53,8 +60,12 @@
"offlinePatientSyncDetailsFailedHeader": "Hubo un error al descargar los siguientes elementos",
"offlinePatientSyncDetailsFallbackErrorMessage": "Error desconocido.",
"offlinePatientSyncDetailsHeader": "Detalles de pacientes offline",
"offlinePreparationCanceled": "Offline preparation canceled",
"offlineReady": "Listo offline",
"offlineToolsAppMenuLink": "Herramientas offline",
"preparingOfflineMode": "Preparando modo sin conexión",
"refresh": "Actualizar",
"unavailableOfflineFeatures": "Some features may not be available offline.",
"offlineActionsSynchronizationNotificationTitle": "Subir",
"offlineActionsSynchronizationNotificationSynchronized": "La sincronización de acciones sin conexión ha finalizado.",
"offlineActionsSynchronizationNotificationCanceling": "Cancelando...",
Expand Down
Loading
Loading