diff --git a/app/layout.tsx b/app/layout.tsx
index f9c9c38..3bab0d4 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -5,6 +5,7 @@ import Footer from "@/components/footer";
import localFont from 'next/font/local';
import { CartProvider } from "@/components/context/cartcontext";
import { categoryService } from "@/public/src/services/categories/api";
+import { CategoryProvider } from "@/components/context/categoryprovider";
const Yekanbakh = localFont({
src: [
@@ -35,8 +36,12 @@ export default async function RootLayout({
-
diff --git a/app/page.tsx b/app/page.tsx
index 761ba4a..4e1c77a 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,13 +1,20 @@
"use client";
import { useState } from "react";
-import { Headphones, FileText, Truck, ShieldCheck, Square, MoreVertical, Circle, Target, CookingPot, Settings, CircleDashed, Disc, Hexagon, Droplets, Wrench, Minus, MessageCircleCheckIcon, } from "lucide-react";
+import {
+ Headphones, FileText, Truck, ShieldCheck, CircleDashed,
+ Disc,
+ Hexagon,
+ Settings,
+ Wrench,
+ Droplets, Square, MoreVertical, Circle, Target, CookingPot, Minus, MessageCircleCheckIcon,
+} from "lucide-react";
import ProductCard from "@/components/productcard";
import ArticleCard from "@/components/articlecard";
import FAQItem from "@/components/faq";
import { products } from "@/lib/data";
import { articles } from "@/lib/data";
import Link from "next/link";
-
+import { useCategories } from "@/components/context/categoryprovider";
const features = [
@@ -60,28 +67,35 @@ export default function Home() {
const [activeTab, setActiveTab] = useState(0);
const brands = ["NTN", "KOYO", "NACHI", "TIMKEN", "FAG", "SKF"];
const latestArticles = articles.slice(-4);
+ const { rootCategories } = useCategories();
- const uniqueCategories = Array.from(new Set(products.map((p) => p.category)));
- const getCategoryIcon = (categoryName: string) => {
- switch (categoryName) {
- case "شیار عمیق":
- return CircleDashed;
- case "مخروطی":
- return Disc;
- case "شبکه ای":
- return Hexagon;
- case "سوزنی":
- return Settings;
- case "یاتاقان":
- return Wrench;
- case "گریس و روانکار":
- return Droplets;
- default:
- return Settings; // آیکون پیشفرض
- }
- };
-
+ // const uniqueCategories = Array.from(new Set(products.map((p) => p.category)));
+ // const getCategoryIcon = (categoryName: string) => {
+ // switch (categoryName) {
+ // case "شیار عمیق":
+ // return CircleDashed;
+ // case "مخروطی":
+ // return Disc;
+ // case "شبکه ای":
+ // return Hexagon;
+ // case "سوزنی":
+ // return Settings;
+ // case "یاتاقان":
+ // return Wrench;
+ // case "گریس و روانکار":
+ // return Droplets;
+ // default:
+ // return Settings; // آیکون پیشفرض
+ // }
+ // };
+ const categoryIcons = [
+ CircleDashed,
+ Disc,
+ Hexagon,
+ Wrench,
+ Droplets
+ ];
return (
@@ -225,26 +239,26 @@ export default function Home() {
{/* cards */}
-
- {uniqueCategories.map((category, index) => {
- const Icon = getCategoryIcon(category);
+
- // فقط فاصلهها را به خط تیره تبدیل میکنیم
- const categorySlug = category.replace(/\s+/g, "-");
+
+
+ {rootCategories.map((cat, index) => {
+ const Icon = categoryIcons[index % categoryIcons.length];
return (
-
-
- {category}
-
+
+
{cat.name}
+
);
})}
+
diff --git a/components/context/categoryprovider.tsx b/components/context/categoryprovider.tsx
new file mode 100644
index 0000000..1b3c63f
--- /dev/null
+++ b/components/context/categoryprovider.tsx
@@ -0,0 +1,34 @@
+"use client";
+import { createContext, useContext, useEffect, useState } from "react";
+import { getAllCategories } from "@/public/src/services/categories/store";
+import { Category } from "@/public/src/types/categories";
+
+interface CategoryContextType {
+ categories: Category[];
+ rootCategories: Category[];
+}
+
+const CategoryContext = createContext({
+ categories: [],
+ rootCategories: [],
+});
+
+export function CategoryProvider({ children }: { children: React.ReactNode }) {
+ const [categories, setCategories] = useState([]);
+
+ useEffect(() => {
+ getAllCategories().then((data) => setCategories(data));
+ }, []);
+
+ const rootCategories = categories.filter((c) => !c.parent);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useCategories() {
+ return useContext(CategoryContext);
+}
diff --git a/components/header.tsx b/components/header.tsx
index 94fee39..03e40ec 100644
--- a/components/header.tsx
+++ b/components/header.tsx
@@ -3,13 +3,13 @@ import React, { useState, useEffect, useRef } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
-import { registerUser } from '@/public/src/services/auth/api';
-import { Category } from '@/public/src/types/categories';
-
import { ShoppingCart, Trash2, Search, X, ChevronDown } from "lucide-react";
import { useCart } from './context/cartcontext';
import { products } from '@/lib/data';
import '@/public/src/css/header.css';
+import { registerUser } from '@/public/src/services/auth/api';
+import { loginUser } from '@/public/src/services/auth/api';
+import { useCategories } from './context/categoryprovider';
const topBarLinks = [
{ label: "بخش صنعتی", href: "/" },
@@ -25,7 +25,7 @@ const mainNavLinks = [
];
-export function Header({ categories }: { categories: Category[] }) {
+export function Header() {
const [menuOpen, setMenuOpen] = useState(false);
const pathname = usePathname();
const { cart, removeFromCart } = useCart();
@@ -34,54 +34,143 @@ export function Header({ categories }: { categories: Category[] }) {
const [filteredProducts, setFilteredProducts] = useState([]);
const searchRef = useRef(null);
const router = useRouter();
- const safeCategories = categories || [];
const [isOpen, setIsOpen] = useState(false);
const [formType, setFormType] = useState("mobile");
const [activeTab, setActiveTab] = useState("login");
- const [registerForm, setRegisterForm] = useState({
- phone: "",
+ const [registerError, setRegisterError] = useState("");
+ const [loginError, setLoginError] = useState("");
+ const { rootCategories } = useCategories();
+ const [loginForm, setLoginForm] = useState({
username: "",
- firstName: "",
- lastName: "",
password: "",
- confirmPassword: "",
});
- const handleRegisterChange = (e: any) => {
- const { name, value } = e.target;
+ const [registerForm, setRegisterForm] = useState({
+ phone: "",
+ fullName: "",
+ username: "",
+ password: "",
+ });
- setRegisterForm((prev) => ({
- ...prev,
- [name]: value,
- }));
+ const handleChange = (e: React.ChangeEvent) => {
+ setRegisterForm({
+ ...registerForm,
+ [e.target.name]: e.target.value,
+ });
};
-
-
const handleRegister = async () => {
+
+ setRegisterError("");
+
+ const phone = registerForm.phone.trim();
+ const username = registerForm.username.trim();
+ const password = registerForm.password.trim();
+ const fullName = registerForm.fullName.trim();
+
+ // ✅ بررسی خالی بودن فیلدها
+ if (!phone || !username || !password || !fullName) {
+ setRegisterError("لطفاً فیلدها را پر کنید");
+ return;
+ }
+
try {
const payload = {
- phone: registerForm.phone,
- username: registerForm.username,
- password: registerForm.password,
+ phone,
+ username,
+ password,
+ fullName,
};
const res = await registerUser(payload);
- console.log("ثبت نام موفق", res);
+ localStorage.setItem("accessToken", res.data.accessToken);
+ localStorage.setItem("refreshToken", res.data.refreshToken);
- } catch (err) {
- console.log("خطا:", err);
+ setIsOpen(false);
+ router.push("/dashboard");
+
+ } catch (error: any) {
+
+ const message = error?.message?.toLowerCase() || "";
+
+ const usernameDuplicate = message.includes("username");
+ const phoneDuplicate = message.includes("phone");
+
+ if (usernameDuplicate && phoneDuplicate) {
+ setRegisterError("نام کاربری و شماره موبایل قبلاً ثبت شدهاند");
+ }
+ else if (usernameDuplicate) {
+ setRegisterError("این نام کاربری قبلاً ثبت شده است");
+ }
+ else if (phoneDuplicate) {
+ setRegisterError("این شماره موبایل قبلاً ثبت شده است");
+ }
+ else {
+ setRegisterError("خطا در ثبت نام");
+ }
}
};
- const rootCategories = safeCategories.filter(cat => cat.parent === null);
+ const handleLogin = async () => {
+ setLoginError("");
+
+ const username = loginForm.username.trim();
+ const password = loginForm.password.trim();
+
+ if (!username || !password) {
+ setLoginError("لطفاً نام کاربری و رمز عبور را وارد کنید");
+ return;
+ }
+
+ try {
+ const res = await loginUser({ username, password });
+
+ localStorage.setItem("accessToken", res.data.accessToken);
+ localStorage.setItem("refreshToken", res.data.refreshToken);
+ setIsOpen(false);
+ router.push("/dashboard");
+
+ } catch (error: any) {
+ const msg = error?.message?.toLowerCase() || "";
+
+ console.log("LOGIN ERROR RAW:", msg); // این خیلی مهمه
+
+ // 1) اول بررسی کنیم که نام کاربری وجود ندارد
+ if (
+ msg.includes("not found") ||
+ msg.includes("user") && msg.includes("not") ||
+ msg.includes("username") && msg.includes("not")
+ ) {
+ setLoginError("این نام کاربری وجود ندارد");
+ return;
+ }
+
+ // 2) سپس رمز عبور اشتباه
+ if (
+ msg.includes("password") ||
+ msg.includes("invalid") ||
+ msg.includes("incorrect")
+ ) {
+ setLoginError("رمز عبور اشتباه است");
+ return;
+ }
+
+ // 3) خطای عمومی
+ setLoginError("خطا در ورود. لطفاً دوباره تلاش کنید.");
+ }
+ };
+
+
+
+
const parsePrice = (priceStr?: string | null) => {
if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, ''));
};
+
const totalPrice = cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0);
useEffect(() => {
@@ -422,7 +511,7 @@ export function Header({ categories }: { categories: Category[] }) {