FEAT: Múltiplos perfis para multiplas áreas/especilaliddes
All checks were successful
Deploy Dev / deploy (push) Successful in 3s
All checks were successful
Deploy Dev / deploy (push) Successful in 3s
This commit is contained in:
parent
babe525154
commit
3ae6b7e978
@ -41,7 +41,17 @@ export class AdminAccessController {
|
||||
@Put('users/:id')
|
||||
updateUserAccess(
|
||||
@Param('id') id: string,
|
||||
@Body() body: { perfilId?: number | null; areaId?: number | null },
|
||||
@Body() body: {
|
||||
perfilId?: number | null;
|
||||
perfilIds?: number[];
|
||||
areaId?: number | null;
|
||||
especialidades?: Array<{
|
||||
areaId: number;
|
||||
funcao?: string | null;
|
||||
principal?: boolean;
|
||||
ativo?: boolean;
|
||||
}>;
|
||||
},
|
||||
) {
|
||||
return this.adminAccessService.updateUserAccess(Number(id), body);
|
||||
}
|
||||
|
||||
@ -3,7 +3,14 @@ import { DatabaseService } from '../../infra/database/database.service';
|
||||
|
||||
interface AccessUpdateInput {
|
||||
perfilId?: number | null;
|
||||
perfilIds?: number[];
|
||||
areaId?: number | null;
|
||||
especialidades?: Array<{
|
||||
areaId: number;
|
||||
funcao?: string | null;
|
||||
principal?: boolean;
|
||||
ativo?: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface AreaInput {
|
||||
@ -130,10 +137,17 @@ export class AdminAccessService {
|
||||
a.ativo,
|
||||
a.responsavel_usuario_id,
|
||||
r.nome AS responsavel_nome,
|
||||
COALESCE(
|
||||
JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', su.id, 'nome', su.nome))
|
||||
FILTER (WHERE su.id IS NOT NULL),
|
||||
'[]'
|
||||
) AS supervisores,
|
||||
COUNT(DISTINCT ua.usuario_id)::INTEGER AS members
|
||||
FROM areas a
|
||||
LEFT JOIN usuarios r ON r.id = a.responsavel_usuario_id
|
||||
LEFT JOIN usuarios_areas ua ON ua.area_id = a.id AND ua.ativo = TRUE
|
||||
LEFT JOIN usuarios_areas sua ON sua.area_id = a.id AND sua.ativo = TRUE AND sua.funcao = 'Supervisor'
|
||||
LEFT JOIN usuarios su ON su.id = sua.usuario_id
|
||||
GROUP BY a.id, r.nome
|
||||
ORDER BY a.nome
|
||||
`,
|
||||
@ -156,7 +170,7 @@ export class AdminAccessService {
|
||||
'[]'
|
||||
) AS perfis,
|
||||
COALESCE(
|
||||
JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', a.id, 'nome', a.nome, 'principal', ua.principal))
|
||||
JSON_AGG(DISTINCT JSONB_BUILD_OBJECT('id', a.id, 'nome', a.nome, 'principal', ua.principal, 'funcao', ua.funcao))
|
||||
FILTER (WHERE a.id IS NOT NULL AND ua.ativo = TRUE),
|
||||
'[]'
|
||||
) AS areas
|
||||
@ -174,6 +188,7 @@ export class AdminAccessService {
|
||||
const perfis = Array.isArray(user.perfis) ? user.perfis : [];
|
||||
const areas = Array.isArray(user.areas) ? user.areas : [];
|
||||
const primaryArea = areas.find((area) => area.principal) || areas[0] || null;
|
||||
const isAdmin = perfis.some((perfil) => perfil.nome === 'Admin');
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
@ -184,7 +199,7 @@ export class AdminAccessService {
|
||||
areas,
|
||||
perfilPrincipal: perfis[0] || null,
|
||||
areaPrincipal: primaryArea,
|
||||
accessStatus: perfis.length && areas.length ? 'assigned' : 'unassigned',
|
||||
accessStatus: perfis.length && (areas.length || isAdmin) ? 'assigned' : 'unassigned',
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -194,22 +209,41 @@ export class AdminAccessService {
|
||||
await client.query('DELETE FROM usuarios_perfis WHERE usuario_id = $1', [usuarioId]);
|
||||
await client.query('DELETE FROM usuarios_areas WHERE usuario_id = $1', [usuarioId]);
|
||||
|
||||
if (input.perfilId) {
|
||||
const perfilIds = Array.isArray(input.perfilIds)
|
||||
? input.perfilIds
|
||||
: input.perfilId
|
||||
? [input.perfilId]
|
||||
: [];
|
||||
|
||||
for (const perfilId of [...new Set(perfilIds.filter(Boolean))]) {
|
||||
await client.query(
|
||||
'INSERT INTO usuarios_perfis (usuario_id, perfil_id) VALUES ($1, $2) ON CONFLICT DO NOTHING',
|
||||
[usuarioId, input.perfilId],
|
||||
[usuarioId, perfilId],
|
||||
);
|
||||
}
|
||||
|
||||
if (input.areaId) {
|
||||
const especialidades = Array.isArray(input.especialidades)
|
||||
? input.especialidades
|
||||
: input.areaId
|
||||
? [{ areaId: input.areaId, funcao: 'Agente', principal: true, ativo: true }]
|
||||
: [];
|
||||
let hasPrincipal = false;
|
||||
|
||||
for (const item of especialidades) {
|
||||
const areaId = Number(item.areaId);
|
||||
if (!areaId) continue;
|
||||
|
||||
const isPrincipal = Boolean(item.principal) && !hasPrincipal;
|
||||
hasPrincipal = hasPrincipal || isPrincipal;
|
||||
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO usuarios_areas (usuario_id, area_id, principal, ativo)
|
||||
VALUES ($1, $2, TRUE, TRUE)
|
||||
INSERT INTO usuarios_areas (usuario_id, area_id, funcao, principal, ativo)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (usuario_id, area_id)
|
||||
DO UPDATE SET principal = TRUE, ativo = TRUE, updated_at = NOW()
|
||||
DO UPDATE SET funcao = EXCLUDED.funcao, principal = EXCLUDED.principal, ativo = EXCLUDED.ativo, updated_at = NOW()
|
||||
`,
|
||||
[usuarioId, input.areaId],
|
||||
[usuarioId, areaId, this.normalizeRole(item.funcao), isPrincipal, item.ativo !== false],
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -298,4 +332,9 @@ export class AdminAccessService {
|
||||
const text = String(value || '').trim();
|
||||
return text || null;
|
||||
}
|
||||
|
||||
private normalizeRole(value?: string | null) {
|
||||
const role = String(value || '').trim();
|
||||
return role === 'Supervisor' ? 'Supervisor' : 'Agente';
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user