From d87afd8654f09a3dca0b63abd013f7288e2842d7 Mon Sep 17 00:00:00 2001 From: Babayaga Date: Sat, 21 Feb 2026 20:21:13 +0100 Subject: [PATCH] stripe 2/2 --- packages/ui/src/App.tsx | 1 - packages/ui/src/bundles/ecommerce.tsx | 16 ++++++++-- .../pages/editor/ribbons/PageRibbonBar.tsx | 8 +++-- packages/ui/src/pages/Profile.tsx | 31 +++++++++++++++++-- packages/ui/src/pages/UserProfile.tsx | 1 + .../pages/analytics/AnalyticsDashboard.tsx | 10 ++++-- 6 files changed, 56 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 5e9f32b8..6e739fb4 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -62,7 +62,6 @@ const I18nPlayground = React.lazy(() => import("./components/playground/I18nPlay const VersionMap = React.lazy(() => import("./pages/VersionMap")); - // import { EcommerceBundleWrapper } from "./bundles/ecommerce"; diff --git a/packages/ui/src/bundles/ecommerce.tsx b/packages/ui/src/bundles/ecommerce.tsx index 9773f4d0..2af63469 100644 --- a/packages/ui/src/bundles/ecommerce.tsx +++ b/packages/ui/src/bundles/ecommerce.tsx @@ -2,11 +2,14 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@/hooks/useAuth"; +import { getCurrentLang } from "@/i18n"; + const EcommerceBundle = React.lazy(() => import("@polymech/ecommerce").then(m => ({ default: m.EcommerceBundle }))); export const EcommerceBundleWrapper = () => { - const { user } = useAuth(); + const { user, session } = useAuth(); const navigate = useNavigate(); + const locale = getCurrentLang(); // Memoize dependencies to prevent re-renders const dependencies = React.useMemo(() => { @@ -51,6 +54,9 @@ export const EcommerceBundleWrapper = () => { })), total_amount: subtotal, payment_provider: data.paymentMethod, + // Stripe-specific fields (undefined for non-Stripe payments) + external_order_id: data.external_order_id, + metadata: data.metadata, }); useCartStore.getState().clearCart(); }, @@ -60,9 +66,13 @@ export const EcommerceBundleWrapper = () => { }, onNavigate: (path: string) => navigate(path), siteName: "PolyMech", - contactEmail: "legal@polymech.org" + contactEmail: "sales@plastic-hub.com", + stripePublishableKey: import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY, + apiBaseUrl: import.meta.env.VITE_SERVER_IMAGE_API_URL || "", + getAuthToken: async () => session?.access_token ?? null, + locale, }; - }, [user, navigate]); + }, [user, session, navigate, locale]); return ( Loading...}> diff --git a/packages/ui/src/modules/pages/editor/ribbons/PageRibbonBar.tsx b/packages/ui/src/modules/pages/editor/ribbons/PageRibbonBar.tsx index 0b4c100c..4c217928 100644 --- a/packages/ui/src/modules/pages/editor/ribbons/PageRibbonBar.tsx +++ b/packages/ui/src/modules/pages/editor/ribbons/PageRibbonBar.tsx @@ -236,6 +236,7 @@ interface CompactAction { disabled?: boolean; iconColor?: string; onDelete?: () => void; + testId?: string; } const CompactFlowGroup = ({ @@ -266,6 +267,7 @@ const CompactFlowGroup = ({ disabled={action.disabled} iconColor={action.iconColor} onDelete={action.onDelete} + {...(action.testId ? { 'data-testid': action.testId } : {})} /> ))} {shouldOverflow && ( @@ -738,14 +740,16 @@ export const PageRibbonBar = ({ label: page.visible ? "Visible" : "Hidden", active: !page.visible, onClick: () => handleToggleVisibility(), - iconColor: page.visible ? "text-emerald-500" : "text-gray-400" + iconColor: page.visible ? "text-emerald-500" : "text-gray-400", + testId: 'page-visibility-toggle' }, { icon: page.is_public ? GitMerge : Settings, label: page.is_public ? "Public" : "Private", active: !page.is_public, onClick: () => handleTogglePublic(), - iconColor: page.is_public ? "text-amber-500" : "text-gray-400" + iconColor: page.is_public ? "text-amber-500" : "text-gray-400", + testId: 'page-public-toggle' }, { icon: FolderTree, diff --git a/packages/ui/src/pages/Profile.tsx b/packages/ui/src/pages/Profile.tsx index 519dbce2..a1a10dfd 100644 --- a/packages/ui/src/pages/Profile.tsx +++ b/packages/ui/src/pages/Profile.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { useAuth } from "@/hooks/useAuth"; import ImageGallery from "@/components/ImageGallery"; import { ImageFile } from "@/types"; @@ -10,7 +10,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; -import { User, Images, Save, Camera, Upload, Check, Key, Globe, Hash, MapPin, Building2 } from "lucide-react"; +import { User, Images, Save, Camera, Upload, Check, Key, Globe, Hash, MapPin, Building2, ShoppingBag } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -33,7 +33,11 @@ import { useSidebar } from "@/components/ui/sidebar"; -type ActiveSection = 'general' | 'api-keys' | 'variables' | 'addresses' | 'vendor' | 'gallery'; +const LazyPurchasesList = React.lazy(() => + import("@polymech/ecommerce").then(m => ({ default: m.PurchasesList })) +); + +type ActiveSection = 'general' | 'api-keys' | 'variables' | 'addresses' | 'vendor' | 'gallery' | 'purchases'; const Profile = () => { const { user, loading } = useAuth(); @@ -560,6 +564,26 @@ const Profile = () => { )} + {activeSection === 'purchases' && ( + + + My Purchases + + + Loading...}> + { + const { listTransactions } = await import('@/modules/ecommerce/client-ecommerce'); + return listTransactions(); + }} + onNavigate={navigate} + toast={{ error: (msg: string) => toast.error(msg) }} + /> + + + + )} + {activeSection === 'analytics' && roles.includes('admin') && ( @@ -622,6 +646,7 @@ const ProfileSidebar = ({ { id: 'variables' as ActiveSection, label: translate('Variables'), icon: Hash }, { id: 'addresses' as ActiveSection, label: translate('Shipping Addresses'), icon: MapPin }, { id: 'vendor' as ActiveSection, label: translate('Vendor Profiles'), icon: Building2 }, + { id: 'purchases' as ActiveSection, label: translate('Purchases'), icon: ShoppingBag }, ]; diff --git a/packages/ui/src/pages/UserProfile.tsx b/packages/ui/src/pages/UserProfile.tsx index bb28f1f6..a933f574 100644 --- a/packages/ui/src/pages/UserProfile.tsx +++ b/packages/ui/src/pages/UserProfile.tsx @@ -342,6 +342,7 @@ const UserProfile = () => { + {/* Photos Grid - Using PhotoGrid with customPictures */} {/* Photos Grid - Using PhotoGrid with customPictures */}
{ setData(events.map((e: any, index: number) => ({ ...e, id: `init-${index}` }))); - // Start Streaming - eventSource = new EventSource('/api/analytics/stream'); + // Start Streaming (with auth token as query param — EventSource doesn't support headers) + const { data: sessionData } = await supabase.auth.getSession(); + const token = sessionData.session?.access_token; + const streamUrl = token + ? `/api/analytics/stream?token=${encodeURIComponent(token)}` + : '/api/analytics/stream'; + eventSource = new EventSource(streamUrl); eventSource.addEventListener('log', (event: any) => { if (!isMounted) return;