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')
|
@Put('users/:id')
|
||||||
updateUserAccess(
|
updateUserAccess(
|
||||||
@Param('id') id: string,
|
@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);
|
return this.adminAccessService.updateUserAccess(Number(id), body);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,14 @@ import { DatabaseService } from '../../infra/database/database.service';
|
|||||||
|
|
||||||
interface AccessUpdateInput {
|
interface AccessUpdateInput {
|
||||||
perfilId?: number | null;
|
perfilId?: number | null;
|
||||||
|
perfilIds?: number[];
|
||||||
areaId?: number | null;
|
areaId?: number | null;
|
||||||
|
especialidades?: Array<{
|
||||||
|
areaId: number;
|
||||||
|
funcao?: string | null;
|
||||||
|
principal?: boolean;
|
||||||
|
ativo?: boolean;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AreaInput {
|
interface AreaInput {
|
||||||
@ -130,10 +137,17 @@ export class AdminAccessService {
|
|||||||
a.ativo,
|
a.ativo,
|
||||||
a.responsavel_usuario_id,
|
a.responsavel_usuario_id,
|
||||||
r.nome AS responsavel_nome,
|
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
|
COUNT(DISTINCT ua.usuario_id)::INTEGER AS members
|
||||||
FROM areas a
|
FROM areas a
|
||||||
LEFT JOIN usuarios r ON r.id = a.responsavel_usuario_id
|
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 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
|
GROUP BY a.id, r.nome
|
||||||
ORDER BY a.nome
|
ORDER BY a.nome
|
||||||
`,
|
`,
|
||||||
@ -156,7 +170,7 @@ export class AdminAccessService {
|
|||||||
'[]'
|
'[]'
|
||||||
) AS perfis,
|
) AS perfis,
|
||||||
COALESCE(
|
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),
|
FILTER (WHERE a.id IS NOT NULL AND ua.ativo = TRUE),
|
||||||
'[]'
|
'[]'
|
||||||
) AS areas
|
) AS areas
|
||||||
@ -174,6 +188,7 @@ export class AdminAccessService {
|
|||||||
const perfis = Array.isArray(user.perfis) ? user.perfis : [];
|
const perfis = Array.isArray(user.perfis) ? user.perfis : [];
|
||||||
const areas = Array.isArray(user.areas) ? user.areas : [];
|
const areas = Array.isArray(user.areas) ? user.areas : [];
|
||||||
const primaryArea = areas.find((area) => area.principal) || areas[0] || null;
|
const primaryArea = areas.find((area) => area.principal) || areas[0] || null;
|
||||||
|
const isAdmin = perfis.some((perfil) => perfil.nome === 'Admin');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
@ -184,7 +199,7 @@ export class AdminAccessService {
|
|||||||
areas,
|
areas,
|
||||||
perfilPrincipal: perfis[0] || null,
|
perfilPrincipal: perfis[0] || null,
|
||||||
areaPrincipal: primaryArea,
|
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_perfis WHERE usuario_id = $1', [usuarioId]);
|
||||||
await client.query('DELETE FROM usuarios_areas 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(
|
await client.query(
|
||||||
'INSERT INTO usuarios_perfis (usuario_id, perfil_id) VALUES ($1, $2) ON CONFLICT DO NOTHING',
|
'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(
|
await client.query(
|
||||||
`
|
`
|
||||||
INSERT INTO usuarios_areas (usuario_id, area_id, principal, ativo)
|
INSERT INTO usuarios_areas (usuario_id, area_id, funcao, principal, ativo)
|
||||||
VALUES ($1, $2, TRUE, TRUE)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
ON CONFLICT (usuario_id, area_id)
|
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();
|
const text = String(value || '').trim();
|
||||||
return text || null;
|
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