feat: drive shop "service description" button/content from backend
Replaces the hardcoded DNS-only content and isDns/category-label check with data from the config's `has_extra_description` / `extra_description` fields, so any product can opt into showing free-text extra info. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,10 +53,7 @@ const ProductList = () => {
|
|||||||
activeId={activeId}
|
activeId={activeId}
|
||||||
setActiveId={setActiveId}
|
setActiveId={setActiveId}
|
||||||
/>
|
/>
|
||||||
<ListAllProduct
|
<ListAllProduct activeId={activeId} />
|
||||||
activeId={activeId}
|
|
||||||
categories={data?.data?.result?.categories}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,25 +9,11 @@ import { RootState } from "../../../../../redux/store";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
interface Category {
|
const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
||||||
id: number | string;
|
|
||||||
label?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListAllProduct = ({
|
|
||||||
activeId,
|
|
||||||
categories,
|
|
||||||
}: {
|
|
||||||
activeId: number;
|
|
||||||
categories?: Category[];
|
|
||||||
}) => {
|
|
||||||
const axios = useAxios();
|
const axios = useAxios();
|
||||||
const [selectedConfigId, setSelectedConfigId] = useState<number | null>(null);
|
const [selectedConfigId, setSelectedConfigId] = useState<number | null>(null);
|
||||||
const { token } = useSelector((state: RootState) => state.token);
|
const { token } = useSelector((state: RootState) => state.token);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const isDnsCategory =
|
|
||||||
categories?.find((category) => Number(category.id) === activeId)?.label?.toLowerCase() ===
|
|
||||||
"dns";
|
|
||||||
const { data, error, isLoading, isFetching } = useQuery({
|
const { data, error, isLoading, isFetching } = useQuery({
|
||||||
queryKey: ["configs", activeId],
|
queryKey: ["configs", activeId],
|
||||||
queryFn: () => {
|
queryFn: () => {
|
||||||
@@ -84,7 +70,6 @@ const ListAllProduct = ({
|
|||||||
isOpen={selectedConfigId !== null}
|
isOpen={selectedConfigId !== null}
|
||||||
setIsOpen={(open) => { if (!open) setSelectedConfigId(null); }}
|
setIsOpen={(open) => { if (!open) setSelectedConfigId(null); }}
|
||||||
configId={selectedConfigId}
|
configId={selectedConfigId}
|
||||||
isDns={isDnsCategory}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
buildAdvancedVerificationMessage,
|
buildAdvancedVerificationMessage,
|
||||||
} from "../../../../advancedVerification/constants";
|
} from "../../../../advancedVerification/constants";
|
||||||
|
|
||||||
const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
||||||
const axios = useAxios();
|
const axios = useAxios();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -54,6 +54,10 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
|||||||
);
|
);
|
||||||
const advancedVerificationMessage =
|
const advancedVerificationMessage =
|
||||||
buildAdvancedVerificationMessage(advancedVerificationLimit);
|
buildAdvancedVerificationMessage(advancedVerificationLimit);
|
||||||
|
const hasExtraDescription = Boolean(
|
||||||
|
data?.data?.result?.has_extra_description
|
||||||
|
);
|
||||||
|
const extraDescription = data?.data?.result?.extra_description ?? "";
|
||||||
const advancedVerificationFromApi =
|
const advancedVerificationFromApi =
|
||||||
data?.data?.result?.requires_advanced_verification;
|
data?.data?.result?.requires_advanced_verification;
|
||||||
const hasAdvancedVerificationApiFlag =
|
const hasAdvancedVerificationApiFlag =
|
||||||
@@ -239,7 +243,11 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ModalDnsServiceInfo isOpen={isInfoOpen} setIsOpen={setIsInfoOpen} />
|
<ModalDnsServiceInfo
|
||||||
|
isOpen={isInfoOpen}
|
||||||
|
setIsOpen={setIsInfoOpen}
|
||||||
|
description={extraDescription}
|
||||||
|
/>
|
||||||
<MuiModal
|
<MuiModal
|
||||||
isOpenModal={isOpen}
|
isOpenModal={isOpen}
|
||||||
setIsOpenModal={setIsOpen}
|
setIsOpenModal={setIsOpen}
|
||||||
@@ -293,7 +301,7 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full h-full flex flex-col gap-3 justify-between items-center text-sm md:text-xl">
|
<div className="w-full h-full flex flex-col gap-3 justify-between items-center text-sm md:text-xl">
|
||||||
{isDns && (
|
{hasExtraDescription && (
|
||||||
<Button
|
<Button
|
||||||
extra="!m-0 !w-full !py-1.5 !bg-gray-800 !text-secondTheme border border-secondTheme/40 flex flex-row-reverse justify-center items-center gap-2 shadow-md text-sm md:text-base"
|
extra="!m-0 !w-full !py-1.5 !bg-gray-800 !text-secondTheme border border-secondTheme/40 flex flex-row-reverse justify-center items-center gap-2 shadow-md text-sm md:text-base"
|
||||||
onClick={() => setIsInfoOpen(true)}
|
onClick={() => setIsInfoOpen(true)}
|
||||||
|
|||||||
@@ -4,136 +4,25 @@ import MuiModal from "../../../../../common/modal";
|
|||||||
interface ModalDnsServiceInfoProps {
|
interface ModalDnsServiceInfoProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GAMES = [
|
const ModalDnsServiceInfo = ({
|
||||||
"Valorant",
|
isOpen,
|
||||||
"Call of Duty: Warzone",
|
setIsOpen,
|
||||||
"EA SPORTS FC 25",
|
description,
|
||||||
"EA SPORTS FC 26",
|
}: ModalDnsServiceInfoProps) => {
|
||||||
"PUBG Mobile",
|
|
||||||
"PUBG: Battlegrounds (PC)",
|
|
||||||
"Call of Duty: Mobile (CODM)",
|
|
||||||
"Fortnite",
|
|
||||||
"League of Legends (LoL)",
|
|
||||||
"Clash Royale",
|
|
||||||
"Clash of Clans",
|
|
||||||
];
|
|
||||||
|
|
||||||
const SERVICES = [
|
|
||||||
"ChatGPT",
|
|
||||||
"Discord",
|
|
||||||
"Spotify",
|
|
||||||
"NVIDIA (App & Services)",
|
|
||||||
"GitHub، Docker، NPM، PyPI و بسیاری از ابزارها و سرویسهای توسعه (Developer Services)",
|
|
||||||
];
|
|
||||||
|
|
||||||
const STREAMING = ["YouTube", "Twitch", "Kick"];
|
|
||||||
|
|
||||||
const BENEFITS = [
|
|
||||||
"بدون نیاز به VPN",
|
|
||||||
"راهاندازی در کمتر از یک دقیقه",
|
|
||||||
"مناسب برای کامپیوتر، موبایل و کنسول بازی",
|
|
||||||
"پشتیبانی و بروزرسانی مداوم سرویسها",
|
|
||||||
];
|
|
||||||
|
|
||||||
const Section = ({
|
|
||||||
emoji,
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
emoji: string;
|
|
||||||
title: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) => (
|
|
||||||
<div className="w-full flex flex-col gap-2 bg-gray-900/60 border border-gray-700 rounded-lg p-3">
|
|
||||||
<div className="flex flex-row-reverse items-center gap-1.5 text-secondTheme font-bold">
|
|
||||||
<span>{emoji}</span>
|
|
||||||
<span>{title}</span>
|
|
||||||
</div>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const ModalDnsServiceInfo = ({ isOpen, setIsOpen }: ModalDnsServiceInfoProps) => {
|
|
||||||
return (
|
return (
|
||||||
<MuiModal
|
<MuiModal
|
||||||
isOpenModal={isOpen}
|
isOpenModal={isOpen}
|
||||||
setIsOpenModal={setIsOpen}
|
setIsOpenModal={setIsOpen}
|
||||||
title="توضیحات سرویس"
|
title="توضیحات سرویس"
|
||||||
nameService="DNS"
|
nameService="توضیحات سرویس"
|
||||||
>
|
>
|
||||||
<div className="w-full h-full flex flex-col gap-3 max-sm:text-xs max-md:text-sm text-base">
|
<div className="w-full h-full flex flex-col gap-3 max-sm:text-xs max-md:text-sm text-base">
|
||||||
<Section emoji="🎮" title="بازیهای پشتیبانیشده">
|
<p className="w-full whitespace-pre-line leading-7 text-gray-200 text-start">
|
||||||
<ul className="flex flex-col gap-1.5">
|
{description}
|
||||||
{GAMES.map((game) => (
|
</p>
|
||||||
<li
|
|
||||||
key={game}
|
|
||||||
className="flex flex-row-reverse items-center gap-2 text-gray-200"
|
|
||||||
>
|
|
||||||
<span>🔹</span>
|
|
||||||
<span dir="ltr" className="text-start">
|
|
||||||
{game}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section emoji="💻" title="سرویسها و پلتفرمها">
|
|
||||||
<ul className="flex flex-col gap-1.5">
|
|
||||||
{SERVICES.map((service) => (
|
|
||||||
<li
|
|
||||||
key={service}
|
|
||||||
className="flex flex-row-reverse items-start gap-2 text-gray-200"
|
|
||||||
>
|
|
||||||
<span className="shrink-0">✔️</span>
|
|
||||||
<span className="text-start leading-6">{service}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section emoji="📺" title="سرویسهای استریم">
|
|
||||||
<ul className="flex flex-col gap-1.5">
|
|
||||||
{STREAMING.map((item) => (
|
|
||||||
<li
|
|
||||||
key={item}
|
|
||||||
className="flex flex-row-reverse items-center justify-between gap-2 text-gray-200"
|
|
||||||
>
|
|
||||||
<div className="flex flex-row-reverse items-center gap-2">
|
|
||||||
<span>▶️</span>
|
|
||||||
<span dir="ltr">{item}</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-[10px] md:text-xs text-yellow-300/90 whitespace-nowrap">
|
|
||||||
فقط روی اینترنت خانگی
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section emoji="⚡️" title="مزایای سرویس">
|
|
||||||
<ul className="flex flex-col gap-1.5">
|
|
||||||
{BENEFITS.map((benefit) => (
|
|
||||||
<li
|
|
||||||
key={benefit}
|
|
||||||
className="flex flex-row-reverse items-center gap-2 text-gray-200"
|
|
||||||
>
|
|
||||||
<span>✅</span>
|
|
||||||
<span>{benefit}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<div className="w-full flex flex-row-reverse items-start gap-2 bg-secondTheme/10 border border-secondTheme/30 text-secondTheme rounded-lg p-3">
|
|
||||||
<span className="shrink-0">📌</span>
|
|
||||||
<span className="text-start leading-6">
|
|
||||||
بازی و سرویسهای جدید بهصورت مداوم به DNS اضافه میشوند و این
|
|
||||||
لیست همواره در حال بروزرسانی است.
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</MuiModal>
|
</MuiModal>
|
||||||
);
|
);
|
||||||
|
|||||||
1
src/types/props.d.ts
vendored
1
src/types/props.d.ts
vendored
@@ -203,7 +203,6 @@ interface ModalBuyProps {
|
|||||||
// unknown
|
// unknown
|
||||||
// >;
|
// >;
|
||||||
configId: number;
|
configId: number;
|
||||||
isDns?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BuyConfigDataProps {
|
interface BuyConfigDataProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user