]> PHS Git Server - phs-admin.git/commitdiff
Cleaning up Dashboard.jsx main view.
authorcharleswrayjr <charleswrayjr@gmail.com>
Sun, 14 Sep 2025 00:00:16 +0000 (19:00 -0500)
committercharleswrayjr <charleswrayjr@gmail.com>
Sun, 14 Sep 2025 00:00:16 +0000 (19:00 -0500)
src/app/views/Dashboard/Dashboard.jsx

index 2432153d6ee6706be8208fcb74fb46bea6e8b8f1..3c70e51b2a322d9e1a41d1621342d831b0f75d62 100755 (executable)
  * @file Dashboard view with informative widgets
  */
 
-import React, { useContext, useEffect, useState } from 'react';
-import { Container, Grid, Card, CardContent, Typography, Box, Button } from '@mui/material';
-import { MaterialReactTable } from 'material-react-table';
+import React, { useContext, useEffect, useMemo, useState } from 'react';
+import { Box, Button, Card, CardContent, Container, Grid, Typography } from '@mui/material';
+import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
 import { useNavigate } from 'react-router-dom';
 import { useSnackbar } from 'notistack';
 import { AuthContext } from '../../components/AuthContext';
-import { MessageService, MediaService, PostService, MessageGroupService } from '../../services';
+import { MediaService, MessageGroupService, MessageService, PostService } from '../../services';
 import { getFormattedDate, truncateContent } from '../../utils';
 
