"fix: navItem no use links"

This commit is contained in:
Ruben 2025-03-26 19:08:50 +01:00
parent 93363c875f
commit 322f4d8637
19 changed files with 305 additions and 113 deletions

10
svgr.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module '*.svg' {
import { FC, SVGProps } from 'react'
const content: FC<SVGProps<SVGElement>>
export default content
}
declare module '*.svg?url' {
const content: any
export default content
}

View File

@ -1,13 +1,31 @@
import type { NextConfig } from "next"; module.exports = {
webpack(config:any) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule:any) =>
rule.test?.test?.('.svg'),
)
const nextConfig: NextConfig = { config.module.rules.push(
webpack(config) { // Reapply the existing rule, but only for svg imports ending in ?url
config.module.rules.push({ {
test: /\.svg$/, ...fileLoaderRule,
use: ['@svgr/webpack'], test: /\.svg$/i,
}); resourceQuery: /url/, // *.svg?url
return config; },
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: ['@svgr/webpack'],
},
)
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i
return config
}, },
};
export default nextConfig; // ...other config
}

View File

@ -35,6 +35,8 @@
"@types/react-dom": "^19", "@types/react-dom": "^19",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.2.3", "eslint-config-next": "15.2.3",
"install": "^0.13.0",
"npm": "^11.2.0",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }

86
pnpm-lock.yaml generated
View File

