This commit is contained in:
lovebird 2026-02-18 22:44:09 +01:00
parent 6397ab64f4
commit f99cf78e6b
7 changed files with 116 additions and 146 deletions

View File

@ -20,7 +20,6 @@ import GlobalDragDrop from "@/components/GlobalDragDrop";
// Register all widgets on app boot
registerAllWidgets();
import "./debug_env";
import Index from "./pages/Index";
import Auth from "./pages/Auth";
@ -64,76 +63,7 @@ const VersionMap = React.lazy(() => import("./pages/VersionMap"));
// <GlobalDebug />
// Ecommerce route wrappers (need useNavigate which requires component context)
const EcommerceBundle = React.lazy(() => import("@polymech/ecommerce").then(m => ({ default: m.EcommerceBundle })));
const EcommerceBundleWrapper = () => {
const { user } = useAuth();
const navigate = useNavigate();
// Memoize dependencies to prevent re-renders
const dependencies = React.useMemo(() => {
return {
user: user ? {
id: user.id,
email: user.email,
user_metadata: {
display_name: user.user_metadata?.display_name
}
} : null,
toast: {
success: (msg: string) => import('sonner').then(s => s.toast.success(msg)),
error: (msg: string) => import('sonner').then(s => s.toast.error(msg))
},
onFetchAddresses: async (userId: string) => {
const { getShippingAddresses } = await import('@/modules/user/client-user');
return getShippingAddresses(userId) as Promise<any[]>;
},
onSaveAddress: async (userId: string, addresses: any[]) => {
const { saveShippingAddresses } = await import('@/modules/user/client-user');
await saveShippingAddresses(userId, addresses);
},
onPlaceOrder: async (data: any) => {
const { useCartStore } = await import('@polymech/ecommerce');
const { createTransaction } = await import('@/modules/ecommerce/client-ecommerce');
const items = useCartStore.getState().items;
const subtotal = useCartStore.getState().subtotal;
await createTransaction({
shipping_info: data.shipping,
vendor_info: {},
product_info: items.map((i: any) => ({
id: i.id,
title: i.title,
image: i.image,
price: i.price,
quantity: i.quantity,
variant: i.variant,
vendorSlug: i.vendorSlug,
pageSlug: i.pageSlug,
})),
total_amount: subtotal,
payment_provider: data.paymentMethod,
});
useCartStore.getState().clearCart();
},
onFetchTransactions: async () => {
const { listTransactions } = await import('@/modules/ecommerce/client-ecommerce');
return listTransactions();
},
onNavigate: (path: string) => navigate(path),
siteName: "PolyMech",
contactEmail: "legal@polymech.org"
};
}, [user, navigate]);
return (
<React.Suspense fallback={<div className="flex items-center justify-center p-12">Loading...</div>}>
<EcommerceBundle {...dependencies} />
</React.Suspense>
);
};
import { EcommerceBundleWrapper } from "./bundles/ecommerce";
const AppWrapper = () => {
@ -146,7 +76,6 @@ const AppWrapper = () => {
: "mx-auto 2xl:max-w-7xl flex flex-col min-h-svh transition-colors duration-200 h-full";
const ecommerce = import.meta.env.VITE_ENABLE_ECOMMERCE === 'true';
console.log('DEBUG: ecommerce:', ecommerce);
return (
<div className={containerClassName}>
{!isFullScreenPage && <TopNavigation />}
@ -285,13 +214,4 @@ const App = () => {
);
};
// Update Routes in AppWrapper to include /test-cache/:id
// ...
// We need to inject the route inside AppWrapper component defined above
// Since ReplaceFileContent works on blocks, I'll target the Routes block in a separate call or try to merge.
// Merging is safer if I can target the App component specifically.
// But the Routes are in AppWrapper.
// Let's split this into two edits.
export default App;

View File

@ -0,0 +1,72 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "@/hooks/useAuth";
const EcommerceBundle = React.lazy(() => import("@polymech/ecommerce").then(m => ({ default: m.EcommerceBundle })));
export const EcommerceBundleWrapper = () => {
const { user } = useAuth();
const navigate = useNavigate();
// Memoize dependencies to prevent re-renders
const dependencies = React.useMemo(() => {
return {
user: user ? {
id: user.id,
email: user.email,
user_metadata: {
display_name: user.user_metadata?.display_name
}
} : null,
toast: {
success: (msg: string) => import('sonner').then(s => s.toast.success(msg)),
error: (msg: string) => import('sonner').then(s => s.toast.error(msg))
},
onFetchAddresses: async (userId: string) => {
const { getShippingAddresses } = await import('@/modules/user/client-user');
return getShippingAddresses(userId) as Promise<any[]>;
},
onSaveAddress: async (userId: string, addresses: any[]) => {
const { saveShippingAddresses } = await import('@/modules/user/client-user');
await saveShippingAddresses(userId, addresses);
},
onPlaceOrder: async (data: any) => {
const { useCartStore } = await import('@polymech/ecommerce');
const { createTransaction } = await import('@/modules/ecommerce/client-ecommerce');
const items = useCartStore.getState().items;
const subtotal = useCartStore.getState().subtotal;
await createTransaction({
shipping_info: data.shipping,
vendor_info: {},
product_info: items.map((i: any) => ({
id: i.id,
title: i.title,
image: i.image,
price: i.price,
quantity: i.quantity,
variant: i.variant,
vendorSlug: i.vendorSlug,
pageSlug: i.pageSlug,
})),
total_amount: subtotal,
payment_provider: data.paymentMethod,
});
useCartStore.getState().clearCart();
},
onFetchTransactions: async () => {
const { listTransactions } = await import('@/modules/ecommerce/client-ecommerce');
return listTransactions();
},
onNavigate: (path: string) => navigate(path),
siteName: "PolyMech",
contactEmail: "legal@polymech.org"
};
}, [user, navigate]);
return (
<React.Suspense fallback={<div className="flex items-center justify-center p-12">Loading...</div>}>
<EcommerceBundle {...dependencies} />
</React.Suspense>
);
};

View File

@ -185,7 +185,6 @@ export const ImagePickerDialog: React.FC<ImagePickerDialogProps> = ({
}
}
};
const toggleSelection = (id: string) => {
if (multiple) {
setSelectedIds(prev =>
@ -197,11 +196,6 @@ export const ImagePickerDialog: React.FC<ImagePickerDialogProps> = ({
setSelectedId(id);
}
};
console.log('selectedIds', selectedIds);
console.log('selectedId', selectedId);
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col">

View File

@ -1 +0,0 @@
console.log('DEBUG: process.env.ENABLE_ECOMMERCE:', import.meta.env.VITE_ENABLE_ECOMMERCE);

View File

@ -71,17 +71,15 @@ export const useResponsiveImage = ({
if (!requestCache.has(cacheKey)) {
const requestPromise = (async () => {
const formData = new FormData();
formData.append('url', src);
formData.append('sizes', JSON.stringify(responsiveSizes));
formData.append('formats', JSON.stringify(formats));
const serverUrl = apiUrl || import.meta.env.VITE_SERVER_IMAGE_API_URL || 'http://192.168.1.11:3333';
const response = await fetch(`${serverUrl}/api/images/responsive`, {
method: 'POST',
body: formData,
const params = new URLSearchParams({
url: src,
sizes: JSON.stringify(responsiveSizes),
formats: JSON.stringify(formats),
});
const response = await fetch(`${serverUrl}/api/images/responsive?${params.toString()}`);
if (!response.ok) {
const txt = await response.text();
// Remove from cache on error so it can be retried

View File

@ -30,26 +30,27 @@ export const fetchAuthorProfile = async (userId: string, client?: SupabaseClient
};
export const getUserSettings = async (userId: string, client?: SupabaseClient) => {
const supabase = client || defaultSupabase;
return fetchWithDeduplication(`settings-${userId}`, async () => {
const { data, error } = await supabase
.from('profiles')
.select('settings')
.eq('user_id', userId)
.single();
if (error) throw error;
return (data?.settings as any) || {};
const token = await getAuthToken();
const res = await fetch('/api/me/settings', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) throw new Error(`Failed to fetch settings: ${res.statusText}`);
return await res.json();
}, 100000);
};
export const updateUserSettings = async (userId: string, settings: any, client?: SupabaseClient) => {
const supabase = client || defaultSupabase;
await supabase
.from('profiles')
.update({ settings })
.eq('user_id', userId);
// Cache invalidation handled by React Query or not needed for now
const token = await getAuthToken();
const res = await fetch('/api/me/settings', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(settings)
});
if (!res.ok) throw new Error(`Failed to update settings: ${res.statusText}`);
};
export const getUserOpenAIKey = async (userId: string, client?: SupabaseClient) => {
@ -118,48 +119,30 @@ export const getUserSecrets = async (userId: string, client?: SupabaseClient) =>
};
export const getProviderConfig = async (userId: string, provider: string, client?: SupabaseClient) => {
const supabase = client || defaultSupabase;
return fetchWithDeduplication(`provider-${userId}-${provider}`, async () => {
console.log('Fetching provider config for user:', userId, 'provider:', provider);
const { data, error } = await supabase
.from('provider_configs')
.select('settings')
.eq('user_id', userId)
.eq('name', provider)
.eq('is_active', true)
.single();
if (error) {
// It's common to not have configs for all providers, so we might want to suppress some errors or handle them gracefully
// However, checking error code might be robust. For now let's just throw if it's not a "no rows" error,
// or just return null if not found.
// The original code used .single() which errors if 0 rows.
// Let's use maybeSingle() to be safe? The original code caught the error and returned null.
// But the original query strictly used .single().
// Let's stick to .single() but catch it here if we want to mimic exact behavior, OR use maybeSingle and return null.
// The calling code expects null if not found.
if (error.code === 'PGRST116') return null; // No rows found
throw error;
}
return data as { settings: any };
const token = await getAuthToken();
const res = await fetch(`/api/me/provider-config/${encodeURIComponent(provider)}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to fetch provider config: ${res.statusText}`);
return await res.json();
});
}
export const fetchUserRoles = async (userId: string, client?: SupabaseClient) => {
const supabase = client || defaultSupabase;
return fetchWithDeduplication(`roles-${userId}`, async () => {
const { data, error } = await supabase
.from('user_roles')
.select('role')
.eq('user_id', userId);
if (error) {
console.error('Error fetching user roles:', error);
const token = await getAuthToken();
const res = await fetch('/api/me/roles', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) {
console.error('Error fetching user roles:', res.statusText);
return [];
}
return data.map(r => r.role);
return await res.json();
});
};

View File

@ -9,7 +9,7 @@ const SW_VERSION = '1.0.5-debug';
console.log(`[SW] Initializing Version: ${SW_VERSION}`);
self.addEventListener('fetch', (event) => { });
declare let self: ServiceWorkerGlobalScope
@ -25,8 +25,7 @@ precacheAndRoute(self.__WB_MANIFEST)
// clean old assets
cleanupOutdatedCaches()
self.skipWaiting()
clientsClaim()
// allow only fallback in dev: we don't want to cache everything
let allowlist: undefined | RegExp[]
@ -69,6 +68,7 @@ const navigationHandler = async (params: any) => {
try {
const strategy = new NetworkFirst({
cacheName: 'pages',
networkTimeoutSeconds: 3, // Fallback to cache if network is slow
plugins: [
{
cacheWillUpdate: async ({ response }) => {
@ -91,3 +91,7 @@ registerRoute(new NavigationRoute(
denylist: [/^\/upload-share-target/]
}
))
self.addEventListener('activate', () => {
clientsClaim();
});