* @file Component for creating a new post
*/
-import React from 'react';
-import { useForm, Controller } from 'react-hook-form';
+import React, { useEffect } from 'react';
+import { Controller, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
-import { TextField, MenuItem, Button } from '@mui/material';
+import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, MenuItem, TextField } from '@mui/material';
import { useSnackbar } from 'notistack';
-import { useNavigate } from 'react-router-dom';
import { PostService } from '../services';
-const schema = yup.object({
- user_id: yup.number().required('User ID is required'),
- title: yup.string().required('Title is required'),
- content: yup.string().required('Content is required'),
- post_type: yup.string().oneOf(['blog', 'vlog']).required('Post type is required'),
- visibility: yup.string().oneOf(['private', 'family', 'public']).required('Visibility is required'),
-}).required();
+const schema = yup.object( {
+ user_id: yup.number().required( 'User ID is required' ),
+ title: yup.string().required( 'Title is required' ),
+ content: yup.string().required( 'Content is required' ),
+ post_type: yup.string().oneOf( [ 'blog', 'vlog' ] ).required( 'Post type is required' ),
+ visibility: yup.string().oneOf( [ 'private', 'family', 'public' ] ).required( 'Visibility is required' ),
+} ).required();
-const PostForm = () => {
+const PostForm = ( { isOpen, setOpen, refresh, user } ) => {
const { enqueueSnackbar } = useSnackbar();
- const navigate = useNavigate();
- const { control, handleSubmit, formState: { errors } } = useForm({
- resolver: yupResolver(schema),
- });
+ const { control, handleSubmit, setValue, reset, formState: { errors } } = useForm( {
+ resolver: yupResolver( schema ),
+ } );
- const onSubmit = async (data) => {
+ useEffect( () => {
+ setValue( 'user_id', user?.id );
+ }, [ user, setValue ] );
+
+ const onSubmit = async ( data ) => {
try {
- await PostService.createPost(data);
- enqueueSnackbar('Post created successfully', { variant: 'success' });
- navigate('/posts');
- } catch (error) {
- enqueueSnackbar(`Error creating post: ${error.message}`, { variant: 'error' });
+ await PostService.createPost( data );
+ await reset();
+ await refresh();
+ enqueueSnackbar( 'Post created successfully', { variant: 'success' } );
+ setOpen( false );
+ } catch ( error ) {
+ enqueueSnackbar( `Error creating post: ${ error.message }`, { variant: 'error' } );
}
};
+ const onCancel = () => {
+ reset();
+ setOpen( false );
+ };
+
return (
- <div className="container">
- <h2 className="text-2xl font-bold mb-4">Create Post</h2>
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
- <Controller
- name="user_id"
- control={control}
- defaultValue=""
- render={({ field }) => (
- <TextField
- {...field}
- label="User ID"
- type="number"
- fullWidth
- error={!!errors.user_id}
- helperText={errors.user_id?.message}
- className="mb-4"
- />
- )}
- />
- <Controller
- name="title"
- control={control}
- defaultValue=""
- render={({ field }) => (
- <TextField
- {...field}
- label="Title"
- fullWidth
- error={!!errors.title}
- helperText={errors.title?.message}
- className="mb-4"
- />
- )}
- />
- <Controller
- name="content"
- control={control}
- defaultValue=""
- render={({ field }) => (
- <TextField
- {...field}
- label="Content"
- multiline
- rows={6}
- fullWidth
- error={!!errors.content}
- helperText={errors.content?.message}
- className="mb-4"
- />
- )}
- />
- <Controller
- name="post_type"
- control={control}
- defaultValue=""
- render={({ field }) => (
- <TextField
- {...field}
- label="Post Type"
- select
- fullWidth
- error={!!errors.post_type}
- helperText={errors.post_type?.message}
- className="mb-4"
- >
- <MenuItem value="blog">Blog</MenuItem>
- <MenuItem value="vlog">Vlog</MenuItem>
- </TextField>
- )}
- />
- <Controller
- name="visibility"
- control={control}
- defaultValue=""
- render={({ field }) => (
- <TextField
- {...field}
- label="Visibility"
- select
- fullWidth
- error={!!errors.visibility}
- helperText={errors.visibility?.message}
- className="mb-4"
- >
- <MenuItem value="private">Private</MenuItem>
- <MenuItem value="family">Family</MenuItem>
- <MenuItem value="public">Public</MenuItem>
- </TextField>
- )}
- />
- <Button type="submit" variant="contained" color="primary" fullWidth>
- Create Post
- </Button>
+ <Dialog open={ isOpen } onClose={ onCancel } fullWidth maxWidth="md">
+ <DialogTitle component="h2">Create Post From { ''.concat( user?.first_name, ' ', user.last_name ) }</DialogTitle>
+ <form onSubmit={ handleSubmit( onSubmit ) }>
+ <DialogContent>
+ <Grid container spacing={ 1 }>
+ <Grid size={ { xs: 12 } }>
+ <Controller
+ name="title"
+ control={ control }
+ defaultValue=""
+ render={ ( { field } ) => (
+ <TextField
+ { ...field }
+ label="Title"
+ fullWidth
+ error={ !!errors.title }
+ helperText={ errors.title?.message }
+ className="mb-4"
+ />
+ ) }
+ />
+ </Grid>
+ <Grid size={ { xs: 12 } }>
+ <Controller
+ name="content"
+ control={ control }
+ defaultValue=""
+ render={ ( { field } ) => (
+ <TextField
+ { ...field }
+ label="Content"
+ multiline
+ rows={ 6 }
+ fullWidth
+ error={ !!errors.content }
+ helperText={ errors.content?.message }
+ className="mb-4"
+ />
+ ) }
+ />
+ </Grid>
+ <Grid size={ { xs: 12 } }>
+ <Controller
+ name="post_type"
+ control={ control }
+ defaultValue=""
+ render={ ( { field } ) => (
+ <TextField
+ { ...field }
+ label="Post Type"
+ select
+ fullWidth
+ error={ !!errors.post_type }
+ helperText={ errors.post_type?.message }
+ className="mb-4"
+ >
+ <MenuItem value="blog">Blog</MenuItem>
+ <MenuItem value="vlog">Vlog</MenuItem>
+ </TextField>
+ ) }
+ />
+ </Grid>
+ <Grid size={ { xs: 12 } }>
+ <Controller
+ name="visibility"
+ control={ control }
+ defaultValue=""
+ render={ ( { field } ) => (
+ <TextField
+ { ...field }
+ label="Visibility"
+ select
+ fullWidth
+ error={ !!errors.visibility }
+ helperText={ errors.visibility?.message }
+ className="mb-4"
+ >
+ <MenuItem value="private">Private</MenuItem>
+ <MenuItem value="family">Family</MenuItem>
+ <MenuItem value="public">Public</MenuItem>
+ </TextField>
+ ) }
+ />
+ </Grid>
+ </Grid>
+ </DialogContent>
+ <DialogActions className="mr-16 mb-12">
+ <Button onClick={ onCancel } variant="contained" color="info" size="small">
+ Cancel
+ </Button>
+ <Button type="submit" variant="contained" color="warning" size="small">
+ Create Post
+ </Button>
+ </DialogActions>
</form>
- </div>
+ </Dialog>
);
};
* @file Component for displaying posts in a table
*/
-import React, { useMemo, useState } from 'react';
+import React, { useContext, useMemo, useState } from 'react';
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
-import { Button, Container } from '@mui/material';
+import { Button, Container, Grid } from '@mui/material';
+import { AuthContext } from '../components/AuthContext';
import { useSnackbar } from 'notistack';
-import { useNavigate } from 'react-router-dom';
import { PostService } from '../services';
import { getFormattedDate } from '../utils';
+import PostForm from './PostForm';
const PostsTable = () => {
const { enqueueSnackbar } = useSnackbar();
- const navigate = useNavigate();
- const [posts, setPosts] = useState([]);
+ const [ posts, setPosts ] = useState( [] );
+ const [ showForm, setShowForm ] = useState( false );
+ const { user } = useContext( AuthContext );
const fetchPosts = useMemo( () => async () => {
try {
- console.log('Fetching posts...');
- const response = await PostService.getPosts();
- setPosts(response.data);
- } catch (error) {
- enqueueSnackbar(`Error fetching posts: ${error.message}`, { variant: 'error' });
+ console.log( 'Fetching posts...' );
+ const response = await PostService.getPosts( { is_deleted: false } );
+ setPosts( response.data );
+ } catch ( error ) {
+ enqueueSnackbar( `Error fetching posts: ${ error.message }`, { variant: 'error' } );
}
- }, [enqueueSnackbar] );
+ }, [ enqueueSnackbar ] );
// Fetch posts on mount
- React.useEffect(() => {
+ React.useEffect( () => {
fetchPosts().catch();
- }, [fetchPosts, enqueueSnackbar]);
+ }, [ fetchPosts, enqueueSnackbar ] );
- const columns = useMemo(() => [
+ const columns = useMemo( () => [
{
accessorKey: 'id',
header: 'ID',
size: 80,
+ visibleInShowHideMenu: false,
},
{
- accessorKey: 'user_id',
- header: 'User ID',
+ accessorKey: 'user_name',
+ header: 'Creator',
size: 100,
},
{
accessorKey: 'created_at',
header: 'Created At',
size: 150,
- Cell: ({ cell }) => getFormattedDate(cell.getValue()),
+ Cell: ( { cell } ) => getFormattedDate( cell.getValue() ),
},
- ], []);
+ ], [] );
- const table = useMaterialReactTable({
+ const table = useMaterialReactTable( {
columns,
data: posts,
enableColumnActions: false,
},
muiTableBodyCellProps: {
className: 'table-row',
- }});
+ },
+ initialState: {
+ columnVisibility: {
+ id: false
+ }
+ },
+ renderTopToolbarCustomActions: () => (
+ <Grid container spacing={ 2 } alignItems="center">
+ <Grid item>
+ <h2 className="text-2xl font-bold mb-4">Posts</h2>
+ </Grid>
+ <Grid item>
+ <Button
+ variant="contained"
+ color="primary"
+ onClick={ () => setShowForm( true ) }
+ >
+ Create Post
+ </Button>
+ </Grid>
+ </Grid>
+ )
+ } );
return (
- <Container maxWidth='xxl'>
- <h2 className="text-2xl font-bold mb-4">Posts</h2>
- <Button
- variant="contained"
- color="primary"
- onClick={() => navigate('/posts/new')}
- className="mb-4"
- >
- Create Post
- </Button>
- <MaterialReactTable table={table} />
+ <Container maxWidth="xxl" className="mt-4">
+ { showForm && <PostForm isOpen={ showForm } setOpen={ setShowForm } refresh={ fetchPosts } user={ user }/> }
+ <MaterialReactTable table={ table }/>
</Container>
);
};