]> PHS Git Server - phs-admin.git/commitdiff
Cleaning up main views.
authorcharleswrayjr <charleswrayjr@gmail.com>
Mon, 15 Sep 2025 04:26:01 +0000 (23:26 -0500)
committercharleswrayjr <charleswrayjr@gmail.com>
Mon, 15 Sep 2025 04:26:01 +0000 (23:26 -0500)
src/app/components/MessageForm.js
src/app/components/MessageGroupForm.js
src/app/components/MessageGroupMemberForm.js
src/app/components/PostForm.js
src/app/components/VPN/VPNContext.jsx
src/app/services/VPN/VPNConfig.js
src/app/services/VPN/VPNService.js
src/app/views/VPN/VPN.jsx
src/globals.js
tailwind.config.js

index 1e64c3c879e4a250c8f05a59c649dbf52ced4b1c..cb5b4e13f9d71d4772f1143e52f0617611c9da1b 100644 (file)
@@ -1,3 +1,5 @@
+// noinspection JSValidateTypes
+
 /**
  * @file Component for creating a new message
  */
index b312a55bedb1f3ff274b400f972dc7ecb3cd45de..3d3d6619f7ab598aea6bfa91dcefe6a90e619e43 100644 (file)
@@ -1,3 +1,5 @@
+// noinspection JSValidateTypes
+
 /**
  * @file Component for creating a new message group
  */
index 5e46d1caa23280570ca59df02aa1f267f951f12d..922f86da3a7dd8725f55d7ed2f932fedb13d29bf 100644 (file)
@@ -1,3 +1,5 @@
+// noinspection JSValidateTypes
+
 /**
  * @file Component for adding a message group member
  */
index 24e8ec6727b366d8db4dfd63dc969b582b88c1bd..297612668997daf0a24a813b11489f2ed43e5373 100644 (file)
@@ -1,3 +1,5 @@
+// noinspection JSValidateTypes
+
 /**
  * @file Component for creating a new post
  */
index 7692c3c391cdd4582ce8dab44804df52fd52b1f5..0ae28a98a11df6c8729d13540d26c57e27fd66b2 100644 (file)
@@ -1,6 +1,7 @@
 import React, { createContext, useContext, useEffect, useState } from 'react';
 import { VPNService } from '../../services';
 import { ConfirmRevoke, CreateVPNClientDialog, SuspendClient } from '../index';
+import { useSnackbar } from 'notistack';
 
 const VPNContext = createContext( undefined );
 
