Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,19 @@ import type { StorybookConfig } from '@storybook/react-webpack5';
import path from 'path';

const config: StorybookConfig = {
"stories": [
"../src/**/*.mdx",
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-webpack5-compiler-swc',
'@storybook/addon-essentials',
'@storybook/addon-onboarding',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
"addons": [
"@storybook/addon-webpack5-compiler-swc",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@chromatic-com/storybook",
"@storybook/addon-interactions"
],
"framework": {
"name": "@storybook/react-webpack5",
"options": {}
framework: {
name: '@storybook/react-webpack5',
options: {},
},
"staticDirs": ['../public'],
staticDirs: ['../public'],
webpackFinal: async (config) => {
// Add SCSS support
if (config.module && config.module.rules) {
Expand All @@ -33,7 +30,7 @@ const config: StorybookConfig = {
includePaths: [path.resolve(__dirname, '../node_modules')],
},
},
}
},
],
include: path.resolve(__dirname, '../'),
});
Expand All @@ -50,12 +47,13 @@ const config: StorybookConfig = {
'@hooks': path.resolve(__dirname, '../src/hooks'),
'@providers': path.resolve(__dirname, '../src/providers'),
'@services': path.resolve(__dirname, '../src/services'),
'@schemas': path.resolve(__dirname, '../src/schemas'),
'@types': path.resolve(__dirname, '../src/types'),
'@utils': path.resolve(__dirname, '../src/utils'),
};
}

return config;
}
},
};
export default config;
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const config: Config.InitialOptions = {
'@types/(.*)$': ['<rootDir>/src/types/$1'],
'@utils/(.*)$': ['<rootDir>/src/utils/$1'],
'@providers/(.*)$': ['<rootDir>/src/providers/$1'],
'@schemas/(.*)$': ['<rootDir>/src/schemas/$1'],
'@__mocks__/(.*)$': ['<rootDir>/src/__mocks__/$1'],
},
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dependencies": {
"@carbon/react": "^1.78.2",
"@testing-library/react-hooks": "^8.0.1",
"ajv": "^8.17.1",
"axios": "^1.8.4",
"date-fns": "^4.1.0",
"i18next": "^24.2.3",
Expand Down
6 changes: 6 additions & 0 deletions public/locales/locale_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@
"CONDITION_TABLE_ONSET_DATE": "Onset Date",
"CONDITION_TABLE_PROVIDER": "Provider",
"CONDITION_TABLE_RECORDED_DATE": "Recorded Date",
"CONFIG_ERROR_NOT_FOUND": "Configuration not found",
"CONFIG_ERROR_SCHEMA_VALIDATION_FAILED": "Configuration does not match required schema",
"CONFIG_ERROR_VALIDATION_FAILED": "Configuration validation failed",
"DATE_ERROR_EMPTY_OR_INVALID": "Date string is empty or invalid",
"DATE_ERROR_FORMAT": "Date Format Error",
"DATE_ERROR_INVALID_FORMAT": "Invalid date format",
"DATE_ERROR_NULL_OR_UNDEFINED": "Date is null or undefined",
"DATE_ERROR_PARSE": "Date Parse Error",
"ERROR_BAD_REQUEST_MESSAGE": "Invalid input parameters. Please check your request and try again.",
"ERROR_BAD_REQUEST_TITLE": "Bad Request",
"ERROR_CONFIG_TITLE": "Configuration Error",
"ERROR_DASHBOARD_CONFIG_TITLE": "Department Configuration Error",
"ERROR_DEFAULT_MESSAGE": "An unexpected error occurred",
"ERROR_DEFAULT_TITLE": "Error",
"ERROR_NETWORK_MESSAGE": "Unable to connect to the server. Please check your internet connection.",
Expand All @@ -39,6 +44,7 @@
"ERROR_UNAUTHORIZED_MESSAGE": "You are not authorized to perform this action. Please log in again.",
"ERROR_UNAUTHORIZED_TITLE": "Unauthorized",
"ERROR_UNKNOWN_MESSAGE": "An unknown error occurred",
"ERROR_VALIDATION_TITLE": "Validation Error",
"EXPANDABLE_TABLE_EMPTY_STATE_MESSAGE": "No data available",
"EXPANDABLE_TABLE_ERROR_MESSAGE": "{{title}}: {{message}}",
"NO_ALLERGIES": "No Allergies recorded for this patient.",
Expand Down
6 changes: 6 additions & 0 deletions public/locales/locale_es.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@
"CONDITION_TABLE_ONSET_DATE": "Fecha de inicio",
"CONDITION_TABLE_PROVIDER": "Proveedor",
"CONDITION_TABLE_RECORDED_DATE": "Fecha de grabación",
"CONFIG_ERROR_NOT_FOUND": "Configuración no encontrada",
"CONFIG_ERROR_SCHEMA_VALIDATION_FAILED": "La configuración no coincide con el esquema requerido",
"CONFIG_ERROR_VALIDATION_FAILED": "La validación de la configuración falló",
"DATE_ERROR_EMPTY_OR_INVALID": "La cadena de fecha está vacía o no es válida",
"DATE_ERROR_FORMAT": "Error de formato de fecha",
"DATE_ERROR_INVALID_FORMAT": "Formato de fecha no válido",
"DATE_ERROR_NULL_OR_UNDEFINED": "La fecha es nula o no está definida",
"DATE_ERROR_PARSE": "Error al analizar la fecha",
"ERROR_BAD_REQUEST_MESSAGE": "Parámetros de entrada no válidos. Por favor, verifique su solicitud e intente nuevamente.",
"ERROR_BAD_REQUEST_TITLE": "Solicitud incorrecta",
"ERROR_CONFIG_TITLE": "Error de configuración",
"ERROR_DASHBOARD_CONFIG_TITLE": "Error de configuración del departamento",
"ERROR_DEFAULT_MESSAGE": "Se produjo un error inesperado",
"ERROR_DEFAULT_TITLE": "Error",
"ERROR_NETWORK_MESSAGE": "No se puede conectar al servidor. Por favor, compruebe su conexión a Internet.",
Expand All @@ -39,6 +44,7 @@
"ERROR_UNAUTHORIZED_MESSAGE": "No está autorizado para realizar esta acción. Por favor, inicie sesión nuevamente.",
"ERROR_UNAUTHORIZED_TITLE": "No autorizado",
"ERROR_UNKNOWN_MESSAGE": "Se produjo un error desconocido",
"ERROR_VALIDATION_TITLE": "Error de validación",
"EXPANDABLE_TABLE_EMPTY_STATE_MESSAGE": "No hay datos disponibles",
"EXPANDABLE_TABLE_ERROR_MESSAGE": "{{title}}: {{message}}",
"NO_ALLERGIES": "No hay alergias registradas para este paciente",
Expand Down
196 changes: 196 additions & 0 deletions src/__mocks__/configMocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// src/__mocks__/configMocks.ts