@ -81,6 +81,12 @@ importers:
eslint-config-next: eslint-config-next:
specifier: 15.2.3 specifier: 15.2.3
version: 15.2.3(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2) version: 15.2.3(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
install:
specifier: ^0.13.0
version: 0.13.0
npm:
specifier: ^11.2.0
version: 11.2.0
tailwindcss: tailwindcss:
specifier: ^4 specifier: ^4
version: 4.0.15 version: 4.0.15
@ -2633,6 +2639,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'} engines: {node: '>=0.8.19'}
install@0.13.0:
resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
engines: {node: '>= 0.10'}
internal-slot@1.1.0: internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3011,6 +3021,78 @@ packages:
node-releases@2.0.19: node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
npm@11.2.0:
resolution: {integrity: sha512-PcnFC6gTo9VDkxVaQ1/mZAS3JoWrDjAI+a6e2NgfYQSGDwftJlbdV0jBMi2V8xQPqbGcWaa7p3UP0SKF+Bhm2g==}
engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
bundledDependencies:
- '@isaacs/string-locale-compare'
- '@npmcli/arborist'
- '@npmcli/config'
- '@npmcli/fs'
- '@npmcli/map-workspaces'
- '@npmcli/package-json'
- '@npmcli/promise-spawn'
- '@npmcli/redact'
- '@npmcli/run-script'
- '@sigstore/tuf'
- abbrev
- archy
- cacache
- chalk
- ci-info
- cli-columns
- fastest-levenshtein
- fs-minipass
- glob
- graceful-fs
- hosted-git-info
- ini
- init-package-json
- is-cidr
- json-parse-even-better-errors
- libnpmaccess
- libnpmdiff
- libnpmexec
- libnpmfund
- libnpmorg
- libnpmpack
- libnpmpublish
- libnpmsearch
- libnpmteam
- libnpmversion
- make-fetch-happen
- minimatch
- minipass
- minipass-pipeline
- ms
- node-gyp
- nopt
- normalize-package-data
- npm-audit-report
- npm-install-checks
- npm-package-arg
- npm-pick-manifest
- npm-profile
- npm-registry-fetch
- npm-user-validate
- p-map
- pacote
- parse-conflict-json
- proc-log
- qrcode-terminal
- read
- semver
- spdx-expression-parse
- ssri
- supports-color
- tar
- text-table
- tiny-relative-date
- treeverse
- validate-npm-package-name
- which
nprogress@0.2.0: nprogress@0.2.0:
resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
@ -6463,6 +6545,8 @@ snapshots:
imurmurhash@0.1.4: {} imurmurhash@0.1.4: {}
install@0.13.0: {}
internal-slot@1.1.0: internal-slot@1.1.0:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@ -6815,6 +6899,8 @@ snapshots:
node-releases@2.0.19: {} node-releases@2.0.19: {}
npm@11.2.0: {}
nprogress@0.2.0: {} nprogress@0.2.0: {}
nth-check@2.1.1: nth-check@2.1.1:

View File

@ -0,0 +1,8 @@
export default function Admins (){
return (
<>
</>
)
}

View File

@ -1,7 +1,7 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import "../../assets/css/admin.css" import "../../assets/css/admin.css"
import Sidebar from "../components/sidebar"; import Sidebar from "../../components/admin/sidebar";
import Header from "../components/adminHeader"; import Header from "../../components/admin/adminHeader";
export default function Dashboard({ children }: { children: ReactNode }) { export default function Dashboard({ children }: { children: ReactNode }) {

View File

@ -0,0 +1,8 @@
export default function Organizations (){
return (
<>
</>
)
}

View File

@ -1,38 +0,0 @@
"use client"
import Image from "next/image";
import { icons } from "#/assets/icons"
import NavItem from "./navItem";
import { useState } from "react";
export default function Sidebar() {
const [activeItem, setActiveItem] = useState("home")
const handleNavMenu = (item:string) => {
setActiveItem(item)
console.log("active: ", item);
}
return (
<>
<div className="sidebar r-m-0 d-flex flex-column pt-[25px] max-w-[90px] h-[100vh] relative ">
<div className="logo r-flex-center px-[20px] ">
<Image src={icons.logo} alt="Logo" className="scale-95" />
</div>
<div className="nav-menu r-column-center h-max pt-[160px] r-gap-40 ">
<NavItem link="#" iconSrc={icons.homeIcon} label="Home" isNavHome={true} isActive={activeItem === "home"} onClick={() => handleNavMenu("home") } />
<NavItem link="#" iconSrc={icons.companiesIcon} label="Organizations" isActive={activeItem === "organizations"} onClick={() => handleNavMenu("organizations") } />
<NavItem link="#" iconSrc={icons.userGroup} label="Admins" isActive={activeItem === "admins"} onClick={() => handleNavMenu("admins") } />
</div>
<div className="logout absolute bottom-[40px] left-[28px]">
<button type="button" className="cursor-pointer">
<Image src={icons.logout} alt="Logout" />
</button>
</div>
</div>
</>
)
}

View File

@ -1,46 +0,0 @@
"use client"
import Image from "next/image";
import { useEffect, useState } from "react";
import { icons } from "#/assets/icons"
export default function Theme() {
const [theme, setTheme] = useState<string>(
localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light")
);
useEffect(() => {
document.body.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme);
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = (e: MediaQueryListEvent) => {
const newTheme = e.matches ? "dark" : "light";
setTheme(newTheme);
};
mediaQuery.addEventListener("change", handleChange);
return () => {
mediaQuery.removeEventListener("change", handleChange);
};
}, [theme]);
const handleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<>
<div onClick={() => handleTheme()} className="theme">
<button type="button" className="icon-border">
{theme === "light" ?
<Image src={icons.sunIcon} alt="Light Mode" width={20} height={20} className="m-[2px]" />
:
<Image src={icons.moonIcon} alt="Dark Mode" width={20} height={20} className="m-[2px]" />
}
</button>
</div>
</>
)
}

View File

@ -1,10 +0,0 @@
import Image from "next/image";
import Link from "next/link";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<Link href="/admin">See the admin page</Link>
</div>
);
}

