React Interface v3

This commit is contained in:
DomySh
2022-06-12 16:44:54 +02:00
parent 98652b0fa9
commit 3139cbf288
10 changed files with 284 additions and 163 deletions

View File

@@ -1,33 +1,60 @@
import { Button, Group, NumberInput, Space, TextInput, Notification } from '@mantine/core'; import { Button, Group, NumberInput, Space, TextInput, Notification, Switch, NativeSelect } from '@mantine/core';
import { useForm } from '@mantine/hooks'; import { useForm } from '@mantine/hooks';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ServiceAddForm } from '../js/models'; import { RegexAddForm, ServiceAddForm } from '../js/models';
import { addservice } from '../js/utils'; import { addregex, addservice, b64encode, validateRegex } from '../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
import FilterTypeSelector from './FilterTypeSelector';
type RegexAddInfo = {
regex:string,
type:string,
mode:string,
regex_exact:boolean,
percentage_encoding:boolean
}
function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:string }) { function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:string }) {
return <></>
/*
const form = useForm({ const form = useForm({
initialValues: { initialValues: {
regex:"", regex:"",
is_blacklist:true, type:"blacklist",
mode:"B" mode:"C <-> S",
regex_exact:false,
percentage_encoding:false
}, },
validationRules:{ validationRules:{
regex: (value) => value !== ""?true:false, regex: (value) => value !== "" && validateRegex(value),
port: (value) => value>0 && value<65536 type: (value) => ["blacklist","whitelist"].includes(value),
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
} }
}) })
const [submitLoading, setSubmitLoading] = useState(false) const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null) const [error, setError] = useState<string|null>(null)
const submitRequest = (values:ServiceAddForm) =>{ const submitRequest = (values:RegexAddInfo) => {
setSubmitLoading(true) setSubmitLoading(true)
addservice(values).then( res => { const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode])
let final_regex = values.regex
if (values.percentage_encoding){
final_regex = decodeURIComponent(final_regex)
}
if(!values.regex_exact){
final_regex = ".*"+final_regex+".*"
}
const request:RegexAddForm = {
is_blacklist:values.type !== "whitelist",
service_id: service,
mode: filter_mode?filter_mode:"B",
regex: b64encode(final_regex)
}
setSubmitLoading(false)
addregex(request).then( res => {
if (!res){ if (!res){
setSubmitLoading(false) setSubmitLoading(false)
closePopup(); closePopup();
@@ -39,32 +66,44 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
setSubmitLoading(false) setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]") setError("Request Failed! [ "+err+" ]")
}) })
} }
return <form onSubmit={form.onSubmit(submitRequest)}> return <form onSubmit={form.onSubmit(submitRequest)}>
<TextInput <TextInput
required required
label="Service name" label="Regex"
placeholder="Challenge 01" placeholder="[A-Z0-9]{31}="
{...form.getInputProps('name')} {...form.getInputProps('regex')}
/> />
<Space h="md" /> <Space h="md" />
<Switch
<NumberInput label="Use percentage encoding for binary values"
{...form.getInputProps('percentage_encoding', { type: 'checkbox' })}
/>
<Space h="md" />
<Switch
label="Match the exactly the regex"
{...form.getInputProps('regex_exact', { type: 'checkbox' })}
/>
<Space h="md" />
<NativeSelect
data={['C -> S', 'S -> C', 'C <-> S']}
label="Choose the source of the packets to filter"
variant="filled"
required required
placeholder="8080" {...form.getInputProps('mode')}
min={1}
max={65535}
label="Service port"
{...form.getInputProps('port')}
/> />
<Space h="md" /> <Space h="md" />
<FilterTypeSelector
size="md"
color="gray"
required
{...form.getInputProps('type')}
/>
<Group position="right" mt="md"> <Group position="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button> <Button loading={submitLoading} type="submit">Add Filter</Button>
</Group> </Group>
<Space h="md" /> <Space h="md" />
@@ -75,7 +114,6 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
</Notification><Space h="md" /></>:null} </Notification><Space h="md" /></>:null}
</form> </form>
*/
} }

View File

@@ -0,0 +1,32 @@
import { Box, Center, SegmentedControl } from "@mantine/core";
import React from "react";
import { FaListAlt } from "react-icons/fa";
import { TiCancel } from "react-icons/ti";
export default function FilterTypeSelector(props:any){
return <SegmentedControl
data={[
{
value: 'blacklist',
label: (
<Center style={{color:"#FFF"}}>
<TiCancel size={23} color="red"/>
<Box ml={10}>Blacklist</Box>
</Center>
),
},
{
value: 'whitelist',
label: (
<Center style={{color:"#FFF"}}>
<FaListAlt size={16} color="gray"/>
<Box ml={10}>Whitelist</Box>
</Center>
),
},
]}
{...props}
/>
}