// Happy Path Mocks
export const validFullClinicalConfig = {
patientInformation: {
displayPatientIdentifiers: true,
showPatientPhoto: true,
additionalAttributes: ['caste', 'education', 'occupation'],
},
actions: [
{
name: 'Start Visit',
url: '/openmrs/ws/rest/v1/visit',
icon: 'fa fa-stethoscope',
requiredPrivilege: 'Start Visit',
},
{
name: 'Add Diagnosis',
url: '/openmrs/ws/rest/v1/diagnosis',
icon: 'fa fa-heartbeat',
requiredPrivilege: 'Add Diagnosis',
},
{
name: 'Record Allergy',
url: '/openmrs/ws/rest/v1/allergy',
icon: 'fa fa-exclamation-triangle',
requiredPrivilege: 'Record Allergy',
},
],
dashboards: [
{
name: 'Patient Information',
url: 'patient-information',
requiredPrivileges: ['View Patient Information'],
icon: 'fa fa-user',
default: true,
},
{
name: 'Conditions',
url: 'conditions',
requiredPrivileges: ['View Conditions'],
icon: 'fa fa-heartbeat',
},
{
name: 'Allergies',
url: 'allergies',
requiredPrivileges: ['View Allergies'],
icon: 'fa fa-exclamation-triangle',
},
],
};

export const minimalClinicalConfig = {
patientInformation: {},
actions: [],
dashboards: [
{
name: 'Basic Information',
url: 'basic-information',
requiredPrivileges: ['View Patient Dashboard'],
},
],
};

export const mixedClinicalConfig = {
patientInformation: {
displayPatientIdentifiers: true,
},
actions: [
{
name: 'Start Visit',
url: '/openmrs/ws/rest/v1/visit',
requiredPrivilege: 'Start Visit',
},
],
dashboards: [
{
name: 'Required Section',
url: 'required-section',
requiredPrivileges: ['View Patient Dashboard'],
},
{
name: 'Optional Section',
url: 'optional-section',
requiredPrivileges: ['View Optional Dashboard'],
icon: 'fa fa-plus',
default: false,
},
],
};

// Sad Path Mocks
export const invalidClinicalConfig = {
// Missing required properties
patientInformation: {},
// Missing actions array
// Missing dashboards array
otherProperty: 'value',
};

export const emptyResponse = null;

export const malformedJsonResponse = '{invalid-json}';

// Edge Case Mocks
export const largeConfig = {
patientInformation: {
displayPatientIdentifiers: true,
showPatientPhoto: true,
additionalAttributes: Array(50)
.fill(0)
.map((_, i) => `attribute${i}`),
},
actions: Array(20)
.fill(0)
.map((_, i) => ({
name: `Action ${i}`,
url: `/openmrs/ws/rest/v1/action${i}`,
icon: 'fa fa-cog',
requiredPrivilege: `Privilege ${i}`,
})),
dashboards: generateLargeDashboards(50), // Generates 50 dashboards
};