BIN
src/assets/icons.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,62 @@
import { GeneralHtmlAttr } from "#/lib/declarations";
const icons = {
CalendarMark: "Calendar Mark.svg",
Document: "Document.svg",
Ellipse2: "Ellipse 2.svg",
Group1: "Group (1).svg",
Group: "Group.svg",
Icons1: "Icons (1).svg",
Icons: "Icons.svg",
LineDuotone: "Line Duotone.svg",
MenuDots: "Menu Dots.svg",
NavItem: "NavItem.svg",
Path1: "Path (1).svg",
Path: "Path.svg",
Rectangle: "Rectangle.svg",
Search: "Search.svg",
Vector: "Vector.svg",
Add: "add.svg",
Archives: "archives.svg",
ArrowLeft: "arrowLeft.svg",
ArrowRight: "arrowRight.svg",
Buildings: "buildings.svg",
Checked: "checked.svg",
Cross: "cross.svg",
DocumentText: "document-text.svg",
Edit2: "edit-2.svg",
Element3White: "element-3-white.svg",
Element3: "element-3.svg",
EyeSlash: "eye-slash.svg",
Eye: "eye.svg",
IconAdd: "icon-add.svg",
Logo: "logo.svg",
LogoutRed: "logout-red.svg",
Logout: "logout.svg",
Maximize3: "maximize-3.svg",
Message: "message.svg",
Notifications: "notifications.svg",
PhFiles: "ph_files.svg",
PrimeFilePdf: "prime_file-pdf.svg",
PrimeFileWord: "prime_file-word.svg",
Profile2UserBlue: "profile-2user-blue.svg",
Profile2User: "profile-2user.svg",
Profile: "profile.svg",
Setting2: "setting-2.svg",
Setting3: "setting-3.svg",
Share: "share.svg",
Star: "star.svg",
Moon: "moon.svg",
Sun: "sun.svg",
};
export const Icon = ({ name, ...props }: { name: keyof typeof icons } & GeneralHtmlAttr) => {
const IconComponent = icons[name];
if (!IconComponent) return null;
return (
<div {...props}>
<img src={`/icons/${IconComponent}`} alt={name} />
</div>
);
};

View File

@ -1,4 +1,4 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.10671 7.74667C8.04004 7.74 7.96004 7.74 7.88671 7.74667C6.30004 7.69334 5.04004 6.39334 5.04004 4.79334C5.04004 3.16 6.36004 1.83334 8.00004 1.83334C9.63337 1.83334 10.96 3.16 10.96 4.79334C10.9534 6.39334 9.69337 7.69334 8.10671 7.74667Z" stroke="#9FA8BC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8.10671 7.74667C8.04004 7.74 7.96004 7.74 7.88671 7.74667C6.30004 7.69334 5.04004 6.39334 5.04004 4.79334C5.04004 3.16 6.36004 1.83334 8.00004 1.83334C9.63337 1.83334 10.96 3.16 10.96 4.79334C10.9534 6.39334 9.69337 7.69334 8.10671 7.74667Z" stroke="#9FA8BC" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M4.77348 10.2067C3.16014 11.2867 3.16014 13.0467 4.77348 14.12C6.60681 15.3467 9.61348 15.3467 11.4468 14.12C13.0601 13.04 13.0601 11.28 11.4468 10.2067C9.62014 8.98666 6.61348 8.98666 4.77348 10.2067Z" stroke="#9FA8BC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4.77348 10.2067C3.16014 11.2867 3.16014 13.0467 4.77348 14.12C6.60681 15.3467 9.61348 15.3467 11.4468 14.12C13.0601 13.04 13.0601 11.28 11.4468 10.2067C9.62014 8.98666 6.61348 8.98666 4.77348 10.2067Z" stroke="#9FA8BC" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 731 B

View File