View File

@@ -1,15 +1,14 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { ActionIcon, Badge, Modal } from '@mantine/core'; import { ActionIcon, Badge, Modal } from '@mantine/core';
import style from "./Header.module.scss"; import style from "./Header.module.scss";
import { generalstats } from '../../js/utils'; import { errorNotify, generalstats } from '../../js/utils';
import { GeneralStats, notification_time, update_freq } from '../../js/models'; import { GeneralStats, update_freq } from '../../js/models';
import { BsPlusLg } from "react-icons/bs" import { BsPlusLg } from "react-icons/bs"
import { AiFillHome } from "react-icons/ai" import { AiFillHome } from "react-icons/ai"
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useLocation, useNavigate, useParams } from 'react-router-dom';
import AddNewRegex from '../AddNewRegex'; import AddNewRegex from '../AddNewRegex';
import AddNewService from '../AddNewService'; import AddNewService from '../AddNewService';
import { showNotification } from '@mantine/notifications';
import { ImCross } from 'react-icons/im';
function Header() { function Header() {
@@ -22,15 +21,8 @@ function Header() {
generalstats().then(res => { generalstats().then(res => {
setGeneralStats(res) setGeneralStats(res)
}).catch( }).catch(
err =>{ err => errorNotify("General Info Auto-Update failed!", err.toString())
showNotification({ )
autoClose: notification_time,
title: "General Info Auto-Update failed!",
message: "[ "+err+" ]",
color: 'red',
icon: <ImCross />,
});
})
} }
useEffect(()=>{ useEffect(()=>{

View File

@@ -1,18 +1,20 @@
import { Center, Grid, SegmentedControl, Text, Title, Box, Badge, Space, ActionIcon } from '@mantine/core'; import { Center, Grid, SegmentedControl, Text, Title, Box, Badge, Space, ActionIcon } from '@mantine/core';
import React from 'react'; import React, { useState } from 'react';
import { RegexFilter } from '../../js/models'; import { RegexFilter } from '../../js/models';
import { getHumanReadableRegex } from '../../js/utils'; import { getHumanReadableRegex } from '../../js/utils';
import style from "./RegexView.module.scss"; import style from "./RegexView.module.scss";
import { FaListAlt } from "react-icons/fa" import { FaListAlt } from "react-icons/fa"
import { TiCancel } from "react-icons/ti" import { TiCancel } from "react-icons/ti"
import { BsTrashFill } from "react-icons/bs" import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal';
import FilterTypeSelector from '../FilterTypeSelector';
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) { function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
const mode_string = regexInfo.mode == "C"? "C -> S": const mode_string = regexInfo.mode === "C"? "C -> S":
regexInfo.mode == "S"? "S -> C": regexInfo.mode === "S"? "S -> C":
regexInfo.mode == "B"? "S <-> C": "🤔" regexInfo.mode === "B"? "S <-> C": "🤔"
let regex_expr = getHumanReadableRegex(regexInfo.regex); let regex_expr = getHumanReadableRegex(regexInfo.regex);
let exact_regex = true; let exact_regex = true;
@@ -22,6 +24,8 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
exact_regex = false; exact_regex = false;
} }
const [deleteModal, setDeleteModal] = useState(false);
return <div className={style.box}> return <div className={style.box}>
<Grid> <Grid>
<Grid.Col span={2}> <Grid.Col span={2}>
@@ -31,35 +35,16 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
<Text className={style.regex_text}> {regex_expr}</Text> <Text className={style.regex_text}> {regex_expr}</Text>
</Grid.Col> </Grid.Col>
<Grid.Col span={2}> <Grid.Col span={2}>
<ActionIcon color="red" onClick={()=>{}} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon> <ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
</Grid.Col> </Grid.Col>
<Grid.Col span={2} /> <Grid.Col span={2} />
<Grid.Col className='center-flex-row' span={4}> <Grid.Col className='center-flex-row' span={4}>
<Space h="xs" /> <Space h="xs" />
<SegmentedControl <FilterTypeSelector
data={[
{
value: 'blacklist',
label: (
<Center style={{color:"#FFF"}}>
<TiCancel size={23} color="red"/>
<Box ml={10}>Blacklist</Box>
</Center>
),
},
{
value: 'whitelist',
label: (
<Center style={{color:"#FFF"}}>
<FaListAlt size={16} color="gray"/>
<Box ml={10}>Whitelist</Box>
</Center>
),
},
]}
size="md" size="md"
color="gray" color="gray"
disabled disabled
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
/> />
<Space h="md" /> <Space h="md" />
<div className='center-flex'> <div className='center-flex'>
@@ -78,8 +63,14 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge> <Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
</div> </div>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<YesNoModal
title='Are you sure to delete this regex?'
description={`You are going to delete the regex '${regex_expr}', causing the restart of the firewall if it is active.`}
onClose={()=>setDeleteModal(false)}
action={()=>console.log("Delete regex please!")}
opened={deleteModal}
/>
</div> </div>
} }

View File

@@ -1,9 +1,10 @@
import { ActionIcon, Badge, Grid, Space, Title } from '@mantine/core'; import { ActionIcon, Badge, Grid, Space, Title } from '@mantine/core';
import React from 'react'; import React, { useState } from 'react';
import { FaPause, FaPlay, FaStop } from 'react-icons/fa'; import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
import { Service } from '../../js/models'; import { Service } from '../../js/models';
import { MdOutlineArrowForwardIos } from "react-icons/md" import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./ServiceRow.module.scss"; import style from "./ServiceRow.module.scss";
import YesNoModal from '../YesNoModal';
//"status":"stop"/"wait"/"active"/"pause", //"status":"stop"/"wait"/"active"/"pause",
function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) { function ServiceRow({ service, onClick, additional_buttons }:{ service:Service, onClick?:()=>void, additional_buttons?:any }) {
@@ -15,6 +16,31 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
case "active": status_color = "teal"; break; case "active": status_color = "teal"; break;
case "pause": status_color = "cyan"; break; case "pause": status_color = "cyan"; break;
} }
const [stopModal, setStopModal] = useState(false);
const [buttonLoading, setButtonLoading] = useState(false)
const stopService = () => {
setButtonLoading(true)
console.log("Stop this service please!")
setButtonLoading(false)
}
const startService = () => {
setButtonLoading(true)
console.log("Start this service please!")
setButtonLoading(false)
}
const pauseService = () => {
if (service.status === "pause") return setStopModal(true)
setButtonLoading(true)
console.log("Pause this service please!")
setButtonLoading(false)
}
return <> return <>
<Grid className={style.row} style={{width:"100%"}}> <Grid className={style.row} style={{width:"100%"}}>
<Grid.Col span={4}> <Grid.Col span={4}>
@@ -33,17 +59,29 @@ function ServiceRow({ service, onClick, additional_buttons }:{ service:Service,
<Space w="xl" /><Space w="xl" /> <Space w="xl" /><Space w="xl" />
<div className="center-flex"> <div className="center-flex">
{additional_buttons} {additional_buttons}
<ActionIcon color={service.status === "pause"?"yellow":"red"} size="xl" radius="md" variant="filled" disabled={!["wait","active","pause"].includes(service.status)?true:false}> <ActionIcon color={service.status === "pause"?"yellow":"red"} loading={buttonLoading}
onClick={pauseService} size="xl" radius="md" variant="filled"
disabled={!["wait","active","pause"].includes(service.status)?true:false}>
{service.status === "pause"?<FaStop size="20px" />:<FaPause size="20px" />} {service.status === "pause"?<FaStop size="20px" />:<FaPause size="20px" />}
</ActionIcon> </ActionIcon>
<Space w="md"/> <Space w="md"/>
<ActionIcon color="teal" size="xl" radius="md" variant="filled" disabled={!["stop","pause"].includes(service.status)?true:false}><FaPlay size="20px" /></ActionIcon> <ActionIcon color="teal" size="xl" radius="md" onClick={startService} loading={buttonLoading}
variant="filled" disabled={!["stop","pause"].includes(service.status)?true:false}>
<FaPlay size="20px" />
</ActionIcon>
</div> </div>
<Space w="xl" /><Space w="xl" /> <Space w="xl" /><Space w="xl" />
{onClick?<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size="45px" />:null} {onClick?<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size="45px" />:null}
<Space w="xl" /> <Space w="xl" />
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<YesNoModal
title='Are you sure to stop this service!'
description={`You are going to delete the service '${service.id}', causing the stopping of the firewall. This will cause the shutdown of your service ⚠️!`}
onClose={()=>setStopModal(false)}
action={stopService}
opened={stopModal}
/>
<hr style={{width:"100%"}}/> <hr style={{width:"100%"}}/>
</> </>
} }

