feat(config): modulo Configuraciones admin + permisos por rol
- Tablas ConfiguracionSistema (7 configs) + RolConfiguracion (6 roles) - Endpoints: config/sistema GET/PATCH, config/roles GET/PATCH, config/usuarios GET/PATCH, config/reset-roles POST, config/acciones GET - modulosDeRol() async con fallback a MATRIZ estatica - auth/me devuelve acciones[] segun rol del usuario - saludMonday.estaDisponible() lee modo_local_forzado de DB (cache 30s) - Landing: vista Configuraciones con 4 tabs (Modo, Roles, Usuarios, Sistema) - Mi-dia: boton Descartar se deshabilita si !descartar_lead - 630 API tests + 24 landing tests + 20 mi-dia tests verdes Seed: npm run seed:config
This commit is contained in:
@@ -173,6 +173,7 @@ function FilaOportunidad({
|
||||
onAfluencia,
|
||||
onDescartar,
|
||||
enviando,
|
||||
acciones,
|
||||
}: {
|
||||
op: OportunidadMiDia
|
||||
onEtapa: (etapa: string, fecha?: string) => void
|
||||
@@ -180,6 +181,7 @@ function FilaOportunidad({
|
||||
onAfluencia: () => void
|
||||
onDescartar: () => void
|
||||
enviando: boolean
|
||||
acciones?: string[]
|
||||
}) {
|
||||
const [conCita, setConCita] = useState(false)
|
||||
const [fechaCita, setFechaCita] = useState('')
|
||||
@@ -264,7 +266,7 @@ function FilaOportunidad({
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
disabled={enviando}
|
||||
disabled={enviando || (acciones !== undefined && !acciones.includes('descartar_lead'))}
|
||||
onClick={onDescartar}
|
||||
className="rounded-lg border border-red-200 bg-white px-3 py-1.5 text-xs font-medium text-red-600 shadow-sm transition-all duration-100 ease-out hover:bg-red-50 disabled:opacity-50"
|
||||
>
|
||||
@@ -503,12 +505,14 @@ function FilaLead({
|
||||
onConvertir,
|
||||
onDescartar,
|
||||
enviando,
|
||||
acciones,
|
||||
}: {
|
||||
lead: LeadMiDia
|
||||
onAccion: (estado: string, fecha?: string) => void
|
||||
onConvertir: () => void
|
||||
onDescartar: () => void
|
||||
enviando: boolean
|
||||
acciones?: string[]
|
||||
}) {
|
||||
return (
|
||||
<li className="rounded-xl border border-[#E5E7EB] bg-white p-4 shadow-sm">
|
||||
@@ -564,7 +568,7 @@ function FilaLead({
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={enviando}
|
||||
disabled={enviando || (acciones !== undefined && !acciones.includes("descartar_lead"))}
|
||||
onClick={onDescartar}
|
||||
className="rounded-lg border border-red-200 bg-white px-3 py-1.5 text-xs font-medium text-red-600 shadow-sm transition-all duration-100 ease-out hover:bg-red-50 disabled:opacity-50"
|
||||
>
|
||||
@@ -578,6 +582,7 @@ function FilaLead({
|
||||
export default function MiDiaPage({ accionesSesion }: { accionesSesion: AccionesSesion }) {
|
||||
const nombreVendedor = getSesion()?.usuario.nombre ?? ''
|
||||
const rol = getSesion()?.usuario.rol ?? 'vendedor'
|
||||
const acciones = getSesion()?.usuario.acciones ?? []
|
||||
const [resumen, setResumen] = useState<ResumenMiDia | null>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [cargando, setCargando] = useState(true)
|
||||
@@ -836,6 +841,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
onAccion={(estado, fecha) => void ejecutar(() => cambiarEstadoLead(lead.id, estado, fecha, lead.fuente), 'No se pudo actualizar el lead')}
|
||||
onConvertir={() => void convertir(lead)}
|
||||
onDescartar={() => setDescartando(lead)}
|
||||
acciones={acciones}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
@@ -859,6 +865,7 @@ export default function MiDiaPage({ accionesSesion }: { accionesSesion: Acciones
|
||||
window.location.href = urlAfluenciaParaOportunidad(op)
|
||||
}}
|
||||
onDescartar={() => setDescartando({ id: op.id, nombre: op.nombre, fuente: 'oportunidad' })}
|
||||
acciones={acciones}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface UsuarioSesion {
|
||||
nombre: string
|
||||
rol: string
|
||||
asignacion: string | null
|
||||
acciones?: string[]
|
||||
}
|
||||
|
||||
export interface Sesion {
|
||||
|
||||
Reference in New Issue
Block a user