Skip to content

Commit d0d42d2

Browse files
authored
Merge branch 'main' into claude/setup-store-access-links-oQGmn
2 parents bf29c9d + 0514d45 commit d0d42d2

209 files changed

Lines changed: 34801 additions & 3197 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 1275 additions & 209 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
{
2-
"name": "vite_react_shadcn_ts",
2+
"name": "asper-beauty-shop",
33
"private": true,
4-
"version": "0.0.0",
4+
"version": "1.0.0",
55
"type": "module",
6+
"homepage": "https://www.asperbeautyshop.com",
67
"scripts": {
78
"dev": "vite",
89
"build": "vite build",
910
"build:dev": "vite build --mode development",
1011
"lint": "eslint .",
12+
"lint:fix": "eslint . --fix",
13+
"typecheck": "tsc --noEmit",
14+
"check": "npm run lint && tsc --noEmit",
15+
"check:all": "npm run lint && tsc --noEmit && npm run build",
1116
"preview": "vite preview",
1217
"test": "vitest run",
13-
"test:watch": "vitest"
18+
"test:watch": "vitest",
19+
"test:bulk-upload": "node scripts/test-bulk-upload-validation.mjs"
1420
},
1521
"dependencies": {
22+
"@hcaptcha/react-hcaptcha": "^2.0.2",
1623
"@hookform/resolvers": "^3.10.0",
1724
"@radix-ui/react-accordion": "^1.2.11",
1825
"@radix-ui/react-alert-dialog": "^1.1.14",
@@ -48,6 +55,7 @@
4855
"cmdk": "^1.1.1",
4956
"date-fns": "^3.6.0",
5057
"embla-carousel-react": "^8.6.0",
58+
"exceljs": "^4.4.0",
5159
"framer-motion": "^12.34.0",
5260
"input-otp": "^1.4.2",
5361
"lucide-react": "^0.462.0",
@@ -64,6 +72,7 @@
6472
"tailwind-merge": "^2.6.0",
6573
"tailwindcss-animate": "^1.0.7",
6674
"vaul": "^0.9.9",
75+
"xlsx": "^0.18.5",
6776
"zod": "^3.25.76",
6877
"zustand": "^5.0.11"
6978
},
@@ -85,6 +94,7 @@
8594
"lovable-tagger": "^1.1.13",
8695
"postcss": "^8.5.6",
8796
"tailwindcss": "^3.4.17",
97+
"tsx": "^4.19.2",
8898
"typescript": "^5.8.3",
8999
"typescript-eslint": "^8.38.0",
90100
"vite": "^7.3.1",

src/App.tsx

Lines changed: 108 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,129 @@
1-
import { useState, useCallback, useEffect, lazy, Suspense } from "react";
1+
import { lazy, Suspense, useEffect } from "react";
22
import { Toaster } from "@/components/ui/toaster";
33
import { Toaster as Sonner } from "@/components/ui/sonner";
44
import { TooltipProvider } from "@/components/ui/tooltip";
55
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6-
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
7-
import { useCartSync } from "@/hooks/useCartSync";
6+
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
87
import { LanguageProvider } from "@/contexts/LanguageContext";
9-
import SplashScreen from "@/components/SplashScreen";
10-
import { useIncognitoStore } from "./stores/incognitoStore";
11-
import AIConcierge from "./components/AIConcierge";
8+
import { useCartSync } from "@/hooks/useCartSync";
9+
import { verifyBrandDNA } from "@/lib/verifyBrandDNA";
1210

13-
// Lazy-load route pages to reduce initial bundle & main-thread work
14-
const Index = lazy(() => import("./pages/Index"));
15-
const Products = lazy(() => import("./pages/Products"));
16-
const ProductDetail = lazy(() => import("./pages/ProductDetail"));
17-
const NotFound = lazy(() => import("./pages/NotFound"));
18-
const Auth = lazy(() => import("./pages/Auth"));
19-
const BrandShowcase = lazy(() => import("./pages/BrandShowcase"));
20-
const LabTools = lazy(() => import("./pages/LabTools"));
21-
const Intelligence = lazy(() => import("./pages/Intelligence"));
22-
const AdminEnrichment = lazy(() => import("./pages/AdminEnrichment"));
23-
const Checkout = lazy(() => import("./pages/Checkout"));
24-
const Profile = lazy(() => import("./pages/Profile"));
25-
const Contact = lazy(() => import("./pages/Contact"));
26-
const Shop = lazy(() => import("./pages/Shop"));
27-
const Health = lazy(() => import("./pages/Health"));
28-
const MomBaby = lazy(() => import("./pages/MomBaby"));
11+
import Index from "./pages/Index";
12+
import ProductDetail from "./pages/ProductDetail";
13+
import Collections from "./pages/Collections";
14+
import CollectionDetail from "./pages/CollectionDetail";
15+
import Brands from "./pages/Brands";
16+
import BrandVichy from "./pages/BrandVichy";
17+
import BestSellers from "./pages/BestSellers";
18+
import Offers from "./pages/Offers";
19+
import Contact from "./pages/Contact";
20+
import SkinConcerns from "./pages/SkinConcerns";
21+
import ConcernCollection from "./pages/ConcernCollection";
22+
import Wishlist from "./pages/Wishlist";
23+
import NotFound from "./pages/NotFound";
24+
import Auth from "./pages/Auth";
25+
import Account from "./pages/Account";
26+
import Philosophy from "./pages/Philosophy";
27+
import BulkUpload from "./pages/BulkUpload";
28+
import AdminOrders from "./pages/AdminOrders";
29+
import TrackOrder from "./pages/TrackOrder";
30+
import ManageProducts from "./pages/ManageProducts";
31+
import Shop from "./pages/Shop";
32+
import ShopAllOrganized from "./components/ShopAllOrganized";
33+
import DriverDashboard from "./pages/DriverDashboard";
34+
import AdminAuditLogs from "./pages/AdminAuditLogs";
35+
import AsperIntelligence from "./pages/AsperIntelligence";
36+
import BrandIntelligenceDashboard from "./pages/BrandIntelligenceDashboard";
37+
import Health from "./pages/Health";
38+
import { RequireAdmin } from "./components/RequireAdmin";
39+
40+
const BeautyAssistant = lazy(() =>
41+
import("@/components/BeautyAssistant").then((m) => ({ default: m.BeautyAssistant })),
42+
);
2943

3044
const queryClient = new QueryClient();
3145

32-
/** Scroll restoration: save/restore scroll position per route */
33-
function ScrollRestoration() {
34-
const location = useLocation();
35-
useEffect(() => {
36-
// Restore saved position for this path
37-
const saved = sessionStorage.getItem(`scroll-${location.pathname}`);
38-
if (saved) {
39-
requestAnimationFrame(() => window.scrollTo(0, parseInt(saved, 10)));
40-
} else {
41-
window.scrollTo(0, 0);
42-
}
43-
// Save position on leave
44-
return () => {
45-
sessionStorage.setItem(`scroll-${location.pathname}`, String(window.scrollY));
46-
};
47-
}, [location.pathname]);
48-
return null;
46+
// Cart sync wrapper component
47+
function CartSyncProvider({ children }: { children: React.ReactNode }) {
48+
useCartSync();
49+
return <>{children}</>;
4950
}
5051

51-
function AppContent() {
52-
useCartSync();
53-
const incognito = useIncognitoStore((s) => s.enabled);
54-
52+
// Clinical DNA verification: runs once on load to ensure brand tokens are active (see docs/LAUNCH_DAY_PROTOCOL.md)
53+
function useBrandDNAGuard() {
5554
useEffect(() => {
56-
document.body.classList.toggle("incognito-mode", incognito);
57-
}, [incognito]);
58-
59-
return (
60-
<>
61-
<ScrollRestoration />
62-
<Suspense fallback={<div className="min-h-screen bg-background" />}>
63-
<Routes>
64-
<Route path="/" element={<Index />} />
65-
<Route path="/products" element={<Products />} />
66-
<Route path="/shop" element={<Shop />} />
67-
<Route path="/product/:handle" element={<ProductDetail />} />
68-
<Route path="/brand" element={<BrandShowcase />} />
69-
<Route path="/auth" element={<Auth />} />
70-
<Route path="/intelligence" element={<Intelligence />} />
71-
<Route path="/lab" element={<LabTools />} />
72-
<Route path="/checkout" element={<Checkout />} />
73-
<Route path="/profile" element={<Profile />} />
74-
<Route path="/contact" element={<Contact />} />
75-
<Route path="/admin/enrichment" element={<AdminEnrichment />} />
76-
<Route path="/health" element={<Health />} />
77-
<Route path="/mom-baby" element={<MomBaby />} />
78-
<Route path="*" element={<NotFound />} />
79-
</Routes>
80-
</Suspense>
81-
<AIConcierge />
82-
</>
83-
);
55+
const t = setTimeout(verifyBrandDNA, 100);
56+
return () => clearTimeout(t);
57+
}, []);
8458
}
8559

8660
const App = () => {
87-
const [showSplash, setShowSplash] = useState(() => {
88-
// Only show splash once per session
89-
if (sessionStorage.getItem("asper-splash-seen")) return false;
90-
return true;
91-
});
92-
93-
const handleSplashComplete = useCallback(() => {
94-
sessionStorage.setItem("asper-splash-seen", "true");
95-
setShowSplash(false);
96-
}, []);
97-
61+
useBrandDNAGuard();
9862
return (
9963
<QueryClientProvider client={queryClient}>
100-
<TooltipProvider>
101-
<LanguageProvider>
102-
<Toaster />
103-
<Sonner />
104-
{showSplash && <SplashScreen onComplete={handleSplashComplete} />}
105-
<BrowserRouter>
106-
<AppContent />
107-
</BrowserRouter>
108-
</LanguageProvider>
109-
</TooltipProvider>
64+
<LanguageProvider>
65+
<CartSyncProvider>
66+
<TooltipProvider>
67+
<Toaster />
68+
<Sonner position="top-center" />
69+
<BrowserRouter>
70+
<Suspense fallback={null}>
71+
<BeautyAssistant />
72+
</Suspense>
73+
<Routes>
74+
<Route path="/" element={<Index />} />
75+
<Route path="/chat" element={<Index />} />
76+
<Route path="/shop" element={<Shop />} />
77+
<Route path="/products" element={<Shop />} />
78+
<Route path="/shop/organized" element={<ShopAllOrganized />} />
79+
<Route path="/product/:handle" element={<ProductDetail />} />
80+
<Route path="/collections" element={<Collections />} />
81+
<Route
82+
path="/collections/:slug"
83+
element={<CollectionDetail />}
84+
/>
85+
<Route path="/brands" element={<Brands />} />
86+
<Route path="/brands/vichy" element={<BrandVichy />} />
87+
<Route path="/best-sellers" element={<BestSellers />} />
88+
<Route path="/offers" element={<Offers />} />
89+
<Route path="/contact" element={<Contact />} />
90+
<Route path="/skin-concerns" element={<SkinConcerns />} />
91+
<Route
92+
path="/concerns/:concernSlug"
93+
element={<ConcernCollection />}
94+
/>
95+
<Route path="/wishlist" element={<Wishlist />} />
96+
<Route path="/auth" element={<Auth />} />
97+
<Route path="/account" element={<Account />} />
98+
<Route path="/philosophy" element={<Philosophy />} />
99+
<Route path="/intelligence" element={<AsperIntelligence />} />
100+
<Route path="/health" element={<Health />} />
101+
<Route path="/admin/bulk-upload" element={<BulkUpload />} />
102+
<Route path="/admin/orders" element={<AdminOrders />} />
103+
<Route path="/admin/products" element={<ManageProducts />} />
104+
<Route path="/track-order" element={<TrackOrder />} />
105+
<Route path="/tracking" element={<Navigate to="/track-order" replace />} />
106+
<Route path="/shipping" element={<Navigate to="/contact" replace />} />
107+
<Route path="/returns" element={<Navigate to="/contact" replace />} />
108+
<Route path="/consultation" element={<Navigate to="/skin-concerns" replace />} />
109+
<Route path="/driver" element={<DriverDashboard />} />
110+
<Route path="/admin/audit-logs" element={<AdminAuditLogs />} />
111+
<Route
112+
path="/brand-intelligence"
113+
element={
114+
<RequireAdmin>
115+
<BrandIntelligenceDashboard />
116+
</RequireAdmin>
117+
}
118+
/>
119+
<Route path="*" element={<NotFound />} />
120+
</Routes>
121+
</BrowserRouter>
122+
</TooltipProvider>
123+
</CartSyncProvider>
124+
</LanguageProvider>
110125
</QueryClientProvider>
111126
);
112127
};
113128

114-
export default App;
129+
export default App;

0 commit comments

Comments
 (0)