omnichannel-frontend/src/modules/management/pages/AdminPage.jsx

271 lines
7.7 KiB
React
Raw Normal View History

import { useEffect, useMemo, useState } from 'react';
import { DataPanel } from '../components/DataPanel';
import { ManagementLayout } from '../components/ManagementLayout';
import { ManagementTable } from '../components/ManagementTable';
import { MetricGrid } from '../components/MetricGrid';
import { adminMetrics, aiContentRows, areaRows, userRows } from '../services/managementMocks';
import { getAccessOptions, getAccessUsers, updateUserAccess } from '../services/adminAccessService';
import { useViewport } from '../../../shared/hooks/useViewport';
const areaColumns = [
{ key: 'name', label: 'Area' },
{ key: 'owner', label: 'Responsavel' },
{ key: 'members', label: 'Usuarios' },
{ key: 'status', label: 'Status' },
];
const contentColumns = [
{ key: 'title', label: 'Conteudo' },
{ key: 'area', label: 'Area' },
{ key: 'status', label: 'Status' },
{ key: 'updatedAt', label: 'Atualizado' },
];
const selectStyle = {
width: '100%',
border: '1px solid var(--color-border)',
borderRadius: '14px',
padding: '0.75rem 0.85rem',
background: '#fff',
color: 'var(--color-text)',
fontWeight: 600,
};
function mapMockUsers() {
return userRows.map((user) => ({
id: user.id,
nome: user.name,
email: user.email,
perfilPrincipal: { id: user.role, nome: user.role },
areaPrincipal: { id: user.area, nome: user.area },
accessStatus: 'assigned',
}));
}
export function AdminPage() {
const { isDesktop, isMobile } = useViewport();
const [users, setUsers] = useState(mapMockUsers);
const [profiles, setProfiles] = useState([]);
const [areas, setAreas] = useState([]);
const [isLoadingAccess, setIsLoadingAccess] = useState(true);
const [accessError, setAccessError] = useState('');
useEffect(() => {
let isMounted = true;
async function loadAccessData() {
try {
const [options, accessUsers] = await Promise.all([getAccessOptions(), getAccessUsers()]);
if (!isMounted) {
return;
}
setProfiles(options.profiles || []);
setAreas(options.areas || []);
setUsers(accessUsers || []);
setAccessError('');
} catch {
if (isMounted) {
setAccessError('Backend indisponivel. Exibindo dados demonstrativos.');
}
} finally {
if (isMounted) {
setIsLoadingAccess(false);
}
}
}
loadAccessData();
return () => {
isMounted = false;
};
}, []);
async function handleAccessChange(user, field, value) {
const currentPerfilId = user.perfilPrincipal?.id || null;
const currentAreaId = user.areaPrincipal?.id || null;
const nextAccess = {
perfilId: field === 'perfil' ? Number(value) || null : currentPerfilId,
areaId: field === 'area' ? Number(value) || null : currentAreaId,
};
setUsers((current) =>
current.map((item) =>
item.id === user.id
? {
...item,
perfilPrincipal:
profiles.find((profile) => profile.id === nextAccess.perfilId) || null,
areaPrincipal: areas.find((area) => area.id === nextAccess.areaId) || null,
accessStatus: nextAccess.perfilId && nextAccess.areaId ? 'assigned' : 'unassigned',
}
: item,
),
);
try {
const updatedUser = await updateUserAccess(user.id, nextAccess);
if (updatedUser) {
setUsers((current) =>
current.map((item) => (item.id === updatedUser.id ? updatedUser : item)),
);
}
setAccessError('');
} catch {
setAccessError('Nao foi possivel salvar a atribuicao. Confira o backend.');
}
}
const userColumns = useMemo(
() => [
{
key: 'nome',
label: 'Usuario',
render: (row) => (
<div>
<strong style={{ display: 'block' }}>{row.nome}</strong>
<span style={{ color: 'var(--color-text-soft)', fontSize: '0.9rem' }}>
{row.email || 'Sem email'}
</span>
</div>
),
},
{
key: 'perfil',
label: 'Perfil',
render: (row) =>
profiles.length ? (
<select
value={row.perfilPrincipal?.id || ''}
onChange={(event) => handleAccessChange(row, 'perfil', event.target.value)}
style={selectStyle}
>
<option value="">Sem perfil</option>
{profiles.map((profile) => (
<option key={profile.id} value={profile.id}>
{profile.nome}
</option>
))}
</select>
) : (
<span>{row.perfilPrincipal?.nome || 'Sem perfil'}</span>
),
},
{
key: 'area',
label: 'Area',
render: (row) =>
areas.length ? (
<select
value={row.areaPrincipal?.id || ''}
onChange={(event) => handleAccessChange(row, 'area', event.target.value)}
style={selectStyle}
>
<option value="">Sem area</option>
{areas.map((area) => (
<option key={area.id} value={area.id}>
{area.nome}
</option>
))}
</select>
) : (
<span>{row.areaPrincipal?.nome || 'Sem area'}</span>
),
},
{
key: 'status',
label: 'Status',
render: (row) => {
const isAssigned = row.accessStatus === 'assigned';
return (
<span
style={{
width: 'fit-content',
borderRadius: 999,
padding: '0.25rem 0.6rem',
background: isAssigned ? 'rgba(0, 164, 183, 0.1)' : 'rgba(229, 162, 42, 0.16)',
color: isAssigned ? 'var(--color-primary)' : '#8a5a00',
fontWeight: 700,
}}
>
{isAssigned ? 'Atribuido' : 'Pendente'}
</span>
);
},
},
],
[areas, profiles],
);
return (
<ManagementLayout
title="Painel administrativo"
subtitle="Controle de usuarios, perfis, areas e base de conteudo para IA."
activeSection="admin"
profileLabel="Lucas Admin"
initials="LA"
isDesktop={isDesktop}
isMobile={isMobile}
>
<MetricGrid metrics={adminMetrics} />
<div
style={{
display: 'grid',
gridTemplateColumns: isDesktop ? 'minmax(0, 1.2fr) minmax(320px, 0.8fr)' : '1fr',
gap: '1rem',
alignItems: 'start',
}}
>
<DataPanel
title="Usuarios e niveis de acesso"
description={
isLoadingAccess
? 'Carregando usuarios do banco...'
: accessError || 'Gerencie perfil e area principal dos usuarios autenticados.'
}
actionLabel="Adicionar usuario"
>
<ManagementTable
columns={userColumns}
rows={users}
getRowId={(row) => row.id}
isMobile={isMobile}
/>
</DataPanel>
<DataPanel
title="Areas"
description="Areas operacionais e seus responsaveis."
actionLabel="Nova area"
>
<ManagementTable
columns={areaColumns}
rows={areaRows}
getRowId={(row) => row.id}
isMobile={isMobile}
/>
</DataPanel>
</div>
<DataPanel
title="Conteudo para IA"
description="Entradas mockadas para alimentar a base de conhecimento."
actionLabel="Adicionar conteudo"
>
<ManagementTable
columns={contentColumns}
rows={aiContentRows}
getRowId={(row) => row.id}
isMobile={isMobile}
/>
</DataPanel>
</ManagementLayout>
);
}