Loading capif_frontend/src/components/ConfigCapif.js +119 −68 Original line number Diff line number Diff line import React, { useEffect, useState } from 'react'; import { useQuery, useMutation } from '@tanstack/react-query'; import { getConfiguration, updateConfigParam, replaceConfiguration } from 'src/configs/apiService'; import { getConfiguration, updateConfigParam, replaceConfiguration, addCategoryConfiguration, addParamConfiguration } from 'src/configs/apiService'; import { Grid, Card, Loading @@ -12,7 +12,11 @@ import { Accordion, AccordionSummary, AccordionDetails, Box Box, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import Ajv from 'ajv'; Loading Loading @@ -73,6 +77,12 @@ const ConfigCapif = () => { const [editableConfig, setEditableConfig] = useState(null); const [jsonConfig, setJsonConfig] = useState(''); const [isJsonValid, setIsJsonValid] = useState(true); const [newParams, setNewParams] = useState({}); // Estado para nuevos parámetros const [openDialog, setOpenDialog] = useState(false); const [currentSection, setCurrentSection] = useState(''); const [newParamName, setNewParamName] = useState(''); const [newParamValue, setNewParamValue] = useState(''); useEffect(() => { if (configData) { Loading @@ -91,6 +101,14 @@ const ConfigCapif = () => { onSuccess: () => refetch(), }); const addParamMutation1 = useMutation({ mutationFn: addParamConfiguration, onSuccess: () => { refetch(); setNewParams({}); }, }); const handleValueChange = (path, value) => { setEditableConfig((prev) => { const newConfig = { ...prev }; Loading Loading @@ -152,6 +170,49 @@ const ConfigCapif = () => { if (isLoading) return <Typography>Loading...</Typography>; if (isError) return <Typography>Error loading configuration</Typography>; const formatTitle = (text) => { return text .split(" ") .map(word => word.toLowerCase() === "acl" ? "ACL" : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() ) .join(" "); }; const addParamMutation = useMutation({ mutationFn: addParamConfiguration, onSuccess: () => { refetch(); setOpenDialog(false); setNewParamName(''); setNewParamValue(''); }, }); const handleOpenDialog = (sectionKey) => { setCurrentSection(sectionKey); setOpenDialog(true); }; const handleCloseDialog = () => { setOpenDialog(false); setNewParamName(''); setNewParamValue(''); }; const handleAddConfig = () => { if (!newParamName || newParamValue === undefined) { alert("Debe ingresar un nombre y un valor para la configuración."); return; } const paramPath = `${currentSection}.${newParamName}`; addParamMutation.mutate({ param_path: paramPath, new_value: newParamValue }); }; return ( <Grid container spacing={3} marginTop={2}> {/* Cuadro principal con datos generales y secciones desplegables */} Loading Loading @@ -191,74 +252,64 @@ const ConfigCapif = () => { /> </Box> {/* ACL Policy Settings */} {/* 🔥 Accordions Dinámicos 🔥 */} {editableConfig?.settings && Object.entries(editableConfig.settings).map(([sectionKey, sectionValue]) => ( <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">ACL Policy Settings</Typography> <Typography variant="h6">{formatTitle(sectionKey.replace(/_/g, ' '))}</Typography> </AccordionSummary> <AccordionDetails> {editableConfig?.settings?.acl_policy_settings && Object.entries(editableConfig.settings.acl_policy_settings).map(([key, value]) => ( {sectionValue && Object.entries(sectionValue).map(([key, value]) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key}</Typography> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key.replace(/_/g, ' ')}</Typography> <TextField sx={{ width: '700px' }} value={String(value)} onChange={(e) => handleValueChange(`settings.acl_policy_settings.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.acl_policy_settings.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.acl_policy_settings.${key}`, e.target.value)} onChange={(e) => handleValueChange(`settings.${sectionKey}.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.${sectionKey}.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.${sectionKey}.${key}`, e.target.value)} /> </Box> )) } ))} <Box> <Box display="flex" justifyContent="flex-end" mt={3}> <Button variant="contained" onClick={() => handleOpenDialog(sectionKey)}> Add new param </Button> </Box> </Box> </AccordionDetails> </Accordion> ))} </CardContent> </Card> {/* Security Method Priority */} <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">Security Method Priority</Typography> </AccordionSummary> <AccordionDetails> {editableConfig?.settings?.security_method_priority && Object.entries(editableConfig.settings.security_method_priority).map(([key, value]) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key}</Typography> {/* 🔹 Dialog para agregar nuevo parámetro 🔹 */} <Dialog open={openDialog} onClose={handleCloseDialog} fullWidth maxWidth="sm"> <DialogTitle>Añadir nuevo parámetro</DialogTitle> <DialogContent> <TextField sx={{ width: '700px' }} value={String(value)} onChange={(e) => handleValueChange(`settings.security_method_priority.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.security_method_priority.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.security_method_priority.${key}`, e.target.value)} label="Nombre del parámetro" fullWidth sx={{ mt: 2 }} value={newParamName} onChange={(e) => setNewParamName(e.target.value)} /> </Box> )) } </AccordionDetails> </Accordion> {/* Certificate Expiration Period */} <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">Certificate Expiration Period</Typography> </AccordionSummary> <AccordionDetails> {['ttl_invoker_cert', 'ttl_provider_cert', 'ttl_superadmin_cert'].map((key) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key.replace('_', ' ')}</Typography> <TextField sx={{ width: '700px' }} value={editableConfig?.settings?.certificates_expiry?.[key] || ''} onChange={(e) => handleValueChange(`settings.certificates_expiry.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.certificates_expiry.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.certificates_expiry.${key}`, e.target.value)} label="Valor" fullWidth sx={{ mt: 2 }} value={newParamValue} onChange={(e) => setNewParamValue(e.target.value)} /> </Box> ))} </AccordionDetails> </Accordion> </CardContent> </Card> </DialogContent> <DialogActions> <Button onClick={handleCloseDialog} color="secondary">Cancelar</Button> <Button onClick={handleAddConfig} variant="contained" color="primary">Guardar</Button> </DialogActions> </Dialog> </Grid> {/* Upload JSON Configuration */} Loading capif_frontend/src/configs/apiService.js +31 −0 Original line number Diff line number Diff line Loading @@ -445,7 +445,38 @@ export const replaceConfiguration = async (newConfig) => { } } // Add new section inside settings export const addCategoryConfiguration = async ({ category_name, category_values }) => { try { const response = await axiosInstance.post(`/configuration/add-category`, { category_name, category_values, }); return response.data; } catch (error) { throw new Error(`Error adding category: ${error.message}`); } }; // Add param config inside a section export const addParamConfiguration = async ({ param_path, new_value }) => { try { const response = await axiosInstance.patch(`/configuration/add-config-setting`, { param_path, new_value, }); return response.data; } catch (error) { throw new Error(`Error adding config setting: ${error.message}`); } }; // REGISTER CONFIGURATION export const getRegisterConfiguration = async () => { try { const response = await axiosInstance.get(`/configuration/register`); Loading Loading
capif_frontend/src/components/ConfigCapif.js +119 −68 Original line number Diff line number Diff line import React, { useEffect, useState } from 'react'; import { useQuery, useMutation } from '@tanstack/react-query'; import { getConfiguration, updateConfigParam, replaceConfiguration } from 'src/configs/apiService'; import { getConfiguration, updateConfigParam, replaceConfiguration, addCategoryConfiguration, addParamConfiguration } from 'src/configs/apiService'; import { Grid, Card, Loading @@ -12,7 +12,11 @@ import { Accordion, AccordionSummary, AccordionDetails, Box Box, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import Ajv from 'ajv'; Loading Loading @@ -73,6 +77,12 @@ const ConfigCapif = () => { const [editableConfig, setEditableConfig] = useState(null); const [jsonConfig, setJsonConfig] = useState(''); const [isJsonValid, setIsJsonValid] = useState(true); const [newParams, setNewParams] = useState({}); // Estado para nuevos parámetros const [openDialog, setOpenDialog] = useState(false); const [currentSection, setCurrentSection] = useState(''); const [newParamName, setNewParamName] = useState(''); const [newParamValue, setNewParamValue] = useState(''); useEffect(() => { if (configData) { Loading @@ -91,6 +101,14 @@ const ConfigCapif = () => { onSuccess: () => refetch(), }); const addParamMutation1 = useMutation({ mutationFn: addParamConfiguration, onSuccess: () => { refetch(); setNewParams({}); }, }); const handleValueChange = (path, value) => { setEditableConfig((prev) => { const newConfig = { ...prev }; Loading Loading @@ -152,6 +170,49 @@ const ConfigCapif = () => { if (isLoading) return <Typography>Loading...</Typography>; if (isError) return <Typography>Error loading configuration</Typography>; const formatTitle = (text) => { return text .split(" ") .map(word => word.toLowerCase() === "acl" ? "ACL" : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() ) .join(" "); }; const addParamMutation = useMutation({ mutationFn: addParamConfiguration, onSuccess: () => { refetch(); setOpenDialog(false); setNewParamName(''); setNewParamValue(''); }, }); const handleOpenDialog = (sectionKey) => { setCurrentSection(sectionKey); setOpenDialog(true); }; const handleCloseDialog = () => { setOpenDialog(false); setNewParamName(''); setNewParamValue(''); }; const handleAddConfig = () => { if (!newParamName || newParamValue === undefined) { alert("Debe ingresar un nombre y un valor para la configuración."); return; } const paramPath = `${currentSection}.${newParamName}`; addParamMutation.mutate({ param_path: paramPath, new_value: newParamValue }); }; return ( <Grid container spacing={3} marginTop={2}> {/* Cuadro principal con datos generales y secciones desplegables */} Loading Loading @@ -191,74 +252,64 @@ const ConfigCapif = () => { /> </Box> {/* ACL Policy Settings */} {/* 🔥 Accordions Dinámicos 🔥 */} {editableConfig?.settings && Object.entries(editableConfig.settings).map(([sectionKey, sectionValue]) => ( <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">ACL Policy Settings</Typography> <Typography variant="h6">{formatTitle(sectionKey.replace(/_/g, ' '))}</Typography> </AccordionSummary> <AccordionDetails> {editableConfig?.settings?.acl_policy_settings && Object.entries(editableConfig.settings.acl_policy_settings).map(([key, value]) => ( {sectionValue && Object.entries(sectionValue).map(([key, value]) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key}</Typography> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key.replace(/_/g, ' ')}</Typography> <TextField sx={{ width: '700px' }} value={String(value)} onChange={(e) => handleValueChange(`settings.acl_policy_settings.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.acl_policy_settings.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.acl_policy_settings.${key}`, e.target.value)} onChange={(e) => handleValueChange(`settings.${sectionKey}.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.${sectionKey}.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.${sectionKey}.${key}`, e.target.value)} /> </Box> )) } ))} <Box> <Box display="flex" justifyContent="flex-end" mt={3}> <Button variant="contained" onClick={() => handleOpenDialog(sectionKey)}> Add new param </Button> </Box> </Box> </AccordionDetails> </Accordion> ))} </CardContent> </Card> {/* Security Method Priority */} <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">Security Method Priority</Typography> </AccordionSummary> <AccordionDetails> {editableConfig?.settings?.security_method_priority && Object.entries(editableConfig.settings.security_method_priority).map(([key, value]) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key}</Typography> {/* 🔹 Dialog para agregar nuevo parámetro 🔹 */} <Dialog open={openDialog} onClose={handleCloseDialog} fullWidth maxWidth="sm"> <DialogTitle>Añadir nuevo parámetro</DialogTitle> <DialogContent> <TextField sx={{ width: '700px' }} value={String(value)} onChange={(e) => handleValueChange(`settings.security_method_priority.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.security_method_priority.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.security_method_priority.${key}`, e.target.value)} label="Nombre del parámetro" fullWidth sx={{ mt: 2 }} value={newParamName} onChange={(e) => setNewParamName(e.target.value)} /> </Box> )) } </AccordionDetails> </Accordion> {/* Certificate Expiration Period */} <Accordion disableGutters> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography variant="h6">Certificate Expiration Period</Typography> </AccordionSummary> <AccordionDetails> {['ttl_invoker_cert', 'ttl_provider_cert', 'ttl_superadmin_cert'].map((key) => ( <Box key={key} display="flex" alignItems="center" mb={1}> <Typography variant="subtitle1" sx={{ flex: 1 }}>{key.replace('_', ' ')}</Typography> <TextField sx={{ width: '700px' }} value={editableConfig?.settings?.certificates_expiry?.[key] || ''} onChange={(e) => handleValueChange(`settings.certificates_expiry.${key}`, e.target.value)} onBlur={(e) => handleBlur(`settings.certificates_expiry.${key}`, e.target.value)} onKeyDown={(e) => handleKeyDown(e, `settings.certificates_expiry.${key}`, e.target.value)} label="Valor" fullWidth sx={{ mt: 2 }} value={newParamValue} onChange={(e) => setNewParamValue(e.target.value)} /> </Box> ))} </AccordionDetails> </Accordion> </CardContent> </Card> </DialogContent> <DialogActions> <Button onClick={handleCloseDialog} color="secondary">Cancelar</Button> <Button onClick={handleAddConfig} variant="contained" color="primary">Guardar</Button> </DialogActions> </Dialog> </Grid> {/* Upload JSON Configuration */} Loading
capif_frontend/src/configs/apiService.js +31 −0 Original line number Diff line number Diff line Loading @@ -445,7 +445,38 @@ export const replaceConfiguration = async (newConfig) => { } } // Add new section inside settings export const addCategoryConfiguration = async ({ category_name, category_values }) => { try { const response = await axiosInstance.post(`/configuration/add-category`, { category_name, category_values, }); return response.data; } catch (error) { throw new Error(`Error adding category: ${error.message}`); } }; // Add param config inside a section export const addParamConfiguration = async ({ param_path, new_value }) => { try { const response = await axiosInstance.patch(`/configuration/add-config-setting`, { param_path, new_value, }); return response.data; } catch (error) { throw new Error(`Error adding config setting: ${error.message}`); } }; // REGISTER CONFIGURATION export const getRegisterConfiguration = async () => { try { const response = await axiosInstance.get(`/configuration/register`); Loading