edit cofe

This commit is contained in:
Mohammadreza 2025-12-28 15:15:34 +03:30
parent 5a308dff2a
commit c748620429
15 changed files with 1609 additions and 771 deletions

668
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.9.4", "react-router-dom": "^7.9.4",
"recharts": "^3.6.0",
"tailwind-scrollbar-hide": "^4.0.0", "tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.1.14" "tailwindcss": "^4.1.14"
}, },
@ -31,7 +32,7 @@
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.22", "eslint-plugin-react-refresh": "^0.4.22",
"globals": "^16.4.0", "globals": "^16.4.0",
"vite-plugin-svgr": "^4.5.0", "vite": "^7.3.0",
"vite": "^5.2.0" "vite-plugin-svgr": "^4.5.0"
} }
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -29,34 +29,28 @@ import Search from "../../../assets/icons/search.svg";
import Arrow from "../../../assets/icons/arrow.svg"; import Arrow from "../../../assets/icons/arrow.svg";
import Vector7 from "../../../assets/icons/Vector7.svg"; import Vector7 from "../../../assets/icons/Vector7.svg";
import Pic from "../../../assets/icons/pic.png"; import Pic from "../../../assets/icons/pic.png";
import toggleTheme from "../../ToggleTheme";
import { CgDarkMode } from "react-icons/cg";
const Header = () => {
const Header = ({ title }) => {
return ( return (
<header className="flex items-center justify-end gap-4 p-3"> <header className="flex items-center w-full h-full justify-between gap-4 p-3">
{/* <h3 className="text-[#402E32] font-bold text-lg">{title}</h3> */} <h3 className="text-text1 font-bold text-lg md:text-xl">{title}</h3>
<div className="flex items-center gap-1 lg:gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-12 h-10 border-2 border-[#8B8886] flex justify-center items-center rounded-2xl bg-header2"> <div className="w-10 h-10 lg:w-12 lg:h-10 border-2 border-[#8B8886] flex justify-center items-center rounded-2xl bg-header2">
<img src={Search} alt="search" /> <img src={Search} alt="search" />
</div> </div>
<button
onClick={toggleTheme}
className="fixed top-16 left-2 w-12 h-10 flex justify-center items-center"
>
<CgDarkMode className="w-6 h-6 text-[#8B8886]" />
</button>
</div> </div>
<div className="flex items-center w-40 md:w-60 justify-between border-2 border-[#8B8886] rounded-2xl px-3 py-1 bg-header2"> <div className="flex items-center w-36 md:w-60 justify-between border-2 border-[#8B8886] rounded-2xl px-3 py-1 bg-header2">
<img src={Pic} className="w-8 h-8 rounded-full ml-1" alt="user" /> <img src={Pic} className="w-8 h-8 rounded-full ml-1" alt="user" />
<p className="text-[#402E32] text-sm">سارا راد</p> <p className="text-[#402E32] text-sm">سارا راد</p>
<img src={Arrow} className="w-4 h-4 ml-1" alt="arrow" /> <img src={Arrow} className="w-4 h-4 ml-1" alt="arrow" />
</div> </div>
<img src={Vector7} alt="vector" /> <img src={Vector7} alt="vector" className="w-6 h-6 lg:w-7 lg:h-7" />
</div>
</header > </header >
); );
}; };

View File

@ -1,13 +1,15 @@
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
import { useState, useEffect } from "react";
import LogoDM from "../../../assets/icons/LogoDM.svg"; import LogoDM from "../../../assets/icons/LogoDM.svg";
import LogoLM from "../../../assets/icons/Logo-LM.svg";
import { BiBarChartAlt2 } from "react-icons/bi"; import { BiBarChartAlt2 } from "react-icons/bi";
import { AiOutlinePieChart } from "react-icons/ai"; import { AiOutlinePieChart } from "react-icons/ai";
import { PiCoffee } from "react-icons/pi"; import { PiCoffee } from "react-icons/pi";
import { HiOutlineLogout } from "react-icons/hi"; import { HiOutlineLogout } from "react-icons/hi";
import { FiMenu, FiX } from "react-icons/fi"; import { FiMenu, FiX } from "react-icons/fi";
const SIDEBAR = { const SIDEBAR = {
logo: LogoDM,
navigation: [ navigation: [
{ {
id: 'dashboard', id: 'dashboard',
@ -33,7 +35,7 @@ const SIDEBAR = {
label: 'خروج', label: 'خروج',
}, },
styles: { styles: {
activeColor: '#7F4629', activeColor: 'primary',
inactiveColor: '#402E32', inactiveColor: '#402E32',
borderColor: '#D9CAB3', borderColor: '#D9CAB3',
padding: 'p-2.5', padding: 'p-2.5',
@ -45,6 +47,20 @@ const SIDEBAR = {
export default function Sidebar({ className, isOpen, setIsOpen }) { export default function Sidebar({ className, isOpen, setIsOpen }) {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const [currentLogo, setCurrentLogo] = useState(localStorage.getItem('theme') === 'light' ? LogoDM : LogoLM);
useEffect(() => {
const observer = new MutationObserver(() => {
setCurrentLogo(localStorage.getItem('theme') === 'light' ? LogoDM : LogoLM);
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
return () => observer.disconnect();
}, []);
const isActive = (path) => location.pathname === path; const isActive = (path) => location.pathname === path;
@ -73,24 +89,21 @@ export default function Sidebar({ className, isOpen, setIsOpen }) {
key={item.id} key={item.id}
to={item.path} to={item.path}
onClick={closeSidebar} onClick={closeSidebar}
className={`group flex items-center ${SIDEBAR.styles.gap} ${SIDEBAR.styles.rounded} ${SIDEBAR.styles.padding} transition-all duration-200 ${ className={`group flex items-center ${SIDEBAR.styles.gap} ${SIDEBAR.styles.rounded} ${SIDEBAR.styles.padding} transition-all duration-200 ${active
active ? `bg-primary shadow-md`
? `bg-[${SIDEBAR.styles.activeColor}] shadow-md` : `hover:bg-primary hover:text-text2`
: `hover:bg-[${SIDEBAR.styles.activeColor}]`
}`} }`}
> >
<Icon <Icon
className={`w-5 h-5 flex-shrink-0 transition-colors ${ className={`w-5 h-5 flex-shrink-0 text-text1 transition-colors ${active
active
? "text-white" ? "text-white"
: `text-[${SIDEBAR.styles.inactiveColor}] group-hover:text-white` : `text-[${SIDEBAR.styles.inactiveColor}] group-hover:text-white`
}`} }`}
/> />
<span <span
className={`text-sm font-medium transition-colors ${ className={`text-sm font-medium transition-colors ${active
active ? "text-text2"
? "text-white" : ` text-text1 group-hover:text-white`
: `text-[${SIDEBAR.styles.inactiveColor}] group-hover:text-white`
}`} }`}
> >
{item.label} {item.label}
@ -101,31 +114,42 @@ export default function Sidebar({ className, isOpen, setIsOpen }) {
return ( return (
<> <>
<div className="lg:hidden fixed top-0 px-6 bg-header right-0 left-0 z-[70] flex justify-between p-3">
<button <button
onClick={() => setIsOpen(!isOpen)} onClick={() => setIsOpen(true)}
className="lg:hidden fixed top-4 right-4 z-[70] bg-button1 text-white p-2.5 rounded-lg shadow-lg" className=" text-primary"
aria-label="Toggle menu" aria-label="Toggle menu"
> >
{isOpen ? <FiX className="w-5 h-5" /> : <FiMenu className="w-5 h-5" />} <FiMenu className="w-6 h-6" />
</button> </button>
<img src={currentLogo} className="h-12 w-12 " alt="Logo" />
</div>
{isOpen && ( {isOpen && (
<div <div
className="lg:hidden fixed inset-0 bg-black/50 z-[60]" className="lg:hidden fixed inset-0 bg-black/50 z-[60]"
onClick={closeSidebar} onClick={closeSidebar}
/> />
)} )}
<aside <aside
dir="rtl" dir="rtl"
className={`${className} className={`${className}
transition-transform duration-300 ease-in-out transition-transform duration-300 ease-in-out
${isOpen ? 'translate-x-0' : 'translate-x-full'} ${isOpen ? 'translate-x-0' : 'translate-x-full'}
lg:translate-x-0 lg:w-[220px] lg:translate-x-0 lg:w-[220px] bg-header
`} `}
> >
<button
onClick={() => setIsOpen(false)}
className="lg:hidden absolute top-4 right-4 z-[70] text-primary p-2.5"
aria-label="Toggle menu"
>
<FiX className="w-6 h-6" />
</button>
<div className="flex justify-center mt-5 mb-2"> <div className="flex justify-center mt-5 mb-2">
<img src={SIDEBAR.logo} className="h-10 w-10 lg:h-12 lg:w-12" alt="Logo" /> <img src={currentLogo} className="h-10 w-10 lg:h-16 lg:w-16" alt="Logo" />
</div> </div>
<nav className="flex-1 px-3 mt-6 space-y-3 overflow-y-auto"> <nav className="flex-1 px-3 mt-6 space-y-3 overflow-y-auto">

View File

@ -1,32 +1,41 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Outlet, useLocation } from "react-router-dom"; import { Outlet, useLocation } from "react-router-dom";
import Sidebar from "./Sidebar/sidebar"; import Sidebar from "./Sidebar/sidebar";
import Header from "./Header/header"; import Header from "./Header/header";
import toggleTheme from "../ToggleTheme";
import { CgDarkMode } from "react-icons/cg";
const HEADER_PATHS = [ const HEADER_PATHS = [
"/management", { route: "/cafe-management", title: "مدیریت کافه ها" },
"/management/", { route: "/dashboard", title: "داشبورد" },
"/management/cafes", { route: "/stats", title: "آمار و تحلیل" },
"/edit-cafe", { route: "/edit-cafe/", title: "ویرایش کافه" },
"/edit-cafe/",
"/cafe",
"/cafes",
"/admin",
]; ];
export default function Layout() { export default function Layout() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const location = useLocation(); const location = useLocation();
const path = location.pathname; const [headerTitle, setHeaderTitle] = useState("");
useEffect(() => {
const currentPath = location.pathname;
const headerPath = HEADER_PATHS.find((path) => currentPath.startsWith(path.route));
if (headerPath) {
setHeaderTitle(headerPath.title);
} else {
setHeaderTitle("");
}
}, [location.pathname]);
// Check if current path should show header // Check if current path should show header
const showHeader = HEADER_PATHS.some((p) => path.startsWith(p) || path.includes(p));
return ( return (
<div className="flex min-h-screen w-full bg-[#FDF8F4]"> <div className="flex min-h-screen w-full bg-background">
{/* Sidebar */} {/* Sidebar */}
<Sidebar <Sidebar
className="fixed top-0 right-0 bottom-0 z-[65] w-[260px] flex flex-col bg-[#EFEEEE] rounded-l-2xl shadow-xl border-l border-[#D9CAB3]" className="fixed top-0 right-0 bottom-0 z-[80] w-[260px] flex flex-col bg-[#EFEEEE] rounded-l-2xl shadow-xl border-l border-[#D9CAB3]"
isOpen={isOpen} isOpen={isOpen}
setIsOpen={setIsOpen} setIsOpen={setIsOpen}
/> />
@ -34,11 +43,18 @@ export default function Layout() {
{/* Main Content */} {/* Main Content */}
<div className="flex-1 flex flex-col min-h-screen w-full lg:mr-[220px]"> <div className="flex-1 flex flex-col min-h-screen w-full lg:mr-[220px]">
{/* Header */} {/* Header */}
{showHeader && ( {/* {showHeader && ( */}
<div className="fixed top-0 right-0 left-0 lg:right-[220px] z-30"> <div className="mt-18 lg:mt-0 w-full h-16 bg-background p-2 lg:right-[220px] z-30">
<Header title="مدیریت کافه‌ها" /> <Header title={headerTitle} />
</div> </div>
)} {/* )} */}
<button
onClick={toggleTheme}
className="fixed top-14 left-0 w-12 h-10 z-[1000] flex justify-center items-center"
>
<CgDarkMode className="w-6 h-6 text-[#8B8886]" />
</button>
{/* Main Content Area */} {/* Main Content Area */}
<main <main

View File

@ -29,7 +29,9 @@ const CafeManagement = () => {
console.error("❌ Error loading cafes:", error); console.error("❌ Error loading cafes:", error);
const errorMessage = const errorMessage =
error.response?.data?.message || error.response?.data?.message ||
(error.request ? "خطا در برقراری ارتباط با سرور" : "خطای نامشخص رخ داده است"); (error.request
? "خطا در برقراری ارتباط با سرور"
: "خطای نامشخص رخ داده است");
setError(errorMessage); setError(errorMessage);
} finally { } finally {
setLoading(false); setLoading(false);
@ -43,7 +45,9 @@ const CafeManagement = () => {
if (loading) { if (loading) {
return ( return (
<div className="flex justify-center items-center h-screen"> <div className="flex justify-center items-center h-screen">
<div className="text-button1 text-xl font-bold">در حال بارگذاری...</div> <div className="text-button1 text-xl font-bold">
در حال بارگذاری...
</div>
</div> </div>
); );
} }
@ -70,18 +74,33 @@ const CafeManagement = () => {
<div className="hidden lg:block mt-10 overflow-x-auto"> <div className="hidden lg:block mt-10 overflow-x-auto">
<table className="w-full border-collapse"> <table className="w-full border-collapse">
<thead> <thead>
<tr className="bg-[#EFEEEE]"> <tr className="bg-thead">
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">لوگو</th> <th className="px-4 py-3 text-right text-text2 font-medium text-sm">
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">اسم</th> لوگو
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">آدرس</th> </th>
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">ریتینگ</th> <th className="px-4 py-3 text-right text-text2 font-medium text-sm">
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">ساعت کاری</th> اسم
<th className="px-4 py-3 text-right text-[#402E32] font-medium text-sm">ادیت</th> </th>
<th className="px-4 py-3 text-right text-text2 font-medium text-sm">
آدرس
</th>
<th className="px-4 py-3 text-right text-text2 font-medium text-sm">
ریتینگ
</th>
<th className="px-4 py-3 text-right text-text2 font-medium text-sm">
ساعت کاری
</th>
<th className="px-4 py-3 text-right text-text2 font-medium text-sm">
ادیت
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{cafes.map((cafe) => ( {cafes.map((cafe) => (
<tr key={cafe._id} className="border-b border-[#EFEEEE] hover:bg-gray-50 transition-colors"> <tr
key={cafe._id}
className="border-b border-[#EFEEEE] hover:bg-hover transition-colors"
>
<td className="px-4 py-4 text-right"> <td className="px-4 py-4 text-right">
<img <img
src={cafe.photo || Pic1} src={cafe.photo || Pic1}
@ -98,7 +117,9 @@ const CafeManagement = () => {
<td className="px-4 py-4 text-right"> <td className="px-4 py-4 text-right">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<img src={Star1} alt="rating" className="w-5 h-5" /> <img src={Star1} alt="rating" className="w-5 h-5" />
<span className="text-[#402E32] text-sm">{cafe.rating || 0}</span> <span className="text-[#402E32] text-sm">
{cafe.rating || 0}
</span>
</div> </div>
</td> </td>
<td className="px-4 py-4 text-right"> <td className="px-4 py-4 text-right">
@ -112,7 +133,7 @@ const CafeManagement = () => {
<td className="px-4 py-4 text-center"> <td className="px-4 py-4 text-center">
<Link <Link
to={`/edit-cafe/${cafe._id}`} to={`/edit-cafe/${cafe._id}`}
className="inline-flex items-center justify-center gap-2 border-2 border-[#BB8F70] px-6 py-2 rounded-3xl text-sm font-light text-[#402E32] hover:bg-button1 hover:text-white hover:border-button1 transition-all duration-300" className="inline-flex items-center justify-center gap-2 border-2 border-[#BB8F70] px-6 py-2 rounded-3xl text-sm font-light text-text1 hover:bg-button1 hover:border-button1 transition-all duration-300"
> >
<span>ادیت</span> <span>ادیت</span>
<BiEdit className="w-4 h-4" /> <BiEdit className="w-4 h-4" />
@ -127,7 +148,10 @@ const CafeManagement = () => {
{/* کارت موبایل */} {/* کارت موبایل */}
<div className="lg:hidden mt-6 space-y-4"> <div className="lg:hidden mt-6 space-y-4">
{cafes.map((cafe) => ( {cafes.map((cafe) => (
<div key={cafe._id} className="bg-white border-2 border-[#8b8886] rounded-xl p-4"> <div
key={cafe._id}
className="bg-white border-2 border-[#8b8886] rounded-xl p-4"
>
<div className="flex items-start gap-4 mb-4"> <div className="flex items-start gap-4 mb-4">
<img <img
src={cafe.photo || Pic1} src={cafe.photo || Pic1}
@ -135,8 +159,12 @@ const CafeManagement = () => {
className="w-16 h-16 rounded-full object-cover flex-shrink-0" className="w-16 h-16 rounded-full object-cover flex-shrink-0"
/> />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h3 className="font-bold text-[#402E32] mb-1 text-sm">{cafe.Name}</h3> <h3 className="font-bold text-[#402E32] mb-1 text-sm">
<p className="text-xs text-gray-600 truncate">{cafe.address}</p> {cafe.Name}
</h3>
<p className="text-xs text-gray-600 truncate">
{cafe.address}
</p>
</div> </div>
</div> </div>
@ -147,15 +175,17 @@ const CafeManagement = () => {
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<img src={Group} alt="time" className="w-4 h-4" /> <img src={Group} alt="time" className="w-4 h-4" />
<span className="text-[#402E32]">{cafe.openinghour || "نامشخص"}</span> <span className="text-[#402E32]">
{cafe.openinghour || "نامشخص"}
</span>
</div> </div>
</div> </div>
<Link <Link
to={`/edit-cafe/${cafe._id}`} to={`/edit-cafe/${cafe._id}`}
className="flex justify-center items-center gap-2 w-full border-2 border-[#BB8F70] py-2 rounded-full text-sm font-medium text-[#402E32] hover:bg-button1 hover:text-white hover:border-button1 transition-all duration-300" className="flex justify-center items-center gap-2 w-full border-2 border-[#BB8F70] py-2 rounded-full text-sm font-medium text-text1 hover:bg-button1 hover:border-button1 transition-all duration-300"
> >
<span>ادیت کافه</span> <span className="">ادیت کافه</span>
<BiEdit className="w-4 h-4" /> <BiEdit className="w-4 h-4" />
</Link> </Link>
</div> </div>
@ -169,14 +199,16 @@ const CafeManagement = () => {
<section dir="rtl" className="w-full pt-24 max-w-full overflow-x-hidden"> <section dir="rtl" className="w-full pt-24 max-w-full overflow-x-hidden">
{/* بخش دکمه اضافه کردن */} {/* بخش دکمه اضافه کردن */}
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<button className="flex items-center justify-center gap-3 px-6 py-3 bg-button1 text-white rounded-3xl text-sm lg:text-base font-medium hover:bg-amber-950 transition-all duration-300 cursor-pointer"> <button className="flex items-center justify-center gap-3 px-6 py-3 bg-button1 text-white rounded-3xl text-sm lg:text-base font-medium hover:bg-hover2 transition-all duration-300 cursor-pointer">
<span>افزودن شعبه جدید</span> <span>افزودن شعبه جدید</span>
<img src={Vector9} alt="افزودن" className="w-5 h-5" /> <img src={Vector9} alt="افزودن" className="w-5 h-5" />
</button> </button>
</div> </div>
{/* عنوان */} {/* عنوان */}
<h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">کافه های شما</h1> <h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">
کافه های شما
</h1>
{/* محتوای اصلی */} {/* محتوای اصلی */}
{renderContent()} {renderContent()}

View File

@ -0,0 +1,43 @@
import { FaRegEdit } from "react-icons/fa";
export default function CoffeeCard({
title,
description,
price,
image,
onEdit,
}) {
return (
<div
className="bg-white border border-gray-200 rounded-2xl p-4
hover:shadow-lg transition-all duration-300"
>
{/* تصویر */}
<img
src={image}
alt={title}
className="w-full h-40 object-cover rounded-xl mb-3"
/>
{/* عنوان */}
<h3 className="font-bold text-text1 text-base mb-2">{title}</h3>
{/* توضیح */}
<p className="text-sm text-gray-500 mb-3 line-clamp-2">{description}</p>
{/* فوتر */}
<div className="flex justify-between items-center">
<span className="font-bold text-text1">{price} تومان</span>
<button
onClick={onEdit}
className="flex items-center gap-1 text-sm
text-button1 hover:text-[#5f494f] transition-colors"
>
<FaRegEdit />
ویرایش
</button>
</div>
</div>
);
}

View File

@ -4,6 +4,8 @@ import { GrLocation } from "react-icons/gr";
import { BiEdit } from "react-icons/bi"; import { BiEdit } from "react-icons/bi";
import { FaRegStar } from "react-icons/fa"; import { FaRegStar } from "react-icons/fa";
import { IoMdCheckmark, IoMdClose } from "react-icons/io"; import { IoMdCheckmark, IoMdClose } from "react-icons/io";
import { FaRegEdit } from "react-icons/fa";
import EditCafeMenu from "./EditCafeMenu";
// Assets // Assets
import Bg1 from "../../assets/icons/bg1.svg"; import Bg1 from "../../assets/icons/bg1.svg";
@ -47,13 +49,15 @@ const CAFE_PRODUCTS = [
name: "اسپرسو100%", name: "اسپرسو100%",
price: "118.000", price: "118.000",
image: Sperso, image: Sperso,
description: "45 میلی لیتر، قهوه، 100% عربیکا، دم شده با دستگاه اسپرسو ساز، به همراه یک عدد آب معدنی مینی", description:
"45 میلی لیتر، قهوه، 100% عربیکا، دم شده با دستگاه اسپرسو ساز، به همراه یک عدد آب معدنی مینی",
}, },
{ {
name: "کارامل ماکیاتو", name: "کارامل ماکیاتو",
price: "149.000", price: "149.000",
image: Coffee1, image: Coffee1,
description: "220 میلی لیتر، 2 شات اسپرسو 30% روبوستا، 70% عربیکا، یک لکه فوم شیر، سیروپ کارامل", description:
"220 میلی لیتر، 2 شات اسپرسو 30% روبوستا، 70% عربیکا، یک لکه فوم شیر، سیروپ کارامل",
}, },
{ {
name: "اسپرسو آفوگاتو", name: "اسپرسو آفوگاتو",
@ -66,6 +70,7 @@ const CAFE_PRODUCTS = [
export default function EditCafe() { export default function EditCafe() {
const { id } = useParams(); const { id } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [editmenu, setEditMenu] = useState(false);
// State Management // State Management
const [cafeData, setCafeData] = useState(null); const [cafeData, setCafeData] = useState(null);
@ -111,7 +116,9 @@ export default function EditCafe() {
console.error("❌ Error loading cafe:", error); console.error("❌ Error loading cafe:", error);
const errorMessage = const errorMessage =
error.response?.data?.message || error.response?.data?.message ||
(error.request ? "خطا در برقراری ارتباط با سرور" : "خطای نامشخص رخ داده است"); (error.request
? "خطا در برقراری ارتباط با سرور"
: "خطای نامشخص رخ داده است");
setError(errorMessage); setError(errorMessage);
} finally { } finally {
setLoading(false); setLoading(false);
@ -161,7 +168,9 @@ export default function EditCafe() {
if (loading) { if (loading) {
return ( return (
<div className="flex justify-center items-center h-screen"> <div className="flex justify-center items-center h-screen">
<div className="text-button1 text-2xl font-bold">در حال بارگذاری...</div> <div className="text-button1 text-2xl font-bold">
در حال بارگذاری...
</div>
</div> </div>
); );
} }
@ -178,21 +187,25 @@ export default function EditCafe() {
if (!cafeData) { if (!cafeData) {
return ( return (
<div className="p-6 text-center text-gray-500"> <div className="p-6 text-center text-gray-500">اطلاعات کافه یافت نشد</div>
اطلاعات کافه یافت نشد
</div>
); );
} }
return ( return (
<section dir="rtl" className="w-full overflow-x-hidden"> <section dir="rtl" className="w-full overflow-x-hidden">
{editmenu ? (
<EditCafeMenu setEditMenu={setEditMenu} />
) : (
<>
<style>{` <style>{`
.scrollbar-hide::-webkit-scrollbar { display: none; } .scrollbar-hide::-webkit-scrollbar { display: none; }
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; } .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
`}</style> `}</style>
{/* Header */} {/* Header */}
<h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">ادیت {cafeData.Name}</h1> <h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">
ادیت {cafeData.Name}
</h1>
{/* Main Container */} {/* Main Container */}
<div className="border-2 mt-1 border-[#8b8886] p-4 md:p-6 lg:p-10 rounded-2xl"> <div className="border-2 mt-1 border-[#8b8886] p-4 md:p-6 lg:p-10 rounded-2xl">
@ -225,45 +238,70 @@ export default function EditCafe() {
{/* Basic Info */} {/* Basic Info */}
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<h2 className="font-bold text-lg text-[#402E32] mb-2">{cafeData.Name}</h2> <h2 className="font-bold text-lg text-[#402E32] mb-2">
{cafeData.Name}
</h2>
<div className="w-16 h-1 bg-[#80931e] rounded-full"></div> <div className="w-16 h-1 bg-[#80931e] rounded-full"></div>
</div> </div>
<div className="flex items-center gap-3 text-[#402E32]"> <div className="flex items-center gap-3 text-[#402E32]">
<GrLocation className="text-lg flex-shrink-0" /> <GrLocation className="text-lg flex-shrink-0" />
<span className="text-sm md:text-base">{cafeData.address || "آدرس موجود نیست"}</span> <span className="text-sm md:text-base">
{cafeData.address || "آدرس موجود نیست"}
</span>
</div> </div>
<div className="flex items-center gap-3 text-[#402E32]"> <div className="flex items-center gap-3 text-[#402E32]">
<FaRegStar className="text-lg flex-shrink-0" /> <FaRegStar className="text-lg flex-shrink-0" />
<span className="text-sm md:text-base">{cafeData.rating || 0}</span> <span className="text-sm md:text-base">
{cafeData.rating || 0}
</span>
</div> </div>
<div> <div>
<h3 className="font-bold text-[#402E32] mb-2">درباره کافه</h3> <h3 className="font-bold text-[#402E32] mb-2">
درباره کافه
</h3>
<p className="text-sm md:text-base text-[#555]"> <p className="text-sm md:text-base text-[#555]">
{cafeData.description || "توضیحاتی برای این کافه وجود ندارد."} {cafeData.description ||
"توضیحاتی برای این کافه وجود ندارد."}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button
onClick={() => setEditMenu(true)}
className="flex items-center justify-center text-text1 gap-2 w-full h-14 border-4 border-border mb-14 rounded-4xl font-bold"
>
<FaRegEdit size={20} />
ادیت منو کافه
</button>
{/* Features Section */} {/* Features Section */}
<div className="mb-8"> <div className="mb-8">
<h2 className="font-bold text-[#402E32] mb-4 text-base md:text-lg">ویژگی ها</h2> <h2 className="font-bold text-[#402E32] mb-4 text-base md:text-lg">
ویژگی ها
</h2>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
{CAFE_FEATURES.map((feature, idx) => ( {CAFE_FEATURES.map((feature, idx) => (
<div <div
key={idx} key={idx}
className="bg-[#e1d5c2] p-4 rounded-2xl flex flex-col items-center gap-3 text-center transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md" className="bg-[#e1d5c2] p-4 rounded-2xl flex flex-col items-center gap-3 text-center transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md"
> >
<img src={feature.icon} alt={feature.label} className="w-6 h-6" /> <img
src={feature.icon}
alt={feature.label}
className="w-6 h-6"
/>
<span className="text-[#402E32] font-medium text-xs md:text-sm whitespace-normal"> <span className="text-[#402E32] font-medium text-xs md:text-sm whitespace-normal">
{feature.label} {feature.label}
</span> </span>
{feature.value && ( {feature.value && (
<span className="text-[#402E32] text-xs">{feature.value}</span> <span className="text-[#402E32] text-xs">
{feature.value}
</span>
)} )}
</div> </div>
))} ))}
@ -276,14 +314,20 @@ export default function EditCafe() {
{/* Categories Section */} {/* Categories Section */}
<div className="mb-8"> <div className="mb-8">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h2 className="font-bold text-[#402E32] text-base md:text-lg">دستهبندیها</h2> <h2 className="font-bold text-[#402E32] text-base md:text-lg">
دستهبندیها
</h2>
<button <button
onClick={() => setIsEditMode(!isEditMode)} onClick={() => setIsEditMode(!isEditMode)}
className="flex items-center gap-2 text-button1 hover:text-[#5f494f] transition-colors" className="flex items-center gap-2 text-button1 hover:text-[#5f494f] transition-colors"
title={isEditMode ? "خروج از حالت ویرایش" : "ویرایش دسته‌بندی‌ها"} title={
isEditMode ? "خروج از حالت ویرایش" : "ویرایش دسته‌بندی‌ها"
}
> >
<img src={Vector16} alt="ویرایش" className="w-5 h-5" /> <img src={Vector16} alt="ویرایش" className="w-5 h-5" />
<span className="text-sm md:text-base">{isEditMode ? "تمام" : "ویرایش"}</span> <span className="text-sm md:text-base">
{isEditMode ? "تمام" : "ویرایش"}
</span>
</button> </button>
</div> </div>
@ -313,11 +357,13 @@ export default function EditCafe() {
/> />
</div> </div>
) : ( ) : (
<div className={`flex items-center gap-2 px-3 py-2 rounded-lg text-[#402E32] text-xs md:text-sm whitespace-nowrap ${ <div
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-[#402E32] text-xs md:text-sm whitespace-nowrap ${
isEditMode isEditMode
? "bg-gray-100 hover:bg-gray-200" ? "bg-gray-100 hover:bg-gray-200"
: "bg-[#e1d5c2]" : "bg-[#e1d5c2]"
} transition-colors`}> } transition-colors`}
>
<span>{category}</span> <span>{category}</span>
{isEditMode && ( {isEditMode && (
<> <>
@ -379,18 +425,28 @@ export default function EditCafe() {
{/* Products Section */} {/* Products Section */}
<div> <div>
<h2 className="font-bold text-[#402E32] mb-4 text-base md:text-lg">محصولات</h2> <h2 className="font-bold text-[#402E32] mb-4 text-base md:text-lg">
محصولات
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6">
{CAFE_PRODUCTS.map((product, idx) => ( {CAFE_PRODUCTS.map((product, idx) => (
<div <div
key={idx} key={idx}
className="bg-white border border-gray-200 rounded-xl p-4 transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md" className="bg-white border border-gray-200 rounded-xl p-4 transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md"
> >
<h3 className="font-bold text-[#402E32] text-sm md:text-base mb-3">{product.name}</h3> <h3 className="font-bold text-[#402E32] text-sm md:text-base mb-3">
<img src={product.image} alt={product.name} className="w-full h-40 object-cover rounded-lg mb-3" /> {product.name}
</h3>
<img
src={product.image}
alt={product.name}
className="w-full h-40 object-cover rounded-lg mb-3"
/>
<div className="flex justify-between items-center mb-3 text-sm"> <div className="flex justify-between items-center mb-3 text-sm">
<span className="text-[#66585b]">قیمت:</span> <span className="text-[#66585b]">قیمت:</span>
<span className="font-medium text-[#402E32]">{product.price}</span> <span className="font-medium text-[#402E32]">
{product.price}
</span>
</div> </div>
<p className="text-xs md:text-sm text-[#66585b] line-clamp-3"> <p className="text-xs md:text-sm text-[#66585b] line-clamp-3">
{product.description} {product.description}
@ -400,6 +456,8 @@ export default function EditCafe() {
</div> </div>
</div> </div>
</div> </div>
</>
)}
</section> </section>
); );
} }

View File

@ -0,0 +1,124 @@
import { useState } from "react";
import { CgAddR } from "react-icons/cg";
import { PiCoffee } from "react-icons/pi";
import { FaRegEdit } from "react-icons/fa";
import CoffeeCard from "./Component/CoffeeCard";
import coffee12 from "../../assets/image/coffee12.png";
const DEFAULT_CATEGORIES = [
"نوشیدنی سرد",
"نوشیدنی گرم",
"کیک و دسر",
"صبحانه",
"ساندویچ و برگر",
"سالاد و پیش غذا",
];
const PRODUCTS = [
{
id: 1,
title: "اسپرسو",
description: "قهوه 100٪ عربیکا با عطر بالا",
price: "118,000",
image: coffee12,
},
{
id: 2,
title: "لاته",
description: "شیر بخار داده شده + اسپرسو",
price: "135,000",
image: coffee12,
},
{
id: 3,
title: "کاپوچینو",
description: "کلاسیک ایتالیایی با فوم شیر",
price: "142,000",
image: coffee12,
},
];
export default function EditCafeMenu({ setEditMenu }) {
const [selectedCategory, setSelectedCategory] = useState(null);
return (
<div>
<h1 className="text-bold text-2xl text-text1">ادیت منو کافه</h1>
<div className="flex gap-3 justify-end mb-6">
<button
onClick={() => setEditMenu(false)}
className="px-4 md:px-6 py-2 border-2 border-[#bb8f70] text-[#402e32] rounded-3xl
hover:bg-button1 hover:text-white hover:border-button1
transition-all duration-300 text-sm md:text-base font-medium"
>
انصراف
</button>
<button
className="px-4 md:px-6 py-2 bg-button1 text-white rounded-3xl
hover:bg-[#5f494f] transition-all duration-300
text-sm md:text-base font-medium"
>
تایید
</button>
</div>
<div className="flex mt-10 border-b-2 border-border pb-2 relative">
<div className="flex items-center gap-1 text-text1">
<CgAddR />
<p>عنوان</p>
</div>
<div className="flex mr-6 gap-4 relative">
{DEFAULT_CATEGORIES.map((cat, index) => (
<button
key={index}
onClick={() => setSelectedCategory(cat)}
className={`relative -bottom-[2px] pb-2 text-sm md:text-base transition-all
${
selectedCategory === cat
? "text-text1 font-bold"
: "text-gray-400 hover:text-text1"
}`}
>
{cat}
{selectedCategory === cat && (
<span
className="absolute left-0 -bottom-[9px] w-full h-[5px]
bg-border rounded-full transition-all duration-300"
/>
)}
</button>
))}
</div>
</div>
{/* جزئیات category انتخاب شده */}
{selectedCategory && (
<>
<div className="flex mt-8 gap-1 font-bold items-center text-center text-text1">
<CgAddR />
افزودن زیر عنوان
</div>
<div className="flex font-bold items-center text-center gap-2 mt-4">
<FaRegEdit size={30} />
<PiCoffee size={30} />
<p className="border-b-2 border-border">قهوه ها</p>
</div>
<section className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{PRODUCTS.map((item) => (
<CoffeeCard
key={item.id}
title={item.title}
description={item.description}
price={item.price}
image={item.image}
onEdit={() => console.log("edit", item.id)}
/>
))}
</section>
</>
)}
</div>
);
}

View File

@ -1,11 +1,61 @@
import React from "react"; import React from "react";
import VisitorsChart from "./components/viewstats";
export default function Stats() { export default function Stats() {
return ( return (
<section dir="rtl" className="w-full"> <section dir="rtl" className="w-full h-full flex flex-col gap-6">
<h1 className="text-2xl font-bold text-[#402E32] mb-6">آمار و تحلیل</h1> <div className="flex flex-col gap-2 border-2 border-border rounded-2xl p-6 md:p-8">
<div className="bg-white border-2 border-[#8b8886] rounded-2xl p-6 md:p-8"> <div className="flex gap-5">
<p className="text-gray-600">محتوای آمار و تحلیل بهزودی اضافه خواهد شد</p> <h4 className="font-bold text-md">آمار بازدید کننده ها</h4>
<button className="ps-5 pe-10 bg-hover text-text1 rounded-lg text-sm lg:text-base font-medium hover:bg-hover2 transition-all duration-300 cursor-pointer">
آبان
</button>
</div>
<div className="flex gap-2">
<div className="self-end">
مجموع فروش: 0 تومان
</div>
<div className="flex-1">
<VisitorsChart />
</div>
</div>
</div>
<div className="flex gap-5 rounded-2xl">
<div className="flex flex-col w-1/2 gap-2 border-2 border-border rounded-2xl p-6 md:p-8">
<div className="flex gap-5">
<h4 className="font-bold text-md">آمار بازدید کننده ها</h4>
<button className="ps-5 pe-10 bg-hover text-text1 rounded-lg text-sm lg:text-base font-medium hover:bg-hover2 transition-all duration-300 cursor-pointer">
آبان
</button>
</div>
<div className="flex gap-2">
<div className="self-end">
مجموع فروش: 0 تومان
</div>
<div className="flex-1">
<VisitorsChart />
</div>
</div>
</div>
<div className="flex flex-col gap-2 w-1/2 border-2 border-border rounded-2xl p-6 md:p-8">
<div className="flex gap-5">
<h4 className="font-bold text-md">آمار بازدید کننده ها</h4>
<button className="ps-5 pe-10 bg-hover text-text1 rounded-lg text-sm lg:text-base font-medium hover:bg-hover2 transition-all duration-300 cursor-pointer">
آبان
</button>
</div>
<div className="flex gap-2">
<div className="self-end">
مجموع فروش: 0 تومان
</div>
<div className="flex-1">
<VisitorsChart />
</div>
</div>
</div>
</div> </div>
</section> </section>
); );

View File

@ -0,0 +1,65 @@
import {
LineChart,
Line,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
} from "recharts";
const data = [
{ name: "هفته اول", a: 200000, b: 250000 },
{ name: "هفته دوم", a: 450000, b: 380000 },
{ name: "هفته سوم", a: 350000, b: 600000 },
{ name: "هفته چهارم", a: 150000, b: 500000 },
{ name: "هفته پنجم", a: 500000, b: 700000 },
{ name: "هفته ششم", a: 600000, b: 800000 },
{ name: "هفته هفتم", a: 700000, b: 900000 },
{ name: "هفته هشتم", a: 800000, b: 950000 },
{ name: "هفته نهم", a: 900000, b: 1000000 },
];
export default function VisitorsChart() {
return (
<div dir="ltr" className="w-full h-72 rounded-xl bg-white shadow-sm p-6">
<p className="text-sm font-medium mb-3 text-right">آمار بازدید کنندهها</p>
<ResponsiveContainer width="100%" height="90%">
<LineChart data={data}>
{/* گرادیانت‌ها */}
<defs>
<linearGradient id="gradA" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#C2574E" stopOpacity={0.4} />
<stop offset="100%" stopColor="#C2574E" stopOpacity={0} />
</linearGradient>
<linearGradient id="gradB" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#6B432B" stopOpacity={0.4} />
<stop offset="100%" stopColor="#6B432B" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="name" tick={{ fontSize: 12 }} />
<YAxis tick={{ fontSize: 12 }} />
<Tooltip />
<Line
type="monotone"
dataKey="a"
stroke="#C2574E"
strokeWidth={3}
fill="url(#gradA)"
dot={false}
/>
<Line
type="monotone"
dataKey="b"
stroke="#6B432B"
strokeWidth={3}
fill="url(#gradB)"
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
);
}

View File

@ -7,6 +7,7 @@ import CafeManagement from '../pages/CafeManagement/CafeManagement';
import EditCafe from '../pages/CafeManagement/EditCafe'; import EditCafe from '../pages/CafeManagement/EditCafe';
import Stats from '../pages/Stats/Stats'; import Stats from '../pages/Stats/Stats';
export default function AppRoutes() { export default function AppRoutes() {
return ( return (
<Routes> <Routes>

View File

@ -4,29 +4,48 @@
:root { :root {
--color-primary: #7f4629; --color-primary: #7f4629;
--color-button1: #99582a; --color-button1: #99582a;
--color-text1: #402e32;
--color-text2: #ffffff;
--color-header: #f3efe7; --color-header: #f3efe7;
--color-header2: #e6dbcc; --color-header2: #e6dbcc;
--color-sidebar: #efeeee; --color-sidebar: #efeeee;
--color-background: #fdf8f4; --color-background: #fdf8f4;
--color-hover: #e6dbcc;
--color-hover2: #a2795c;
--color-thead: #7f4629;
--color-border: #7f4629;
--font-sans: "Vazirmatn", system-ui, sans-serif; --font-sans: "Vazirmatn", system-ui, sans-serif;
} }
.dark { .dark {
--color-primary: #d9cab3; --color-primary: #93836c;
--color-button1: #99582a; --color-button1: #99582a;
--color-text1: #fdf8f4;
--color-text2: #272322;
--color-header: #342e2c; --color-header: #342e2c;
--color-header2: #5e5450; --color-header2: #5e5450;
--color-sidebar: #564c49; --color-sidebar: #564c49;
--color-background: #272322; --color-background: #272322;
--color-hover: #5e5450;
--color-hover2: #a2795c;
--color-thead: #d9cab3;
--color-border: #93836c;
}
@theme {
--font-MyEstedad: "MyEstedad", sans-serif;
--color-primary: var(--color-primary);
--color-header: var(--color-header);
--color-sidebar: var(--color-sidebar);
--color-background: var(--color-background);
--color-button1: var(--color-button1);
--color-text1: var(--color-text1);
--color-text2: var(--color-text2);
--color-hover: var(--color-hover);
--color-hover2: var(--color-hover2);
--color-thead: var(--color-thead);
--color-border: var(--color-border);
} }
/* Chrome / Edge / Safari autofill FIX (light + dark, no color) */ /* Chrome / Edge / Safari autofill FIX (light + dark, no color) */
@ -54,7 +73,39 @@ select:-webkit-autofill {
} }
h1,
h2,
h3,
h4,
h5,
h6,
p,
td,
body {
color: var(--color-text1);
}
div:hover
{
color: var(--color-text1);
}
.dark h1,
.dark h2,
.dark h3,
.dark h4,
.dark h5,
.dark h6,
.dark p,
.dark span,
.dark td,
.dark body {
color: var(--color-text1) !important;
}
.dark div:hover {
color: var(--color-text2) !important;
}
@ -82,36 +133,7 @@ body {
src: url("/fonts/Estedad.ttf") format("truetype"); src: url("/fonts/Estedad.ttf") format("truetype");
} }
@theme {
--font-MyEstedad: "MyEstedad", sans-serif;
--color-primary: var(--color-primary);
--color-header: var(--color-header);
--color-sidebar: var(--color-sidebar);
--color-background: var(--color-background);
--color-button1: var(--color-button1);
/* --color-text1: var(--color-text1-menu-and-card);
--color-text2: var(--color-text2);
--color-text3: var(--color-text3);
--color-text4: var(--color-text4);
--color-btn1: var(--color-button1);
--color-btn2: var(--color-button2);
--color-btn3: var(--color-button3);
--color-primary1: var(--color-primary1);
--color-primary2: var(--color-primary2);
--color-primary3: var(--color-primary3);
--color-primary4: var(--color-primary4);
--color-primary5-dark: var(--color-primary5-dark);
--color-primary6-dark: var(--color-primary6-dark);
--color-bg-light: var(--color-background-light);
--color-bg-dark: var(--color-background-dark); */
}
@layer base { @layer base {
html { html {
@ -153,8 +175,8 @@ svg {
/* رنگ انتخاب متن (drag select) */ /* رنگ انتخاب متن (drag select) */
::selection { ::selection {
background-color: var(--primary-200); background-color: var(--color-hover);
/* بک‌گراند انتخاب */ /* بک‌گراند انتخاب */
color: var(--primary-foreground); color: var(--color-text1);
/* رنگ متن انتخاب */ /* رنگ متن انتخاب */
} }