Skip to content

Commit 7d28c12

Browse files
authored
Merge pull request #1 from VirtusLab-Open-Source/feat/navigation-nextjs-example
2 parents 5354c04 + 48eb348 commit 7d28c12

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+20546
-0
lines changed

strapi-nextjs-navigation/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Strapi Plugin Navigation example with NextJS
2+
3+
This example is set up to show how to create a project with our navigation plugin installed as well as integrate it with a sample frontend.
4+
5+
## 🔧 Getting Started
6+
7+
To run this project, you need to prepare two shell windows. One window will be used to set up and run strapi server and the second one will be used to run development server for nextjs frontend.
8+
9+
### Strapi Server
10+
11+
Install all packages
12+
13+
```sh
14+
cd ./strapi-app
15+
yarn install
16+
```
17+
18+
Run the server
19+
20+
> Before running the strapi server be sure to create your own `.env` file. Example of this file can be found in strapi app folder.
21+
```sh
22+
yarn build
23+
yarn develop
24+
# or
25+
yarn develop --watch-admin
26+
```
27+
28+
After that open strapi admin panel and create admin user. The strapi project should be ready to use at this point.
29+
30+
### NextJS development server
31+
32+
Install all packages
33+
```sh
34+
cd ./next-app
35+
yarn install
36+
```
37+
38+
Run the server
39+
```sh
40+
yarn dev
41+
```
42+
43+
After that the nextJs frontend should be ready to use. You can direct to `localhost:3000` to see it.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"presets": [
3+
"next/babel"
4+
],
5+
"plugins": [
6+
[
7+
"styled-components",
8+
{
9+
"ssr": true,
10+
"displayName": true,
11+
"preprocess": false
12+
}
13+
]
14+
]
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { NavigationItemFlat, NavigationItemTree } from "../types/navigation";
2+
3+
export const fetchMainNavigation = async (idOrSlug: string | number) => {
4+
const res = await fetch(`http://localhost:1337/api/navigation/render/${idOrSlug}?type=TREE`)
5+
const data: NavigationItemTree[] = await res.json();
6+
return data;
7+
}
8+
9+
export const fetchFooterNavigation = async () => {
10+
const res = await fetch("http://localhost:1337/api/navigation/render/2?type=FLAT");
11+
const data: NavigationItemFlat[] = await res.json();
12+
return data;
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useEffect, useState } from "react";
2+
import { NavigationItemFlat } from "../../types/navigation";
3+
import Link from "next/link";
4+
import { fetchFooterNavigation } from "../../api";
5+
6+
const MainFooter = () => {
7+
const [footerItems, setFooterItems] = useState<NavigationItemFlat[]>([]);
8+
useEffect(() => {
9+
fetchFooterNavigation()
10+
.then(setFooterItems)
11+
.catch(e => console.error(e));
12+
}, []);
13+
14+
return (
15+
<footer className="footer mt-auto py-3 bg-light">
16+
<div className="container d-flex flex-wrap justify-content-between align-items-center">
17+
<p className="col-md-4 mb-0 text-muted">Created by VirtusLab</p>
18+
<ul className="nav col-md-auto justify-content-end">
19+
{footerItems.map((navItem) => {
20+
const isExternal = navItem.type === "EXTERNAL";
21+
return (
22+
<Link key={navItem.id} href={isExternal ? navItem.externalPath : navItem.path}>
23+
<li className="nav-item nav-link px-3 text-muted">
24+
{navItem.title}
25+
</li>
26+
</Link>
27+
);
28+
})}
29+
</ul>
30+
</div>
31+
</footer>
32+
);
33+
}
34+
35+
export default MainFooter;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Container } from "react-bootstrap";
2+
import Footer from "../Footer";
3+
import Navbar from "../Navbar";
4+
5+
const MainLayout: React.FC<any> = ({ children }) => {
6+
return (
7+
<div className="d-flex flex-column" style={{ height: "100vh" }}>
8+
<Navbar />
9+
<Container className="my-5">
10+
{children}
11+
</Container>
12+
<Footer />
13+
</div>
14+
);
15+
}
16+
17+
export default MainLayout;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect, useState } from "react";
2+
import { NavigationItemTree } from "../../types/navigation";
3+
import { Container, Nav, Navbar } from 'react-bootstrap';
4+
import Link from "next/link";
5+
import List from "./list";
6+
import { fetchMainNavigation } from "../../api";
7+
8+
interface IProps {
9+
idOrSlug?: number | string;
10+
}
11+
12+
const MainNavbar: React.FC<IProps> = ({ idOrSlug = 1 }) => {
13+
const [navigationItems, setNavigationItems] = useState<NavigationItemTree[]>([]);
14+
useEffect(() => {
15+
fetchMainNavigation(idOrSlug)
16+
.then(setNavigationItems)
17+
.catch(e => console.error(e));
18+
}, [idOrSlug]);
19+
20+
return (
21+
<Navbar bg="light" expand="lg">
22+
<Container>
23+
<Link href="/">
24+
<Navbar.Brand className="cursor-pointer">
25+
Strapi Plugin Navigation Example
26+
</Navbar.Brand>
27+
</Link>
28+
<Navbar.Toggle aria-controls="basic-navbar-nav" />
29+
<Navbar.Collapse id="basic-navbar-nav" className="justify-content-end">
30+
<Nav>
31+
<List items={navigationItems} level={0} />
32+
</Nav>
33+
</Navbar.Collapse>
34+
</Container>
35+
</Navbar>
36+
);
37+
}
38+
39+
export default MainNavbar;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Link from "next/link";
2+
import { Nav } from "react-bootstrap";
3+
import { NavigationItemTree } from "../../types/navigation";
4+
import List from "./list";
5+
6+
interface IProps {
7+
item: NavigationItemTree;
8+
level: number;
9+
}
10+
11+
const Item: React.FC<IProps> = ({ item, level }) => {
12+
return (
13+
<Nav.Link href={item.path}>
14+
<Link href={item.path} passHref={item.type === "EXTERNAL"}>
15+
{item.title}
16+
</Link>
17+
{item.items && item.items.length ? <List items={item.items} level={level + 1} /> : null}
18+
</Nav.Link>
19+
);
20+
}
21+
22+
export default Item;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { NavigationItemTree } from "../../types/navigation";
2+
import Item from "./item";
3+
4+
interface IProps {
5+
items: NavigationItemTree[];
6+
level: number;
7+
}
8+
9+
const List: React.FC<IProps> = ({ items, level }) => {
10+
const preparedItems = items.map(item => <Item item={item} level={level} key={item.id} />);
11+
return level > 0
12+
? <div className="dropdown-menu">{preparedItems}</div>
13+
: <>{preparedItems}</>;
14+
}
15+
16+
export default List;

0 commit comments

Comments
 (0)