-
Notifications
You must be signed in to change notification settings - Fork 1
08 React Frontend
JP Barbosa edited this page Apr 15, 2023
·
2 revisions
npm install axios react-hook-form react-query
rm ./packages/web/src/styles.scss
rm ./packages/web/src/app/app.module.scss
rm ./packages/web/src/app/nx-welcome.tsx
mkdir -p ./packages/web/src/styles
ASSETS_DST="./packages/web/src/styles"
ASSETS_SRC="https://raw.githubusercontent.com/jpbarbosa/neo4j-crud/main/packages/web/src/styles/"
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}actions-bar.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}alert.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}button.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}form.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}header.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}index.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}record-list.scss")
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}spacing.scss")
mkdir -p ./packages/web/public/img
ASSETS_DST="./packages/web/public/img"
ASSETS_SRC="https://raw.githubusercontent.com/jpbarbosa/neo4j-crud/main/packages/web/public/img/"
(cd $ASSETS_DST && curl -OL "${ASSETS_SRC}px.gif")
code ./packages/web/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Neo4j Fullstack CRUD</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles/index.scss" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
code ./packages/web/src/main.tsx
import * as ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from 'react-query';
import App from './app/app';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 1,
},
},
});
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</BrowserRouter>
);
code ./packages/web/src/app/app.tsx
import { Route, Routes, Navigate } from 'react-router-dom';
import { Movies } from '../pages';
import { Header } from '../components';
export function App() {
return (
<>
<Header />
<Routes>
<Route path="/" element={<Navigate to="/movies" />} />
<Route path="/movies/*" element={<Movies />} />
</Routes>
</>
);
}
export default App;
code ./packages/web/src/components/Header.tsx
import { Link, PathPattern, useMatch } from 'react-router-dom';
type NavItem = {
path: string;
label: string;
};
type PageButtonProps = {
item: NavItem;
};
const PageButton: React.FC<PageButtonProps> = ({ item: { path, label } }) => {
const pathPattern: PathPattern<string> = { path: `${[path]}/*` };
const match = useMatch(pathPattern);
const isActive = !!match;
const classList = ['button', isActive ? 'active' : ''];
return (
<Link to={path} className={classList.join(' ')}>
{label}
</Link>
);
};
export const Header: React.FC = () => {
const navItems: NavItem[] = [
{ path: '/movies', label: 'Movies' },
];
return (
<header>
<h1>
<div className="title">Neo4j Fullstack CRUD</div>
<div className="subtitle">
With NX Monorepo, Express, React and TypeScript
</div>
</h1>
<div className="navigation">
<div className="internal">
{navItems.map((item) => (
<PageButton item={item} key={item.path} />
))}
</div>
<div className="external">
<a
href="https://github.com/jpbarbosa/neo4j-crud"
target="_blank"
rel="noreferrer"
className="button"
>
GitHub / jpbarbosa
</a>
</div>
</div>
</header>
);
};
code ./packages/web/src/components/index.ts
export * from './Header';
code ./packages/web/src/pages/movies/index.tsx
import { Route, Routes } from 'react-router-dom';
import { List } from './List';
export const Movies = () => {
return (
<Routes>
<Route path="/" element={<List />} />
<Route path="/:id/edit" element={<div>To be implemented</div>} />
<Route path="/new" element={<div>To be implemented</div>} />
</Routes>
);
};
code ./packages/web/src/pages/movies/List/index.tsx
import { Content } from './Content';
export const List = () => {
return (
<div>
<div className="actions-bar">
<h2>Movies</h2>
</div>
<Content />
</div>
);
};
code ./packages/web/src/pages/movies/List/Content.tsx
import axios from 'axios';
import { useQuery } from 'react-query';
import { AxiosCustomError, Movie } from '@neo4j-crud/shared';
const url = `${import.meta.env.VITE_API_URL}/movies`;
export const Content: React.FC = () => {
const { data, error, isLoading } = useQuery<Movie[], AxiosCustomError>(
['movies'],
() => axios.get<Movie[]>(url).then((res) => res.data)
);
if (error) {
return <div>{error.message}</div>;
}
if (isLoading) {
return <div>Loading...</div>;
}
if (!data) {
return <div>No data.</div>;
}
return (
<ul className="record-list">
{data.map((movie) => (
<li>{movie.title}</li>
))}
</ul>
);
};
code ./packages/web/src/pages/index.ts
export * from './movies';
nx run-many --target=serve --all
open http://localhost:4200
git add .
git commit -m "React Frontend"