]> PHS Git Server - phs-admin.git/commitdiff
Adding authentication, media, and messaging.
authorcharleswrayjr <charleswrayjr@gmail.com>
Sat, 13 Sep 2025 14:58:55 +0000 (09:58 -0500)
committercharleswrayjr <charleswrayjr@gmail.com>
Sat, 13 Sep 2025 14:58:55 +0000 (09:58 -0500)
src/App.js
src/app/components/AppHeader.js
src/app/services/auth.js
src/app/views/Dashboard/Dashboard.jsx
src/app/views/Login/Login.jsx

index 5f3e664a1ad422782152669432d2b8c2c382bef7..19a34bcecedb0f0191ce7a5898d4d9e0f6848346 100755 (executable)
@@ -4,6 +4,7 @@ import React from 'react';
 import { SnackbarProvider } from 'notistack';
 import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
 import AppHeader from './app/components/AppHeader';
+import { AuthProvider } from './app/components/AuthContext';
 
 const theme = createTheme({
   palette: {
@@ -17,8 +18,10 @@ const App = () => {
     <ThemeProvider theme={theme}>
       <CssBaseline />
       <SnackbarProvider maxSnack={3}>
-        <AppHeader />
-        <AppRoutes />
+        <AuthProvider>
+          <AppHeader />
+          <AppRoutes />
+        </AuthProvider>
       </SnackbarProvider>
     </ThemeProvider>
   );
index a5c522e6bac55aad0d6364f5a93434a5f7cf8c7f..52883993039accfce87c2e479924c669a9c66ea9 100644 (file)
@@ -2,7 +2,7 @@
  * @file Reusable header component with navigation
  */
 
-import React, { useContext } from 'react';
+import React, { useContext, useMemo } from 'react';
 import { AppBar, Toolbar, Typography, Button, Box } from '@mui/material';
 import { useNavigate } from 'react-router-dom';
 import { AuthContext } from './AuthContext';
@@ -11,14 +11,33 @@ const AppHeader = () => {
   const navigate = useNavigate();
   const { user, logout } = useContext(AuthContext) || {};
 
-  const navItems = [
+  const navItems = useMemo( () => user ? [
     { label: 'Dashboard', path: '/', roles: ['User', 'Admin'] },
     { label: 'Messages', path: '/messages', roles: ['User', 'Admin'] },
     { label: 'Message Groups', path: '/message-groups', roles: ['User', 'Admin'] },
     { label: 'Media', path: '/media', roles: ['User', 'Admin'] },
     { label: 'Posts', path: '/posts', roles: ['User', 'Admin'] },
     { label: 'Docker', path: '/docker', roles: ['Admin'] },
-  ];
+    { label: 'VPN', path: '/vpn', roles: ['Admin'] },
+    { label: 'GIT', path: '/git', roles: ['Admin'] },
+  ].filter(item => item.roles.some( role => [...user.roles]?.map( r => r.name )?.includes( role ) ) ) : [], [user] );
+
+  /**
+   *
+   * @type {React.ReactNode[]}
+   */
+  const navButtons = useMemo( () => navItems.map((item) => (
+    (
+      <Button
+        key={item.label}
+        color="inherit"
+        onClick={() => navigate(item.path)}
+        className="mx-2"
+      >
+        {item.label}
+      </Button>
+    )
+  )), [navItems, navigate] );
 
   return (
     <AppBar position="static" className="bg-blue-600">
@@ -27,18 +46,7 @@ const AppHeader = () => {
           PHS Admin
         </Typography>
         <Box>
-          {navItems.map((item) => (
-            (!user || item.roles.some(role => user.roles?.includes(role))) && (
-              <Button
-                key={item.label}
-                color="inherit"
-                onClick={() => navigate(item.path)}
-                className="mx-2"
-              >
-                {item.label}
-              </Button>
-            )
-          ))}
+          {navButtons}
           {user ? (
             <Button color="inherit" onClick={logout}>
               Logout
index de99b9ac8ca42029ae83874766f450bf58764624..1ffc6f6c963795de5f60e162f7ef2d035e069710 100644 (file)
@@ -4,7 +4,7 @@
 
 import axios from 'axios';
 
-const BASE_URL = 'http://localhost:3601';
+const BASE_URL = 'https://api.phasecustomsoft.com';
 
 const getAuthHeaders = () => ({
   headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
index e8aa6f18e98a49052dbe5ce596651033df81e038..436750b5e44dfa9c4cc99ff34fad694427587e0d 100755 (executable)
@@ -2,23 +2,54 @@
  * @file Dashboard view with navigation to main features
  */
 
-import React, { useContext } from 'react';
+import React, { useContext, useMemo } from 'react';
 import { Container, Grid, Card, CardContent, Typography, Button } from '@mui/material';
 import { useNavigate } from 'react-router-dom';
 import { AuthContext } from '../../components/AuthContext';
-import AppHeader from '../../components/AppHeader';
 
 const Dashboard = () => {
   const navigate = useNavigate();
   const { user } = useContext(AuthContext) || {};
 
-  const features = [
+  console.log(user);
+
+  const features = useMemo( () => user ? [
     { title: 'Messages', path: '/messages', description: 'View and send messages', roles: ['User', 'Admin'] },
     { title: 'Message Groups', path: '/message-groups', description: 'Manage group chats', roles: ['User', 'Admin'] },
     { title: 'Media', path: '/media', description: 'Upload and view media', roles: ['User', 'Admin'] },
     { title: 'Posts', path: '/posts', description: 'Create and view posts', roles: ['User', 'Admin'] },
     { title: 'Docker', path: '/docker', description: 'Manage Docker containers', roles: ['Admin'] },
-  ];
+    { title: 'VPN', path: '/vpn', description: 'Manage OpenVPN Connections and Clients', roles: ['Admin'] },
+    { title: 'GIT', path: '/git', description: 'Manage GIT repositories', roles: ['Admin'] },
+  ].filter(feature => feature.roles.some( role => [...user.roles]?.map( r => r.name )?.includes( role ) ) ) : [], [user] );
+
+  /**
+   *
+   * @type {unknown[]}
+   */
+  const featureCards = useMemo( () => features.map((feature) => (
+    (
+      <Grid item xs={12} sm= {6} md={4} key={feature.title}>
+        <Card className="shadow-md rounded-lg">
+          <CardContent>
+            <Typography variant="h6" className="font-semiold">
+              {feature.title}
+            </Typography>
+            <Typography variant="body2" className="text-gray-600 mb-4">
+              {feature.description}
+            </Typography>
+            <Button
+              variant="contained"
+              color="primary"
+              onClick={() => navigate(feature.path)}
+            >
+              Go to {feature.title}
+            </Button>
+          </CardContent>
+        </Card>
+      </Grid>
+    )
+  )), [features, navigate] );
 
   return (
     <div>
@@ -27,29 +58,7 @@ const Dashboard = () => {
           Welcome to PHS Admin{user ? `, ${user.first_name || 'User ' + user.id}` : ''}
         </Typography>
         <Grid container spacing={3}>
-          {features.map((feature) => (
-            (!user || feature.roles.some(role => user.roles?.includes(role))) && (
-              <Grid item xs={12} sm= {6} md={4} key={feature.title}>
-                <Card className="shadow-md rounded-lg">
-                  <CardContent>
-                    <Typography variant="h6" className="font-semibold">
-                      {feature.title}
-                    </Typography>
-                    <Typography variant="body2" className="text-gray-600 mb-4">
-                      {feature.description}
-                    </Typography>
-                    <Button
-                      variant="contained"
-                      color="primary"
-                      onClick={() => navigate(feature.path)}
-                    >
-                      Go to {feature.title}
-                    </Button>
-                  </CardContent>
-                </Card>
-              </Grid>
-            )
-          ))}
+          {featureCards}
         </Grid>
       </Container>
     </div>
index 45ec1538f25d6d3b8c1ad8ecfdb2a60ecc79772d..4e909e96019d82d1b5fdfefbf7d0d513a8234603 100755 (executable)
@@ -1,5 +1,5 @@
 /**
- * @file Login view with authentication form
+ * @file Login view with an authentication form
  */
 
 import React, { useContext } from 'react';
@@ -8,9 +8,7 @@ import { useForm, Controller } from 'react-hook-form';
 import { yupResolver } from '@hookform/resolvers/yup';
 import * as yup from 'yup';
 import { useSnackbar } from 'notistack';
-import { useNavigate } from 'react-router-dom';
 import { AuthContext } from '../../components/AuthContext';
-import AppHeader from '../../components/AppHeader';
 
 const schema = yup.object({
   email: yup.string().email('Invalid email').required('Email is required'),
@@ -19,7 +17,6 @@ const schema = yup.object({
 
 const Login = () => {
   const { enqueueSnackbar } = useSnackbar();
-  const navigate = useNavigate();
   const { login } = useContext(AuthContext) || {};
   const { control, handleSubmit, formState: { errors } } = useForm({
     resolver: yupResolver(schema),
@@ -36,7 +33,6 @@ const Login = () => {
 
   return (
     <div>
-      <AppHeader />
       <Container maxWidth="sm" className="container">
         <Typography variant="h4" className="text-3xl font-bold mb-6 mt-4">
           Login to PHS Admin