From 95e05cbabb74a2791fa533e8761325d11d8ea4f7 Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Tue, 28 Oct 2025 02:06:45 +0100 Subject: [PATCH] add video guide to dashboard --- messages/de.json | 38 +-- messages/en.json | 47 +--- messages/es.json | 38 +-- src/app/[locale]/(core)/page.tsx | 5 + .../VideoGuidesOffer.module.scss | 38 +-- .../VideoGuidesOffer/VideoGuidesOffer.tsx | 54 ++-- .../VideoGuidesOffers/VideoGuidesOffers.tsx | 3 +- .../VideoGuideCard/VideoGuideCard.module.scss | 236 ++++++++++++++++++ .../cards/VideoGuideCard/VideoGuideCard.tsx | 102 ++++++++ .../dashboard/cards/VideoGuideCard/index.ts | 1 + .../domains/dashboard/cards/index.ts | 1 + .../AdvisersSection/AdvisersSection.tsx | 7 +- .../CompatibilitySection.tsx | 7 +- .../MeditationSection/MeditationSection.tsx | 7 +- .../sections/PalmSection/PalmSection.tsx | 7 +- .../VideoGuidesSection.module.scss | 12 + .../VideoGuidesSection/VideoGuidesSection.tsx | 38 +++ .../sections/VideoGuidesSection/index.ts | 1 + .../domains/dashboard/sections/index.ts | 1 + src/entities/dashboard/loaders.ts | 11 +- src/entities/dashboard/types.ts | 25 +- src/entities/session/funnel/types.ts | 3 + 22 files changed, 510 insertions(+), 172 deletions(-) create mode 100644 src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.module.scss create mode 100644 src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.tsx create mode 100644 src/components/domains/dashboard/cards/VideoGuideCard/index.ts create mode 100644 src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.module.scss create mode 100644 src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.tsx create mode 100644 src/components/domains/dashboard/sections/VideoGuidesSection/index.ts diff --git a/messages/de.json b/messages/de.json index 4c19c40..49c3b33 100644 --- a/messages/de.json +++ b/messages/de.json @@ -305,43 +305,7 @@ "button": "Continue", "skip_button": "Skip this offer and proceed further", "copyright": "© 2025, Wit Lab LLC, California, US", - "products": { - "main_ultra_pack": { - "title": "Ultra Pack", - "subtitle": "3 in 1+2 secret bonus readings", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "star_struck.webp" - }, - "main_numerology_analysis": { - "title": "Relationship plan", - "subtitle": "Discover the future without losing yourself", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "ring.webp" - }, - "main_tarot_reading": { - "title": "Healthy compatibility", - "subtitle": "Balance between closeness and freedom", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "rose.webp" - }, - "main_palmistry_guide": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - }, - "main_money_reading": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - } - }, + "now": "Now", "payment_error": "Something went wrong. Please try again later.", "select_product_error": "Please select a product" } diff --git a/messages/en.json b/messages/en.json index 94d7dd2..4ad28d3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -312,43 +312,16 @@ "button": "Continue", "skip_button": "Skip this offer and proceed further", "copyright": "© 2025, Wit Lab LLC, California, US", - "products": { - "main_ultra_pack": { - "title": "Ultra Pack", - "subtitle": "3 in 1+2 secret bonus readings", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "star_struck.webp" - }, - "main_numerology_analysis": { - "title": "Relationship plan", - "subtitle": "Discover the future without losing yourself", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "ring.webp" - }, - "main_tarot_reading": { - "title": "Healthy compatibility", - "subtitle": "Balance between closeness and freedom", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "rose.webp" - }, - "main_palmistry_guide": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - }, - "main_money_reading": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - } - } + "now": "Now" + } + }, + "Dashboard": { + "adviser": { + "title": "Talk to an Astrologer" + }, + "videoGuides": { + "now": "Now", + "purchaseFor": "Buy for {price}" } }, "Chat": { diff --git a/messages/es.json b/messages/es.json index fefa091..614f564 100644 --- a/messages/es.json +++ b/messages/es.json @@ -305,43 +305,7 @@ "button": "Continue", "skip_button": "Skip this offer and proceed further", "copyright": "© 2025, Wit Lab LLC, California, US", - "products": { - "main_ultra_pack": { - "title": "Ultra Pack", - "subtitle": "3 in 1+2 secret bonus readings", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "star_struck.webp" - }, - "main_numerology_analysis": { - "title": "Relationship plan", - "subtitle": "Discover the future without losing yourself", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "ring.webp" - }, - "main_tarot_reading": { - "title": "Healthy compatibility", - "subtitle": "Balance between closeness and freedom", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "rose.webp" - }, - "main_palmistry_guide": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - }, - "main_money_reading": { - "title": "How to talk about feelings", - "subtitle": "Express your emotions and be understood", - "discount": "{discount}% OFF", - "price": "Now ", - "emoji": "heart_from_hands.webp" - } - }, + "now": "Now", "payment_error": "Something went wrong. Please try again later.", "select_product_error": "Please select a product" } diff --git a/src/app/[locale]/(core)/page.tsx b/src/app/[locale]/(core)/page.tsx index 4f4e81f..898b6e8 100644 --- a/src/app/[locale]/(core)/page.tsx +++ b/src/app/[locale]/(core)/page.tsx @@ -11,6 +11,7 @@ import { PalmSection, PalmSectionSkeleton, PortraitsSection, + VideoGuidesSection, } from "@/components/domains/dashboard"; import { loadChatsList } from "@/entities/chats/loaders"; import { @@ -19,6 +20,7 @@ import { loadMeditations, loadPalms, loadPortraits, + loadVideoGuides, } from "@/entities/dashboard/loaders"; import styles from "./page.module.scss"; @@ -26,11 +28,14 @@ import styles from "./page.module.scss"; export default async function Home() { const chatsPromise = loadChatsList(); const portraits = await loadPortraits(); + const videoGuides = await loadVideoGuides(); return (
+ + }> diff --git a/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.module.scss b/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.module.scss index 67878d4..aa1012a 100644 --- a/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.module.scss +++ b/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.module.scss @@ -7,6 +7,25 @@ box-shadow: 0px 0px 30px 0px #0000001f; gap: 4px; + & > .topBadge { + position: absolute; + top: -15px; + right: 49px; + background: #ff3737; + box-shadow: 0px 1px 11.98px 0px #ff44448c; + border: 2px solid #ffffff4d; + padding: 8px 12px; + border-radius: 9999px; + z-index: 10; + + & > .topBadgeText { + color: #fff; + font-size: 14px; + font-weight: 800; + letter-spacing: 0.5px; + } + } + & > .content { display: grid; grid-template-columns: 40px 1fr 20px; @@ -109,23 +128,4 @@ } } } - - &.main_ultra_pack { - & > .footer { - & > .discount { - position: absolute; - top: -15px; - right: 49px; - background: #ff3737; - box-shadow: 0px 1px 11.98px 0px #ff44448c; - border: 2px solid #ffffff4d; - padding: 8px 12px; - color: #fff; - font-size: 14px; - font-weight: 800; - letter-spacing: 0.5px; - margin-right: 0; - } - } - } } diff --git a/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.tsx b/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.tsx index 2ec300c..9ff7d27 100644 --- a/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.tsx +++ b/src/components/domains/additional-purchases/VideoGuidesOffer/VideoGuidesOffer.tsx @@ -11,30 +11,25 @@ import styles from "./VideoGuidesOffer.module.scss"; interface VideoGuidesOfferProps { offer: IFunnelPaymentVariant; isActive: boolean; + isFirstOffer?: boolean; className?: string; onClick: () => void; } export default function VideoGuidesOffer(props: VideoGuidesOfferProps) { - const { offer, isActive, className, onClick } = props; + const { offer, isActive, isFirstOffer, className, onClick } = props; - const { key, price, oldPrice } = offer; + const { key, name, description, emoji, price, oldPrice } = offer; const productKey = key.replaceAll(".", "_"); - const t = useTranslations( - `AdditionalPurchases.video-guides.products.${productKey}` - ); - const currency = Currency.USD; - const subtitle = t.has("subtitle") ? t("subtitle") : undefined; - const discount = Math.round( (((oldPrice || 0) - price) / (oldPrice || 0)) * 100 ); - const emoji = t.has("emoji") ? t("emoji") : undefined; + const t = useTranslations("AdditionalPurchases.video-guides"); return ( + {isFirstOffer && ( +
+ {discount}% OFF +
+ )}
- {t("title")} + {name} - {subtitle && ( + {description && ( - {subtitle} + {description} )}
@@ -76,24 +76,20 @@ export default function VideoGuidesOffer(props: VideoGuidesOfferProps) {
- {t.rich("price", { - price: () => ( - - {getFormattedPrice(price, currency)} - - ), - oldPrice: () => ( - - {getFormattedPrice(oldPrice || 0, currency)} - - ), - })} - - - {t("discount", { - discount: discount || 0, - })} + {t("now")}{" "} + + {getFormattedPrice(price, currency)} + + {" "} + + {getFormattedPrice(oldPrice || 0, currency)} + + {!isFirstOffer && ( + + {discount}% OFF + + )}
); diff --git a/src/components/domains/additional-purchases/VideoGuidesOffers/VideoGuidesOffers.tsx b/src/components/domains/additional-purchases/VideoGuidesOffers/VideoGuidesOffers.tsx index 3b972d0..7bc3d3c 100644 --- a/src/components/domains/additional-purchases/VideoGuidesOffers/VideoGuidesOffers.tsx +++ b/src/components/domains/additional-purchases/VideoGuidesOffers/VideoGuidesOffers.tsx @@ -49,11 +49,12 @@ export default function VideoGuidesOffers() { return (
- {offers.map(offer => ( + {offers.map((offer, index) => ( handleOfferClick(offer)} /> ))} diff --git a/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.module.scss b/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.module.scss new file mode 100644 index 0000000..96a3e87 --- /dev/null +++ b/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.module.scss @@ -0,0 +1,236 @@ +.container.container { + display: flex; + min-width: 260px; + min-height: 280px; + flex-direction: column; + align-items: flex-start; + border-radius: 24px; + border: 0 solid #E5E7EB; + background: rgba(0, 0, 0, 0); + box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.10), 0 10px 15px 0 rgba(0, 0, 0, 0.10); + cursor: pointer; + overflow: hidden; + padding: 0; + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.12), 0 12px 18px 0 rgba(0, 0, 0, 0.12); + } +} + +// Image section +.image { + display: flex; + min-height: 160px; + padding: 16px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + align-self: stretch; + border: 0 solid #E5E7EB; + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + inset: 0; + background: lightgray 50% / cover no-repeat; + z-index: 0; + } + + .imageContent { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + pointer-events: none; + z-index: 0; + } + + .playIcon { + position: relative; + z-index: 1; + width: 64px; + height: 65px; + + svg { + width: 64px; + height: 65px; + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.25)); + } + } +} + +// Content section +.content { + display: flex; + padding: 16px; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: 24px; + align-self: stretch; + background: #FFF; + flex: 1; + + .purchased & { + gap: 6px; + } +} + +// Top section +.top { + display: flex; + align-items: flex-start; + gap: 14px; + align-self: stretch; + + .text { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 3px; + flex: 1 0 0; + } + + .arrowButton { + display: flex; + width: 40px; + height: 40px; + padding: 12px 0; + justify-content: center; + align-items: center; + border-radius: 9999px; + border: 0 solid #E5E7EB; + background: #F5F5F7; + cursor: pointer; + transition: opacity 0.2s ease; + + svg { + width: 8px; + height: 14px; + flex-shrink: 0; + } + + &:hover { + opacity: 0.8; + } + } +} + +.title { + align-self: stretch; + color: #1D1D1F; + font-family: Inter, sans-serif; + font-size: 20px; + font-style: normal; + font-weight: 500; + line-height: 28px; + text-align: left; +} + +.subtitle { + align-self: stretch; + color: #6B7280; + font-family: Inter, sans-serif; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 20px; + text-align: left; +} + +// Bottom section +.bottom { + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-end; + gap: 8px; + align-self: stretch; +} + +.bottomText { + display: flex; + height: 24px; + justify-content: space-between; + align-items: center; + align-self: stretch; +} + +.duration { + display: flex; + width: 49px; + flex-direction: column; + justify-content: center; + align-self: stretch; + color: #6B7280; + font-family: Inter, sans-serif; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 20px; +} + +.discountBadge { + display: flex; + padding: 6px 10px; + justify-content: center; + align-items: center; + gap: 10px; + align-self: stretch; + border-radius: 9999px; + border: 0 solid #E5E7EB; + background: rgba(255, 107, 107, 0.10); +} + +.discountText { + color: #FF6B6B; + text-align: center; + font-family: Inter, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: normal; + + .oldPrice { + color: #8B8B8B; + font-family: Inter, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + text-decoration-line: line-through; + } +} + +.buyButton.buyButton { + display: flex; + padding: 8px 10px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 0 solid #E5E7EB; + background: #2563EB; + cursor: pointer; + transition: opacity 0.2s ease; + width: auto; + + color: #FFF; + text-align: center; + font-family: Inter, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; + + &:hover { + opacity: 0.9; + } +} diff --git a/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.tsx b/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.tsx new file mode 100644 index 0000000..2361c9a --- /dev/null +++ b/src/components/domains/dashboard/cards/VideoGuideCard/VideoGuideCard.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import clsx from "clsx"; + +import { Button, Card, Typography } from "@/components/ui"; +import { getFormattedPrice } from "@/shared/utils/price"; +import { Currency } from "@/types"; + +import styles from "./VideoGuideCard.module.scss"; + +interface VideoGuideCardProps { + name: string; + description: string; + imageUrl: string; + duration: string; + price: number; + oldPrice: number; + discount: number; + isPurchased: boolean; + className?: string; +} + +export default function VideoGuideCard(props: VideoGuideCardProps) { + const { name, description, imageUrl, duration, price, oldPrice, discount, isPurchased, className } = props; + + const tCommon = useTranslations("Dashboard.videoGuides"); + + const currency = Currency.USD; + + const handleClick = () => { + // TODO: Implement navigation or purchase logic + console.log("Video guide clicked", name); + }; + + return ( + + {/* Image with Play Icon */} +
+ {name} +
+ + + + + + + + + + + + + + + + + +
+
+ + {/* Content */} +
+ {/* Top Section */} +
+
+ + {name} + + + {description} + +
+ +
+ + {/* Bottom Section */} +
+
+ {duration} + {!isPurchased && ( +
+ + {discount}% OFF {getFormattedPrice(oldPrice, currency)} + +
+ )} +
+ {!isPurchased && ( + + )} +
+
+
+ ); +} diff --git a/src/components/domains/dashboard/cards/VideoGuideCard/index.ts b/src/components/domains/dashboard/cards/VideoGuideCard/index.ts new file mode 100644 index 0000000..e1d5d96 --- /dev/null +++ b/src/components/domains/dashboard/cards/VideoGuideCard/index.ts @@ -0,0 +1 @@ +export { default as VideoGuideCard } from "./VideoGuideCard"; diff --git a/src/components/domains/dashboard/cards/index.ts b/src/components/domains/dashboard/cards/index.ts index 65540f0..1a27805 100644 --- a/src/components/domains/dashboard/cards/index.ts +++ b/src/components/domains/dashboard/cards/index.ts @@ -3,3 +3,4 @@ export { default as CompatibilityCard } from "./CompatibilityCard/CompatibilityC export { default as MeditationCard } from "./MeditationCard/MeditationCard"; export { default as PalmCard } from "./PalmCard/PalmCard"; export { default as PortraitCard } from "./PortraitCard/PortraitCard"; +export { default as VideoGuideCard } from "./VideoGuideCard/VideoGuideCard"; diff --git a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx index addacd3..b083fa3 100644 --- a/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx +++ b/src/components/domains/dashboard/sections/AdvisersSection/AdvisersSection.tsx @@ -31,7 +31,12 @@ export default function AdvisersSection({ }: AdvisersSectionProps) { const assistants = use(promiseAssistants); const chats = use(promiseChats); - const columns = getOptimalColumns(assistants?.length || 0); + + if (!assistants || assistants.length === 0) { + return null; + } + + const columns = getOptimalColumns(assistants.length); return (
diff --git a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx index b0455c8..5289ff1 100644 --- a/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx +++ b/src/components/domains/dashboard/sections/CompatibilitySection/CompatibilitySection.tsx @@ -22,7 +22,12 @@ export default function CompatibilitySection({ gridDisplayMode = "horizontal", }: CompatibilitySectionProps) { const compatibilities = use(promise); - const columns = Math.ceil(compatibilities?.length / 2); + + if (!compatibilities || compatibilities.length === 0) { + return null; + } + + const columns = Math.ceil(compatibilities.length / 2); return (
diff --git a/src/components/domains/dashboard/sections/MeditationSection/MeditationSection.tsx b/src/components/domains/dashboard/sections/MeditationSection/MeditationSection.tsx index 45d9638..8e3de49 100644 --- a/src/components/domains/dashboard/sections/MeditationSection/MeditationSection.tsx +++ b/src/components/domains/dashboard/sections/MeditationSection/MeditationSection.tsx @@ -20,7 +20,12 @@ export default function MeditationSection({ gridDisplayMode = "horizontal", }: MeditationSectionProps) { const meditations = use(promise); - const columns = meditations?.length; + + if (!meditations || meditations.length === 0) { + return null; + } + + const columns = meditations.length; return (
diff --git a/src/components/domains/dashboard/sections/PalmSection/PalmSection.tsx b/src/components/domains/dashboard/sections/PalmSection/PalmSection.tsx index bffcc40..d6fa71a 100644 --- a/src/components/domains/dashboard/sections/PalmSection/PalmSection.tsx +++ b/src/components/domains/dashboard/sections/PalmSection/PalmSection.tsx @@ -15,7 +15,12 @@ export default function PalmSection({ promise: Promise; }) { const palms = use(promise); - const columns = palms?.length; + + if (!palms || palms.length === 0) { + return null; + } + + const columns = palms.length; return (
diff --git a/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.module.scss b/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.module.scss new file mode 100644 index 0000000..d00ff19 --- /dev/null +++ b/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.module.scss @@ -0,0 +1,12 @@ +.sectionContent.sectionContent { + overflow-x: scroll; + -webkit-overflow-scrolling: touch; + width: calc(100% + 32px); + padding: 20px 16px 24px 16px; + padding-right: 0; + margin: -20px -16px -24px -16px; +} + +.grid { + padding-right: 16px; +} diff --git a/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.tsx b/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.tsx new file mode 100644 index 0000000..a4426b1 --- /dev/null +++ b/src/components/domains/dashboard/sections/VideoGuidesSection/VideoGuidesSection.tsx @@ -0,0 +1,38 @@ +import { Grid, Section } from "@/components/ui"; +import { VideoGuide } from "@/entities/dashboard/types"; + +import { VideoGuideCard } from "../../cards"; + +import styles from "./VideoGuidesSection.module.scss"; + +interface VideoGuidesSectionProps { + videoGuides: VideoGuide[]; +} + +export default function VideoGuidesSection({ videoGuides }: VideoGuidesSectionProps) { + if (!videoGuides || videoGuides.length === 0) { + return null; + } + + const columns = videoGuides.length; + + return ( +
+ + {videoGuides.map(videoGuide => ( + + ))} + +
+ ); +} diff --git a/src/components/domains/dashboard/sections/VideoGuidesSection/index.ts b/src/components/domains/dashboard/sections/VideoGuidesSection/index.ts new file mode 100644 index 0000000..444247a --- /dev/null +++ b/src/components/domains/dashboard/sections/VideoGuidesSection/index.ts @@ -0,0 +1 @@ +export { default as VideoGuidesSection } from "./VideoGuidesSection"; diff --git a/src/components/domains/dashboard/sections/index.ts b/src/components/domains/dashboard/sections/index.ts index 989e957..bc600d1 100644 --- a/src/components/domains/dashboard/sections/index.ts +++ b/src/components/domains/dashboard/sections/index.ts @@ -20,3 +20,4 @@ export { PalmSectionSkeleton, } from "./PalmSection/PalmSection"; export { default as PortraitsSection } from "./PortraitsSection/PortraitsSection"; +export { default as VideoGuidesSection } from "./VideoGuidesSection/VideoGuidesSection"; diff --git a/src/entities/dashboard/loaders.ts b/src/entities/dashboard/loaders.ts index c1d5fa0..af0a7e2 100644 --- a/src/entities/dashboard/loaders.ts +++ b/src/entities/dashboard/loaders.ts @@ -5,15 +5,18 @@ import { getDashboard } from "./api"; export const loadDashboard = cache(getDashboard); export const loadAssistants = cache(() => - loadDashboard().then(d => d.assistants) + loadDashboard().then(d => d.assistants || []) ); export const loadCompatibility = cache(() => - loadDashboard().then(d => d.compatibilityActions) + loadDashboard().then(d => d.compatibilityActions || []) ); export const loadMeditations = cache(() => - loadDashboard().then(d => d.meditations) + loadDashboard().then(d => d.meditations || []) ); -export const loadPalms = cache(() => loadDashboard().then(d => d.palmActions)); +export const loadPalms = cache(() => loadDashboard().then(d => d.palmActions || [])); export const loadPortraits = cache(() => loadDashboard().then(d => d.partnerPortraits || []) ); +export const loadVideoGuides = cache(() => + loadDashboard().then(d => d.videoGuides || []) +); diff --git a/src/entities/dashboard/types.ts b/src/entities/dashboard/types.ts index 29a784e..ddb413e 100644 --- a/src/entities/dashboard/types.ts +++ b/src/entities/dashboard/types.ts @@ -61,12 +61,29 @@ export const PartnerPortraitSchema = z.object({ }); export type PartnerPortrait = z.infer; +/* ---------- Video Guide ---------- */ +export const VideoGuideSchema = z.object({ + id: z.string(), + key: z.string(), + type: z.string(), + name: z.string(), + description: z.string(), + imageUrl: z.string(), + duration: z.string(), + price: z.number(), + oldPrice: z.number(), + discount: z.number(), + isPurchased: z.boolean(), +}); +export type VideoGuide = z.infer; + /* ---------- Итоговый ответ /dashboard ---------- */ export const DashboardSchema = z.object({ - assistants: z.array(AssistantSchema), - compatibilityActions: z.array(ActionSchema), - palmActions: z.array(ActionSchema), - meditations: z.array(ActionSchema), + assistants: z.array(AssistantSchema).optional(), + compatibilityActions: z.array(ActionSchema).optional(), + palmActions: z.array(ActionSchema).optional(), + meditations: z.array(ActionSchema).optional(), partnerPortraits: z.array(PartnerPortraitSchema).optional(), + videoGuides: z.array(VideoGuideSchema).optional(), }); export type DashboardData = z.infer; diff --git a/src/entities/session/funnel/types.ts b/src/entities/session/funnel/types.ts index 3735dda..8bc3cf6 100644 --- a/src/entities/session/funnel/types.ts +++ b/src/entities/session/funnel/types.ts @@ -17,6 +17,9 @@ export const FunnelPaymentVariantSchema = z.object({ id: z.string(), key: z.string(), type: z.string(), + name: z.string().optional(), + description: z.string().optional(), + emoji: z.string().optional(), price: z.number(), oldPrice: z.number().optional(), trialPrice: z.number().optional(),