+/**
+ * @typedef {Object} User
+ * @property {number} id
+ * @property {string} first_name
+ * @property {string} last_name
+ * @property {string} email
+ */
+
 const Dashboard = () => {
   const navigate = useNavigate();
   const { enqueueSnackbar } = useSnackbar();
-  const { user } = useContext(AuthContext);
-  const [recentMessages, setRecentMessages] = useState([]);
-  const [recentMedia, setRecentMedia] = useState([]);
-  const [recentPosts, setRecentPosts] = useState([]);
-  const [recentGroups, setRecentGroups] = useState([]);
-
-  useEffect(() => {
-    const fetchData = async () => {
-      try {
-        // Fetch recent messages
-        const messages = await MessageService.getMessages({ limit: 5 });
-        setRecentMessages(messages.data);
-
-        // Fetch recent media (filtered by visibility for non-Admin)
-        const mediaParams = user?.roles.includes('Admin') ? { limit: 5 } : { limit: 5, visibility: ['public', 'family'] };
-        const media = await MediaService.getMedia(mediaParams);
-        setRecentMedia(media.data);
-
-        // Fetch recent posts (filtered by visibility for non-Admin)
-        const postsParams = user?.roles.includes('Admin') ? { limit: 5 } : { limit: 5, visibility: ['public', 'family'] };
-        const posts = await PostService.getPosts(postsParams);
-        setRecentPosts(posts.data);
-
-        // Fetch recent message groups
-        const groups = await MessageGroupService.getMessageGroups({ limit: 5 });
-        setRecentGroups(groups.data);
-      } catch (error) {
-        enqueueSnackbar(`Error fetching dashboard data: ${error.message}`, { variant: 'error' });
-      }
-    };
-    fetchData();
-  }, [enqueueSnackbar, user]);
+  const { user } = useContext( AuthContext );
+  const [recentMessages, setRecentMessages] = useState( [] );
+  const [recentMedia, setRecentMedia] = useState( [] );
+  const [recentPosts, setRecentPosts] = useState( [] );
+  const [recentGroups, setRecentGroups] = useState( [] );
+
+  const fetchData = useMemo( () => async () => {
+    try {
+      // Fetch recent messages
+      const messages = await MessageService.getMessages( { limit:5 } );
+      setRecentMessages( messages.data );
+
+      // Fetch recent media (filtered by visibility for non-Admin)
+      const mediaParams = user?.roles.includes( 'Admin' ) ? { limit:5 } : { limit:5, visibility:['public', 'family'] };
+      const media = await MediaService.getMedia( mediaParams );
+      setRecentMedia( media.data );
+
+      // Fetch recent posts (filtered by visibility for non-Admin)
+      const postsParams = user?.roles.includes( 'Admin' ) ? { limit:5 } : { limit:5, visibility:['public', 'family'] };
+      const posts = await PostService.getPosts( postsParams );
+      setRecentPosts( posts.data );
+
+      // Fetch recent message groups
+      const groups = await MessageGroupService.getMessageGroups( { limit:5 } );
+      setRecentGroups( groups.data );
+    } catch (error) {
+      enqueueSnackbar( `Error fetching dashboard data: ${ error.message }`, { variant:'error' } );
+    }
+  }, [enqueueSnackbar, user] );
+
+  useEffect( () => {
+    fetchData().catch();
+  }, [enqueueSnackbar, user, fetchData] );
 
   const messageColumns = [
-    { accessorKey: 'sender_id', header: 'Sender ID', size: 100 },
-    { accessorKey: 'content', header: 'Content', size: 200, Cell: ({ cell }) => cell.getValue() },
-    { accessorKey: 'created_at', header: 'Sent At', size: 150, Cell: ({ cell }) => getFormattedDate(cell.getValue()) },
+    { accessorKey:'sender_id', header:'Sender ID', size:100 },
+    { accessorKey:'content', header:'Content', size:200, Cell:( { cell } ) => truncateContent( cell.getValue(), 50 ) },
+    { accessorKey:'read', header:'Read', size:80, Cell:( { cell } ) => (cell.getValue() ? 'Yes' : 'No') },
+    { accessorKey:'created_at', header:'Sent At', size:150, Cell:( { cell } ) => getFormattedDate( cell.getValue() ) },
   ];
 
   const mediaColumns = [
-    { accessorKey: 'file_path', header: 'File Path', size: 200 },
-    { accessorKey: 'file_type', header: 'Type', size: 100 },
-    { accessorKey: 'visibility', header: 'Visibility', size: 100 },
-    { accessorKey: 'created_at', header: 'Uploaded At', size: 150, Cell: ({ cell }) => getFormattedDate(cell.getValue()) },
+    { accessorKey:'file_path', header:'File Path', size:200 },
+    { accessorKey:'file_type', header:'Type', size:100 },
+    { accessorKey:'visibility', header:'Visibility', size:100 },
+    {
+      accessorKey:'created_at',
+      header:'Uploaded At',
+      size:150,
+      Cell:( { cell } ) => getFormattedDate( cell.getValue() )
+    },
   ];
 
   const postColumns = [
-    { accessorKey: 'title', header: 'Title', size: 200 },
-    { accessorKey: 'post_type', header: 'Type', size: 100 },
-    { accessorKey: 'visibility', header: 'Visibility', size: 100 },
-    { accessorKey: 'created_at', header: 'Posted At', size: 150, Cell: ({ cell }) => getFormattedDate(cell.getValue()) },
+    { accessorKey:'title', header:'Title', size:200 },
+    { accessorKey:'post_type', header:'Type', size:100 },
+    { accessorKey:'visibility', header:'Visibility', size:100 },
+    {
+      accessorKey:'created_at',
+      header:'Posted At',
+      size:150,
+      Cell:( { cell } ) => getFormattedDate( cell.getValue() )
+    },
   ];
 
   const groupColumns = [
-    { accessorKey: 'name', header: 'Group Name', size: 200 },
-    { accessorKey: 'created_by_id', header: 'Created By', size: 100 },
-    { accessorKey: 'created_at', header: 'Created At', size: 150, Cell: ({ cell }) => getFormattedDate(cell.getValue()) },
+    { accessorKey:'name', header:'Group Name', size:200 },
+    { accessorKey:'created_by_id', header:'Created By', size:100 },
+    {
+      accessorKey:'created_at',
+      header:'Created At',
+      size:150,
+      Cell:( { cell } ) => getFormattedDate( cell.getValue() )
+    },
   ];
 
+  const messageTable = useMaterialReactTable( {
+    columns:messageColumns,
+    data:recentMessages,
+    enableColumnActions:false,
+    enableColumnFilters:false,
+    enablePagination:false,
+    enableSorting:false,
+    muiTablePaperProps:{
+      className:'shadow-none',
+    },
+    muiTableHeadCellProps:{
+      className:'table-header',
+    },
+    muiTableBodyCellProps:{
+      className:'table-row',
+    }
+  } );
+
+  const mediaTable = useMaterialReactTable( {
+    columns:mediaColumns,
+    data:recentMedia,
+    enableColumnActions:false,
+    enableColumnFilters:false,
+    enablePagination:false,
+    enableSorting:false,
+    muiTablePaperProps:{
+      className:'shadow-none',
+    },
+    muiTableHeadCellProps:{
+      className:'table-header',
+    },
+    muiTableBodyCellProps:{
+      className:'table-row',
+    }
+  } );
+
+  const postTable = useMaterialReactTable( {
+    columns:postColumns,
+    data:recentPosts,
+    enableColumnActions:false,
+    enableColumnFilters:false,
+    enablePagination:false,
+    enableSorting:false,
+    muiTablePaperProps:{
+      className:'shadow-none',
+    },
+    muiTableHeadCellProps:{
+      className:'table-header',
+    },
+    muiTableBodyCellProps:{
+      className:'table-row',
+    }
+  } );
+
+  const groupTable = useMaterialReactTable( {
+    columns:groupColumns,
+    data:recentGroups,
+    enableColumnActions:false,
+    enableColumnFilters:false,
+    enablePagination:false,
+    enableSorting:false,
+    muiTablePaperProps:{
+      className:'shadow-none',
+    },
+    muiTableHeadCellProps:{
+      className:'table-header',
+    },
+    muiTableBodyCellProps:{
+      className:'table-row',
+    }
+  } );
+
   return (
-    <Container maxWidth="xl" className="container">
-      <Typography variant="h4" className="text-3xl font-bold mb-6 mt-4">
-        Welcome to PHS Admin{user ? `, ${user.first_name || 'User ' + user.id}` : ''}
+    <Container maxWidth="xxl">
+      <Typography variant="h4"
+                  className="text-3xl font-bold mb-6 mt-4">
+        Welcome to PHS Admin{ user ? `, ${ user.first_name || 'User ' + user.id }` : '' }
       </Typography>
-      <Grid container spacing={3}>
-        <Grid item xs={12} md={6}>
-          <Card className="shadow-md rounded-lg">
+      <Grid container
+            spacing={ 2 }
+            className="w-full">
+        <Grid item
+              size={ { xs:12, md:6 } }>
+          <Card className="shadow-md rounded-lg w-full">
             <CardContent>
-              <Typography variant="h6" className="font-semibold mb-2">
+              <Typography variant="h6"
+                          className="font-semibold mb-2">
                 Recent Messages
               </Typography>
-              <MaterialReactTable
-                columns={messageColumns}
-                data={recentMessages}
-                enableColumnActions={false}
-                enableColumnFilters={false}
-                enablePagination={false}
-                enableSorting={false}
-                muiTablePaperProps={{ className: 'shadow-none' }}
-                muiTableHeadCellProps={{ className: 'table-header' }}
-                muiTableBodyCellProps={{ className: 'table-row' }}
-              />
-              <Box mt={2}>
-                <Button variant="contained" color="primary" onClick={() => navigate('/messages')}>
+              <MaterialReactTable table={ messageTable }/>
+              <Box mt={ 2 }>
+                <Button variant="contained"
+                        color="primary"
+                        onClick={ () => navigate( '/messages' ) }>
                   View All Messages
                 </Button>
               </Box>
             </CardContent>
           </Card>
         </Grid>
-        <Grid item xs={12} md={6}>
+        <Grid item
+              size={ { xs:12, md:6 } }>
           <Card className="shadow-md rounded-lg">
             <CardContent>
-              <Typography variant="h6" className="font-semibold mb-2">
+              <Typography variant="h6"
+                          className="font-semibold mb-2">
                 Recent Media
               </Typography>
-              <MaterialReactTable
-                columns={mediaColumns}
-                data={recentMedia}
-                enableColumnActions={false}
-                enableColumnFilters={false}
-                enablePagination={false}
-                enableSorting={false}
-                muiTablePaperProps={{ className: 'shadow-none' }}
-                muiTableHeadCellProps={{ className: 'table-header' }}
-                muiTableBodyCellProps={{ className: 'table-row' }}
-              />
-              <Box mt={2}>
-                <Button variant="contained" color="primary" onClick={() => navigate('/media')}>
+              <MaterialReactTable table={ mediaTable }/>
+              <Box mt={ 2 }>
+                <Button variant="contained"
+                        color="primary"
+                        onClick={ () => navigate( '/media' ) }>
                   View All Media
                 </Button>
               </Box>
             </CardContent>
           </Card>
         </Grid>
-        <Grid item xs={12} md={6}>
+        <Grid item
+              size={ { xs:12, md:6 } }>
           <Card className="shadow-md rounded-lg">
             <CardContent>
-              <Typography variant="h6" className="font-semibold mb-2">
+              <Typography variant="h6"
+                          className="font-semibold mb-2">
                 Recent Posts
               </Typography>
-              <MaterialReactTable
-                columns={postColumns}
-                data={recentPosts}
-                enableColumnActions={false}
-                enableColumnFilters={false}
-                enablePagination={false}
-                enableSorting={false}
-                muiTablePaperProps={{ className: 'shadow-none' }}
-                muiTableHeadCellProps={{ className: 'table-header' }}
-                muiTableBodyCellProps={{ className: 'table-row' }}
-              />
-              <Box mt={2}>
-                <Button variant="contained" color="primary" onClick={() => navigate('/posts')}>
+              <MaterialReactTable table={ postTable }/>
+              <Box mt={ 2 }>
+                <Button variant="contained"
+                        color="primary"
+                        onClick={ () => navigate( '/posts' ) }>
                   View All Posts
                 </Button>
               </Box>
             </CardContent>
           </Card>
         </Grid>
-        <Grid item xs={12} md={6}>
+        <Grid item
+              size={ { xs:12, md:6 } }>
           <Card className="shadow-md rounded-lg">
             <CardContent>
-              <Typography variant="h6" className="font-semibold mb-2">
+              <Typography variant="h6"
+                          className="font-semibold mb-2">
                 Recent Message Groups
               </Typography>
-              <MaterialReactTable
-                columns={groupColumns}
-                data={recentGroups}
-                enableColumnActions={false}
-                enableColumnFilters={false}
-                enablePagination={false}
-                enableSorting={false}
-                muiTablePaperProps={{ className: 'shadow-none' }}
-                muiTableHeadCellProps={{ className: 'table-header' }}
-                muiTableBodyCellProps={{ className: 'table-row' }}
-              />
-              <Box mt={2}>
-                <Button variant="contained" color="primary" onClick={() => navigate('/message-groups')}>
+              <MaterialReactTable table={ groupTable }/>
+              <Box mt={ 2 }>
+                <Button variant="contained"
+                        color="primary"
+                        onClick={ () => navigate( '/message-groups' ) }>
                   View All Groups
                 </Button>
               </Box>