cache
This commit is contained in:
parent
6397ab64f4
commit
f99cf78e6b
@ -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;
|
||||
|
||||
72
packages/ui/src/bundles/ecommerce.tsx
Normal file
72
packages/ui/src/bundles/ecommerce.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -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">
|
||||
|
||||
@ -1 +0,0 @@
|
||||
console.log('DEBUG: process.env.ENABLE_ECOMMERCE:', import.meta.env.VITE_ENABLE_ECOMMERCE);
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user