Loading capif_frontend/.env +3 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,9 @@ NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET=7c4c1c50-3230-45bf-9eae-c9b2e401c767 # Peñuelas capif # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="http://localhost:8085/api" NEXT_PUBLIC_GRAFANA_BASE_URL="https://grafana.5gnacar.int" NODE_ENV=development capif_frontend/src/configs/apiService.js +36 −1 Original line number Diff line number Diff line Loading @@ -409,3 +409,38 @@ export const searchUsers = async (searchTerm, page, pageSize) => { throw new Error(`Error searching users: ${error.message}`); } }; export const getConfiguration = async () => { try { const response = await axiosInstance.get(`/configuration`); return response.data; } catch (error) { throw new Error(`Error fetching configuration: ${error.message}`); } } export const updateConfigParam = async ({ param_path, new_value }) => { try { const response = await axiosInstance.patch(`/configuration/update-param`, { param_path, new_value }); return response.data; } catch (error) { throw new Error(`Error updating configuration parameter: ${error.message}`); } } export const replaceConfiguration = async (newConfig) => { try { const response = await axiosInstance.put(`/configuration/replace-config`, newConfig); return response.data; } catch (error) { throw new Error(`Error replacing configuration: ${error.message}`); } } capif_frontend/src/navigation/vertical/index.js +5 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,11 @@ const navigation = () => { title: 'Users', icon: 'mdi:baseline-people', path: '/users' }, { title: 'Configuration', icon: 'mdi:baseline-people', path: '/configuration' } // , Loading capif_frontend/src/pages/configuration/[id].js 0 → 100644 +0 −0 Empty file added. capif_frontend/src/pages/configuration/index.js 0 → 100644 +142 −0 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 { Grid, Card, Typography, CardHeader, CardContent, TextField, Button, } from '@mui/material'; const Configuration = () => { // Obtener configuración desde el backend const { data: configData, isLoading, isError, refetch } = useQuery({ queryKey: ['configuration'], queryFn: getConfiguration, }); const [editableConfig, setEditableConfig] = useState(null); const [jsonConfig, setJsonConfig] = useState(''); useEffect(() => { if (configData) { setEditableConfig(configData); } }, [configData]); // Mutaciones para actualizar parámetros individuales y reemplazar la configuración const updateParamMutation = useMutation({ mutationFn: updateConfigParam, onSuccess: () => refetch(), }); const replaceConfigMutation = useMutation({ mutationFn: replaceConfiguration, onSuccess: () => refetch(), }); const handleValueChange = (path, value) => { setEditableConfig((prev) => { const newConfig = { ...prev }; const keys = path.split('.'); let current = newConfig; for (let i = 0; i < keys.length - 1; i++) { current = current[keys[i]]; } current[keys[keys.length - 1]] = value; return { ...newConfig }; }); }; const handleBlur = (path, value) => { updateParamMutation.mutate({ param_path: path, new_value: value }); }; const handleJsonSubmit = () => { try { const parsedJson = JSON.parse(jsonConfig); replaceConfigMutation.mutate(parsedJson); } catch (error) { alert('Invalid JSON format'); } }; if (isLoading) return <Typography>Loading...</Typography>; if (isError) return <Typography>Error loading configuration</Typography>; return ( <Grid container spacing={3}> {/* General Info */} <Grid item xs={12} md={4}> <Card> <CardHeader title="General Configuration" /> <CardContent> <Typography variant="subtitle1">Configuration Name</Typography> <TextField fullWidth value={editableConfig?.configuration_name || ''} onChange={(e) => handleValueChange('configuration_name', e.target.value)} onBlur={(e) => handleBlur('configuration_name', e.target.value)} /> <Typography variant="subtitle1">Description</Typography> <TextField fullWidth value={editableConfig?.description || ''} onChange={(e) => handleValueChange('description', e.target.value)} onBlur={(e) => handleBlur('description', e.target.value)} /> <Typography variant="subtitle1">Version</Typography> <TextField fullWidth value={editableConfig?.version || ''} onChange={(e) => handleValueChange('version', e.target.value)} onBlur={(e) => handleBlur('version', e.target.value)} /> </CardContent> </Card> </Grid> {/* ACL Policy Settings */} <Grid item xs={12} md={4}> <Card> <CardHeader title="ACL Policy Settings" /> <CardContent> {editableConfig?.settings?.acl_policy_settings && Object.entries(editableConfig.settings.acl_policy_settings).map(([key, value]) => ( <div key={key}> <Typography variant="subtitle1">{key}</Typography> <TextField fullWidth 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)} /> </div> ))} </CardContent> </Card> </Grid> {/* Security Method Priority */} <Grid item xs={12} md={4}> <Card> <CardHeader title="Security Method Priority" /> <CardContent> {editableConfig?.settings?.security_method_priority && Object.entries(editableConfig.settings.security_method_priority).map(([key, value]) => ( <div key={key}> <Typography variant="subtitle1">{key}</Typography> <TextField fullWidth 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)} /> </div> ))} </CardContent> </Card> </Grid> {/* Certify Expiration Period */} <Grid item xs={12} md={4}> <Card> <CardHeader title="Certify Expiration Period" /> <CardContent> <Typography variant="subtitle1">certify_expiration_period</Typography> <TextField fullWidth value={String(editableConfig?.settings?.certify_expiration_period || '')} onChange={(e) => handleValueChange('settings.certify_expiration_period', e.target.value)} onBlur={(e) => handleBlur('settings.certify_expiration_period', e.target.value)} /> </CardContent> </Card> </Grid> {/* Upload JSON Configuration */} <Grid item xs={12}> <Card> <CardHeader title="Upload New Configuration" /> <CardContent> <TextField fullWidth label="Paste JSON here" multiline rows={8} variant="outlined" value={jsonConfig} onChange={(e) => setJsonConfig(e.target.value)} /> <Button variant="contained" fullWidth onClick={handleJsonSubmit} disabled={replaceConfigMutation.isLoading}>{replaceConfigMutation.isLoading ? 'Updating...' : 'Replace Configuration'}</Button> </CardContent> </Card> </Grid> </Grid> ); }; export default Configuration; Loading
capif_frontend/.env +3 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,9 @@ NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET=7c4c1c50-3230-45bf-9eae-c9b2e401c767 # Peñuelas capif # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="http://localhost:8085/api" NEXT_PUBLIC_GRAFANA_BASE_URL="https://grafana.5gnacar.int" NODE_ENV=development
capif_frontend/src/configs/apiService.js +36 −1 Original line number Diff line number Diff line Loading @@ -409,3 +409,38 @@ export const searchUsers = async (searchTerm, page, pageSize) => { throw new Error(`Error searching users: ${error.message}`); } }; export const getConfiguration = async () => { try { const response = await axiosInstance.get(`/configuration`); return response.data; } catch (error) { throw new Error(`Error fetching configuration: ${error.message}`); } } export const updateConfigParam = async ({ param_path, new_value }) => { try { const response = await axiosInstance.patch(`/configuration/update-param`, { param_path, new_value }); return response.data; } catch (error) { throw new Error(`Error updating configuration parameter: ${error.message}`); } } export const replaceConfiguration = async (newConfig) => { try { const response = await axiosInstance.put(`/configuration/replace-config`, newConfig); return response.data; } catch (error) { throw new Error(`Error replacing configuration: ${error.message}`); } }
capif_frontend/src/navigation/vertical/index.js +5 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,11 @@ const navigation = () => { title: 'Users', icon: 'mdi:baseline-people', path: '/users' }, { title: 'Configuration', icon: 'mdi:baseline-people', path: '/configuration' } // , Loading
capif_frontend/src/pages/configuration/index.js 0 → 100644 +142 −0 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 { Grid, Card, Typography, CardHeader, CardContent, TextField, Button, } from '@mui/material'; const Configuration = () => { // Obtener configuración desde el backend const { data: configData, isLoading, isError, refetch } = useQuery({ queryKey: ['configuration'], queryFn: getConfiguration, }); const [editableConfig, setEditableConfig] = useState(null); const [jsonConfig, setJsonConfig] = useState(''); useEffect(() => { if (configData) { setEditableConfig(configData); } }, [configData]); // Mutaciones para actualizar parámetros individuales y reemplazar la configuración const updateParamMutation = useMutation({ mutationFn: updateConfigParam, onSuccess: () => refetch(), }); const replaceConfigMutation = useMutation({ mutationFn: replaceConfiguration, onSuccess: () => refetch(), }); const handleValueChange = (path, value) => { setEditableConfig((prev) => { const newConfig = { ...prev }; const keys = path.split('.'); let current = newConfig; for (let i = 0; i < keys.length - 1; i++) { current = current[keys[i]]; } current[keys[keys.length - 1]] = value; return { ...newConfig }; }); }; const handleBlur = (path, value) => { updateParamMutation.mutate({ param_path: path, new_value: value }); }; const handleJsonSubmit = () => { try { const parsedJson = JSON.parse(jsonConfig); replaceConfigMutation.mutate(parsedJson); } catch (error) { alert('Invalid JSON format'); } }; if (isLoading) return <Typography>Loading...</Typography>; if (isError) return <Typography>Error loading configuration</Typography>; return ( <Grid container spacing={3}> {/* General Info */} <Grid item xs={12} md={4}> <Card> <CardHeader title="General Configuration" /> <CardContent> <Typography variant="subtitle1">Configuration Name</Typography> <TextField fullWidth value={editableConfig?.configuration_name || ''} onChange={(e) => handleValueChange('configuration_name', e.target.value)} onBlur={(e) => handleBlur('configuration_name', e.target.value)} /> <Typography variant="subtitle1">Description</Typography> <TextField fullWidth value={editableConfig?.description || ''} onChange={(e) => handleValueChange('description', e.target.value)} onBlur={(e) => handleBlur('description', e.target.value)} /> <Typography variant="subtitle1">Version</Typography> <TextField fullWidth value={editableConfig?.version || ''} onChange={(e) => handleValueChange('version', e.target.value)} onBlur={(e) => handleBlur('version', e.target.value)} /> </CardContent> </Card> </Grid> {/* ACL Policy Settings */} <Grid item xs={12} md={4}> <Card> <CardHeader title="ACL Policy Settings" /> <CardContent> {editableConfig?.settings?.acl_policy_settings && Object.entries(editableConfig.settings.acl_policy_settings).map(([key, value]) => ( <div key={key}> <Typography variant="subtitle1">{key}</Typography> <TextField fullWidth 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)} /> </div> ))} </CardContent> </Card> </Grid> {/* Security Method Priority */} <Grid item xs={12} md={4}> <Card> <CardHeader title="Security Method Priority" /> <CardContent> {editableConfig?.settings?.security_method_priority && Object.entries(editableConfig.settings.security_method_priority).map(([key, value]) => ( <div key={key}> <Typography variant="subtitle1">{key}</Typography> <TextField fullWidth 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)} /> </div> ))} </CardContent> </Card> </Grid> {/* Certify Expiration Period */} <Grid item xs={12} md={4}> <Card> <CardHeader title="Certify Expiration Period" /> <CardContent> <Typography variant="subtitle1">certify_expiration_period</Typography> <TextField fullWidth value={String(editableConfig?.settings?.certify_expiration_period || '')} onChange={(e) => handleValueChange('settings.certify_expiration_period', e.target.value)} onBlur={(e) => handleBlur('settings.certify_expiration_period', e.target.value)} /> </CardContent> </Card> </Grid> {/* Upload JSON Configuration */} <Grid item xs={12}> <Card> <CardHeader title="Upload New Configuration" /> <CardContent> <TextField fullWidth label="Paste JSON here" multiline rows={8} variant="outlined" value={jsonConfig} onChange={(e) => setJsonConfig(e.target.value)} /> <Button variant="contained" fullWidth onClick={handleJsonSubmit} disabled={replaceConfigMutation.isLoading}>{replaceConfigMutation.isLoading ? 'Updating...' : 'Replace Configuration'}</Button> </CardContent> </Card> </Grid> </Grid> ); }; export default Configuration;