@@ -18,6 +19,7 @@ export const VPNProvider = ( { children } ) => {
   const [suspendOpen, setSuspendOpen] = useState( false );
   /** TODO: Change the default time to something more reasonable. */
   const [duration, setDuration] = useState( 1 );
+  const { enqueueSnackbar } = useSnackbar();
 
   const fetchServerStatus = async () => {
     await VPNService.getServerStatus()
@@ -60,6 +62,14 @@ export const VPNProvider = ( { children } ) => {
     setRevokeClientName( '' );
   };
 
+  const handleToggleVPN = (action) => VPNService[action]().then( () => {
+    enqueueSnackbar( `VPN ${action} completed successfully`, { variant: 'success' } );
+    setTimeout( () => fetchServerStatus(), 1000 );
+  } ).catch( e => {
+    enqueueSnackbar( `VPN ${action} failed`, { variant: 'error' } );
+    setMessage( e );
+  });
+
   useEffect( () => {
     fetchClients().catch( e => setMessage( e ) );
     fetchAvailableClients().catch( e => setMessage( e ) );
@@ -95,7 +105,8 @@ export const VPNProvider = ( { children } ) => {
     suspendOpen,
     setSuspendOpen,
     duration,
-    setDuration
+    setDuration,
+    handleToggleVPN
   };
 
   return <VPNContext.Provider value={ value }>
index 17b8750f55962b79464ede404d73e98f3dea115f..46045a1f960391881d622a437930e94463983a1c 100644 (file)
@@ -6,6 +6,8 @@ const VPNConfig = {
   availableClients: `${base}available-clients/`,
   getStatus: `${base}status/`,
   disconnect: `${base}disconnect/`,
+  stop: `${base}stop/`,
+  start: `${base}start/`,
   restart: `${base}restart/`
 };
 
index b2fb1d06bd640b0cb5eef1f83f8005f44cdc2d9f..7b0504535989b5e9f1b0eff81bd30f0b461f45a9 100644 (file)
@@ -87,6 +87,28 @@ class VPNService {
     } );
   };
 
+  stop = () => {
+    return new Promise( async ( resolve ) => {
+      try {
+        const response = await axios.put( `${ base_url }${ VPNConfig.stop }` );
+        resolve( response.data.message );
+      } catch (error) {
+        resolve( `Error: ${ error.response?.data?.error || 'Failed to stop' }` );
+      }
+    } );
+  };
+
+  start = () => {
+    return new Promise( async ( resolve ) => {
+      try {
+        const response = await axios.put( `${ base_url }${ VPNConfig.start }` );
+        resolve( response.data.message );
+      } catch (error) {
+        resolve( `Error: ${ error.response?.data?.error || 'Failed to start' }` );
+      }
+    } );
+  };
+
   restart = () => {
     return new Promise( async ( resolve ) => {
       try {
index 44f48ddcb87eef203cc2f4faf270070ec55c9cf1..eda4db1c70b939328649f300c9d1069243e627ee 100644 (file)
@@ -1,4 +1,4 @@
-import { Container, Typography, Button, Grid, IconButton } from '@mui/material';
+import { Container, Typography, Button, Grid, IconButton, Card, CardHeader, CardContent } from '@mui/material';
 import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
 import { useVPN } from '../../components';
 import AccessTimeOutlined from '@mui/icons-material/AccessTimeOutlined';
@@ -11,6 +11,7 @@ const VPN = () => {
     availableClients,
     message,
     setCreateOpen,
+    handleToggleVPN,
     serverStatus,
     setSuspendOpen } = useVPN();
   const clientTable = useMaterialReactTable( {
@@ -21,6 +22,13 @@ const VPN = () => {
       { accessorFn: row => row.connectedSince, header: 'Connected Since' },
     ],
     data: clients,
+    renderTopToolbarCustomActions: () => (
+      <Grid container spacing={4}>
+        <Grid item>
+          <Typography variant="h5">Connected OpenVPN Clients</Typography>
+        </Grid>
+      </Grid>
+    )
   } );
   const availableTable = useMaterialReactTable({
     columns:[
@@ -47,30 +55,49 @@ const VPN = () => {
       },
     ],
     data: availableClients,
-    renderTopToolbarCustomActions: () => (
-      <>
-        <Button variant='contained'
-                color='primary'
-                size='small'
-                onClick={ () => setCreateOpen( true ) }>
-          Create Client
-        </Button>
-      </>
-    )
+    renderTopToolbarCustomActions: () => ( <Typography variant="h5" className="mt-6">Available Clients</Typography> )
   });
 
   console.log('serverStatus', serverStatus);
 
   return (
-    <Container>
-      <Typography variant="h5" className="mt-6">Available OpenVPN Clients<small style={{ float: 'right', marginRight: '1rem' }} >Status: <small style={{ color: serverStatus ? '#00FF00' : '#FF0000' }}>{ serverStatus ? 'Active' : 'Inactive' }</small></small></Typography>
-      <MaterialReactTable table={ availableTable } />
-
-      <Typography variant="h5" className="mt-6">Connected OpenVPN Clients</Typography>
-      <MaterialReactTable table={ clientTable } />
-
+    <Card maxWidth="xxl" className="mt-6">
+      <CardHeader title={(
+        <Grid container spacing={4}>
+          <Grid item>
+            <Typography variant="h5" className="mt-6">OpenVPN Management<small style={{ marginLeft: '1rem' }} >Status: <small style={{ color: serverStatus ? '#00FF00' : '#FF0000' }}>{ serverStatus ? 'Active' : 'Inactive' }</small></small></Typography>
+          </Grid>
+          <Grid item>
+            <Button variant='contained' color='error' size='small' onClick={ () => handleToggleVPN('stop') }>Stop Server</Button>
+          </Grid>
+          <Grid item>
+            <Button variant='contained' color='success' size='small' onClick={ () => handleToggleVPN('start') }>Start Server</Button>
+          </Grid>
+          <Grid item>
+            <Button variant='contained' color='warning' size='small' onClick={ () => handleToggleVPN('restart') }>Restart Server</Button>
+          </Grid>
+          <Grid item>
+            <Button variant='contained'
+                    color='primary'
+                    size='small'
+                    onClick={ () => setCreateOpen( true ) }>
+              Create Client
+            </Button>
+          </Grid>
+        </Grid>
+      )} />
+      <CardContent>
+        <Grid container spacing={2} className="w-full">
+          <Grid item size={{ xs: 12 }} >
+            <MaterialReactTable table={ availableTable } />
+          </Grid>
+          <Grid item size={{ xs: 12 }}>
+            <MaterialReactTable table={ clientTable } />
+          </Grid>
+        </Grid>
+      </CardContent>
       {message && <Typography color={message.startsWith('Error') ? 'error' : 'success'}>{message}</Typography>}
-    </Container>
+    </Card>
   );
 };
 
index e748f6131651e8ba4f8274d9a3041e99a632b052..5fbb3e171334efa944ea1814ea1ce29fcbb3f9eb 100755 (executable)
@@ -1 +1 @@
-export const base_url = 'http://localhost:23601/';
+export const base_url = 'https://api.phasecustomsoft.com/';
index c0f998f28ce678b58025d0ca292db12ea47ccaa9..0623b975d455eef15de59e20bd37ef8b7555b031 100755 (executable)
@@ -1,6 +1,6 @@
 /** @type {import('tailwindcss').Config} */
 /* eslint-disable import/no-extraneous-dependencies */
-// noinspection SpellCheckingInspection
+// noinspection SpellCheckingInspection,JSUnusedGlobalSymbols
 
 const path = require('path');