This commit is contained in:
dev.daminik00 2025-10-28 06:02:25 +01:00
parent 1adac2836b
commit 83d1faaa38
12 changed files with 39 additions and 39 deletions

View File

@ -26,7 +26,7 @@ export default function AddConsultantButton() {
onSuccess: async () => { onSuccess: async () => {
// Устанавливаем флаг навигации чтобы заблокировать повторные нажатия // Устанавливаем флаг навигации чтобы заблокировать повторные нажатия
setIsNavigating(true); setIsNavigating(true);
// Переходим на следующую страницу или на главную // Переходим на следующую страницу или на главную
if (navigation.hasNext) { if (navigation.hasNext) {
await navigation.goToNext(); await navigation.goToNext();

View File

@ -23,9 +23,9 @@ export default function AddConsultantPage() {
return ( return (
<> <>
<Progress <Progress
items={progressItems} items={progressItems}
activeItemIndex={navigation.currentIndex} activeItemIndex={navigation.currentIndex}
/> />
<Typography as="h2" size="xl" weight="semiBold" className={styles.title}> <Typography as="h2" size="xl" weight="semiBold" className={styles.title}>
{t("title")} {t("title")}

View File

@ -25,7 +25,7 @@ export default function AddGuidesButton() {
onSuccess: async () => { onSuccess: async () => {
// Устанавливаем флаг навигации чтобы заблокировать повторные нажатия // Устанавливаем флаг навигации чтобы заблокировать повторные нажатия
setIsNavigating(true); setIsNavigating(true);
// Переходим на следующую страницу или на главную // Переходим на следующую страницу или на главную
if (navigation.hasNext) { if (navigation.hasNext) {
await navigation.goToNext(); await navigation.goToNext();

View File

@ -26,9 +26,9 @@ export default function AddGuidesPage() {
return ( return (
<ProductSelectionProvider> <ProductSelectionProvider>
<Progress <Progress
items={progressItems} items={progressItems}
activeItemIndex={navigation.currentIndex} activeItemIndex={navigation.currentIndex}
/> />
<Typography as="h2" size="xl" weight="semiBold" className={styles.title}> <Typography as="h2" size="xl" weight="semiBold" className={styles.title}>
{t("title")} {t("title")}

View File

@ -30,11 +30,11 @@ export default function Progress({ items, activeItemIndex }: IProgressProps) {
// Фиксированные отступы от краев (центр кружочка = 50px от края) // Фиксированные отступы от краев (центр кружочка = 50px от края)
const edgeOffset = 50; // 50px от края до центра кружочка const edgeOffset = 50; // 50px от края до центра кружочка
const totalItems = allItems.length; const totalItems = allItems.length;
// Рассчитываем позицию каждого элемента с равными отступами // Рассчитываем позицию каждого элемента с равными отступами
const calculateItemPosition = (index: number) => { const calculateItemPosition = (index: number) => {
if (totalItems === 1) return 50; // Центрируем если один элемент if (totalItems === 1) return 50; // Центрируем если один элемент
// Распределяем элементы равномерно между краями // Распределяем элементы равномерно между краями
const availableWidth = containerWidth - (edgeOffset * 2); const availableWidth = containerWidth - (edgeOffset * 2);
const spacing = availableWidth / (totalItems - 1); const spacing = availableWidth / (totalItems - 1);
@ -47,14 +47,14 @@ export default function Progress({ items, activeItemIndex }: IProgressProps) {
// Находим все элементы прогресс бара // Находим все элементы прогресс бара
const items = elementRef.current.querySelectorAll(`.${styles.item}`); const items = elementRef.current.querySelectorAll(`.${styles.item}`);
let maxHeight = 0; let maxHeight = 0;
items.forEach((item) => { items.forEach((item) => {
const height = (item as HTMLElement).offsetHeight; const height = (item as HTMLElement).offsetHeight;
if (height > maxHeight) { if (height > maxHeight) {
maxHeight = height; maxHeight = height;
} }
}); });
setContainerHeight(maxHeight); setContainerHeight(maxHeight);
} }
}, [allItems, containerWidth]); }, [allItems, containerWidth]);
@ -79,12 +79,12 @@ export default function Progress({ items, activeItemIndex }: IProgressProps) {
}, []); }, []);
return ( return (
<div <div
className={clsx(styles.stickyWrapper, isScrolled && styles.scrolled)} className={clsx(styles.stickyWrapper, isScrolled && styles.scrolled)}
ref={wrapperRef} ref={wrapperRef}
> >
<div <div
className={styles.container} className={styles.container}
ref={elementRef} ref={elementRef}
style={{ style={{
minHeight: containerHeight > 0 ? `${containerHeight}px` : undefined, minHeight: containerHeight > 0 ? `${containerHeight}px` : undefined,

View File

@ -25,7 +25,7 @@ export default function VideoGuidesButton() {
onSuccess: async () => { onSuccess: async () => {
// Устанавливаем флаг навигации чтобы заблокировать повторные нажатия // Устанавливаем флаг навигации чтобы заблокировать повторные нажатия
setIsNavigating(true); setIsNavigating(true);
// Переходим на следующую страницу или на главную // Переходим на следующую страницу или на главную
if (navigation.hasNext) { if (navigation.hasNext) {
await navigation.goToNext(); await navigation.goToNext();

View File

@ -7,10 +7,10 @@ import {
AdditionalPurchaseBanner, AdditionalPurchaseBanner,
ProductSelectionProvider, ProductSelectionProvider,
Progress, Progress,
useMultiPageNavigationContext,
VideoGuidesButton, VideoGuidesButton,
VideoGuidesOffers, VideoGuidesOffers,
VideoGuidesOffersSkeleton, VideoGuidesOffersSkeleton,
useMultiPageNavigationContext,
} from "@/components/domains/additional-purchases"; } from "@/components/domains/additional-purchases";
import { Typography } from "@/components/ui"; import { Typography } from "@/components/ui";
@ -27,9 +27,9 @@ export default function VideoGuidesPage() {
return ( return (
<ProductSelectionProvider> <ProductSelectionProvider>
<Progress <Progress
items={progressItems} items={progressItems}
activeItemIndex={navigation.currentIndex} activeItemIndex={navigation.currentIndex}
/> />
<AdditionalPurchaseBanner /> <AdditionalPurchaseBanner />
<Typography as="h1" className={styles.title}> <Typography as="h1" className={styles.title}>

View File

@ -101,7 +101,7 @@ export default function VideoGuideCard(props: VideoGuideCardProps) {
</Typography> </Typography>
</div> </div>
</div> </div>
<Button <Button
className={styles.buyButton} className={styles.buyButton}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();

View File

@ -29,7 +29,7 @@ function VideoGuideCardWrapper({ videoGuide }: { videoGuide: VideoGuide }) {
const isClickable = videoGuide.isPurchased && videoGuide.videoLink; const isClickable = videoGuide.isPurchased && videoGuide.videoLink;
const cardElement = ( const cardElement = (
<VideoGuideCard <VideoGuideCard
name={videoGuide.name} name={videoGuide.name}
description={videoGuide.description} description={videoGuide.description}
imageUrl={videoGuide.imageUrl} imageUrl={videoGuide.imageUrl}
@ -46,8 +46,8 @@ function VideoGuideCardWrapper({ videoGuide }: { videoGuide: VideoGuide }) {
if (isClickable) { if (isClickable) {
return ( return (
<Link <Link
href={href} href={href}
key={`video-guide-${videoGuide.id}`} key={`video-guide-${videoGuide.id}`}
> >
{cardElement} {cardElement}
@ -69,7 +69,7 @@ export default function VideoGuidesSection({ videoGuides }: VideoGuidesSectionPr
<Section title="Video Guides" contentClassName={styles.sectionContent}> <Section title="Video Guides" contentClassName={styles.sectionContent}>
<Grid columns={columns} className={styles.grid}> <Grid columns={columns} className={styles.grid}>
{videoGuides.map(videoGuide => ( {videoGuides.map(videoGuide => (
<VideoGuideCardWrapper <VideoGuideCardWrapper
key={`video-guide-${videoGuide.id}`} key={`video-guide-${videoGuide.id}`}
videoGuide={videoGuide} videoGuide={videoGuide}
/> />

View File

@ -22,7 +22,7 @@ export default function VideoGuideView({ name, description, videoLink }: VideoGu
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
/^([a-zA-Z0-9_-]{11})$/ // Direct video ID /^([a-zA-Z0-9_-]{11})$/ // Direct video ID
]; ];
for (const pattern of patterns) { for (const pattern of patterns) {
const match = url.match(pattern); const match = url.match(pattern);
if (match) return match[1]; if (match) return match[1];
@ -31,7 +31,7 @@ export default function VideoGuideView({ name, description, videoLink }: VideoGu
}; };
const videoId = getYouTubeVideoId(videoLink); const videoId = getYouTubeVideoId(videoLink);
const embedUrl = videoId const embedUrl = videoId
? `https://www.youtube.com/embed/${videoId}?rel=0&modestbranding=1` ? `https://www.youtube.com/embed/${videoId}?rel=0&modestbranding=1`
: videoLink; : videoLink;

View File

@ -12,7 +12,7 @@ export const loadCompatibility = () =>
export const loadMeditations = () => export const loadMeditations = () =>
loadDashboard().then(d => d.meditations || []); loadDashboard().then(d => d.meditations || []);
export const loadPalms = () => export const loadPalms = () =>
loadDashboard().then(d => d.palmActions || []); loadDashboard().then(d => d.palmActions || []);
export const loadPortraits = () => export const loadPortraits = () =>

View File

@ -42,7 +42,7 @@ export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
startTransition(() => { startTransition(() => {
router.refresh(); router.refresh();
}); });
// Убираем наш флаг, но isPending продолжит показывать loader // Убираем наш флаг, но isPending продолжит показывать loader
setIsProcessingPurchase(false); setIsProcessingPurchase(false);
}, },
@ -59,10 +59,10 @@ export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
const handlePurchase = useCallback(async () => { const handlePurchase = useCallback(async () => {
// Сначала проверяем, не куплен ли уже продукт // Сначала проверяем, не куплен ли уже продукт
setIsCheckingPurchase(true); setIsCheckingPurchase(true);
try { try {
const result = await checkVideoGuidePurchase(productKey); const result = await checkVideoGuidePurchase(productKey);
if (result.data && result.data.isPurchased) { if (result.data && result.data.isPurchased) {
// Продукт уже куплен! Показываем сообщение и обновляем страницу // Продукт уже куплен! Показываем сообщение и обновляем страницу
addToast({ addToast({
@ -70,26 +70,26 @@ export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
message: "You already own this video guide!", message: "You already own this video guide!",
duration: 3000, duration: 3000,
}); });
setIsCheckingPurchase(false); setIsCheckingPurchase(false);
// Включаем лоадер на всей карточке // Включаем лоадер на всей карточке
setIsProcessingPurchase(true); setIsProcessingPurchase(true);
// Даем небольшую задержку для плавного UX // Даем небольшую задержку для плавного UX
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise(resolve => setTimeout(resolve, 1000));
// Обновляем данные dashboard в transition // Обновляем данные dashboard в transition
// isPending будет true пока данные загружаются // isPending будет true пока данные загружаются
startTransition(() => { startTransition(() => {
router.refresh(); router.refresh();
}); });
// Убираем наш флаг, но isPending продолжит показывать loader // Убираем наш флаг, но isPending продолжит показывать loader
setIsProcessingPurchase(false); setIsProcessingPurchase(false);
return; return;
} }
// Продукт не куплен, продолжаем с checkout // Продукт не куплен, продолжаем с checkout
setIsCheckingPurchase(false); setIsCheckingPurchase(false);
handleSingleCheckout({ handleSingleCheckout({
@ -99,7 +99,7 @@ export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
} catch (error) { } catch (error) {
console.error("Error checking purchase status:", error); console.error("Error checking purchase status:", error);
setIsCheckingPurchase(false); setIsCheckingPurchase(false);
// Даже если проверка не удалась, продолжаем с checkout // Даже если проверка не удалась, продолжаем с checkout
// чтобы не блокировать покупку // чтобы не блокировать покупку
handleSingleCheckout({ handleSingleCheckout({