View File

@@ -0,0 +1,19 @@
import { Button, Group, Modal } from '@mantine/core';
import React from 'react';
function YesNoModal( { title, description, action, onClose, opened}:{ title:string, description:string, onClose:()=>void, action:()=>void, opened:boolean} ){
return <Modal size="xl" title={title} opened={opened} onClose={onClose} centered>
{description}
<Group position="right" mt="md">
<Button onClick={()=>{
onClose()
action()
}} color="teal" type="submit">Yes</Button>
<Button onClick={onClose} color="red" type="submit">No</Button>
</Group>
</Modal>
}
export default YesNoModal;

View File

@@ -1,6 +1,6 @@
export const update_freq = 5000; export const update_freq = 3000;
export const notification_time = 2000; export const notification_time = 2000;
export type GeneralStats = { export type GeneralStats = {
@@ -37,3 +37,10 @@ export type RegexFilter = {
mode:string //C S B => C->S S->C BOTH mode:string //C S B => C->S S->C BOTH
n_packets:number n_packets:number
} }
export type RegexAddForm = {
"service_id":string,
"regex":string,
"is_blacklist":boolean,
"mode":string // C->S S->C BOTH
}

View File

@@ -1,4 +1,9 @@
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter } from "./models"; import { showNotification } from "@mantine/notifications";
import { ImCross } from "react-icons/im";
import { TiTick } from "react-icons/ti"
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, notification_time, RegexAddForm } from "./models";
var Buffer = require('buffer').Buffer
export async function getapi(path:string):Promise<any>{ export async function getapi(path:string):Promise<any>{
return await fetch(`/api/${path}`).then( res => res.json() ) return await fetch(`/api/${path}`).then( res => res.json() )
@@ -33,6 +38,11 @@ export async function addservice(data:ServiceAddForm) {
return status === "ok"?undefined:status return status === "ok"?undefined:status
} }
export async function addregex(data:RegexAddForm) {
const { status } = await postapi("regexes/add",data) as ServerResponse;
return status === "ok"?undefined:status
}
export async function serviceregexlist(service_id:string){ export async function serviceregexlist(service_id:string){
return await getapi(`service/${service_id}/regexes`) as RegexFilter[]; return await getapi(`service/${service_id}/regexes`) as RegexFilter[];
} }
@@ -40,7 +50,6 @@ export async function serviceregexlist(service_id:string){
const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&\'()*+,-./:;<=>?@[\\]^_`{|}~ "; const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&\'()*+,-./:;<=>?@[\\]^_`{|}~ ";
export function getHumanReadableRegex(regexB64:string){ export function getHumanReadableRegex(regexB64:string){
var Buffer = require('buffer').Buffer
const regex = Buffer.from(regexB64, "base64") const regex = Buffer.from(regexB64, "base64")
let res = "" let res = ""
for (let i=0; i < regex.length; i++){ for (let i=0; i < regex.length; i++){
@@ -53,3 +62,44 @@ export function getHumanReadableRegex(regexB64:string){
} }
return res return res
} }
export function errorNotify(title:string, description:string ){
showNotification({
autoClose: notification_time,
title: title,
message: description,
color: 'red',
icon: <ImCross />,
});
}
export function okNotify(title:string, description:string ){
showNotification({
autoClose: notification_time,
title: title,
message: description,
color: 'teal',
icon: <TiTick />,
});
}
export function validateRegex(pattern:string) {
var parts = pattern.split('/'),
regex = pattern,
options = "";
if (parts.length > 1) {
regex = parts[1];
options = parts[2];
}
try {
new RegExp(regex, options);
return true;
}
catch(e) {
return false;
}
}
export function b64encode(data:string){
return Buffer.from(data).toString('base64')
}

View File

@@ -1,50 +1,22 @@
import { Space, Title } from '@mantine/core'; import { Space, Title } from '@mantine/core';
import { showNotification } from '@mantine/notifications';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import ServiceRow from '../components/ServiceRow'; import ServiceRow from '../components/ServiceRow';
import { notification_time, Service, update_freq } from '../js/models'; import { Service, update_freq } from '../js/models';
import { servicelist } from '../js/utils'; import { errorNotify, servicelist } from '../js/utils';
import { ImCross } from "react-icons/im"
function HomePage() { function HomePage() {
const [services, setServices] = useState<Service[]>([ const [services, setServices] = useState<Service[]>([]);
{
id:"ctfe",
internal_port:18080,
n_packets: 30,
n_regex: 40,
name:"CTFe",
public_port:80,
status:"pause"
},
{
id:"saas",
internal_port:18080,
n_packets: 30,
n_regex: 40,
name:"SaaS",
public_port:5000,
status:"active"
}
]);
const navigator = useNavigate() const navigator = useNavigate()
const updateInfo = () => { const updateInfo = () => {
servicelist().then(res => { servicelist().then(res => {
setServices(res) setServices(res)
}).catch( }).catch(
err =>{ err => errorNotify("Home Page Auto-Update failed!", err.toString())
showNotification({ )
autoClose: notification_time,
title: "Home Page Auto-Update failed!",
message: "[ "+err+" ]",
color: 'red',
icon: <ImCross />,
});
})
} }
useEffect(()=>{ useEffect(()=>{

View File

@@ -1,13 +1,12 @@
import { ActionIcon, Grid, Space, Title } from '@mantine/core'; import { ActionIcon, Grid, Space, Title } from '@mantine/core';
import { showNotification } from '@mantine/notifications';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { BsTrashFill } from 'react-icons/bs'; import { BsTrashFill } from 'react-icons/bs';
import { ImCross } from 'react-icons/im'; import { useNavigate, useParams } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import RegexView from '../components/RegexView'; import RegexView from '../components/RegexView';
import ServiceRow from '../components/ServiceRow'; import ServiceRow from '../components/ServiceRow';
import { notification_time, RegexFilter, Service, update_freq } from '../js/models'; import YesNoModal from '../components/YesNoModal';
import { serviceinfo, serviceregexlist } from '../js/utils'; import { RegexFilter, Service, update_freq } from '../js/models';
import { errorNotify, serviceinfo, serviceregexlist } from '../js/utils';
function ServiceDetails() { function ServiceDetails() {
const {srv_id} = useParams() const {srv_id} = useParams()
@@ -22,24 +21,9 @@ function ServiceDetails() {
status:"🤔" status:"🤔"
}) })
const [regexesList, setRegexesList] = useState<RegexFilter[]>([ const [regexesList, setRegexesList] = useState<RegexFilter[]>([])
{
id:3546, const navigator = useNavigate()
is_blacklist:true,
mode:"B",
regex:"LipmbGFnX2NoZWNrLio=",
service_id:"ctfe",
n_packets:5,
},
{
id:3546,
is_blacklist:true,
mode:"B",
regex:"d2VkcmZoaXdlZGZoYnVp",
service_id:"ctfe",
n_packets: 54
}
])
const updateInfo = async () => { const updateInfo = async () => {
if (!srv_id) return if (!srv_id) return
@@ -48,27 +32,16 @@ function ServiceDetails() {
setServiceInfo(res) setServiceInfo(res)
}).catch( }).catch(
err =>{ err =>{
showNotification({ errorNotify(`Updater for ${srv_id} service failed [General Info]!`, err.toString())
autoClose: notification_time,
title: `Updater for ${srv_id} service failed [General Info]!`,
message: "[ "+err+" ]",
color: 'red',
icon: <ImCross />,
});
error = true; error = true;
navigator("/")
}) })
if (error) return if (error) return
await serviceregexlist(srv_id).then(res => { await serviceregexlist(srv_id).then(res => {
setRegexesList(res) setRegexesList(res)
}).catch( }).catch(
err =>{ err =>{
showNotification({ errorNotify(`Updater for ${srv_id} service failed [Regex list]!`, err.toString())
autoClose: notification_time,
title: `Updater for ${srv_id} service failed [Regex list]!`,
message: "[ "+err+" ]",
color: 'red',
icon: <ImCross />,
});
error = true; error = true;
}) })
} }
@@ -79,9 +52,11 @@ function ServiceDetails() {
return () => { clearInterval(updater) } return () => { clearInterval(updater) }
}, []); }, []);
const [deleteModal, setDeleteModal] = useState(false)
return <> return <>
<ServiceRow service={serviceInfo} additional_buttons={<> <ServiceRow service={serviceInfo} additional_buttons={<>
<ActionIcon color="red" onClick={()=>{}} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon> <ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"><BsTrashFill size={22} /></ActionIcon>
<Space w="md"/> <Space w="md"/>
</>}></ServiceRow> </>}></ServiceRow>
{regexesList.length === 0? {regexesList.length === 0?
@@ -90,6 +65,13 @@ function ServiceDetails() {
{regexesList.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={6}><RegexView regexInfo={regexInfo}/></Grid.Col>)} {regexesList.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={6}><RegexView regexInfo={regexInfo}/></Grid.Col>)}
</Grid> </Grid>
} }
<YesNoModal
title='Are you sure to delete this service?'
description={`You are going to delete the service '${serviceInfo.id}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service ⚠️!`}
onClose={()=>setDeleteModal(false)}
action={()=>console.log("Delete the service please!")}
opened={deleteModal}
/>
</> </>
} }