edit cofe
This commit is contained in:
parent
5a308dff2a
commit
c748620429
File diff suppressed because it is too large
Load Diff
|
|
@ -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 |
|
|
@ -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="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" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<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">
|
|
||||||
<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 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>
|
||||||
|
|
||||||
|
<img src={Vector7} alt="vector" className="w-6 h-6 lg:w-7 lg:h-7" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src={Vector7} alt="vector" />
|
|
||||||
|
|
||||||
</header >
|
</header >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,25 +89,22 @@ 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}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -101,13 +114,18 @@ export default function Sidebar({ className, isOpen, setIsOpen }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<div className="lg:hidden fixed top-0 px-6 bg-header right-0 left-0 z-[70] flex justify-between p-3">
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
<button
|
||||||
className="lg:hidden fixed top-4 right-4 z-[70] bg-button1 text-white p-2.5 rounded-lg shadow-lg"
|
onClick={() => setIsOpen(true)}
|
||||||
aria-label="Toggle menu"
|
className=" text-primary"
|
||||||
>
|
aria-label="Toggle menu"
|
||||||
{isOpen ? <FiX className="w-5 h-5" /> : <FiMenu className="w-5 h-5" />}
|
>
|
||||||
</button>
|
<FiMenu className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<img src={currentLogo} className="h-12 w-12 " alt="Logo" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -115,17 +133,23 @@ export default function Sidebar({ className, isOpen, setIsOpen }) {
|
||||||
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">
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,60 @@
|
||||||
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}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 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
|
||||||
|
|
|
||||||
|
|
@ -8,180 +8,212 @@ 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) {
|
||||||
if (error) {
|
console.error("❌ Error loading cafes:", error);
|
||||||
return (
|
const errorMessage =
|
||||||
<div className="bg-red-50 border-2 border-red-300 text-red-700 px-4 py-3 rounded-xl mt-4">
|
error.response?.data?.message ||
|
||||||
{error}
|
(error.request
|
||||||
</div>
|
? "خطا در برقراری ارتباط با سرور"
|
||||||
);
|
: "خطای نامشخص رخ داده است");
|
||||||
}
|
setError(errorMessage);
|
||||||
|
} finally {
|
||||||
if (cafes.length === 0) {
|
setLoading(false);
|
||||||
return (
|
}
|
||||||
<div className="text-center mt-20 text-gray-500">
|
|
||||||
هیچ کافهای یافت نشد
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* جدول دسکتاپ */}
|
|
||||||
<div className="hidden lg:block mt-10 overflow-x-auto">
|
|
||||||
<table className="w-full border-collapse">
|
|
||||||
<thead>
|
|
||||||
<tr className="bg-[#EFEEEE]">
|
|
||||||
<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 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 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>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{cafes.map((cafe) => (
|
|
||||||
<tr key={cafe._id} className="border-b border-[#EFEEEE] hover:bg-gray-50 transition-colors">
|
|
||||||
<td className="px-4 py-4 text-right">
|
|
||||||
<img
|
|
||||||
src={cafe.photo || Pic1}
|
|
||||||
alt={cafe.Name}
|
|
||||||
className="w-10 h-10 rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-4 text-right text-[#402E32] font-medium text-sm whitespace-nowrap">
|
|
||||||
{cafe.Name}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-4 text-right text-[#402E32] text-sm max-w-xs overflow-hidden text-ellipsis">
|
|
||||||
{cafe.address}
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-4 text-right">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<img src={Star1} alt="rating" className="w-5 h-5" />
|
|
||||||
<span className="text-[#402E32] text-sm">{cafe.rating || 0}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-4 text-right">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<img src={Group} alt="time" className="w-5 h-5" />
|
|
||||||
<span className="text-[#402E32] text-sm whitespace-nowrap">
|
|
||||||
{cafe.openinghour || "نامشخص"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-4 text-center">
|
|
||||||
<Link
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
<span>ادیت</span>
|
|
||||||
<BiEdit className="w-4 h-4" />
|
|
||||||
</Link>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* کارت موبایل */}
|
|
||||||
<div className="lg:hidden mt-6 space-y-4">
|
|
||||||
{cafes.map((cafe) => (
|
|
||||||
<div key={cafe._id} className="bg-white border-2 border-[#8b8886] rounded-xl p-4">
|
|
||||||
<div className="flex items-start gap-4 mb-4">
|
|
||||||
<img
|
|
||||||
src={cafe.photo || Pic1}
|
|
||||||
alt={cafe.Name}
|
|
||||||
className="w-16 h-16 rounded-full object-cover flex-shrink-0"
|
|
||||||
/>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<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 gap-1">
|
|
||||||
<img src={Star1} alt="rating" className="w-4 h-4" />
|
|
||||||
<span className="text-[#402E32]">{cafe.rating || 0}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<img src={Group} alt="time" className="w-4 h-4" />
|
|
||||||
<span className="text-[#402E32]">{cafe.openinghour || "نامشخص"}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
<span>ادیت کافه</span>
|
|
||||||
<BiEdit className="w-4 h-4" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
<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="hidden lg:block mt-10 overflow-x-auto">
|
||||||
<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">
|
<table className="w-full border-collapse">
|
||||||
<span>افزودن شعبه جدید</span>
|
<thead>
|
||||||
<img src={Vector9} alt="افزودن" className="w-5 h-5" />
|
<tr className="bg-thead">
|
||||||
</button>
|
<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>
|
||||||
|
<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>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{cafes.map((cafe) => (
|
||||||
|
<tr
|
||||||
|
key={cafe._id}
|
||||||
|
className="border-b border-[#EFEEEE] hover:bg-hover transition-colors"
|
||||||
|
>
|
||||||
|
<td className="px-4 py-4 text-right">
|
||||||
|
<img
|
||||||
|
src={cafe.photo || Pic1}
|
||||||
|
alt={cafe.Name}
|
||||||
|
className="w-10 h-10 rounded-full object-cover"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-4 text-right text-[#402E32] font-medium text-sm whitespace-nowrap">
|
||||||
|
{cafe.Name}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-4 text-right text-[#402E32] text-sm max-w-xs overflow-hidden text-ellipsis">
|
||||||
|
{cafe.address}
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-4 text-right">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<img src={Star1} alt="rating" className="w-5 h-5" />
|
||||||
|
<span className="text-[#402E32] text-sm">
|
||||||
|
{cafe.rating || 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-4 text-right">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<img src={Group} alt="time" className="w-5 h-5" />
|
||||||
|
<span className="text-[#402E32] text-sm whitespace-nowrap">
|
||||||
|
{cafe.openinghour || "نامشخص"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-4 text-center">
|
||||||
|
<Link
|
||||||
|
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-text1 hover:bg-button1 hover:border-button1 transition-all duration-300"
|
||||||
|
>
|
||||||
|
<span>ادیت</span>
|
||||||
|
<BiEdit className="w-4 h-4" />
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* کارت موبایل */}
|
||||||
|
<div className="lg:hidden mt-6 space-y-4">
|
||||||
|
{cafes.map((cafe) => (
|
||||||
|
<div
|
||||||
|
key={cafe._id}
|
||||||
|
className="bg-white border-2 border-[#8b8886] rounded-xl p-4"
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-4 mb-4">
|
||||||
|
<img
|
||||||
|
src={cafe.photo || Pic1}
|
||||||
|
alt={cafe.Name}
|
||||||
|
className="w-16 h-16 rounded-full object-cover flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<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 gap-1">
|
||||||
|
<img src={Star1} alt="rating" className="w-4 h-4" />
|
||||||
|
<span className="text-[#402E32]">{cafe.rating || 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<img src={Group} alt="time" className="w-4 h-4" />
|
||||||
|
<span className="text-[#402E32]">
|
||||||
|
{cafe.openinghour || "نامشخص"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
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-text1 hover:bg-button1 hover:border-button1 transition-all duration-300"
|
||||||
|
>
|
||||||
|
<span className="">ادیت کافه</span>
|
||||||
|
<BiEdit className="w-4 h-4" />
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
{/* عنوان */}
|
</div>
|
||||||
<h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">کافه های شما</h1>
|
</>
|
||||||
|
|
||||||
{/* محتوای اصلی */}
|
|
||||||
{renderContent()}
|
|
||||||
</section>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section dir="rtl" className="w-full pt-24 max-w-full overflow-x-hidden">
|
||||||
|
{/* بخش دکمه اضافه کردن */}
|
||||||
|
<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-hover2 transition-all duration-300 cursor-pointer">
|
||||||
|
<span>افزودن شعبه جدید</span>
|
||||||
|
<img src={Vector9} alt="افزودن" className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* عنوان */}
|
||||||
|
<h1 className="text-[#402E32] font-bold text-lg lg:text-xl mb-6">
|
||||||
|
کافه های شما
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* محتوای اصلی */}
|
||||||
|
{renderContent()}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CafeManagement;
|
export default CafeManagement;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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) */
|
||||||
|
|
@ -37,9 +56,9 @@ input:-webkit-autofill:focus,
|
||||||
textarea:-webkit-autofill,
|
textarea:-webkit-autofill,
|
||||||
textarea:-webkit-autofill:focus,
|
textarea:-webkit-autofill:focus,
|
||||||
select:-webkit-autofill {
|
select:-webkit-autofill {
|
||||||
-webkit-box-shadow: 0 0 0 1000px #fdf8f4 inset !important;
|
-webkit-box-shadow: 0 0 0 1000px #fdf8f4 inset !important;
|
||||||
box-shadow: 0 0 0 1000px #fdf8f4 inset !important;
|
box-shadow: 0 0 0 1000px #fdf8f4 inset !important;
|
||||||
-webkit-text-fill-color: inherit !important;
|
-webkit-text-fill-color: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark input:-webkit-autofill,
|
.dark input:-webkit-autofill,
|
||||||
|
|
@ -48,13 +67,45 @@ select:-webkit-autofill {
|
||||||
.dark textarea:-webkit-autofill,
|
.dark textarea:-webkit-autofill,
|
||||||
.dark textarea:-webkit-autofill:focus,
|
.dark textarea:-webkit-autofill:focus,
|
||||||
.dark select:-webkit-autofill {
|
.dark select:-webkit-autofill {
|
||||||
-webkit-box-shadow: 0 0 0 1000px #272322 inset !important;
|
-webkit-box-shadow: 0 0 0 1000px #272322 inset !important;
|
||||||
box-shadow: 0 0 0 1000px #272322 inset !important;
|
box-shadow: 0 0 0 1000px #272322 inset !important;
|
||||||
-webkit-text-fill-color: inherit !important;
|
-webkit-text-fill-color: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
/* رنگ متن انتخاب */
|
/* رنگ متن انتخاب */
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue