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}
|
||||
setActiveId={setActiveId}
|
||||
/>
|
||||
<ListAllProduct
|
||||
activeId={activeId}
|
||||
categories={data?.data?.result?.categories}
|
||||
/>
|
||||
<ListAllProduct activeId={activeId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,25 +9,11 @@ import { RootState } from "../../../../../redux/store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface Category {
|
||||
id: number | string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
const ListAllProduct = ({
|
||||
activeId,
|
||||
categories,
|
||||
}: {
|
||||
activeId: number;
|
||||
categories?: Category[];
|
||||
}) => {
|
||||
const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
||||
const axios = useAxios();
|
||||
const [selectedConfigId, setSelectedConfigId] = useState<number | null>(null);
|
||||
const { token } = useSelector((state: RootState) => state.token);
|
||||
const navigate = useNavigate();
|
||||
const isDnsCategory =
|
||||
categories?.find((category) => Number(category.id) === activeId)?.label?.toLowerCase() ===
|
||||
"dns";
|
||||
const { data, error, isLoading, isFetching } = useQuery({
|
||||
queryKey: ["configs", activeId],
|
||||
queryFn: () => {
|
||||
@@ -84,7 +70,6 @@ const ListAllProduct = ({
|
||||
isOpen={selectedConfigId !== null}
|
||||
setIsOpen={(open) => { if (!open) setSelectedConfigId(null); }}
|
||||
configId={selectedConfigId}
|
||||
isDns={isDnsCategory}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
buildAdvancedVerificationMessage,
|
||||
} from "../../../../advancedVerification/constants";
|
||||
|
||||
const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
||||
const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
||||
const axios = useAxios();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
@@ -54,6 +54,10 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
||||
);
|
||||
const advancedVerificationMessage =
|
||||
buildAdvancedVerificationMessage(advancedVerificationLimit);
|
||||
const hasExtraDescription = Boolean(
|
||||
data?.data?.result?.has_extra_description
|
||||
);
|
||||
const extraDescription = data?.data?.result?.extra_description ?? "";
|
||||
const advancedVerificationFromApi =
|
||||
data?.data?.result?.requires_advanced_verification;
|
||||
const hasAdvancedVerificationApiFlag =
|
||||
@@ -239,7 +243,11 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalDnsServiceInfo isOpen={isInfoOpen} setIsOpen={setIsInfoOpen} />
|
||||
<ModalDnsServiceInfo
|
||||
isOpen={isInfoOpen}
|
||||
setIsOpen={setIsInfoOpen}
|
||||
description={extraDescription}
|
||||
/>
|
||||
<MuiModal
|
||||
isOpenModal={isOpen}
|
||||
setIsOpenModal={setIsOpen}
|
||||
@@ -293,7 +301,7 @@ const ModalBuy = ({ isOpen, setIsOpen, configId, isDns }: ModalBuyProps) => {
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col gap-3 justify-between items-center text-sm md:text-xl">
|
||||
{isDns && (
|
||||
{hasExtraDescription && (
|
||||
<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"
|
||||
onClick={() => setIsInfoOpen(true)}
|
||||
|
||||
@@ -4,136 +4,25 @@ import MuiModal from "../../../../../common/modal";
|
||||
interface ModalDnsServiceInfoProps {
|
||||
isOpen: boolean;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
description?: string | null;
|
||||
}
|
||||
|
||||
const GAMES = [
|
||||
"Valorant",
|
||||
"Call of Duty: Warzone",
|
||||
"EA SPORTS FC 25",
|
||||
"EA SPORTS FC 26",
|
||||
"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) => {
|
||||
const ModalDnsServiceInfo = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
description,
|
||||
}: ModalDnsServiceInfoProps) => {
|
||||
return (
|
||||
<MuiModal
|
||||
isOpenModal={isOpen}
|
||||
setIsOpenModal={setIsOpen}
|
||||
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">
|
||||
<Section emoji="🎮" title="بازیهای پشتیبانیشده">
|
||||
<ul className="flex flex-col gap-1.5">
|
||||
{GAMES.map((game) => (
|
||||
<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>
|
||||
<p className="w-full whitespace-pre-line leading-7 text-gray-200 text-start">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</MuiModal>
|
||||
);
|
||||
|
||||
1
src/types/props.d.ts
vendored
1
src/types/props.d.ts
vendored
@@ -203,7 +203,6 @@ interface ModalBuyProps {
|
||||
// unknown
|
||||
// >;
|
||||
configId: number;
|
||||
isDns?: boolean;
|
||||
}
|
||||
|
||||
interface BuyConfigDataProps {
|
||||
|
||||
Reference in New Issue
Block a user