├── .firebaserc
├── .github
└── workflows
│ ├── firebase-hosting-merge.yml
│ └── firebase-hosting-pull-request.yml
├── .gitignore
├── README.md
├── firebase.json
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.cjs
├── public
└── vite.svg
├── src
├── App.tsx
├── assets
│ └── react.svg
├── components
│ ├── command-bar
│ │ ├── actions
│ │ │ ├── actions.ts
│ │ │ └── index.ts
│ │ ├── icons
│ │ │ ├── home.tsx
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── results
│ │ │ ├── index.ts
│ │ │ ├── result-item.tsx
│ │ │ └── results-renderer.tsx
│ │ └── wrapper
│ │ │ ├── index.ts
│ │ │ ├── wrapper.interface.ts
│ │ │ └── wrapper.tsx
│ ├── layout
│ │ ├── Container.tsx
│ │ ├── Navbar.tsx
│ │ ├── index.ts
│ │ └── navigation-links.tsx
│ ├── ui-elements
│ │ ├── CompanyLogo
│ │ │ ├── CompanyLogo.tsx
│ │ │ └── index.ts
│ │ ├── HamburgerButton
│ │ │ ├── HamburgerButton.tsx
│ │ │ └── index.ts
│ │ ├── KbarInput
│ │ │ ├── KbarInput.tsx
│ │ │ └── index.ts
│ │ ├── MenuLinks
│ │ │ ├── LinkSubItems.tsx
│ │ │ ├── MenuLinks.tsx
│ │ │ ├── index.ts
│ │ │ └── interfaces.ts
│ │ ├── MobileMenu
│ │ │ ├── LinkSubItems.tsx
│ │ │ ├── MobileMenu.tsx
│ │ │ ├── index.ts
│ │ │ └── interfaces.ts
│ │ ├── User
│ │ │ ├── User.tsx
│ │ │ └── index.ts
│ │ └── index.ts
│ └── views
│ │ ├── Changelog.tsx
│ │ ├── Contact.tsx
│ │ ├── External1.tsx
│ │ ├── External2.tsx
│ │ ├── Home.tsx
│ │ ├── NotFound.tsx
│ │ └── index.ts
├── index.css
├── main.tsx
├── navigation
│ ├── index.ts
│ └── navigation.tsx
└── vite-env.d.ts
├── tailwind.config.cjs
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "awesome-react-navbar"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.github/workflows/firebase-hosting-merge.yml:
--------------------------------------------------------------------------------
1 | # This file was auto-generated by the Firebase CLI
2 | # https://github.com/firebase/firebase-tools
3 |
4 | name: Deploy to Firebase Hosting on merge
5 | 'on':
6 | push:
7 | branches:
8 | - master
9 | jobs:
10 | build_and_deploy:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - run: npm ci && npm run build
15 | - uses: FirebaseExtended/action-hosting-deploy@v0
16 | with:
17 | repoToken: '${{ secrets.GITHUB_TOKEN }}'
18 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_AWESOME_REACT_NAVBAR }}'
19 | channelId: live
20 | projectId: awesome-react-navbar
21 |
--------------------------------------------------------------------------------
/.github/workflows/firebase-hosting-pull-request.yml:
--------------------------------------------------------------------------------
1 | # This file was auto-generated by the Firebase CLI
2 | # https://github.com/firebase/firebase-tools
3 |
4 | name: Deploy to Firebase Hosting on PR
5 | 'on': pull_request
6 | jobs:
7 | build_and_preview:
8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}'
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - run: npm ci && npm run build
13 | - uses: FirebaseExtended/action-hosting-deploy@v0
14 | with:
15 | repoToken: '${{ secrets.GITHUB_TOKEN }}'
16 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_AWESOME_REACT_NAVBAR }}'
17 | projectId: awesome-react-navbar
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Awesome React Navigation Bar
2 |
3 | A modern application's navigation bar.
4 |
5 | ## Purpose
6 |
7 | To learn to build great navigation bars using tailwind with modern design patterns.
8 |
9 | Used patterns:
10 |
11 | - Light / dark theme
12 | - Mega menu
13 | - K Bar
14 |
15 | ## Demo
16 |
17 | A live demo is available at [https://awesome-react-navbar.web.app/](https://awesome-react-navbar.web.app/)
18 |
19 | Screenshots:
20 |
21 | - Light theme:
22 |
23 |
24 | - Dark theme:
25 |
26 |
27 |
28 | ## Used technologies
29 |
30 | - 🎁 **Repository:** regular Git repository
31 | - 🧰 **Frameworks:** React, Typescript
32 | - 🎨 **Styling:** tailwind
33 |
34 | ## How to run
35 |
36 | 1. Install [Node.js](https://nodejs.org/en/download/).
37 | 2. Install [Yarn](https://classic.yarnpkg.com/en/docs/install/).
38 | 3. Clone the repository.
39 | 4. Type and run: `yarn` in the root directory.
40 | 5. Type and run: `yarn dev` in the root directory.
41 | 6. Open `http://localhost:5173` in your browser to see the app.
42 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "dist",
4 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
5 | "rewrites": [
6 | {
7 | "source": "**",
8 | "destination": "/index.html"
9 | }
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Awesome React Navbar
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "awesome-react-navbar",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview",
10 | "generate-changelog": "changelog generate"
11 | },
12 | "dependencies": {
13 | "@heroicons/react": "^2.0.16",
14 | "firebase": "^9.22.2",
15 | "kbar": "^0.1.0-beta.40",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0",
18 | "react-markdown": "^8.0.5",
19 | "react-router-dom": "^6.8.1",
20 | "@vercel/analytics": "^1.0.0"
21 | },
22 | "devDependencies": {
23 | "@tailwindcss/typography": "^0.5.9",
24 | "@types/react": "^18.0.27",
25 | "@types/react-dom": "^18.0.10",
26 | "@vitejs/plugin-react-swc": "^3.0.0",
27 | "autoprefixer": "^10.4.13",
28 | "generate-changelog": "^1.8.0",
29 | "postcss": "^8.4.21",
30 | "tailwindcss": "^3.2.7",
31 | "typescript": "^4.9.3",
32 | "vite": "^4.1.0",
33 | "vite-plugin-pwa": "^0.14.4"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Navigation } from "./navigation";
2 | import { inject } from '@vercel/analytics';
3 |
4 | export const App = () => {
5 | inject();
6 | return ;
7 | };
8 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/command-bar/actions/actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from "kbar";
2 | import {
3 | ClockIcon,
4 | PaperAirplaneIcon,
5 | ReceiptRefundIcon,
6 | HomeIcon,
7 | } from "@heroicons/react/24/outline";
8 | import { useLocation } from "react-router-dom";
9 |
10 | export const initialActions = [
11 | // -------------------basic---------------------
12 | {
13 | id: "homeAction",
14 | name: "Home",
15 | shortcut: ["h"],
16 | keywords: "back home main dashboard",
17 | section: "Basic",
18 | perform: () => (window.location.href = "/"),
19 | //icon: // ClockIcon,
20 | subtitle: "Subtitles can help add more context.",
21 | },
22 |
23 | // -------------------add-------------------
24 | {
25 | id: "workTimeAction",
26 | name: "Work time",
27 | shortcut: ["a", "w"],
28 | keywords: "work time log add",
29 | section: "Add",
30 | subtitle: "Log and manage your work time.",
31 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
32 | },
33 | {
34 | id: "vacationAction",
35 | name: "Vacation",
36 | shortcut: ["a", "v"],
37 | keywords: "vacation add",
38 | section: "Add",
39 | subtitle: "Log and manage your days off.",
40 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
41 | },
42 | {
43 | id: "invoiceAction",
44 | name: "Invoice",
45 | shortcut: ["a", "i"],
46 | keywords: "invoice add",
47 | section: "Add",
48 | subtitle: "Log and manage your invoices.",
49 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
50 | },
51 | {
52 | id: "documentsAction",
53 | name: "Document",
54 | shortcut: ["a", "d"],
55 | keywords: "document add",
56 | section: "Add",
57 | subtitle: "Log and manage your documents.",
58 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
59 | },
60 | // -------------------project---------------------
61 | {
62 | id: "projectCreateAction",
63 | name: "Create project",
64 | shortcut: ["p", "c"],
65 | keywords: "project create",
66 | section: "Project",
67 | subtitle: "Create new project.",
68 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
69 | },
70 | {
71 | id: "projectManageAction",
72 | name: "Manage project",
73 | shortcut: ["p", "m"],
74 | keywords: "project manage",
75 | section: "Project",
76 | subtitle: "Manage project.",
77 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
78 | },
79 | {
80 | id: "projectArchiveAction",
81 | name: "Archive project",
82 | shortcut: ["p", "a"],
83 | keywords: "project archive",
84 | section: "Project",
85 | subtitle: "Archive project.",
86 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
87 | },
88 | // -----------------client-----------------------
89 | {
90 | id: "clientCreateAction",
91 | name: "Create client",
92 | shortcut: ["l", "c"],
93 | keywords: "project create",
94 | section: "Client",
95 | subtitle: "Create new project.",
96 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
97 | },
98 | {
99 | id: "clientManageAction",
100 | name: "Manage client",
101 | shortcut: ["l", "m"],
102 | keywords: "client manage",
103 | section: "Client",
104 | subtitle: "Manage client.",
105 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
106 | },
107 | {
108 | id: "clientArchiveAction",
109 | name: "Archive client",
110 | shortcut: ["l", "a"],
111 | keywords: "client archive",
112 | section: "Client",
113 | subtitle: "Archive client.",
114 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
115 | },
116 | // ---------------employee-------------------------
117 | {
118 | id: "employeeCreateAction",
119 | name: "Create employee",
120 | shortcut: ["e", "c"],
121 | keywords: "employee create",
122 | section: "Employee",
123 | subtitle: "Create new employee.",
124 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
125 | },
126 | {
127 | id: "employeeManageAction",
128 | name: "Manage employee",
129 | shortcut: ["e", "m"],
130 | keywords: "employee manage",
131 | section: "Employee",
132 | subtitle: "Manage employee.",
133 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
134 | },
135 | {
136 | id: "employeeArchiveAction",
137 | name: "Archive employee",
138 | shortcut: ["e", "a"],
139 | keywords: "employee archive",
140 | section: "Employee",
141 | subtitle: "Archive employee.",
142 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
143 | },
144 | // ---------------employee-------------------------
145 | {
146 | id: "employeeCreateAction",
147 | name: "Create employee",
148 | shortcut: ["e", "c"],
149 | keywords: "employee create",
150 | section: "Employee",
151 | subtitle: "Create new employee.",
152 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
153 | },
154 | {
155 | id: "employeeManageAction",
156 | name: "Manage employee",
157 | shortcut: ["e", "m"],
158 | keywords: "employee manage",
159 | section: "Employee",
160 | subtitle: "Manage employee.",
161 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
162 | },
163 | {
164 | id: "employeeArchiveAction",
165 | name: "Archive employee",
166 | shortcut: ["e", "a"],
167 | keywords: "employee archive",
168 | section: "Employee",
169 | subtitle: "Archive employee.",
170 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
171 | },
172 | // ---------------employee-------------------------
173 | {
174 | id: "employeeCreateAction",
175 | name: "Create employee",
176 | shortcut: ["e", "c"],
177 | keywords: "employee create",
178 | section: "Employee",
179 | subtitle: "Create new employee.",
180 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
181 | },
182 | {
183 | id: "employeeManageAction",
184 | name: "Manage employee",
185 | shortcut: ["e", "m"],
186 | keywords: "employee manage",
187 | section: "Employee",
188 | subtitle: "Manage employee.",
189 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
190 | },
191 | {
192 | id: "employeeArchiveAction",
193 | name: "Archive employee",
194 | shortcut: ["e", "a"],
195 | keywords: "employee archive",
196 | section: "Employee",
197 | subtitle: "Archive employee.",
198 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
199 | },
200 | // ---------------company-------------------------
201 | {
202 | id: "paperworkAction",
203 | name: "Paperwork",
204 | shortcut: ["c", "p"],
205 | keywords: "paperwork",
206 | section: "Company",
207 | subtitle: "Paperwork.",
208 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
209 | },
210 | {
211 | id: "licensesAction",
212 | name: "Licenses",
213 | shortcut: ["c", "l"],
214 | keywords: "licenses",
215 | section: "Company",
216 | subtitle: "Licenses.",
217 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
218 | },
219 | {
220 | id: "jiraAction",
221 | name: "Jira tickets",
222 | shortcut: ["c", "j"],
223 | keywords: "jira tickets",
224 | section: "Company",
225 | subtitle: "Jira tickets.",
226 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
227 | },
228 | {
229 | id: "perksBenefitsAction",
230 | name: "Perks and Benefits",
231 | shortcut: ["c", "b"],
232 | keywords: "perks benefits",
233 | section: "Company",
234 | subtitle: "Perks and Benefits.",
235 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
236 | },
237 | {
238 | id: "referralsRewardsAction",
239 | name: "Referrals and Rewards",
240 | shortcut: ["c", "r"],
241 | keywords: "referrals rewards",
242 | section: "Company",
243 | subtitle: "Referrals and Rewards.",
244 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"),
245 | },
246 |
247 | createAction({
248 | name: "This repo on GitHub",
249 | shortcut: ["g", "h"],
250 | keywords: "github repo source code",
251 | section: "Links",
252 | perform: () => window.open("https://github.com/timc1/kbar", "_blank"),
253 | }),
254 | ];
255 |
--------------------------------------------------------------------------------
/src/components/command-bar/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./actions";
2 |
--------------------------------------------------------------------------------
/src/components/command-bar/icons/home.tsx:
--------------------------------------------------------------------------------
1 | export const HomeIcon = () => (
2 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/components/command-bar/icons/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./home";
2 |
--------------------------------------------------------------------------------
/src/components/command-bar/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./actions";
2 | export * from "./wrapper";
3 | export * from "./icons";
4 |
--------------------------------------------------------------------------------
/src/components/command-bar/results/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./result-item";
2 | export * from "./results-renderer";
3 |
--------------------------------------------------------------------------------
/src/components/command-bar/results/result-item.tsx:
--------------------------------------------------------------------------------
1 | import { ActionId, ActionImpl } from "kbar";
2 | import React from "react";
3 |
4 | export const ResultItem = React.forwardRef(
5 | (
6 | {
7 | action,
8 | active,
9 | currentRootActionId,
10 | }: {
11 | action: ActionImpl;
12 | active: boolean;
13 | currentRootActionId: ActionId;
14 | },
15 | ref: React.Ref
16 | ) => {
17 | const ancestors = React.useMemo(() => {
18 | if (!currentRootActionId) return action.ancestors;
19 | const index = action.ancestors.findIndex(
20 | (ancestor) => ancestor.id === currentRootActionId
21 | );
22 | return action.ancestors.slice(index + 1);
23 | }, [action.ancestors, currentRootActionId]);
24 |
25 | return (
26 |
34 |
35 | {action.icon}
36 |
37 |
38 | {ancestors.length > 0 &&
39 | ancestors.map((ancestor) => (
40 |
41 |
42 | {ancestor.name}
43 |
44 | ›
45 |
46 | ))}
47 |
48 | {action.name}
49 |
50 |
51 | {action.subtitle && (
52 |
53 | {action.subtitle}
54 |
55 | )}
56 |
57 |
58 | {action.shortcut?.length ? (
59 |
60 | {action.shortcut.map((sc) => (
61 |
69 | {sc}
70 |
71 | ))}
72 |
73 | ) : null}
74 |
75 | );
76 | }
77 | );
78 |
--------------------------------------------------------------------------------
/src/components/command-bar/results/results-renderer.tsx:
--------------------------------------------------------------------------------
1 | import { KBarResults, useMatches } from "kbar";
2 | import { ResultItem } from "./result-item";
3 |
4 | export const RenderResults = () => {
5 | const { results, rootActionId } = useMatches();
6 |
7 | return (
8 |
11 | typeof item === "string" ? (
12 | {item}
13 | ) : (
14 |
19 | )
20 | }
21 | />
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/command-bar/wrapper/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./wrapper";
2 | export * from "./wrapper.interface";
3 |
--------------------------------------------------------------------------------
/src/components/command-bar/wrapper/wrapper.interface.ts:
--------------------------------------------------------------------------------
1 | export interface KBarWrapperProps {
2 | children: React.ReactNode;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/command-bar/wrapper/wrapper.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | KBarProvider,
3 | KBarPortal,
4 | KBarPositioner,
5 | KBarAnimator,
6 | KBarSearch,
7 | } from "kbar";
8 | import { initialActions } from "../actions";
9 |
10 | import { RenderResults } from "../results";
11 | import { KBarWrapperProps } from "./wrapper.interface";
12 |
13 | export const KBarWrapper = (props: KBarWrapperProps) => {
14 | const { children } = props;
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {children}
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/layout/Container.tsx:
--------------------------------------------------------------------------------
1 | import { Navbar } from "./Navbar";
2 | import { Outlet } from "react-router-dom";
3 |
4 | export const Container = () => {
5 | return (
6 | <>
7 |
8 |
9 | >
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/layout/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { useLocation } from "react-router-dom";
3 | import {
4 | CompanyLogo,
5 | HamburgerButton,
6 | KbarInput,
7 | MenuLinks,
8 | MobileMenu,
9 | } from "../ui-elements";
10 | import User from "../ui-elements/User/User";
11 | import { navigationLinks } from "./navigation-links";
12 |
13 | export const Navbar = () => {
14 | const location = useLocation();
15 |
16 | const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
17 |
18 | useEffect(() => {
19 | setIsMobileMenuOpen(false);
20 | }, [location]);
21 |
22 | return (
23 | <>
24 |
54 | >
55 | );
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Container";
2 | export * from "./Navbar";
3 |
--------------------------------------------------------------------------------
/src/components/layout/navigation-links.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ClockIcon,
3 | PaperAirplaneIcon,
4 | ReceiptRefundIcon,
5 | DocumentIcon,
6 | PlusCircleIcon,
7 | ListBulletIcon,
8 | DocumentTextIcon,
9 | CommandLineIcon,
10 | TicketIcon,
11 | HeartIcon,
12 | GiftIcon,
13 | } from "@heroicons/react/24/outline";
14 |
15 | export const navigationLinks = [
16 | {
17 | name: "Add",
18 | subLinks: [
19 | {
20 | name: "Work time",
21 | description: "Log and manage your work time",
22 | link: "/work-time",
23 | color: "bg-teal-300 dark:bg-teal-700",
24 | icon: ,
25 | },
26 | {
27 | name: "Document",
28 | description: "Add and manage your documents",
29 | link: "/other-documents",
30 | color: "bg-indigo-300 dark:bg-indigo-700",
31 | icon: ,
32 | },
33 | {
34 | name: "Vacation",
35 | description: "Add and manage your vacation",
36 | link: "/vacation",
37 | color: "bg-amber-300 dark:bg-amber-700",
38 | icon: ,
39 | },
40 | {
41 | name: "Invoice",
42 | description: "Add and manage your invoices",
43 | link: "/invoices",
44 | color: "bg-rose-300 dark:bg-rose-700",
45 | icon: ,
46 | },
47 | ],
48 | },
49 | {
50 | name: "Projects",
51 | subLinks: [
52 | {
53 | name: "Create",
54 | description: "Create a new project",
55 | link: "/create-project",
56 | color: "bg-green-300 dark:bg-green-700",
57 | icon: ,
58 | },
59 | {
60 | name: "Manage",
61 | description: "Manage projects",
62 | link: "/manage-projects",
63 | color: "bg-blue-300 dark:bg-blue-700",
64 | icon: ,
65 | },
66 | {
67 | name: "Archive",
68 | description: "Manage all archived projects",
69 | link: "/archive-projects",
70 | color: "bg-red-300 dark:bg-red-700",
71 | icon: ,
72 | },
73 | ],
74 | },
75 | {
76 | name: "Clients",
77 | subLinks: [
78 | {
79 | name: "Create",
80 | description: "Create a new client",
81 | link: "/create-client",
82 | color: "bg-green-300 dark:bg-green-700",
83 | icon: ,
84 | },
85 | {
86 | name: "Manage",
87 | description: "Manage clients",
88 | link: "/manage-clients",
89 | color: "bg-blue-300 dark:bg-blue-700",
90 | icon: ,
91 | },
92 | {
93 | name: "Archive",
94 | description: "Manage all archived clients",
95 | link: "/archive-clients",
96 | color: "bg-red-300 dark:bg-red-700",
97 | icon: ,
98 | },
99 | ],
100 | },
101 | {
102 | name: "Employees",
103 | subLinks: [
104 | {
105 | name: "Create",
106 | description: "Create a new employee",
107 | link: "/create-employee",
108 | color: "bg-green-300 dark:bg-green-700",
109 | icon: ,
110 | },
111 | {
112 | name: "Manage",
113 | description: "Manage employees",
114 | link: "/manage-employees",
115 | color: "bg-blue-300 dark:bg-blue-700",
116 | icon: ,
117 | },
118 | {
119 | name: "Archive",
120 | description: "Manage all archived employees",
121 | link: "/archive-employees",
122 | color: "bg-red-300 dark:bg-red-700",
123 | icon: ,
124 | },
125 | ],
126 | },
127 | {
128 | name: "Company",
129 | subLinks: [
130 | {
131 | name: "Paperwork",
132 | description: "Manage documents and consents",
133 | link: "/company-paperwork",
134 | color: "bg-teal-300 dark:bg-teal-700",
135 | icon: ,
136 | },
137 | {
138 | name: "Licenses",
139 | description: "Manage your software licenses and subscriptions",
140 | link: "/company-licenses",
141 | color: "bg-pink-300 dark:bg-pink-700",
142 | icon: ,
143 | },
144 | {
145 | name: "Jira tickets",
146 | description: "Create and edit your Jira tickets",
147 | link: "/company-jira-tickets",
148 | color: "bg-sky-300 dark:bg-sky-700",
149 | icon: ,
150 | },
151 | {
152 | name: "Perks and benefits",
153 | description: "Manage your company perks and benefits",
154 | link: "/company-perks-and-benefits",
155 | color: "bg-green-300 dark:bg-green-700",
156 | icon: ,
157 | },
158 | {
159 | name: "Referrals and rewards",
160 | description: "Manage your company referrals and rewards",
161 | link: "/company-referrals-and-rewards",
162 | color: "bg-red-300 dark:bg-red-700",
163 | icon: ,
164 | },
165 | ],
166 | extraLinks: [
167 | {
168 | name: "Company details",
169 | link: "/company-details",
170 | },
171 | {
172 | name: "Company structure",
173 | link: "/company-structure",
174 | },
175 | {
176 | name: "Company blog",
177 | link: "/company-blog",
178 | },
179 | {
180 | name: "Company social media",
181 | link: "/company-social-media",
182 | },
183 | ],
184 | },
185 | ];
186 |
--------------------------------------------------------------------------------
/src/components/ui-elements/CompanyLogo/CompanyLogo.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | export const CompanyLogo = () => {
4 | return (
5 |
6 |
7 | My Company
8 |
9 |
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/ui-elements/CompanyLogo/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CompanyLogo'
--------------------------------------------------------------------------------
/src/components/ui-elements/HamburgerButton/HamburgerButton.tsx:
--------------------------------------------------------------------------------
1 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
2 | import { useState } from "react";
3 |
4 | export interface HamburgerButtonProps {
5 | isOpen: boolean;
6 | onClick: () => void;
7 | }
8 |
9 | export const HamburgerButton = ({ isOpen, onClick }: HamburgerButtonProps) => {
10 | return (
11 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/src/components/ui-elements/HamburgerButton/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./HamburgerButton";
2 |
--------------------------------------------------------------------------------
/src/components/ui-elements/KbarInput/KbarInput.tsx:
--------------------------------------------------------------------------------
1 | import { useKBar } from "kbar";
2 | import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
3 |
4 | export const KbarInput = () => {
5 | const { query } = useKBar();
6 |
7 | const onClick = () => {
8 | query.toggle();
9 | };
10 |
11 | return (
12 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/ui-elements/KbarInput/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./KbarInput";
2 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MenuLinks/LinkSubItems.tsx:
--------------------------------------------------------------------------------
1 | import { SubLinksBoxProps } from "./interfaces";
2 | import { Link } from "react-router-dom";
3 |
4 | export const SubLinksBox = (props: SubLinksBoxProps) => {
5 | const { subLinks, extraLinks } = props;
6 | return (
7 |
8 |
9 | {subLinks.map((subLink) => (
10 | -
14 |
15 |
18 |
19 | {subLink.icon}
20 |
21 |
22 |
23 |
24 | {subLink.name}
25 |
26 |
27 | {subLink.description}
28 |
29 |
30 |
31 |
32 | ))}
33 |
34 | {extraLinks && (
35 |
36 |
37 |
38 | {extraLinks?.map((extraLink) => (
39 |
40 | -
44 | {extraLink.name}
45 |
46 |
47 | ))}
48 |
49 |
50 | )}
51 |
52 | );
53 | };
54 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MenuLinks/MenuLinks.tsx:
--------------------------------------------------------------------------------
1 | import { MenuLinksProps } from "./interfaces";
2 | import { SubLinksBox } from "./LinkSubItems";
3 |
4 | export const MenuLinks = ({ menuLinks }: MenuLinksProps) => {
5 | return (
6 |
7 | {menuLinks.map((link) => (
8 |
9 |
-
10 | {link.name}
11 |
12 |
13 |
17 |
18 |
19 | ))}
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MenuLinks/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./MenuLinks";
2 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MenuLinks/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface MenuLinksProps {
2 | menuLinks: MenuLinks[];
3 | }
4 |
5 | export interface MenuLinks {
6 | name: string;
7 | subLinks: SubLink[];
8 | extraLinks?: ExtraLink[];
9 | }
10 |
11 | export interface SubLinksBoxProps {
12 | subLinks: SubLink[];
13 | extraLinks?: ExtraLink[];
14 | }
15 |
16 | export interface SubLink {
17 | name: string;
18 | description: string;
19 | link: string;
20 | color?: string;
21 | icon: JSX.Element;
22 | }
23 |
24 | export interface ExtraLink {
25 | name: string;
26 | link: string;
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MobileMenu/LinkSubItems.tsx:
--------------------------------------------------------------------------------
1 | import { SubLinksBoxProps } from "./interfaces";
2 |
3 | export const SubLinksBox = (props: SubLinksBoxProps) => {
4 | const { subLinks, extraLinks } = props;
5 | return (
6 |
7 |
8 | {subLinks.map((subLink) => (
9 | -
13 |
16 |
17 | {subLink.icon}
18 |
19 |
20 |
21 |
{subLink.name}
22 |
23 | {subLink.description}
24 |
25 |
26 |
27 | ))}
28 |
29 | {extraLinks && (
30 |
31 |
32 |
33 | {extraLinks?.map((extraLink) => (
34 | -
38 | {extraLink.name}
39 |
40 | ))}
41 |
42 |
43 | )}
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MobileMenu/MobileMenu.tsx:
--------------------------------------------------------------------------------
1 | import { MenuLinks } from "./interfaces";
2 | import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline";
3 | import { useState } from "react";
4 | import { Link } from "react-router-dom";
5 |
6 | interface MobileMenuProps {
7 | menuLinks: MenuLinks[];
8 | }
9 |
10 | export const MobileMenu = ({ menuLinks }: MobileMenuProps) => {
11 | const [expandedLinkId, setExpandedLinkId] = useState(null);
12 |
13 | const onMenuItemClick = (index: number) => {
14 | if (expandedLinkId === index) {
15 | setExpandedLinkId(null);
16 | } else {
17 | setExpandedLinkId(index);
18 | }
19 | };
20 |
21 | return (
22 |
23 |
24 |
25 | {menuLinks.map((link, index) => (
26 |
27 |
- onMenuItemClick(index)}
30 | >
31 |
32 |
{link.name}
33 |
34 | {expandedLinkId === index ? (
35 |
36 | ) : (
37 |
38 | )}
39 |
40 |
41 | {expandedLinkId === index && (
42 |
43 |
44 | {link.subLinks.map((subLink) => (
45 | -
49 |
50 |
{subLink.icon}
51 |
52 |
{subLink.name}
53 |
54 | {subLink.description}
55 |
56 |
57 |
58 |
59 | ))}
60 |
61 |
62 | )}
63 |
64 |
65 | ))}
66 |
67 |
68 |
69 | );
70 | };
71 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MobileMenu/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./MobileMenu";
2 |
--------------------------------------------------------------------------------
/src/components/ui-elements/MobileMenu/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface MenuLinksProps {
2 | menuLinks: MenuLinks[];
3 | }
4 |
5 | export interface MenuLinks {
6 | name: string;
7 | subLinks: SubLink[];
8 | extraLinks?: ExtraLink[];
9 | }
10 |
11 | export interface SubLinksBoxProps {
12 | subLinks: SubLink[];
13 | extraLinks?: ExtraLink[];
14 | }
15 |
16 | export interface SubLink {
17 | name: string;
18 | description: string;
19 | link: string;
20 | color?: string;
21 | icon: JSX.Element;
22 | }
23 |
24 | export interface ExtraLink {
25 | name: string;
26 | link: string;
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/ui-elements/User/User.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IdentificationIcon,
3 | SunIcon,
4 | MoonIcon,
5 | AdjustmentsVerticalIcon,
6 | ExclamationCircleIcon,
7 | } from "@heroicons/react/24/outline";
8 | import { useState } from "react";
9 |
10 | const User = () => {
11 | const [theme, setTheme] = useState(localStorage.getItem("theme"));
12 |
13 | const ms = new Date().getUTCMilliseconds();
14 |
15 | const items = [
16 | {
17 | title: "Profile",
18 | icon: ,
19 | color: "bg-indigo-300 dark:bg-indigo-800",
20 | onclick: () => {},
21 | },
22 | {
23 | title: theme === "light" ? "Dark theme" : "Light theme",
24 | icon: theme === "light" ? : ,
25 | color: "bg-teal-300 dark:bg-teal-800",
26 | onclick: () => onChangeThemeClick(),
27 | },
28 | {
29 | title: "Settings",
30 | icon: ,
31 | color: "bg-fuchsia-300 dark:bg-fuchsia-800",
32 | onclick: () => {},
33 | },
34 | {
35 | title: "Logout",
36 | icon: ,
37 | color: "bg-red-300 dark:bg-red-800",
38 | onclick: () => {},
39 | },
40 | ];
41 |
42 | const onChangeThemeClick = () => {
43 | const newTheme = theme === "light" ? "dark" : "light";
44 | setTheme(newTheme);
45 |
46 | if (newTheme === "dark") {
47 | document.documentElement.classList.remove("light");
48 | document.documentElement.classList.add("dark");
49 | } else {
50 | document.documentElement.classList.remove("dark");
51 | document.documentElement.classList.add("light");
52 | }
53 | };
54 |
55 | return (
56 |
57 |
58 |

62 |
Steve
63 |
64 |
65 | {items.map((item) => (
66 | -
71 |
74 |
75 | {item.icon}
76 |
77 |
78 |
79 | {item.title}
80 |
81 |
82 | ))}
83 |
84 |
85 | );
86 | };
87 |
88 | export default User;
89 |
--------------------------------------------------------------------------------
/src/components/ui-elements/User/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./User";
2 |
--------------------------------------------------------------------------------
/src/components/ui-elements/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./User";
2 | export * from "./KbarInput";
3 | export * from "./MenuLinks";
4 | export * from "./CompanyLogo";
5 | export * from "./HamburgerButton";
6 | export * from "./MobileMenu";
7 |
--------------------------------------------------------------------------------
/src/components/views/Changelog.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import ReactMarkdown from "react-markdown";
3 |
4 | export const Changelog = () => {
5 | const [content, setContent] = useState("");
6 |
7 | fetch(
8 | "https://raw.githubusercontent.com/wojciechmarek/AwesomeReactNavbar/master/CHANGELOG.md"
9 | )
10 | .then((res) => res.text())
11 | .then((text) => setContent(text));
12 |
13 | return (
14 |
15 |
16 |
17 | {content}
18 |
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/views/Contact.tsx:
--------------------------------------------------------------------------------
1 | export const Contact = () => {
2 | return (
3 |
4 |
5 | Contact
6 |
7 |
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/src/components/views/External1.tsx:
--------------------------------------------------------------------------------
1 | export const External1 = () => {
2 | return (
3 |
4 |
8 |
9 | );
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/views/External2.tsx:
--------------------------------------------------------------------------------
1 | export const External2 = () => {
2 | return (
3 |
4 |
5 |
6 | );
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/views/Home.tsx:
--------------------------------------------------------------------------------
1 | export const Home = () => {
2 | return (
3 |
4 |
5 | Home
6 |
7 |
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/src/components/views/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | export const NotFound = () => {
4 | return (
5 |
6 |
7 | 404
8 |
9 |
10 |
11 | The page you are looking for does not exist.
12 |
13 |
14 |
18 | Go to Home
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/views/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Home";
2 | export * from "./Changelog";
3 | export * from "./Contact";
4 | export * from "./External1";
5 | export * from "./External2";
6 | export * from "./NotFound";
7 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | color-scheme: light dark;
7 |
8 | font-synthesis: none;
9 | text-rendering: optimizeLegibility;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | -webkit-text-size-adjust: 100%;
13 | }
14 |
15 | body {
16 | margin: 0;
17 | padding: 0;
18 | }
19 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App";
4 | import "./index.css";
5 | import { KBarWrapper } from "./components/command-bar";
6 |
7 | // Import the functions you need from the SDKs you need
8 | import { initializeApp } from "firebase/app";
9 | import { getAnalytics } from "firebase/analytics";
10 | // TODO: Add SDKs for Firebase products that you want to use
11 | // https://firebase.google.com/docs/web/setup#available-libraries
12 |
13 | // Your web app's Firebase configuration
14 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional
15 | const firebaseConfig = {
16 | apiKey: "AIzaSyCa07zE09ONmDK-UtdFJeug2oPoWy5Lcbk",
17 | authDomain: "awesome-react-navbar.firebaseapp.com",
18 | projectId: "awesome-react-navbar",
19 | storageBucket: "awesome-react-navbar.appspot.com",
20 | messagingSenderId: "71219047347",
21 | appId: "1:71219047347:web:82950dedc2ae77b85879c6",
22 | measurementId: "G-EYP11S18BM"
23 | };
24 |
25 | // Initialize Firebase
26 | const app = initializeApp(firebaseConfig);
27 | const analytics = getAnalytics(app);
28 |
29 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
30 |
31 |
32 |
33 |
34 |
35 | );
36 |
--------------------------------------------------------------------------------
/src/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./navigation";
2 |
--------------------------------------------------------------------------------
/src/navigation/navigation.tsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route } from "react-router-dom";
2 | import { Container } from "../components/layout";
3 | import {
4 | Changelog,
5 | Contact,
6 | External1,
7 | External2,
8 | NotFound,
9 | Home,
10 | } from "../components/views";
11 |
12 | export const Navigation = () => (
13 |
14 |
15 | }>
16 | } />
17 | } />
18 | } />
19 | } />
20 | } />
21 | } />
22 |
23 |
24 |
25 | );
26 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
4 | darkMode: "class",
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [require("@tailwindcss/typography")],
9 | };
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react-swc";
3 | import { VitePWA } from "vite-plugin-pwa";
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [
8 | react(),
9 | VitePWA({
10 | registerType: "autoUpdate",
11 | devOptions: {
12 | enabled: true,
13 | },
14 | }),
15 | ],
16 | });
17 |
--------------------------------------------------------------------------------