- Implemented FullPagePageIndex component with mock data for database clusters. - Added pagination and bulk actions for managing database clusters. - Created navigation context hooks for detail, edit, duplicate, and create actions. - Introduced a new store for managing state in full-page and single-page modules. feat: add navigation localization files - Added English and Indonesian localization files for navigation menu items. - Included translations for various modules including CRM, Sales, Supply Chain, and more. feat: create system information shortcuts component - Developed Shortcut component to display keyboard shortcuts with search functionality. - Implemented System component to show placeholder information when system details are unavailable. - Added localization for shortcuts and system information in English and Indonesian. feat: implement global theme store - Created a Zustand store for managing theme color scheme with localStorage persistence. feat: add module page header component - Developed ModulePageHeader component for consistent page header across modules. - Included breadcrumb navigation, title, description, and action buttons. feat: define default privileges for enterprise module - Established default privileges for CRUD operations and other actions in the enterprise module.
164 lines
6.0 KiB
TypeScript
164 lines
6.0 KiB
TypeScript
import { useMemo, useState, useEffect, ReactNode } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useTranslation } from '@repo/core-i18n';
|
|
import { BaseEntity, BaseRemoteDataServices } from '@repo/core-api/data-services';
|
|
|
|
import { ConfigSlice, ModuleConfigEntity, SinglePageFormState, SinglePageModalState } from '../entities/entity';
|
|
import {
|
|
EnterpriseConfigContext,
|
|
EnterpriseDataServiceContext,
|
|
EnterpriseSelectionContext,
|
|
EnterpriseNavigationContext,
|
|
EnterpriseModalContext,
|
|
EnterpriseTranslationContext,
|
|
} from '../hooks/use-module.context';
|
|
import { defaultPrivileges } from '../constant/default-privilege';
|
|
|
|
export interface EnterpriseModuleProviderProps<E extends BaseEntity> {
|
|
children: ReactNode;
|
|
config: ModuleConfigEntity<E>;
|
|
dataServices: BaseRemoteDataServices<E>;
|
|
}
|
|
|
|
export function EnterpriseModuleProvider<E extends BaseEntity>(props: EnterpriseModuleProviderProps<E>) {
|
|
const { children, config, dataServices } = props;
|
|
const navigate = useNavigate();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 1. Config Slice (Static)
|
|
// ---------------------------------------------------------------------------
|
|
const configSlice: ConfigSlice = useMemo(() => {
|
|
return {
|
|
config,
|
|
// FIXME => IMPLEMENT PRIVILEGE
|
|
privileges: defaultPrivileges,
|
|
};
|
|
}, [config]);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 1b. Translation Slice (Dedicated context — decoupled from config)
|
|
// ---------------------------------------------------------------------------
|
|
const namespaces = useMemo(() => [config.translationNamespace, 'common'], [config.translationNamespace]);
|
|
const { t } = useTranslation(namespaces);
|
|
const translationSlice = useMemo(() => ({ t: t as (key: string, options?: Record<string, unknown>) => string }), [t]);
|
|
|
|
// Set document title — uses explicit tabTitle or falls back to translation key 'title'
|
|
useEffect(() => {
|
|
const title = config.tabTitle || t('title');
|
|
if (title) {
|
|
document.title = title;
|
|
}
|
|
}, [config.tabTitle, t]);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 2. Data Service Slice (Stable refs)
|
|
// ---------------------------------------------------------------------------
|
|
const dataServiceSlice = useMemo(() => {
|
|
return { dataServices, privilege: undefined };
|
|
}, [dataServices]);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 3. Selection Slice (Dynamic state)
|
|
// ---------------------------------------------------------------------------
|
|
const [selectedRows, setSelectedRows] = useState<E[]>([]);
|
|
const [metaData, setMetaData] = useState<any>(null);
|
|
const [filterData, setFilterData] = useState<any>(null);
|
|
|
|
const selectionSlice = useMemo(
|
|
() => ({
|
|
selectedRows,
|
|
setSelectedRows,
|
|
metaData,
|
|
setMetaData,
|
|
filterData,
|
|
setFilterData,
|
|
}),
|
|
[selectedRows, metaData, filterData],
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 4. Modal Slice (For Single Page mode)
|
|
// ---------------------------------------------------------------------------
|
|
const [formState, setFormState] = useState<SinglePageFormState>({ open: false, formType: 'CREATE' });
|
|
const [detailState, setDetailState] = useState<SinglePageModalState>({ open: false });
|
|
|
|
const modalSlice = useMemo(
|
|
() => ({ formState, setFormState, detailState, setDetailState }),
|
|
[formState, detailState],
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 5. Navigation Slice
|
|
// ---------------------------------------------------------------------------
|
|
const navigationSlice = useMemo(() => {
|
|
const isSingle = config.moduleCategory === 'SINGLE_PAGE';
|
|
|
|
return {
|
|
navigateToIndex: () => {
|
|
if (isSingle) {
|
|
setFormState({ open: false, formType: 'CREATE' });
|
|
setDetailState({ open: false });
|
|
} else {
|
|
navigate(`${config.webUrl}/index`);
|
|
}
|
|
},
|
|
navigateToCreate: () => {
|
|
if (isSingle) {
|
|
setFormState({ open: true, formType: 'DUPLICATE' });
|
|
} else {
|
|
navigate(`${config.webUrl}/create`);
|
|
}
|
|
},
|
|
navigateToEdit: (id: string) => {
|
|
if (isSingle) {
|
|
setFormState({ open: true, formType: 'EDIT', dataId: id });
|
|
} else {
|
|
navigate(`${config.webUrl}/edit/${id}`);
|
|
}
|
|
},
|
|
navigateToDetail: (id: string) => {
|
|
if (isSingle) {
|
|
setDetailState({ open: true, dataId: id });
|
|
} else {
|
|
navigate(`${config.webUrl}/detail/${id}`);
|
|
}
|
|
},
|
|
navigateToDuplicate: (id: string) => {
|
|
if (isSingle) {
|
|
setFormState({ open: true, formType: 'DUPLICATE', dataId: id });
|
|
} else {
|
|
navigate(`${config.webUrl}/duplicate/${id}`);
|
|
}
|
|
},
|
|
};
|
|
}, [config.moduleCategory, config.webUrl, navigate]);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Render
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const { ALLOW_VIEW } = configSlice.privileges;
|
|
|
|
useEffect(() => {
|
|
if (!ALLOW_VIEW) navigate('/403', { replace: true });
|
|
}, [ALLOW_VIEW, navigate]);
|
|
|
|
if (!ALLOW_VIEW) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<EnterpriseConfigContext.Provider value={configSlice}>
|
|
<EnterpriseTranslationContext.Provider value={translationSlice}>
|
|
<EnterpriseDataServiceContext.Provider value={dataServiceSlice}>
|
|
<EnterpriseNavigationContext.Provider value={navigationSlice}>
|
|
<EnterpriseSelectionContext.Provider value={selectionSlice}>
|
|
<EnterpriseModalContext.Provider value={modalSlice}>{children}</EnterpriseModalContext.Provider>
|
|
</EnterpriseSelectionContext.Provider>
|
|
</EnterpriseNavigationContext.Provider>
|
|
</EnterpriseDataServiceContext.Provider>
|
|
</EnterpriseTranslationContext.Provider>
|
|
</EnterpriseConfigContext.Provider>
|
|
);
|
|
}
|