@ -7,6 +7,7 @@ import { DropdownMenu } from "radix-ui";
import Link from "next/link"; import Link from "next/link";
import Theme from "./theme"; import Theme from "./theme";
import ProfilePicture from "../../assets/icons/profile.svg"
export default function AdminHeader() { export default function AdminHeader() {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
@ -17,6 +18,8 @@ export default function AdminHeader() {
<p className="name text-[26px]">Bienvenue, <span>Ken B.</span> </p> <p className="name text-[26px]">Bienvenue, <span>Ken B.</span> </p>
<div className="r-flex-between justify-center items-center r-gap-12"> <div className="r-flex-between justify-center items-center r-gap-12">
<Theme /> <Theme />
{/* <ProfilePicture /> */}
<button type="button" className="icon-border"> <button type="button" className="icon-border">
<Image src={icons.notificationsIcon} alt="Notifications" /> <Image src={icons.notificationsIcon} alt="Notifications" />
</button> </button>

View File

@ -7,13 +7,12 @@ interface ItemProps {
label: string; label: string;
isActive: boolean; isActive: boolean;
isNavHome?: boolean; isNavHome?: boolean;
onClick: () => void;
} }
export default function NavItem({ link, iconSrc, label, isActive, isNavHome, onClick }: ItemProps) { export default function NavItem({ link, iconSrc, label, isActive, isNavHome }: ItemProps) {
return ( return (
<> <>
<Link href={link} onClick={onClick} className={`nav-item r-flex-center ${isActive ? "active" : ""}`} > <Link href={link} className={`nav-item r-flex-center ${isActive ? "active" : ""}`} >
<Image src={iconSrc} alt={label} className={`scale-100 ${isNavHome ? "nav-home" : ""}`} /> <Image src={iconSrc} alt={label} className={`scale-100 ${isNavHome ? "nav-home" : ""}`} />
</Link> </Link>
</> </>

View File

@ -0,0 +1,32 @@
"use client"
import Image from "next/image";
import { icons } from "#/assets/icons"
import NavItem from "./navItem";
import { usePathname } from "next/navigation";
import Link from "next/link";
export default function Sidebar() {
const pathname = usePathname();
return (
<>
<div className="sidebar r-m-0 d-flex flex-column pt-[25px] max-w-[90px] h-[100vh] relative ">
<div className="logo r-flex-center px-[20px] ">
<Image src={icons.logo} alt="Logo" className="scale-95" />
</div>
<div className="nav-menu r-column-center h-max pt-[160px] r-gap-40 ">
<NavItem link="/admin/home" iconSrc={icons.homeIcon} label="Home" isNavHome={true} isActive={(pathname === "/admin/") || (pathname === "/admin/home")} />
<NavItem link="/admin/organizations" iconSrc={icons.companiesIcon} label="Organizations" isActive={pathname.startsWith("/admin/organizations")} />
<NavItem link="/admin/admins" iconSrc={icons.userGroup} label="Admins" isActive={pathname.startsWith("/admin/admins")} />
</div>
<div className="logout absolute bottom-[40px] left-[28px]">
<Link href="#" className="cursor-pointer">
<Image src={icons.logout} alt="Logout" />
</Link>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,54 @@
"use client"
import Image from "next/image";
import { useEffect, useState } from "react";
import { icons } from "#/assets/icons"
export default function Theme() {
const [theme, setTheme] = useState<string | null>(null);
useEffect(() => {
if (typeof window !== "undefined") {
const savedTheme = localStorage.getItem("theme") ||
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
setTheme(savedTheme);
document.body.setAttribute("data-theme", savedTheme);
}
}, []);
const handleTheme = () => {
const newTheme = theme === "light" ? "dark" : "light";
setTheme(newTheme);
localStorage.setItem("theme", newTheme);
document.body.setAttribute("data-theme", newTheme);
};
useEffect(() => {
if (theme === null) return;
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = (e: MediaQueryListEvent) => {
const newTheme = e.matches ? "dark" : "light";
setTheme(newTheme);
localStorage.setItem("theme", newTheme);
document.body.setAttribute("data-theme", newTheme);
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, [theme]);
if (theme === null) return null;
return (
<div onClick={handleTheme} className="theme">
<button type="button" className="icon-border">
{theme === "light" ? (
<Image src={icons.sunIcon} alt="Light Mode" width={20} height={20} className="m-[2px]" />
) : (
<Image src={icons.moonIcon} alt="Dark Mode" width={20} height={20} className="m-[2px]" />
)}
</button>
</div>
);
}

3
src/lib/declarations.ts Normal file
View File

@ -0,0 +1,3 @@
// src/lib/declarations.ts
export type GeneralHtmlAttr = React.HTMLProps<HTMLDivElement> | React.HTMLProps<HTMLImageElement>;

View File

@ -19,9 +19,10 @@
} }
], ],
"paths": { "paths": {
"#/*": ["./src/*"] "#/*": ["./src/*"],
"@/*": ["./src/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["svgr.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }