Compare commits

...

2 Commits

Author SHA1 Message Date
Mohammadreza 4e4f9685a4 Merge branch 'master' of https://git.caspianoxin.ir/MamadAriaKia/CoffeeJoo_Panel_kafeDar 2025-12-28 15:18:05 +03:30
Mohammadreza c748620429 edit cofe 2025-12-28 15:15:34 +03:30
7 changed files with 677 additions and 434 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -7,12 +7,14 @@ import toggleTheme from "../ToggleTheme";
import { CgDarkMode } from "react-icons/cg"; import { CgDarkMode } from "react-icons/cg";
const HEADER_PATHS = [ const HEADER_PATHS = [
{ route: "/cafe-management", title: "مدیریت کافه ها" }, "/management",
{route: "/users-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() {

View File

@ -8,61 +8,65 @@ import { Link } from "react-router-dom";
import cafeService from "../../services/cafe"; import cafeService from "../../services/cafe";
const CafeManagement = () => { const CafeManagement = () => {
const [cafes, setCafes] = useState([]); const [cafes, setCafes] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(""); const [error, setError] = useState("");
useEffect(() => { useEffect(() => {
const fetchCafes = async () => { const fetchCafes = async () => {
setLoading(true); setLoading(true);
setError(""); setError("");
try { try {
const res = await cafeService.getCafeList(); const res = await cafeService.getCafeList();
if (res.data.success && res.data.data) { if (res.data.success && res.data.data) {
setCafes(res.data.data); setCafes(res.data.data);
console.log("✅ Cafes loaded successfully:", res.data); console.log("✅ Cafes loaded successfully:", res.data);
} else { } else {
setError("داده‌های دریافتی معتبر نیست"); setError("داده‌های دریافتی معتبر نیست");
}
} catch (error) {
console.error("❌ Error loading cafes:", error);
const errorMessage =
error.response?.data?.message ||
(error.request ? "خطا در برقراری ارتباط با سرور" : "خطای نامشخص رخ داده است");
setError(errorMessage);
} finally {
setLoading(false);
}
};
fetchCafes();
}, []);
const renderContent = () => {
if (loading) {
return (
<div className="flex justify-center items-center h-screen">
<div className="text-button1 text-xl font-bold">در حال بارگذاری...</div>
</div>
);
} }
} catch (error) {
console.error("❌ Error loading cafes:", error);
const errorMessage =
error.response?.data?.message ||
(error.request
? "خطا در برقراری ارتباط با سرور"
: "خطای نامشخص رخ داده است");
setError(errorMessage);
} finally {
setLoading(false);
}
};
if (error) { fetchCafes();
return ( }, []);
<div className="bg-red-50 border-2 border-red-300 text-red-700 px-4 py-3 rounded-xl mt-4">
{error}
</div>
);
}
if (cafes.length === 0) { const renderContent = () => {
return ( if (loading) {
<div className="text-center mt-20 text-gray-500"> return (
هیچ کافهای یافت نشد <div className="flex justify-center items-center h-screen">
</div> <div className="text-button1 text-xl font-bold">
); در حال بارگذاری...
} </div>
</div>
);
}
if (error) {
return (
<div className="bg-red-50 border-2 border-red-300 text-red-700 px-4 py-3 rounded-xl mt-4">
{error}
</div>
);
}
if (cafes.length === 0) {
return (
<div className="text-center mt-20 text-gray-500">
هیچ کافهای یافت نشد
</div>
);
}
return ( return (
<> <>
@ -124,32 +128,41 @@ const CafeManagement = () => {
</table> </table>
</div> </div>
{/* کارت موبایل */} {/* کارت موبایل */}
<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
<div className="flex items-start gap-4 mb-4"> key={cafe._id}
<img className="bg-white border-2 border-[#8b8886] rounded-xl p-4"
src={cafe.photo || Pic1} >
alt={cafe.Name} <div className="flex items-start gap-4 mb-4">
className="w-16 h-16 rounded-full object-cover flex-shrink-0" <img
/> src={cafe.photo || Pic1}
<div className="flex-1 min-w-0"> alt={cafe.Name}
<h3 className="font-bold text-[#402E32] mb-1 text-sm">{cafe.Name}</h3> className="w-16 h-16 rounded-full object-cover flex-shrink-0"
<p className="text-xs text-gray-600 truncate">{cafe.address}</p> />
</div> <div className="flex-1 min-w-0">
</div> <h3 className="font-bold text-[#402E32] mb-1 text-sm">
{cafe.Name}
</h3>
<p className="text-xs text-gray-600 truncate">
{cafe.address}
</p>
</div>
</div>
<div className="flex items-center justify-between mb-4 text-xs gap-4"> <div className="flex items-center justify-between mb-4 text-xs gap-4">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<img src={Star1} alt="rating" className="w-4 h-4" /> <img src={Star1} alt="rating" className="w-4 h-4" />
<span className="text-[#402E32]">{cafe.rating || 0}</span> <span className="text-[#402E32]">{cafe.rating || 0}</span>
</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]">
</div> {cafe.openinghour || "نامشخص"}
</div> </span>
</div>
</div>
<Link <Link
to={`/edit-cafe/${cafe._id}`} to={`/edit-cafe/${cafe._id}`}
@ -175,13 +188,15 @@ const CafeManagement = () => {
</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()}
</section> </section>
); );
}; };
export default CafeManagement; export default CafeManagement;

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";
@ -25,381 +27,437 @@ import cafeService from "../../services/cafe";
// Constants // Constants
const DEFAULT_CATEGORIES = [ const DEFAULT_CATEGORIES = [
"نوشیدنی سرد", "نوشیدنی سرد",
"نوشیدنی گرم", "نوشیدنی گرم",
"کیک و دسر", "کیک و دسر",
"صبحانه", "صبحانه",
"ساندویچ و برگر", "ساندویچ و برگر",
"سالاد و پیش غذا", "سالاد و پیش غذا",
]; ];
const CAFE_FEATURES = [ const CAFE_FEATURES = [
{ icon: Coffee3, label: "منو کافه:", width: "lg:w-[140px]" }, { icon: Coffee3, label: "منو کافه:", width: "lg:w-[140px]" },
{ icon: Vector15, label: "ساعت کاری:", value: "23 - 8" }, { icon: Vector15, label: "ساعت کاری:", value: "23 - 8" },
{ icon: Vector14, label: "رزرو :", value: "رزرو آنلاین" }, { icon: Vector14, label: "رزرو :", value: "رزرو آنلاین" },
{ icon: Vector11, label: "موسیقی :", value: "موسیقی زنده آخر هفته" }, { icon: Vector11, label: "موسیقی :", value: "موسیقی زنده آخر هفته" },
{ icon: Vector13, label: "پارکینگ :", value: "عمومی" }, { icon: Vector13, label: "پارکینگ :", value: "عمومی" },
{ icon: Vector12, label: "دسترسی آسان :", value: "مناسب افراد ناتوان" }, { icon: Vector12, label: "دسترسی آسان :", value: "مناسب افراد ناتوان" },
]; ];
const CAFE_PRODUCTS = [ const CAFE_PRODUCTS = [
{ {
name: "اسپرسو100%", name: "اسپرسو100%",
price: "118.000", price: "118.000",
image: Sperso, image: Sperso,
description: "45 میلی لیتر، قهوه، 100% عربیکا، دم شده با دستگاه اسپرسو ساز، به همراه یک عدد آب معدنی مینی", description:
}, "45 میلی لیتر، قهوه، 100% عربیکا، دم شده با دستگاه اسپرسو ساز، به همراه یک عدد آب معدنی مینی",
{ },
name: "کارامل ماکیاتو", {
price: "149.000", name: "کارامل ماکیاتو",
image: Coffee1, price: "149.000",
description: "220 میلی لیتر، 2 شات اسپرسو 30% روبوستا، 70% عربیکا، یک لکه فوم شیر، سیروپ کارامل", image: Coffee1,
}, description:
{ "220 میلی لیتر، 2 شات اسپرسو 30% روبوستا، 70% عربیکا، یک لکه فوم شیر، سیروپ کارامل",
name: "اسپرسو آفوگاتو", },
price: "118.000", {
image: Coffee2, name: "اسپرسو آفوگاتو",
description: "اسپرسو، یک اسکوپ بستنی وانیلی", price: "118.000",
}, image: Coffee2,
description: "اسپرسو، یک اسکوپ بستنی وانیلی",
},
]; ];
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);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [categories, setCategories] = useState(() => { const [categories, setCategories] = useState(() => {
const saved = localStorage.getItem("cafeCategories"); const saved = localStorage.getItem("cafeCategories");
return saved ? JSON.parse(saved) : DEFAULT_CATEGORIES; return saved ? JSON.parse(saved) : DEFAULT_CATEGORIES;
}); });
const [isEditMode, setIsEditMode] = useState(false); const [isEditMode, setIsEditMode] = useState(false);
const [isAddingCategory, setIsAddingCategory] = useState(false); const [isAddingCategory, setIsAddingCategory] = useState(false);
const [newCategory, setNewCategory] = useState(""); const [newCategory, setNewCategory] = useState("");
const [editingIndex, setEditingIndex] = useState(null); const [editingIndex, setEditingIndex] = useState(null);
const [editValue, setEditValue] = useState(""); const [editValue, setEditValue] = useState("");
// Effects // Effects
useEffect(() => { useEffect(() => {
fetchCafeData(); fetchCafeData();
}, [id]); }, [id]);
useEffect(() => { useEffect(() => {
localStorage.setItem("cafeCategories", JSON.stringify(categories)); localStorage.setItem("cafeCategories", JSON.stringify(categories));
}, [categories]); }, [categories]);
// API Calls // API Calls
const fetchCafeData = async () => { const fetchCafeData = async () => {
setLoading(true); setLoading(true);
setError(""); setError("");
try { try {
const res = await cafeService.getCafeList(); const res = await cafeService.getCafeList();
if (res.data.success && res.data.data) { if (res.data.success && res.data.data) {
const cafe = res.data.data.find((c) => c._id === id); const cafe = res.data.data.find((c) => c._id === id);
if (cafe) { if (cafe) {
setCafeData(cafe); setCafeData(cafe);
console.log("✅ Cafe data loaded:", cafe); console.log("✅ Cafe data loaded:", cafe);
} else { } else {
setError("کافه مورد نظر یافت نشد"); setError("کافه مورد نظر یافت نشد");
}
} else {
setError("داده‌های کافه معتبر نیست");
}
} catch (error) {
console.error("❌ Error loading cafe:", error);
const errorMessage =
error.response?.data?.message ||
(error.request ? "خطا در برقراری ارتباط با سرور" : "خطای نامشخص رخ داده است");
setError(errorMessage);
} finally {
setLoading(false);
} }
}; } else {
setError("داده‌های کافه معتبر نیست");
}
} catch (error) {
console.error("❌ Error loading cafe:", error);
const errorMessage =
error.response?.data?.message ||
(error.request
? "خطا در برقراری ارتباط با سرور"
: "خطای نامشخص رخ داده است");
setError(errorMessage);
} finally {
setLoading(false);
}
};
// Category Handlers // Category Handlers
const handleAddCategory = () => { const handleAddCategory = () => {
if (newCategory.trim()) { if (newCategory.trim()) {
setCategories([...categories, newCategory.trim()]); setCategories([...categories, newCategory.trim()]);
setNewCategory(""); setNewCategory("");
setIsAddingCategory(false); setIsAddingCategory(false);
setTimeout(() => { setTimeout(() => {
const scrollContainer = document.querySelector(".categories-scroll"); const scrollContainer = document.querySelector(".categories-scroll");
if (scrollContainer) { if (scrollContainer) {
scrollContainer.scrollLeft = scrollContainer.scrollWidth; scrollContainer.scrollLeft = scrollContainer.scrollWidth;
}
}, 0);
} }
}; }, 0);
const handleDeleteCategory = (index) => {
setCategories(categories.filter((_, i) => i !== index));
};
const handleEditCategory = (index) => {
setEditingIndex(index);
setEditValue(categories[index]);
};
const handleSaveCategory = () => {
if (editValue.trim()) {
const newCategories = [...categories];
newCategories[editingIndex] = editValue.trim();
setCategories(newCategories);
setEditingIndex(null);
setEditValue("");
}
};
const handleCancelEdit = () => {
setEditingIndex(null);
setEditValue("");
};
// Render States
if (loading) {
return (
<div className="flex justify-center items-center h-screen">
<div className="text-button1 text-2xl font-bold">در حال بارگذاری...</div>
</div>
);
} }
};
if (error) { const handleDeleteCategory = (index) => {
return ( setCategories(categories.filter((_, i) => i !== index));
<div className="p-6"> };
<div className="bg-red-50 border-2 border-red-300 text-red-700 px-4 py-3 rounded-xl">
{error} const handleEditCategory = (index) => {
</div> setEditingIndex(index);
</div> setEditValue(categories[index]);
); };
const handleSaveCategory = () => {
if (editValue.trim()) {
const newCategories = [...categories];
newCategories[editingIndex] = editValue.trim();
setCategories(newCategories);
setEditingIndex(null);
setEditValue("");
} }
};
if (!cafeData) { const handleCancelEdit = () => {
return ( setEditingIndex(null);
<div className="p-6 text-center text-gray-500"> setEditValue("");
اطلاعات کافه یافت نشد };
</div>
);
}
return ( // Render States
<section dir="rtl" className="w-full overflow-x-hidden"> if (loading) {
<style>{` return (
<div className="flex justify-center items-center h-screen">
<div className="text-button1 text-2xl font-bold">
در حال بارگذاری...
</div>
</div>
);
}
if (error) {
return (
<div className="p-6">
<div className="bg-red-50 border-2 border-red-300 text-red-700 px-4 py-3 rounded-xl">
{error}
</div>
</div>
);
}
if (!cafeData) {
return (
<div className="p-6 text-center text-gray-500">اطلاعات کافه یافت نشد</div>
);
}
return (
<section dir="rtl" className="w-full overflow-x-hidden">
{editmenu ? (
<EditCafeMenu setEditMenu={setEditMenu} />
) : (
<>
<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">
{/* Cafe Info Section */} {/* Cafe Info Section */}
<div className="flex flex-col lg:flex-row gap-6 mb-8"> <div className="flex flex-col lg:flex-row gap-6 mb-8">
{/* Image */} {/* Image */}
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<img <img
src={cafeData.photo || Bg1} src={cafeData.photo || Bg1}
alt={cafeData.Name} alt={cafeData.Name}
className="w-full lg:w-[300px] xl:w-[400px] rounded-lg object-cover" className="w-full lg:w-[300px] xl:w-[400px] rounded-lg object-cover"
/> />
</div> </div>
{/* Info */} {/* Info */}
<div className="flex-1"> <div className="flex-1">
{/* Action Buttons */} {/* Action Buttons */}
<div className="flex gap-3 justify-end mb-6"> <div className="flex gap-3 justify-end mb-6">
<button <button
onClick={() => navigate("/cafe-management")} onClick={() => navigate("/cafe-management")}
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" 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>
<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 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> </button>
</div>
{/* Basic Info */}
<div className="space-y-4">
<div>
<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>
<div className="flex items-center gap-3 text-[#402E32]">
<GrLocation className="text-lg flex-shrink-0" />
<span className="text-sm md:text-base">{cafeData.address || "آدرس موجود نیست"}</span>
</div>
<div className="flex items-center gap-3 text-[#402E32]">
<FaRegStar className="text-lg flex-shrink-0" />
<span className="text-sm md:text-base">{cafeData.rating || 0}</span>
</div>
<div>
<h3 className="font-bold text-[#402E32] mb-2">درباره کافه</h3>
<p className="text-sm md:text-base text-[#555]">
{cafeData.description || "توضیحاتی برای این کافه وجود ندارد."}
</p>
</div>
</div>
</div>
</div> </div>
{/* Features Section */} {/* Basic Info */}
<div className="mb-8"> <div className="space-y-4">
<h2 className="font-bold text-[#402E32] mb-4 text-base md:text-lg">ویژگی ها</h2> <div>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3"> <h2 className="font-bold text-lg text-[#402E32] mb-2">
{CAFE_FEATURES.map((feature, idx) => ( {cafeData.Name}
<div </h2>
key={idx} <div className="w-16 h-1 bg-[#80931e] rounded-full"></div>
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" </div>
>
<img src={feature.icon} alt={feature.label} className="w-6 h-6" /> <div className="flex items-center gap-3 text-[#402E32]">
<span className="text-[#402E32] font-medium text-xs md:text-sm whitespace-normal"> <GrLocation className="text-lg flex-shrink-0" />
{feature.label} <span className="text-sm md:text-base">
</span> {cafeData.address || "آدرس موجود نیست"}
{feature.value && ( </span>
<span className="text-[#402E32] text-xs">{feature.value}</span> </div>
)}
</div> <div className="flex items-center gap-3 text-[#402E32]">
))} <FaRegStar className="text-lg flex-shrink-0" />
<button className="bg-[#5e5450] p-4 rounded-2xl flex items-center justify-center transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md"> <span className="text-sm md:text-base">
<img src={Vector9} alt="افزودن" className="w-6 h-6" /> {cafeData.rating || 0}
</button> </span>
</div> </div>
</div>
<div>
{/* Categories Section */} <h3 className="font-bold text-[#402E32] mb-2">
<div className="mb-8"> درباره کافه
<div className="flex items-center justify-between mb-4"> </h3>
<h2 className="font-bold text-[#402E32] text-base md:text-lg">دستهبندیها</h2> <p className="text-sm md:text-base text-[#555]">
<button {cafeData.description ||
onClick={() => setIsEditMode(!isEditMode)} "توضیحاتی برای این کافه وجود ندارد."}
className="flex items-center gap-2 text-button1 hover:text-[#5f494f] transition-colors" </p>
title={isEditMode ? "خروج از حالت ویرایش" : "ویرایش دسته‌بندی‌ها"} </div>
>
<img src={Vector16} alt="ویرایش" className="w-5 h-5" />
<span className="text-sm md:text-base">{isEditMode ? "تمام" : "ویرایش"}</span>
</button>
</div>
<div className="categories-scroll flex gap-3 md:gap-4 overflow-x-auto scrollbar-hide pb-2">
{categories.map((category, idx) => (
<div key={idx} className="flex-shrink-0">
{isEditMode && editingIndex === idx ? (
<div className="flex flex-col gap-2">
<div className="flex gap-1">
<IoMdCheckmark
onClick={handleSaveCategory}
className="text-green-600 cursor-pointer hover:text-green-800 w-5 h-5"
title="ذخیره"
/>
<IoMdClose
onClick={handleCancelEdit}
className="text-red-600 cursor-pointer hover:text-red-800 w-5 h-5"
title="لغو"
/>
</div>
<input
type="text"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
className="border-2 border-[#bb8f70] rounded-lg px-2 py-1 text-xs md:text-sm focus:outline-none focus:border-button1 min-w-[100px]"
autoFocus
/>
</div>
) : (
<div className={`flex items-center gap-2 px-3 py-2 rounded-lg text-[#402E32] text-xs md:text-sm whitespace-nowrap ${
isEditMode
? "bg-gray-100 hover:bg-gray-200"
: "bg-[#e1d5c2]"
} transition-colors`}>
<span>{category}</span>
{isEditMode && (
<>
<BiEdit
onClick={() => handleEditCategory(idx)}
className="text-button1 cursor-pointer hover:text-[#5f494f] w-4 h-4 ml-1"
title="ویرایش"
/>
<IoMdClose
onClick={() => handleDeleteCategory(idx)}
className="text-white bg-[#a79fa1] rounded cursor-pointer hover:bg-[#9a8f91] w-4 h-4"
title="حذف"
/>
</>
)}
</div>
)}
</div>
))}
{isEditMode && !isAddingCategory && (
<button
onClick={() => setIsAddingCategory(true)}
className="flex-shrink-0 text-button1 hover:text-[#5f494f] font-bold text-sm md:text-base transition-colors whitespace-nowrap"
>
+ افزودن
</button>
)}
{isEditMode && isAddingCategory && (
<div className="flex-shrink-0 flex flex-col gap-2">
<div className="flex gap-1">
<IoMdCheckmark
onClick={handleAddCategory}
className="text-green-600 cursor-pointer hover:text-green-800 w-5 h-5"
title="ذخیره"
/>
<IoMdClose
onClick={() => {
setIsAddingCategory(false);
setNewCategory("");
}}
className="text-red-600 cursor-pointer hover:text-red-800 w-5 h-5"
title="لغو"
/>
</div>
<input
type="text"
value={newCategory}
onChange={(e) => setNewCategory(e.target.value)}
placeholder="دسته جدید"
className="border-2 border-[#bb8f70] rounded-lg px-2 py-1 text-xs md:text-sm focus:outline-none focus:border-button1 min-w-[100px]"
autoFocus
/>
</div>
)}
</div>
</div>
{/* Products Section */}
<div>
<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">
{CAFE_PRODUCTS.map((product, idx) => (
<div
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"
>
<h3 className="font-bold text-[#402E32] text-sm md:text-base 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">
<span className="text-[#66585b]">قیمت:</span>
<span className="font-medium text-[#402E32]">{product.price}</span>
</div>
<p className="text-xs md:text-sm text-[#66585b] line-clamp-3">
{product.description}
</p>
</div>
))}
</div>
</div> </div>
</div>
</div> </div>
</section>
); <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 */}
<div className="mb-8">
<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">
{CAFE_FEATURES.map((feature, idx) => (
<div
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"
>
<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">
{feature.label}
</span>
{feature.value && (
<span className="text-[#402E32] text-xs">
{feature.value}
</span>
)}
</div>
))}
<button className="bg-[#5e5450] p-4 rounded-2xl flex items-center justify-center transform hover:-translate-y-1 transition-all duration-300 hover:shadow-md">
<img src={Vector9} alt="افزودن" className="w-6 h-6" />
</button>
</div>
</div>
{/* Categories Section */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<h2 className="font-bold text-[#402E32] text-base md:text-lg">
دستهبندیها
</h2>
<button
onClick={() => setIsEditMode(!isEditMode)}
className="flex items-center gap-2 text-button1 hover:text-[#5f494f] transition-colors"
title={
isEditMode ? "خروج از حالت ویرایش" : "ویرایش دسته‌بندی‌ها"
}
>
<img src={Vector16} alt="ویرایش" className="w-5 h-5" />
<span className="text-sm md:text-base">
{isEditMode ? "تمام" : "ویرایش"}
</span>
</button>
</div>
<div className="categories-scroll flex gap-3 md:gap-4 overflow-x-auto scrollbar-hide pb-2">
{categories.map((category, idx) => (
<div key={idx} className="flex-shrink-0">
{isEditMode && editingIndex === idx ? (
<div className="flex flex-col gap-2">
<div className="flex gap-1">
<IoMdCheckmark
onClick={handleSaveCategory}
className="text-green-600 cursor-pointer hover:text-green-800 w-5 h-5"
title="ذخیره"
/>
<IoMdClose
onClick={handleCancelEdit}
className="text-red-600 cursor-pointer hover:text-red-800 w-5 h-5"
title="لغو"
/>
</div>
<input
type="text"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
className="border-2 border-[#bb8f70] rounded-lg px-2 py-1 text-xs md:text-sm focus:outline-none focus:border-button1 min-w-[100px]"
autoFocus
/>
</div>
) : (
<div
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-[#402E32] text-xs md:text-sm whitespace-nowrap ${
isEditMode
? "bg-gray-100 hover:bg-gray-200"
: "bg-[#e1d5c2]"
} transition-colors`}
>
<span>{category}</span>
{isEditMode && (
<>
<BiEdit
onClick={() => handleEditCategory(idx)}
className="text-button1 cursor-pointer hover:text-[#5f494f] w-4 h-4 ml-1"
title="ویرایش"
/>
<IoMdClose
onClick={() => handleDeleteCategory(idx)}
className="text-white bg-[#a79fa1] rounded cursor-pointer hover:bg-[#9a8f91] w-4 h-4"
title="حذف"
/>
</>
)}
</div>
)}
</div>
))}
{isEditMode && !isAddingCategory && (
<button
onClick={() => setIsAddingCategory(true)}
className="flex-shrink-0 text-button1 hover:text-[#5f494f] font-bold text-sm md:text-base transition-colors whitespace-nowrap"
>
+ افزودن
</button>
)}
{isEditMode && isAddingCategory && (
<div className="flex-shrink-0 flex flex-col gap-2">
<div className="flex gap-1">
<IoMdCheckmark
onClick={handleAddCategory}
className="text-green-600 cursor-pointer hover:text-green-800 w-5 h-5"
title="ذخیره"
/>
<IoMdClose
onClick={() => {
setIsAddingCategory(false);
setNewCategory("");
}}
className="text-red-600 cursor-pointer hover:text-red-800 w-5 h-5"
title="لغو"
/>
</div>
<input
type="text"
value={newCategory}
onChange={(e) => setNewCategory(e.target.value)}
placeholder="دسته جدید"
className="border-2 border-[#bb8f70] rounded-lg px-2 py-1 text-xs md:text-sm focus:outline-none focus:border-button1 min-w-[100px]"
autoFocus
/>
</div>
)}
</div>
</div>
{/* Products Section */}
<div>
<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">
{CAFE_PRODUCTS.map((product, idx) => (
<div
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"
>
<h3 className="font-bold text-[#402E32] text-sm md:text-base 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">
<span className="text-[#66585b]">قیمت:</span>
<span className="font-medium text-[#402E32]">
{product.price}
</span>
</div>
<p className="text-xs md:text-sm text-[#66585b] line-clamp-3">
{product.description}
</p>
</div>
))}
</div>
</div>
</div>
</>
)}
</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

@ -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>