feat: enhance employee management with purpose-based configurations and improved routing
- Refactored employee management components to support purpose-based configurations for sales and logistics. - Updated routing to dynamically load employee modules based on the selected purpose, improving modularity and code organization. - Introduced new utility functions for handling employee positions and module configurations, enhancing flexibility in employee data handling. - Enhanced form components to conditionally render based on the purpose, improving user experience and data entry accuracy. - Added unit tests to validate new functionalities and ensure reliability across employee management features. These changes improve the employee management experience by providing a more tailored approach to handling employee data based on specific business needs.
This commit is contained in:
@@ -21,6 +21,7 @@ describe('MENU_ITEMS', () => {
|
||||
|
||||
expect(childKeys(sales)).toEqual(['sales-data', 'sales-activities', 'sales-reports']);
|
||||
expect(childKeys(findItem(sales?.children ?? [], 'sales-data'))).toEqual(['sales-employees', 'sales-cycles']);
|
||||
expect(findItem(sales?.children ?? [], 'sales-data')?.children?.[0]?.path).toBe('/app/sales/employees/index');
|
||||
expect(childKeys(findItem(sales?.children ?? [], 'sales-activities'))).toEqual([
|
||||
'sales-requests',
|
||||
'sales-orders',
|
||||
@@ -38,6 +39,9 @@ describe('MENU_ITEMS', () => {
|
||||
'logistics-employees',
|
||||
'logistics-cycles',
|
||||
]);
|
||||
expect(findItem(logistics?.children ?? [], 'logistics-data')?.children?.[0]?.path).toBe(
|
||||
'/app/logistics/employees/index',
|
||||
);
|
||||
expect(childKeys(findItem(logistics?.children ?? [], 'logistics-activities'))).toEqual([
|
||||
'logistics-packing-slips',
|
||||
'logistics-plans',
|
||||
|
||||
@@ -49,7 +49,7 @@ export const MENU_ITEMS: MenuItemType[] = [
|
||||
key: 'sales-employees',
|
||||
label: 'nav:configuration-employees',
|
||||
icon: Users,
|
||||
path: '/app/configuration/employees/index',
|
||||
path: '/app/sales/employees/index',
|
||||
moduleKey: 'CONFIGURATION.EMPLOYEE',
|
||||
},
|
||||
{
|
||||
@@ -129,7 +129,7 @@ export const MENU_ITEMS: MenuItemType[] = [
|
||||
key: 'logistics-employees',
|
||||
label: 'nav:configuration-employees',
|
||||
icon: Users,
|
||||
path: '/app/configuration/employees/index',
|
||||
path: '/app/logistics/employees/index',
|
||||
moduleKey: 'CONFIGURATION.EMPLOYEE',
|
||||
},
|
||||
{
|
||||
|
||||
+7
-1
@@ -1,6 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { AxiosInstance } from '@repo/core-api/http-client';
|
||||
import { EmployeesRemoteDataServices } from './employee.remote.service';
|
||||
import { EmployeesRemoteDataServices, serializeRepeatQuery } from './employee.remote.service';
|
||||
import { EmployeesRemoteDataTransformer } from '../domain/transformers/employee.remote.transformer';
|
||||
|
||||
function createMockHttpClient(): AxiosInstance {
|
||||
@@ -51,4 +51,10 @@ describe('EmployeesRemoteDataServices', () => {
|
||||
expect.objectContaining({ url: '/employees/bulk-delete', method: 'POST' }),
|
||||
);
|
||||
});
|
||||
|
||||
it('serializes position arrays as repeated query keys', () => {
|
||||
expect(serializeRepeatQuery({ page: 1, position: ['driver', 'crew'] })).toBe(
|
||||
'page=1&position=driver&position=crew',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+25
-1
@@ -1,8 +1,25 @@
|
||||
import type { AxiosInstance } from '@repo/core-api/http-client';
|
||||
import type { AxiosInstance, AxiosRequestConfig } from '@repo/core-api/http-client';
|
||||
import type { DataServicesConfig } from '@repo/core-api/data-services';
|
||||
import { TrackGoRemoteDataServices } from '../../../../../../core/lib/trackgo-remote-data-services';
|
||||
import type { EmployeeEntity } from '../domain/entities';
|
||||
|
||||
export function serializeRepeatQuery(params: Record<string, unknown>): string {
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
search.append(key, String(item));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
search.append(key, String(value));
|
||||
}
|
||||
return search.toString();
|
||||
}
|
||||
|
||||
export class EmployeesRemoteDataServices extends TrackGoRemoteDataServices<EmployeeEntity> {
|
||||
constructor(httpClient: AxiosInstance, config: DataServicesConfig<EmployeeEntity>) {
|
||||
super(httpClient, {
|
||||
@@ -10,4 +27,11 @@ export class EmployeesRemoteDataServices extends TrackGoRemoteDataServices<Emplo
|
||||
apiUrl: config.apiUrl ?? '/employees',
|
||||
});
|
||||
}
|
||||
|
||||
async getMany<T = unknown>(config?: AxiosRequestConfig) {
|
||||
return super.getMany<T>({
|
||||
...config,
|
||||
paramsSerializer: serializeRepeatQuery,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createEmployeeModuleConfig } from './employee.constants';
|
||||
import { employeePositionsForPurpose } from '../entities';
|
||||
|
||||
describe('employee purpose helpers', () => {
|
||||
it('maps sales to the sales position and logistics to crew and driver', () => {
|
||||
expect(employeePositionsForPurpose('sales')).toEqual(['sales']);
|
||||
expect(employeePositionsForPurpose('logistics')).toEqual(['driver', 'crew']);
|
||||
});
|
||||
|
||||
it('uses distinct web urls per purpose', () => {
|
||||
expect(createEmployeeModuleConfig('sales').webUrl).toBe('/app/sales/employees');
|
||||
expect(createEmployeeModuleConfig('logistics').webUrl).toBe('/app/logistics/employees');
|
||||
});
|
||||
|
||||
it('keeps a shared employee privilege key', () => {
|
||||
expect(createEmployeeModuleConfig('sales').moduleKey).toBe('CONFIGURATION.EMPLOYEE');
|
||||
expect(createEmployeeModuleConfig('logistics').moduleKey).toBe('CONFIGURATION.EMPLOYEE');
|
||||
});
|
||||
});
|
||||
+11
-8
@@ -1,11 +1,14 @@
|
||||
import { ModuleConfigEntity } from '@repo/ui/foundations';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import type { EmployeeEntity } from '../entities';
|
||||
|
||||
export const employeesModuleConfig: ModuleConfigEntity<EmployeeEntity> = {
|
||||
moduleKey: 'CONFIGURATION.EMPLOYEE',
|
||||
translationNamespace: 'EMPLOYEES',
|
||||
apiUrl: '/employees',
|
||||
webUrl: '/app/configuration/employees',
|
||||
moduleCategory: 'FULL_PAGE',
|
||||
moduleType: 'MASTER_DATA',
|
||||
} as const;
|
||||
export function createEmployeeModuleConfig(purpose: FieldPurpose): ModuleConfigEntity<EmployeeEntity> {
|
||||
return {
|
||||
moduleKey: 'CONFIGURATION.EMPLOYEE',
|
||||
translationNamespace: 'EMPLOYEES',
|
||||
apiUrl: '/employees',
|
||||
webUrl: `/app/${purpose}/employees`,
|
||||
moduleCategory: 'FULL_PAGE',
|
||||
moduleType: 'MASTER_DATA',
|
||||
};
|
||||
}
|
||||
|
||||
+9
@@ -1,9 +1,18 @@
|
||||
import { BaseEntity } from '@repo/core-api/data-services';
|
||||
import type { ConfigurationStatus } from '../../../divisions/domain/entities';
|
||||
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
|
||||
export const EMPLOYEE_POSITIONS = ['sales', 'driver', 'crew'] as const;
|
||||
export type EmployeePosition = (typeof EMPLOYEE_POSITIONS)[number];
|
||||
|
||||
export const SALES_EMPLOYEE_POSITIONS = ['sales'] as const satisfies readonly EmployeePosition[];
|
||||
export const LOGISTICS_EMPLOYEE_POSITIONS = ['driver', 'crew'] as const satisfies readonly EmployeePosition[];
|
||||
|
||||
export function employeePositionsForPurpose(purpose: FieldPurpose): readonly EmployeePosition[] {
|
||||
return purpose === 'logistics' ? LOGISTICS_EMPLOYEE_POSITIONS : SALES_EMPLOYEE_POSITIONS;
|
||||
}
|
||||
|
||||
export interface EmployeeEntity extends BaseEntity {
|
||||
code: string;
|
||||
name: string;
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { apiClient } from '../../../../../../../core/lib/api-client';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import { EmployeesRemoteDataServices } from '../../data/employee.remote.service';
|
||||
import { employeesModuleConfig } from '../constants/employee.constants';
|
||||
import { createEmployeeModuleConfig } from '../constants/employee.constants';
|
||||
import { employeePositionsForPurpose } from '../entities';
|
||||
import { EmployeesRemoteDataTransformer } from '../transformers/employee.remote.transformer';
|
||||
|
||||
export const employeesDataTransformer = new EmployeesRemoteDataTransformer();
|
||||
export function createEmployeeDataService(purpose?: FieldPurpose) {
|
||||
const config = createEmployeeModuleConfig(purpose ?? 'sales');
|
||||
const transformer = new EmployeesRemoteDataTransformer(purpose ? employeePositionsForPurpose(purpose) : undefined);
|
||||
return new EmployeesRemoteDataServices(apiClient, {
|
||||
apiUrl: config.apiUrl,
|
||||
moduleKey: config.moduleKey,
|
||||
transformer,
|
||||
});
|
||||
}
|
||||
|
||||
export const employeesDataService = new EmployeesRemoteDataServices(apiClient, {
|
||||
apiUrl: employeesModuleConfig.apiUrl,
|
||||
moduleKey: employeesModuleConfig.moduleKey,
|
||||
transformer: employeesDataTransformer,
|
||||
});
|
||||
export const employeesDataService = createEmployeeDataService();
|
||||
export const salesEmployeesDataService = createEmployeeDataService('sales');
|
||||
export const logisticsEmployeesDataService = createEmployeeDataService('logistics');
|
||||
|
||||
+49
-2
@@ -2,8 +2,6 @@ import { describe, expect, it } from 'vitest';
|
||||
import { EmployeesRemoteDataTransformer } from './employee.remote.transformer';
|
||||
import type { EmployeeEntity } from '../entities';
|
||||
|
||||
const transformer = new EmployeesRemoteDataTransformer();
|
||||
|
||||
const dto = {
|
||||
id: 'emp-1',
|
||||
code: 'EMP_01',
|
||||
@@ -18,6 +16,8 @@ const dto = {
|
||||
};
|
||||
|
||||
describe('EmployeesRemoteDataTransformer', () => {
|
||||
const transformer = new EmployeesRemoteDataTransformer();
|
||||
|
||||
it('maps dto fields onto the entity', () => {
|
||||
const entity = transformer.transformToEntity(dto);
|
||||
expect(entity).toMatchObject({
|
||||
@@ -52,4 +52,51 @@ describe('EmployeesRemoteDataTransformer', () => {
|
||||
expect(payload).not.toHaveProperty('status');
|
||||
expect(payload).not.toHaveProperty('id');
|
||||
});
|
||||
|
||||
it('leaves list filters unchanged when no positions are scoped', () => {
|
||||
const filter = transformer.transformPayloadFilter({ name: 'Ada', status: 'active' });
|
||||
expect(filter).toEqual({ name: 'Ada', status: 'active' });
|
||||
});
|
||||
|
||||
it('injects the sales position array into list filters', () => {
|
||||
const salesTransformer = new EmployeesRemoteDataTransformer(['sales']);
|
||||
expect(salesTransformer.transformPayloadFilter({ name: 'Ada' })).toEqual({
|
||||
name: 'Ada',
|
||||
position: ['sales'],
|
||||
});
|
||||
});
|
||||
|
||||
it('narrows a selected logistics position to an array still inside the allowed set', () => {
|
||||
const logisticsTransformer = new EmployeesRemoteDataTransformer(['driver', 'crew']);
|
||||
expect(logisticsTransformer.transformPayloadFilter({ position: 'crew' })).toEqual({
|
||||
position: ['crew'],
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to the allowed logistics positions when the selected position is invalid', () => {
|
||||
const logisticsTransformer = new EmployeesRemoteDataTransformer(['driver', 'crew']);
|
||||
expect(logisticsTransformer.transformPayloadFilter({ position: 'sales' })).toEqual({
|
||||
position: ['driver', 'crew'],
|
||||
});
|
||||
});
|
||||
|
||||
it('forces sales position on create when the module is sales-scoped', () => {
|
||||
const salesTransformer = new EmployeesRemoteDataTransformer(['sales']);
|
||||
expect(salesTransformer.transformCreatePayload({ ...dto, position: 'driver' })).toEqual({
|
||||
code: 'EMP_01',
|
||||
name: 'Ada Lovelace',
|
||||
phone: '+6281234567890',
|
||||
position: 'sales',
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps a logistics position on create when it is allowed', () => {
|
||||
const logisticsTransformer = new EmployeesRemoteDataTransformer(['driver', 'crew']);
|
||||
expect(logisticsTransformer.transformCreatePayload({ ...dto, position: 'crew' }).position).toBe('crew');
|
||||
});
|
||||
|
||||
it('drops a disallowed position on logistics create instead of forwarding it', () => {
|
||||
const logisticsTransformer = new EmployeesRemoteDataTransformer(['driver', 'crew']);
|
||||
expect(logisticsTransformer.transformCreatePayload({ ...dto, position: 'sales' }).position).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
+40
-3
@@ -1,7 +1,12 @@
|
||||
import { BaseDataTransformer } from '@repo/core-api/data-services';
|
||||
import type { EmployeeDto, EmployeeEntity } from '../entities';
|
||||
import { omitEmptyFields } from '../../../../../../../core/domain/configuration-field-validators';
|
||||
import type { EmployeeDto, EmployeeEntity, EmployeePosition } from '../entities';
|
||||
|
||||
export class EmployeesRemoteDataTransformer extends BaseDataTransformer<EmployeeEntity> {
|
||||
constructor(private readonly allowedPositions?: readonly EmployeePosition[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
transformToEntity(dto: EmployeeDto | EmployeeEntity): EmployeeEntity {
|
||||
return {
|
||||
id: dto.id,
|
||||
@@ -26,7 +31,7 @@ export class EmployeesRemoteDataTransformer extends BaseDataTransformer<Employee
|
||||
code: entity.code,
|
||||
name: entity.name,
|
||||
phone: entity.phone,
|
||||
position: entity.position,
|
||||
position: this.resolvePosition(entity.position),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +40,39 @@ export class EmployeesRemoteDataTransformer extends BaseDataTransformer<Employee
|
||||
code: entity.code,
|
||||
name: entity.name,
|
||||
phone: entity.phone,
|
||||
position: entity.position,
|
||||
position: this.resolvePosition(entity.position),
|
||||
};
|
||||
}
|
||||
|
||||
transformPayloadFilter(filter: Record<string, any>): Record<string, any> {
|
||||
if (!this.allowedPositions) {
|
||||
return omitEmptyFields(filter);
|
||||
}
|
||||
|
||||
return omitEmptyFields({ ...filter, position: this.resolveFilterPositions(filter.position) });
|
||||
}
|
||||
|
||||
private resolvePosition(position?: EmployeePosition): EmployeePosition | undefined {
|
||||
if (!this.allowedPositions) {
|
||||
return position;
|
||||
}
|
||||
if (position && this.allowedPositions.includes(position)) {
|
||||
return position;
|
||||
}
|
||||
return this.allowedPositions.length === 1 ? this.allowedPositions[0] : undefined;
|
||||
}
|
||||
|
||||
private resolveFilterPositions(selected: unknown): EmployeePosition[] {
|
||||
const allowed = [...this.allowedPositions!];
|
||||
if (typeof selected === 'string' && allowed.includes(selected as EmployeePosition)) {
|
||||
return [selected as EmployeePosition];
|
||||
}
|
||||
if (Array.isArray(selected)) {
|
||||
const intersection = selected.filter((value): value is EmployeePosition => allowed.includes(value));
|
||||
if (intersection.length > 0) {
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -22,4 +22,16 @@ describe('createEmployeeSchema', () => {
|
||||
it('rejects an unknown position', () => {
|
||||
expect(schema.safeParse({ ...valid, position: 'manager' }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects a logistics position when the schema is sales-scoped', () => {
|
||||
const salesSchema = createEmployeeSchema(t, ['sales']);
|
||||
expect(salesSchema.safeParse({ ...valid, position: 'driver' }).success).toBe(false);
|
||||
expect(salesSchema.safeParse(valid).success).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a sales position when the schema is logistics-scoped', () => {
|
||||
const logisticsSchema = createEmployeeSchema(t, ['driver', 'crew']);
|
||||
expect(logisticsSchema.safeParse(valid).success).toBe(false);
|
||||
expect(logisticsSchema.safeParse({ ...valid, position: 'crew' }).success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
+7
-3
@@ -4,14 +4,18 @@ import {
|
||||
configNameSchema,
|
||||
configPhoneSchema,
|
||||
} from '../../../../../../../core/domain/configuration-field-validators';
|
||||
import { EMPLOYEE_POSITIONS } from '../entities';
|
||||
import { EMPLOYEE_POSITIONS, type EmployeePosition } from '../entities';
|
||||
|
||||
export const createEmployeeSchema = (t: (key: string) => string) => {
|
||||
export const createEmployeeSchema = (
|
||||
t: (key: string) => string,
|
||||
positions: readonly EmployeePosition[] = EMPLOYEE_POSITIONS,
|
||||
) => {
|
||||
const allowed = positions as [EmployeePosition, ...EmployeePosition[]];
|
||||
return z.object({
|
||||
code: configCodeSchema(t),
|
||||
name: configNameSchema(t),
|
||||
phone: configPhoneSchema(t),
|
||||
position: z.enum(EMPLOYEE_POSITIONS, {
|
||||
position: z.enum(allowed, {
|
||||
required_error: JSON.stringify({ key: 'validation:required', values: { field: t('common:fields.position') } }),
|
||||
}),
|
||||
});
|
||||
|
||||
+12
-10
@@ -1,8 +1,8 @@
|
||||
import { Box, FieldTextInput, FieldSelect, Paper, SimpleGrid, Text } from '@repo/ui/components';
|
||||
import { useEnterpriseModuleTranslationContext, useFormPageContext } from '@repo/ui/foundations';
|
||||
import { EMPLOYEE_POSITIONS } from '../../../domain/entities';
|
||||
import type { EmployeePosition } from '../../../domain/entities';
|
||||
|
||||
export function FormGeneral() {
|
||||
export function FormGeneral({ positions }: { positions: readonly EmployeePosition[] }) {
|
||||
const { formControl } = useFormPageContext();
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
|
||||
@@ -37,14 +37,16 @@ export function FormGeneral() {
|
||||
required
|
||||
radius="md"
|
||||
/>
|
||||
<FieldSelect
|
||||
control={formControl.control}
|
||||
name="position"
|
||||
label={t('common:fields.position')}
|
||||
data={EMPLOYEE_POSITIONS.map((value) => ({ value, label: t(`position_${value}`) }))}
|
||||
required
|
||||
radius="md"
|
||||
/>
|
||||
{positions.length > 1 ? (
|
||||
<FieldSelect
|
||||
control={formControl.control}
|
||||
name="position"
|
||||
label={t('common:fields.position')}
|
||||
data={positions.map((value) => ({ value, label: t(`position_${value}`) }))}
|
||||
required
|
||||
radius="md"
|
||||
/>
|
||||
) : null}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
+19
-9
@@ -2,9 +2,17 @@ import { SimpleGrid } from '@repo/ui/components';
|
||||
import { FieldTextInput, FieldSelect } from '@repo/ui/form';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { statusFilterOptions } from '../../../../shared/status-filter-options';
|
||||
import { EMPLOYEE_POSITIONS } from '../../../domain/entities';
|
||||
import type { EmployeePosition } from '../../../domain/entities';
|
||||
|
||||
export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (key: string) => string }) => {
|
||||
export const FilterFormContent = ({
|
||||
form,
|
||||
t,
|
||||
positions,
|
||||
}: {
|
||||
form: UseFormReturn<any>;
|
||||
t: (key: string) => string;
|
||||
positions: readonly EmployeePosition[];
|
||||
}) => {
|
||||
return (
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||||
<FieldTextInput
|
||||
@@ -25,13 +33,15 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (k
|
||||
label={t('common:fields.phone')}
|
||||
placeholder={`Enter ${t('common:fields.phone')}`}
|
||||
/>
|
||||
<FieldSelect
|
||||
control={form.control}
|
||||
name="position"
|
||||
label={t('common:fields.position')}
|
||||
clearable
|
||||
data={EMPLOYEE_POSITIONS.map((value) => ({ value, label: t(`position_${value}`) }))}
|
||||
/>
|
||||
{positions.length > 1 ? (
|
||||
<FieldSelect
|
||||
control={form.control}
|
||||
name="position"
|
||||
label={t('common:fields.position')}
|
||||
clearable
|
||||
data={positions.map((value) => ({ value, label: t(`position_${value}`) }))}
|
||||
/>
|
||||
) : null}
|
||||
<FieldSelect
|
||||
control={form.control}
|
||||
name="status"
|
||||
|
||||
+17
-16
@@ -2,10 +2,11 @@ import { lazy } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { EnterpriseModuleProvider } from '@repo/ui/foundations';
|
||||
import { registerModuleNamespace } from '@repo/core-i18n';
|
||||
import { employeesModuleConfig } from '../../domain/constants';
|
||||
import { employeesDataService } from '../../domain/factories';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import { createEmployeeModuleConfig } from '../../domain/constants';
|
||||
import { logisticsEmployeesDataService, salesEmployeesDataService } from '../../domain/factories';
|
||||
import { EmployeeEntity } from '../../domain/entities';
|
||||
import { employeesStore } from '../store';
|
||||
import { logisticsEmployeesStore, salesEmployeesStore } from '../store';
|
||||
|
||||
import employeesId from '../languages/id/employees.json';
|
||||
import employeesEn from '../languages/en/employees.json';
|
||||
@@ -14,25 +15,25 @@ const IndexPage = lazy(() => import('../pages/employee.page.index'));
|
||||
const FormPage = lazy(() => import('../pages/employee.page.form'));
|
||||
const DetailPage = lazy(() => import('../pages/employee.page.detail'));
|
||||
|
||||
registerModuleNamespace(employeesModuleConfig.translationNamespace, {
|
||||
registerModuleNamespace('EMPLOYEES', {
|
||||
id: employeesId,
|
||||
en: employeesEn,
|
||||
});
|
||||
|
||||
export default function EmployeesModule() {
|
||||
export default function EmployeesModule({ purpose }: { purpose: FieldPurpose }) {
|
||||
const config = createEmployeeModuleConfig(purpose);
|
||||
const dataService = purpose === 'sales' ? salesEmployeesDataService : logisticsEmployeesDataService;
|
||||
const store = purpose === 'sales' ? salesEmployeesStore : logisticsEmployeesStore;
|
||||
|
||||
return (
|
||||
<EnterpriseModuleProvider<EmployeeEntity>
|
||||
config={employeesModuleConfig}
|
||||
dataServices={employeesDataService}
|
||||
store={employeesStore}
|
||||
>
|
||||
<EnterpriseModuleProvider<EmployeeEntity> config={config} dataServices={dataService} store={store}>
|
||||
<Routes>
|
||||
<Route path="/index" element={<IndexPage />} />
|
||||
<Route path="/detail/:dataId" element={<DetailPage />} />
|
||||
<Route path="/edit/:dataId" element={<FormPage formPageType="EDIT" />} />
|
||||
<Route path="/duplicate/:dataId" element={<FormPage formPageType="DUPLICATE" />} />
|
||||
<Route path="/create" element={<FormPage formPageType="CREATE" />} />
|
||||
<Route path="/" element={<Navigate to={`${employeesModuleConfig.webUrl}/index`} replace={true} />} />
|
||||
<Route path="/index" element={<IndexPage purpose={purpose} />} />
|
||||
<Route path="/detail/:dataId" element={<DetailPage purpose={purpose} />} />
|
||||
<Route path="/edit/:dataId" element={<FormPage purpose={purpose} formPageType="EDIT" />} />
|
||||
<Route path="/duplicate/:dataId" element={<FormPage purpose={purpose} formPageType="DUPLICATE" />} />
|
||||
<Route path="/create" element={<FormPage purpose={purpose} formPageType="CREATE" />} />
|
||||
<Route path="/" element={<Navigate to={`${config.webUrl}/index`} replace={true} />} />
|
||||
<Route path="*" element={<Navigate to={'/404'} replace={true} />} />
|
||||
</Routes>
|
||||
</EnterpriseModuleProvider>
|
||||
|
||||
+6
-4
@@ -1,9 +1,11 @@
|
||||
import { EnterpriseDetailPageProvider, useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
|
||||
import { employeesModuleConfig } from '../../domain/constants';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import { createEmployeeModuleConfig } from '../../domain/constants';
|
||||
import { DetailGeneral } from '../components/detail-component/detail-general';
|
||||
|
||||
export default function EmployeePageDetail() {
|
||||
export default function EmployeePageDetail({ purpose }: { purpose: FieldPurpose }) {
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
const moduleConfig = createEmployeeModuleConfig(purpose);
|
||||
|
||||
return (
|
||||
<EnterpriseDetailPageProvider
|
||||
@@ -12,8 +14,8 @@ export default function EmployeePageDetail() {
|
||||
title: t('detail_page_title'),
|
||||
description: t('detail_page_description'),
|
||||
breadcrumbs: [
|
||||
{ label: t('nav:configuration'), type: 'text' },
|
||||
{ label: t('nav:configuration-employees'), type: 'link', href: `${employeesModuleConfig.webUrl}/index` },
|
||||
{ label: t(`nav:${purpose}`), type: 'text' },
|
||||
{ label: t('nav:configuration-employees'), type: 'link', href: `${moduleConfig.webUrl}/index` },
|
||||
],
|
||||
}}
|
||||
>
|
||||
|
||||
+20
-7
@@ -2,12 +2,22 @@ import { useMemo } from 'react';
|
||||
import { useEnterpriseModuleTranslationContext, EnterpriseFormPageProvider, FormPageType } from '@repo/ui/foundations';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { employeesModuleConfig } from '../../domain/constants';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import { createEmployeeModuleConfig } from '../../domain/constants';
|
||||
import { employeePositionsForPurpose } from '../../domain/entities';
|
||||
import { createEmployeeSchema } from '../../domain/validators/employee.validator';
|
||||
import { FormGeneral } from '../components/form-component/form-general';
|
||||
|
||||
export default function EmployeePageForm({ formPageType }: { formPageType: FormPageType }) {
|
||||
export default function EmployeePageForm({
|
||||
formPageType,
|
||||
purpose,
|
||||
}: {
|
||||
formPageType: FormPageType;
|
||||
purpose: FieldPurpose;
|
||||
}) {
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
const moduleConfig = createEmployeeModuleConfig(purpose);
|
||||
const positions = employeePositionsForPurpose(purpose);
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (formPageType === 'CREATE') {
|
||||
@@ -22,8 +32,11 @@ export default function EmployeePageForm({ formPageType }: { formPageType: FormP
|
||||
return { title: '', description: '' };
|
||||
}, [formPageType, t]);
|
||||
|
||||
const validator = useMemo(() => createEmployeeSchema(t), [t]);
|
||||
const formControl = useForm({ resolver: zodResolver(validator) });
|
||||
const validator = useMemo(() => createEmployeeSchema(t, positions), [positions, t]);
|
||||
const formControl = useForm({
|
||||
resolver: zodResolver(validator),
|
||||
defaultValues: positions.length === 1 ? { position: positions[0] } : undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<EnterpriseFormPageProvider
|
||||
@@ -36,12 +49,12 @@ export default function EmployeePageForm({ formPageType }: { formPageType: FormP
|
||||
title: title?.title,
|
||||
description: title?.description,
|
||||
breadcrumbs: [
|
||||
{ label: t('nav:configuration'), type: 'text' },
|
||||
{ label: t('nav:configuration-employees'), type: 'link', href: `${employeesModuleConfig.webUrl}/index` },
|
||||
{ label: t(`nav:${purpose}`), type: 'text' },
|
||||
{ label: t('nav:configuration-employees'), type: 'link', href: `${moduleConfig.webUrl}/index` },
|
||||
],
|
||||
}}
|
||||
>
|
||||
<FormGeneral />
|
||||
<FormGeneral positions={positions} />
|
||||
</EnterpriseFormPageProvider>
|
||||
);
|
||||
}
|
||||
|
||||
+7
-5
@@ -7,11 +7,13 @@ import {
|
||||
import { ColDef, Text } from '@repo/ui/components';
|
||||
import { Trans } from '@repo/core-i18n';
|
||||
import { Users } from 'lucide-react';
|
||||
import type { FieldPurpose } from '../../../../../../../core/domain/field-purpose';
|
||||
import { employeePositionsForPurpose, type EmployeeEntity } from '../../domain/entities';
|
||||
import { FilterFormContent } from '../components/index-component/filter-content';
|
||||
import type { EmployeeEntity } from '../../domain/entities';
|
||||
|
||||
export default function EmployeePageIndex() {
|
||||
export default function EmployeePageIndex({ purpose }: { purpose: FieldPurpose }) {
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
const positions = employeePositionsForPurpose(purpose);
|
||||
|
||||
const columnDefs: ColDef<EmployeeEntity>[] = useMemo(() => {
|
||||
return [
|
||||
@@ -31,10 +33,10 @@ export default function EmployeePageIndex() {
|
||||
return {
|
||||
renderBody: (form: any) => {
|
||||
if (!form) return null;
|
||||
return <FilterFormContent form={form} t={t} />;
|
||||
return <FilterFormContent form={form} t={t} positions={positions} />;
|
||||
},
|
||||
};
|
||||
}, [t]);
|
||||
}, [positions, t]);
|
||||
|
||||
return (
|
||||
<EnterpriseIndexPageProvider
|
||||
@@ -45,7 +47,7 @@ export default function EmployeePageIndex() {
|
||||
),
|
||||
icon: Users,
|
||||
breadcrumbs: [
|
||||
{ label: t('nav:configuration'), type: 'text' },
|
||||
{ label: t(`nav:${purpose}`), type: 'text' },
|
||||
{ label: t('nav:configuration-employees'), type: 'text' },
|
||||
],
|
||||
}}
|
||||
|
||||
+16
-15
@@ -4,19 +4,20 @@ import { EmployeeEntity } from '../../domain/entities';
|
||||
|
||||
export interface EmployeesStoreState extends EnterpriseModuleState<EmployeeEntity> {}
|
||||
|
||||
export const employeesStore = create<EmployeesStoreState>((set) => ({
|
||||
metaData: { limit: 15 },
|
||||
setMetaData: (data) => set({ metaData: data }),
|
||||
export function createEmployeesStore() {
|
||||
return create<EmployeesStoreState>((set) => ({
|
||||
metaData: { limit: 15 },
|
||||
setMetaData: (data) => set({ metaData: data }),
|
||||
filterData: {},
|
||||
setFilterData: (data) => set({ filterData: data }),
|
||||
selectedRows: [],
|
||||
setSelectedRows: (rows) => set({ selectedRows: rows }),
|
||||
privileges: [],
|
||||
setPrivileges: (privileges) => set({ privileges }),
|
||||
tableConfig: null,
|
||||
setTableConfig: (config) => set({ tableConfig: config }),
|
||||
}));
|
||||
}
|
||||
|
||||
filterData: {},
|
||||
setFilterData: (data) => set({ filterData: data }),
|
||||
|
||||
selectedRows: [],
|
||||
setSelectedRows: (rows) => set({ selectedRows: rows }),
|
||||
|
||||
privileges: [],
|
||||
setPrivileges: (privileges) => set({ privileges }),
|
||||
|
||||
tableConfig: null,
|
||||
setTableConfig: (config) => set({ tableConfig: config }),
|
||||
}));
|
||||
export const salesEmployeesStore = createEmployeesStore();
|
||||
export const logisticsEmployeesStore = createEmployeesStore();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { lazy } from 'react';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { WEB_URL } from '../../../../core/constants/web-url';
|
||||
|
||||
const DivisionsModule = lazy(() => import('./divisions/presentation/factory'));
|
||||
const BranchesModule = lazy(() => import('./branches/presentation/factory'));
|
||||
const CustomersModule = lazy(() => import('./customers/presentation/factory'));
|
||||
const EmployeesModule = lazy(() => import('./employees/presentation/factory'));
|
||||
const ProductsModule = lazy(() => import('./products/presentation/factory'));
|
||||
|
||||
export default function ConfigurationModule() {
|
||||
@@ -13,8 +13,8 @@ export default function ConfigurationModule() {
|
||||
<Route path="/divisions/*" element={<DivisionsModule />} />
|
||||
<Route path="/branches/*" element={<BranchesModule />} />
|
||||
<Route path="/customers/*" element={<CustomersModule />} />
|
||||
<Route path="/employees/*" element={<EmployeesModule />} />
|
||||
<Route path="/products/*" element={<ProductsModule />} />
|
||||
<Route path="/employees/*" element={<Navigate to={`${WEB_URL.SALES_EMPLOYEES}/index`} replace={true} />} />
|
||||
<Route path="*" element={<Navigate to={'/404'} replace={true} />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
+10
-3
@@ -1,12 +1,19 @@
|
||||
import { Box, FieldAsyncSelect, FieldNumberInput, Paper, SimpleGrid, Text } from '@repo/ui/components';
|
||||
import { useEnterpriseModuleTranslationContext, useFormPageContext } from '@repo/ui/foundations';
|
||||
import { loadEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import {
|
||||
useEnterpriseModuleTranslationContext,
|
||||
useEnterpriseModuleConfigContext,
|
||||
useFormPageContext,
|
||||
} from '@repo/ui/foundations';
|
||||
import { purposeFromModuleKey } from '../../../../../../../../core/domain/field-purpose';
|
||||
import { loadLogisticsEmployeeOptions, loadSalesEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { relationLabel } from '../../../../shared/relation-label';
|
||||
import type { EmployeeEntity } from '../../../../../configuration/employees/domain/entities';
|
||||
|
||||
export function FormGeneral() {
|
||||
const { formControl } = useFormPageContext();
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
const { config } = useEnterpriseModuleConfigContext();
|
||||
const purpose = purposeFromModuleKey(config.moduleKey);
|
||||
const employee = formControl.watch('employee') as EmployeeEntity | null | undefined;
|
||||
|
||||
return (
|
||||
@@ -25,7 +32,7 @@ export function FormGeneral() {
|
||||
labelKey="name"
|
||||
required
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={purpose === 'sales' ? loadSalesEmployeeOptions : loadLogisticsEmployeeOptions}
|
||||
defaultOptions={employee ? [employee] : []}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
|
||||
+7
-2
@@ -1,12 +1,17 @@
|
||||
import { SimpleGrid } from '@repo/ui/components';
|
||||
import { FieldAsyncSelect, FieldNumberInput, FieldSelect } from '@repo/ui/form';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { useEnterpriseModuleConfigContext } from '@repo/ui/foundations';
|
||||
import { purposeFromModuleKey } from '../../../../../../../../core/domain/field-purpose';
|
||||
import { statusFilterOptions } from '../../../../../configuration/shared/status-filter-options';
|
||||
import { loadEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { loadLogisticsEmployeeOptions, loadSalesEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { relationLabel } from '../../../../shared/relation-label';
|
||||
import type { EmployeeEntity } from '../../../../../configuration/employees/domain/entities';
|
||||
|
||||
export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (key: string) => string }) => {
|
||||
const { config } = useEnterpriseModuleConfigContext();
|
||||
const purpose = purposeFromModuleKey(config.moduleKey);
|
||||
|
||||
return (
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||||
<FieldAsyncSelect<EmployeeEntity>
|
||||
@@ -18,7 +23,7 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (k
|
||||
labelKey="name"
|
||||
clearable
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={purpose === 'sales' ? loadSalesEmployeeOptions : loadLogisticsEmployeeOptions}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
<FieldNumberInput
|
||||
|
||||
@@ -2,12 +2,14 @@ import { lazy } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { EmbeddedComingSoonPage } from '../../../../../core/components/coming-soon-page';
|
||||
|
||||
const EmployeesModule = lazy(() => import('../../configuration/employees/presentation/factory'));
|
||||
const CyclesModule = lazy(() => import('../cycles/presentation/factory'));
|
||||
const PlansModule = lazy(() => import('../plans/presentation/factory'));
|
||||
|
||||
export default function LogisticsFieldModule() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/employees/*" element={<EmployeesModule purpose="logistics" />} />
|
||||
<Route path="/cycles/*" element={<CyclesModule purpose="logistics" />} />
|
||||
<Route path="/plans/*" element={<PlansModule purpose="logistics" />} />
|
||||
<Route path="/packing-slips" element={<EmbeddedComingSoonPage />} />
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import {
|
||||
useEnterpriseModuleConfigContext,
|
||||
} from '@repo/ui/foundations';
|
||||
import { purposeFromModuleKey } from '../../../../../../../../core/domain/field-purpose';
|
||||
import { loadEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { loadLogisticsEmployeeOptions, loadSalesEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { loadBranchOptions } from '../../../../shared/load-branch-options';
|
||||
import { loadCustomerOptions } from '../../../../shared/load-customer-options';
|
||||
import { loadSalesInvoiceOptions, loadPackingSlipOptions } from '../../../../shared/lookup.factories';
|
||||
@@ -42,7 +42,7 @@ export function FormGeneral() {
|
||||
labelKey="name"
|
||||
required
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={purpose === 'sales' ? loadSalesEmployeeOptions : loadLogisticsEmployeeOptions}
|
||||
defaultOptions={employee ? [employee] : []}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
|
||||
+7
-2
@@ -1,12 +1,17 @@
|
||||
import { SimpleGrid } from '@repo/ui/components';
|
||||
import { FieldAsyncSelect, FieldDatePicker, FieldSelect } from '@repo/ui/form';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { useEnterpriseModuleConfigContext } from '@repo/ui/foundations';
|
||||
import { purposeFromModuleKey } from '../../../../../../../../core/domain/field-purpose';
|
||||
import { statusFilterOptions } from '../../../../../configuration/shared/status-filter-options';
|
||||
import { loadEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { loadLogisticsEmployeeOptions, loadSalesEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { relationLabel } from '../../../../shared/relation-label';
|
||||
import type { EmployeeEntity } from '../../../../../configuration/employees/domain/entities';
|
||||
|
||||
export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (key: string) => string }) => {
|
||||
const { config } = useEnterpriseModuleConfigContext();
|
||||
const purpose = purposeFromModuleKey(config.moduleKey);
|
||||
|
||||
return (
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||||
<FieldAsyncSelect<EmployeeEntity>
|
||||
@@ -17,7 +22,7 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: (k
|
||||
labelKey="name"
|
||||
clearable
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={purpose === 'sales' ? loadSalesEmployeeOptions : loadLogisticsEmployeeOptions}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
<FieldDatePicker control={form.control} name="date" label={t('common:fields.date')} clearable />
|
||||
|
||||
+10
-3
@@ -2,8 +2,13 @@ import { useMemo } from 'react';
|
||||
import { Button, FieldAsyncSelect, FieldDatePicker, Group, Modal, Stack, notifications } from '@repo/ui/components';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useEnterpriseModuleTranslationContext, useEnterpriseModuleDataServiceContext } from '@repo/ui/foundations';
|
||||
import { loadEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import {
|
||||
useEnterpriseModuleConfigContext,
|
||||
useEnterpriseModuleDataServiceContext,
|
||||
useEnterpriseModuleTranslationContext,
|
||||
} from '@repo/ui/foundations';
|
||||
import { purposeFromModuleKey } from '../../../../../../../../core/domain/field-purpose';
|
||||
import { loadLogisticsEmployeeOptions, loadSalesEmployeeOptions } from '../../../../shared/load-employee-options';
|
||||
import { relationLabel } from '../../../../shared/relation-label';
|
||||
import { createGeneratePlansSchema } from '../../../domain/validators/plan.validator';
|
||||
import type { PlansRemoteDataServices } from '../../../data/plan.remote.service';
|
||||
@@ -12,6 +17,8 @@ import type { PlanEntity } from '../../../domain/entities';
|
||||
|
||||
export function GeneratePlansModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
|
||||
const { t } = useEnterpriseModuleTranslationContext();
|
||||
const { config } = useEnterpriseModuleConfigContext();
|
||||
const purpose = purposeFromModuleKey(config.moduleKey);
|
||||
const { dataServices } = useEnterpriseModuleDataServiceContext<PlanEntity, PlansRemoteDataServices>();
|
||||
const validator = useMemo(() => createGeneratePlansSchema(t), [t]);
|
||||
const form = useForm({ resolver: zodResolver(validator) });
|
||||
@@ -45,7 +52,7 @@ export function GeneratePlansModal({ opened, onClose }: { opened: boolean; onClo
|
||||
labelKey="name"
|
||||
required
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={purpose === 'sales' ? loadSalesEmployeeOptions : loadLogisticsEmployeeOptions}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
<FieldDatePicker control={form.control as any} name="from" label={t('common:fields.from')} required />
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
import { employeesDataService } from '../../configuration/employees/domain/factories';
|
||||
import type { LoadOptionsFn } from '@repo/ui/form';
|
||||
import type { FieldPurpose } from '../../../../../core/domain/field-purpose';
|
||||
import {
|
||||
employeesDataService,
|
||||
logisticsEmployeesDataService,
|
||||
salesEmployeesDataService,
|
||||
} from '../../configuration/employees/domain/factories';
|
||||
import type { EmployeeEntity } from '../../configuration/employees/domain/entities';
|
||||
import { createOptionLoader } from './create-option-loader';
|
||||
|
||||
export const loadEmployeeOptions = createOptionLoader<EmployeeEntity>((config) => employeesDataService.getMany(config));
|
||||
function employeeServiceForPurpose(purpose?: FieldPurpose) {
|
||||
if (purpose === 'sales') {
|
||||
return salesEmployeesDataService;
|
||||
}
|
||||
if (purpose === 'logistics') {
|
||||
return logisticsEmployeesDataService;
|
||||
}
|
||||
return employeesDataService;
|
||||
}
|
||||
|
||||
export function loadEmployeeOptionsForPurpose(purpose?: FieldPurpose): LoadOptionsFn<EmployeeEntity> {
|
||||
const service = employeeServiceForPurpose(purpose);
|
||||
return createOptionLoader<EmployeeEntity>((config) => service.getMany(config));
|
||||
}
|
||||
|
||||
export const loadSalesEmployeeOptions = loadEmployeeOptionsForPurpose('sales');
|
||||
export const loadLogisticsEmployeeOptions = loadEmployeeOptionsForPurpose('logistics');
|
||||
|
||||
@@ -4,12 +4,14 @@ import { EmbeddedComingSoonPage } from '../../../../core/components/coming-soon-
|
||||
|
||||
const RequestsModule = lazy(() => import('./requests/presentation/factory'));
|
||||
const OrdersModule = lazy(() => import('./orders/presentation/factory'));
|
||||
const EmployeesModule = lazy(() => import('../configuration/employees/presentation/factory'));
|
||||
const CyclesModule = lazy(() => import('../field/cycles/presentation/factory'));
|
||||
const PlansModule = lazy(() => import('../field/plans/presentation/factory'));
|
||||
|
||||
export default function SalesModule() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/employees/*" element={<EmployeesModule purpose="sales" />} />
|
||||
<Route path="/requests/*" element={<RequestsModule />} />
|
||||
<Route path="/orders/*" element={<OrdersModule />} />
|
||||
<Route path="/cycles/*" element={<CyclesModule purpose="sales" />} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SimpleGrid } from '@repo/ui/components';
|
||||
import { FieldAsyncSelect, FieldSelect, FieldTextInput } from '@repo/ui/form';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { loadEmployeeOptions } from '../../field/shared/load-employee-options';
|
||||
import { loadSalesEmployeeOptions } from '../../field/shared/load-employee-options';
|
||||
import { loadBranchOptions } from '../../field/shared/load-branch-options';
|
||||
import { loadCustomerOptions } from '../../field/shared/load-customer-options';
|
||||
import { loadDivisionOptions } from '../../configuration/shared/load-division-options';
|
||||
@@ -50,7 +50,7 @@ export function SalesFilterFormContent({
|
||||
labelKey="name"
|
||||
searchable
|
||||
clearable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={loadSalesEmployeeOptions}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
<FieldAsyncSelect<BranchEntity>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box, FieldAsyncSelect, FieldDatePicker, FieldTextInput, Paper, SimpleGrid, Text } from '@repo/ui/components';
|
||||
import { useEnterpriseModuleTranslationContext, useFormPageContext } from '@repo/ui/foundations';
|
||||
import { loadEmployeeOptions } from '../../field/shared/load-employee-options';
|
||||
import { loadSalesEmployeeOptions } from '../../field/shared/load-employee-options';
|
||||
import { loadBranchOptions } from '../../field/shared/load-branch-options';
|
||||
import { loadCustomerOptions } from '../../field/shared/load-customer-options';
|
||||
import { loadDivisionOptions } from '../../configuration/shared/load-division-options';
|
||||
@@ -47,7 +47,7 @@ export function FormGeneral() {
|
||||
labelKey="name"
|
||||
required
|
||||
searchable
|
||||
loadOptions={loadEmployeeOptions}
|
||||
loadOptions={loadSalesEmployeeOptions}
|
||||
defaultOptions={salesPerson ? [salesPerson] : []}
|
||||
renderLabel={relationLabel}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,8 @@ export const WEB_URL = {
|
||||
DIVISIONS: '/app/configuration/divisions',
|
||||
BRANCHES: '/app/configuration/branches',
|
||||
CUSTOMERS: '/app/configuration/customers',
|
||||
EMPLOYEES: '/app/configuration/employees',
|
||||
SALES_EMPLOYEES: '/app/sales/employees',
|
||||
LOGISTICS_EMPLOYEES: '/app/logistics/employees',
|
||||
PRODUCTS: '/app/configuration/products',
|
||||
SALES_REQUESTS: '/app/sales/requests',
|
||||
SALES_ORDERS: '/app/sales/orders',
|
||||
|
||||
Reference in New Issue
Block a user