Files
trackgo-fe/packages/ui/src/foundations/enterprise-module/providers/module.provider.tsx
T
Firman Ramdhani 2b8f9a9cbc feat: add full page index component with pagination and actions
- 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.
2026-07-10 22:58:55 +07:00

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>
);
}