feat: enhance user entity and transformer with employeeId support

- Added employeeId field to UserEntity and UserDto interfaces for better user management.
- Updated UsersRemoteDataTransformer to handle employeeId mapping and transformation.
- Enhanced unit tests to validate employeeId handling and added new test cases for audit actor UUIDs.
- Modified user page index to display employeeId alongside employee details.

These changes improve the user management module by providing additional employee identification capabilities, enhancing data handling and user experience.
This commit is contained in:
shancheas
2026-08-27 11:06:08 +07:00
parent 9e710a92b2
commit eadd4e3c81
4 changed files with 47 additions and 9 deletions
@@ -8,6 +8,7 @@ export interface UserEntity extends BaseEntity {
isSuperadmin?: boolean; isSuperadmin?: boolean;
privilegeId?: string | null; privilegeId?: string | null;
privilege?: RelationRef | null; privilege?: RelationRef | null;
employeeId?: string | null;
employee?: RelationRef | null; employee?: RelationRef | null;
status?: ConfigurationStatus; status?: ConfigurationStatus;
createdAt?: number; createdAt?: number;
@@ -22,10 +23,11 @@ export interface UserDto {
isSuperadmin?: boolean; isSuperadmin?: boolean;
privilegeId?: string | null; privilegeId?: string | null;
privilege?: RelationRef | null; privilege?: RelationRef | null;
employeeId?: string | null;
employee?: RelationRef | null; employee?: RelationRef | null;
status?: ConfigurationStatus; status?: ConfigurationStatus;
createdAt?: number; createdAt?: number;
updatedAt?: number; updatedAt?: number;
createdBy?: string | { id: string; username: string }; createdBy?: string | { id: string; username?: string };
updatedBy?: string | { id: string; username: string }; updatedBy?: string | { id: string; username?: string };
} }
@@ -27,11 +27,22 @@ describe('UsersRemoteDataTransformer', () => {
privilegeId: 'priv-1', privilegeId: 'priv-1',
privilege: { id: 'priv-1', code: 'SALES_STAFF', name: 'Sales Staff' }, privilege: { id: 'priv-1', code: 'SALES_STAFF', name: 'Sales Staff' },
employee: { id: 'emp-1', code: 'EMP_01', name: 'Ada Lovelace' }, employee: { id: 'emp-1', code: 'EMP_01', name: 'Ada Lovelace' },
employeeId: 'emp-1',
createdBy: 'admin', createdBy: 'admin',
updatedBy: 'bob', updatedBy: 'bob',
}); });
}); });
it('keeps audit actor uuids and falls back to id when username is missing', () => {
const entity = transformer.transformToEntity({
...dto,
createdBy: '550e8400-e29b-41d4-a716-446655440000',
updatedBy: { id: 'u-99' },
});
expect(entity.createdBy).toBe('550e8400-e29b-41d4-a716-446655440000');
expect(entity.updatedBy).toBe('u-99');
});
it('builds create payload with username, password, and privilegeId, without status', () => { it('builds create payload with username, password, and privilegeId, without status', () => {
const entity: UserEntity = { const entity: UserEntity = {
id: dto.id, id: dto.id,
@@ -120,4 +131,15 @@ describe('UsersRemoteDataTransformer', () => {
privilegeId: 'priv-1', privilegeId: 'priv-1',
}); });
}); });
it('maps privilege string to privilegeId on filter payload', () => {
const payload = transformer.transformPayloadFilter({
username: 'alice',
privilege: 'priv-1',
});
expect(payload).toEqual({
username: 'alice',
privilegeId: 'priv-1',
});
});
}); });
@@ -11,9 +11,12 @@ function resolvePrivilegeId(entity: Partial<UserEntity>): string | null | undefi
} }
function flattenActor(value: unknown): string | undefined { function flattenActor(value: unknown): string | undefined {
if (typeof value === 'string') return value; if (typeof value === 'string') return value || undefined;
if (value && typeof value === 'object' && 'username' in value) { if (value && typeof value === 'object') {
return String((value as { username: unknown }).username); const actor = value as { username?: unknown; name?: unknown; id?: unknown };
const label = actor.username ?? actor.name ?? actor.id;
if (label === undefined || label === null || label === '') return undefined;
return String(label);
} }
return undefined; return undefined;
} }
@@ -22,13 +25,16 @@ export class UsersRemoteDataTransformer extends BaseDataTransformer<UserEntity>
transformToEntity(dto: UserDto | UserEntity): UserEntity { transformToEntity(dto: UserDto | UserEntity): UserEntity {
const privilegeId = 'privilegeId' in dto ? dto.privilegeId : (dto as UserEntity).privilegeId; const privilegeId = 'privilegeId' in dto ? dto.privilegeId : (dto as UserEntity).privilegeId;
const privilege = (dto as UserEntity).privilege ?? null; const privilege = (dto as UserEntity).privilege ?? null;
const employee = (dto as UserEntity).employee ?? null;
const employeeId = 'employeeId' in dto ? dto.employeeId : (dto as UserEntity).employeeId;
return { return {
id: dto.id, id: dto.id,
username: dto.username, username: dto.username,
isSuperadmin: dto.isSuperadmin, isSuperadmin: dto.isSuperadmin,
privilegeId: privilegeId ?? privilege?.id ?? null, privilegeId: privilegeId ?? privilege?.id ?? null,
privilege, privilege,
employee: (dto as UserEntity).employee ?? null, employeeId: employeeId ?? employee?.id ?? null,
employee,
status: dto.status, status: dto.status,
createdAt: dto.createdAt, createdAt: dto.createdAt,
updatedAt: dto.updatedAt, updatedAt: dto.updatedAt,
@@ -65,6 +71,9 @@ export class UsersRemoteDataTransformer extends BaseDataTransformer<UserEntity>
if (next.privilege && typeof next.privilege === 'object') { if (next.privilege && typeof next.privilege === 'object') {
next.privilegeId = next.privilege.id; next.privilegeId = next.privilege.id;
delete next.privilege; delete next.privilege;
} else if (typeof next.privilege === 'string') {
next.privilegeId = next.privilege;
delete next.privilege;
} }
return omitEmptyFields(next); return omitEmptyFields(next);
} }
@@ -21,25 +21,30 @@ export default function UserPageIndex() {
field: 'privilege', field: 'privilege',
headerName: t('common:fields.privilege'), headerName: t('common:fields.privilege'),
minWidth: 180, minWidth: 180,
valueGetter: ({ data }) => relationLabel(data?.privilege), valueGetter: ({ data }) => relationLabel(data?.privilege) || data?.privilegeId || '',
}, },
{ {
field: 'employee', field: 'employee',
headerName: t('common:fields.employee'), headerName: t('common:fields.employee'),
minWidth: 180, minWidth: 180,
valueGetter: ({ data }) => relationLabel(data?.employee), valueGetter: ({ data }) => relationLabel(data?.employee) || data?.employeeId || '',
}, },
{ {
field: 'isSuperadmin', field: 'isSuperadmin',
headerName: t('common:fields.isSuperadmin'), headerName: t('common:fields.isSuperadmin'),
minWidth: 140, minWidth: 140,
valueFormatter: ({ value }) => t(value ? 'yes' : 'no'), valueGetter: ({ data }) => t(data?.isSuperadmin ? 'yes' : 'no'),
}, },
]; ];
}, [t]); }, [t]);
const filterConfig = useMemo(() => { const filterConfig = useMemo(() => {
return { return {
defaultValues: {
username: '',
privilege: null,
status: '',
},
renderBody: (form: any) => { renderBody: (form: any) => {
if (!form) return null; if (!form) return null;
return <FilterFormContent form={form} t={t} />; return <FilterFormContent form={form} t={t} />;