export const allOptionalFieldsConfig = {
patientInformation: {
displayPatientIdentifiers: true,
showPatientPhoto: true,
additionalAttributes: ['caste', 'education', 'occupation'],
customSections: [
{
name: 'Demographics',
attributes: ['birthdate', 'gender', 'address'],
},
{
name: 'Contact Information',
attributes: ['phoneNumber', 'email'],
},
],
},
actions: [
{
name: 'Comprehensive Action',
url: '/openmrs/ws/rest/v1/comprehensive',
icon: 'fa fa-th-large',
requiredPrivilege: 'Comprehensive Privilege',
order: 1,
type: 'standard',
additionalParams: {
color: 'blue',
size: 'large',
showInHeader: true,
},
},
],
dashboards: [
{
name: 'Comprehensive Dashboard',
url: 'comprehensive-dashboard',
requiredPrivileges: ['View Comprehensive Dashboard'],
icon: 'fa fa-th-large',
default: true,
order: 1,
displayName: 'Comprehensive View',
description: 'A dashboard with all possible controls and features',
config: {
refreshInterval: 60,
layout: 'grid',
maxItems: 10,
},
},
],
};

// Helper function to generate large config
function generateLargeDashboards(count: number) {
const dashboards = [];
const icons = [
'fa fa-user',
'fa fa-heartbeat',
'fa fa-hospital',
'fa fa-medkit',
];

for (let i = 0; i < count; i++) {
dashboards.push({
name: `Dashboard ${i}`,
url: `dashboard-${i}`,
requiredPrivileges: [`View Dashboard ${i}`],
icon: icons[i % icons.length],
default: i === 0, // First one is default
});
}

return dashboards;
}
5 changes: 4 additions & 1 deletion src/constants/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export const PATIENT_CONDITION_RESOURCE_URL = (patientUUID: string) =>
OPENMRS_FHIR_R4 + `/Condition?patient=${patientUUID}`;
export const PATIENT_ALLERGY_RESOURCE_URL = (patientUUID: string) =>
OPENMRS_FHIR_R4 + `/AllergyIntolerance?patient=${patientUUID}`;

export const DASHBOARD_CONFIG_URL = (dashboardURL: string) =>
`/bahmni_config/openmrs/apps/clinical/v2/dashboards/${dashboardURL}`;
export const CLINICAL_CONFIG_URL =
'/bahmni_config/openmrs/apps/clinical/v2/app.json';
export const LOGIN_PATH = '/bahmni/home/index.html#/login';
export const DEFAULT_LOCALE = 'en';
export const LOCALE_STORAGE_KEY = 'NG_TRANSLATE_LANG_KEY';
Expand Down
31 changes: 24 additions & 7 deletions src/constants/errors.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import i18next from 'i18next';
/**
* Configuration-related error messages
* Used for consistent error handling across the application
*/
export const CONFIG_ERROR_MESSAGES = {
CONFIG_NOT_FOUND: 'CONFIG_ERROR_NOT_FOUND',
VALIDATION_FAILED: 'CONFIG_ERROR_VALIDATION_FAILED',
SCHEMA_VALIDATION_FAILED: 'CONFIG_ERROR_SCHEMA_VALIDATION_FAILED',
};

/**
* Error title constants for notifications
*/
export const ERROR_TITLES = {
CONFIG_ERROR: 'ERROR_CONFIG_TITLE',
VALIDATION_ERROR: 'ERROR_VALIDATION_TITLE',
DASHBOARD_ERROR: 'ERROR_DASHBOARD_CONFIG_TITLE',
};

export const DATE_ERROR_MESSAGES = {
PARSE_ERROR: i18next.t('DATE_ERROR_PARSE'),
FORMAT_ERROR: i18next.t('DATE_ERROR_FORMAT'),
EMPTY_OR_INVALID: i18next.t('DATE_ERROR_EMPTY_OR_INVALID'),
INVALID_FORMAT: i18next.t('DATE_ERROR_INVALID_FORMAT'),
NULL_OR_UNDEFINED: i18next.t('DATE_ERROR_NULL_OR_UNDEFINED'),
} as const;
PARSE_ERROR: 'DATE_ERROR_PARSE',
FORMAT_ERROR: 'DATE_ERROR_FORMAT',
EMPTY_OR_INVALID: 'DATE_ERROR_EMPTY_OR_INVALID',
INVALID_FORMAT: 'DATE_ERROR_INVALID_FORMAT',
NULL_OR_UNDEFINED: 'DATE_ERROR_NULL_OR_UNDEFINED',
};
6 changes: 6 additions & 0 deletions src/contexts/ClinicalConfigContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createContext } from 'react';
import { ClinicalConfigContextType } from '@types/config';

export const ClinicalConfigContext = createContext<
ClinicalConfigContextType | undefined
>(undefined);
Loading