Skip to content

Commit

Permalink
feat(router): React Router Dom
Browse files Browse the repository at this point in the history
Added react router dom.
Created Home and About page with basic navigation.

Installed dependencies:
yarn add react-router-dom
  • Loading branch information
rbiedrawa committed Mar 12, 2022
1 parent 2a8d59f commit 226fc88
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 139 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react-dom": "^17.0.2",
"react-hook-form": "^7.27.1",
"react-redux": "^7.2.6",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"redux": "^4.1.2",
"redux-saga": "^1.1.3",
Expand Down
155 changes: 18 additions & 137 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,146 +1,27 @@
import React, {useEffect} from "react";
import React from "react";
import {Provider} from "react-redux";
import {useForm} from 'react-hook-form';

import './App.css';
import {Button, Card, CardActions, CardContent, CardMedia, Grid} from "@mui/material";
import CssBaseline from "@mui/material/CssBaseline";
import AppBar from '@mui/material/AppBar';
import LocalPostOfficeIcon from '@mui/icons-material/LocalPostOffice';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import {createTheme, ThemeProvider} from '@mui/material/styles';
import {FormTextField} from "./libs/ui/FormTextField";
import * as Yup from 'yup';
import {yupResolver} from "@hookform/resolvers/yup";
import {useAppDispatch, useAppSelector} from "./redux/hooks";
import {
postsActions,
selectPosts
} from "./redux/features/posts/posts.slice";
import {BrowserRouter} from "react-router-dom";
import {store} from "./redux/store";

import {createTheme} from '@mui/material/styles';
import AppRoutes from "./routes";
import {ThemeProvider} from "@emotion/react";
import {CssBaseline} from "@mui/material";

const theme = createTheme();

interface CreateFormInput {
title: string;
body: string;
}

const createFormDefaultValues = {
title: "",
body: "",
};

const newPostValidationSchema = Yup.object().shape({
title: Yup.string().required('Title can\'t be empty').max(10, 'Title must not exceed 10 characters'),
body: Yup.string().required('Body is required'),
});

function PostApp() {
const dispatch = useAppDispatch();
const posts = useAppSelector(selectPosts);

useEffect(() => {
dispatch(postsActions.fetchAll());
}, []);

const methods = useForm<CreateFormInput>({defaultValues: createFormDefaultValues, resolver: yupResolver(newPostValidationSchema)});
const {handleSubmit, reset, control} = methods;

const onAdd = (data: CreateFormInput) => dispatch(postsActions.create({title: data!.title, body: data!.body}));

return (
<ThemeProvider theme={theme}>
<CssBaseline/>
<AppBar position="relative">
<Toolbar>
<LocalPostOfficeIcon sx={{mr: 2}}/>
<Typography variant="h6" color="inherit" noWrap>
App
</Typography>
</Toolbar>
</AppBar>

<main>
<Box
sx={{
bgcolor: 'background.paper',
pt: 6,
pb: 6,
}}
>
<Container maxWidth="xs">
<Typography
component="h1"
variant="h2"
align="center"
color="text.primary"
gutterBottom
>
Posts
</Typography>
<Stack
sx={{pt: 4}}
direction="column"
spacing={1}
justifyContent="center"
>
<FormTextField name="title" label="title" control={control} />
<FormTextField name="body" label="body" control={control} />
<Button onClick={handleSubmit(onAdd)} variant={"contained"}>Add new Post</Button>
<Button onClick={() => reset()} variant={"outlined"}>Reset</Button>
</Stack>
</Container>
</Box>
<Container sx={{py: 8}} maxWidth="md">
{/* End hero unit */}
<Grid container spacing={4}>
{posts.map((post) => (
<Grid item key={post.id} xs={12} sm={6} md={4}>
<Card
sx={{height: '100%', display: 'flex', flexDirection: 'column'}}
>
<CardMedia
component="img"
image="https://source.unsplash.com/random"
alt="random"
/>
<CardContent sx={{flexGrow: 1}}>
<Typography gutterBottom variant="h5" component="h2">
{post.title}
</Typography>
<Typography>
{post.body}
</Typography>
</CardContent>
<CardActions>
<Button size="small" onClick={() => dispatch(postsActions.delete(post))}>Delete</Button>
<Button size="small" onClick={() => dispatch(postsActions.update({
...post,
body: `Updated at ${new Date().toISOString()}`
}))}>Update</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
</ThemeProvider>
);
}

function App() {
return (
<Provider store={store}>
<PostApp/>
</Provider>
);
}
const App = () => (
<>
<BrowserRouter>
<ThemeProvider theme={theme}>
<Provider store={store}>
<CssBaseline/>
<AppRoutes/>
</Provider>
</ThemeProvider>
</BrowserRouter>
</>
);

export default App;
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import App from "./App";

ReactDOM.render(
<React.StrictMode>
Expand Down
28 changes: 28 additions & 0 deletions src/pages/About.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Link} from "@mui/material";
import {NavLink as RouterNavLink} from "react-router-dom";
import React from "react";
import Typography from "@mui/material/Typography";

const About = () => {
return (
<>
<Typography
component="h1"
variant="h2"
align="center"
color="text.primary"
gutterBottom
>
About Page
</Typography>
<Link
component={RouterNavLink}
to={'/'}
variant="button"
color="text.primary"
sx={{ my: 1, mx: 1.5 }}
>Back to Home</Link>
</>
);
}
export default About;
156 changes: 156 additions & 0 deletions src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import React, {useEffect} from "react";
import {useForm} from 'react-hook-form';

import '../App.css';
import {Button, Card, CardActions, CardContent, CardMedia, Grid, Link} from "@mui/material";
import AppBar from '@mui/material/AppBar';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import {FormTextField} from "../libs/ui/FormTextField";
import * as Yup from 'yup';
import {yupResolver} from "@hookform/resolvers/yup";
import {useAppDispatch, useAppSelector} from "../redux/hooks";
import {postsActions, selectPosts} from "../redux/features/posts/posts.slice";
import { NavLink as RouterLink } from 'react-router-dom';



interface CreateFormInput {
title: string;
body: string;
}

const createFormDefaultValues = {
title: "",
body: "",
};

const newPostValidationSchema = Yup.object().shape({
title: Yup.string().required('Title can\'t be empty').max(10, 'Title must not exceed 10 characters'),
body: Yup.string().required('Body is required'),
});

function Home() {
const dispatch = useAppDispatch();
const posts = useAppSelector(selectPosts);

useEffect(() => {
dispatch(postsActions.fetchAll());
}, []);

const methods = useForm<CreateFormInput>({
defaultValues: createFormDefaultValues,
resolver: yupResolver(newPostValidationSchema)
});
const {handleSubmit, reset, control} = methods;

const onAdd = (data: CreateFormInput) => dispatch(postsActions.create({title: data!.title, body: data!.body}));

return (
<>
<AppBar
position="static"
color="primary"
elevation={0}
sx={{ borderBottom: (theme) => `1px solid ${theme.palette.divider}` }}
>
<Toolbar sx={{ flexWrap: 'wrap' }}>
<Typography variant="h6" color="inherit" noWrap sx={{ flexGrow: 1 }}>
Company name
</Typography>
<nav>
<Link
component={RouterLink}
to={'/'}
variant="button"
color="text.primary"
sx={{ my: 1, mx: 1.5 }}
>
Home
</Link>
<Link
component={RouterLink}
to={'/about'}
variant="button"
color="text.primary"
sx={{ my: 1, mx: 1.5 }}
>
About
</Link>
</nav>
</Toolbar>
</AppBar>
<main>
<Box
sx={{
bgcolor: 'background.paper',
pt: 6,
pb: 6,
}}
>
<Container maxWidth="xs">
<Typography
component="h1"
variant="h2"
align="center"
color="text.primary"
gutterBottom
>
Posts
</Typography>
<Stack
sx={{pt: 4}}
direction="column"
spacing={1}
justifyContent="center"
>
<FormTextField name="title" label="title" control={control}/>
<FormTextField name="body" label="body" control={control}/>
<Button onClick={handleSubmit(onAdd)} variant={"contained"}>Add new Post</Button>
<Button onClick={() => reset()} variant={"outlined"}>Reset</Button>
</Stack>
</Container>
</Box>
<Container sx={{py: 8}} maxWidth="md">
{/* End hero unit */}
<Grid container spacing={4}>
{posts.map((post) => (
<Grid item key={post.id} xs={12} sm={6} md={4}>
<Card
sx={{height: '100%', display: 'flex', flexDirection: 'column'}}
>
<CardMedia
component="img"
image="https://source.unsplash.com/random"
alt="random"
/>
<CardContent sx={{flexGrow: 1}}>
<Typography gutterBottom variant="h5" component="h2">
{post.title}
</Typography>
<Typography>
{post.body}
</Typography>
</CardContent>
<CardActions>
<Button size="small"
onClick={() => dispatch(postsActions.delete(post))}>Delete</Button>
<Button size="small" onClick={() => dispatch(postsActions.update({
...post,
body: `Updated at ${new Date().toISOString()}`
}))}>Update</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
</>
);
}

export default Home;
14 changes: 14 additions & 0 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Route, Routes} from 'react-router-dom';
import Home from "../pages/Home";
import About from "../pages/About";

const AppRoutes = () => (
<>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
</Routes>
</>
);

export default AppRoutes;
Loading

0 comments on commit 226fc88

Please sign in to comment.