mirror of
https://github.com/harivansh-afk/GymSupps.git
synced 2026-04-15 09:01:16 +00:00
hotfix: shoppify hydrogen initialization with mock data
This commit is contained in:
parent
357979d0af
commit
4e59c6866d
15 changed files with 6510 additions and 115 deletions
3
.env.example
Normal file
3
.env.example
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Shopify Store Configuration
|
||||||
|
VITE_SHOPIFY_STORE_DOMAIN=your-store.myshopify.com
|
||||||
|
VITE_SHOPIFY_STOREFRONT_TOKEN=your-storefront-access-token
|
||||||
40
.gitignore
vendored
40
.gitignore
vendored
|
|
@ -1,24 +1,36 @@
|
||||||
# Logs
|
# Environment variables
|
||||||
logs
|
.env
|
||||||
*.log
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
.pnp/
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Debug logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.idea/
|
||||||
!.vscode/extensions.json
|
.vscode/
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
|
||||||
12
hydrogen.config.ts
Normal file
12
hydrogen.config.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {defineConfig} from '@shopify/hydrogen/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
shopify: {
|
||||||
|
storeDomain: 'your-store.myshopify.com',
|
||||||
|
storefrontToken: 'your_storefront_api_token',
|
||||||
|
storefrontApiVersion: '2024-01', // Use the latest API version
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
storage: 'memory', // You can change this to 'redis' or other storage methods in production
|
||||||
|
},
|
||||||
|
});
|
||||||
6236
package-lock.json
generated
6236
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -16,8 +16,14 @@
|
||||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||||
"@radix-ui/react-separator": "^1.0.3",
|
"@radix-ui/react-separator": "^1.0.3",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"@shopify/cli": "^3.75.4",
|
||||||
|
"@shopify/cli-hydrogen": "^9.0.8",
|
||||||
|
"@shopify/hydrogen": "^2025.1.2",
|
||||||
|
"@shopify/hydrogen-react": "^2025.1.2",
|
||||||
|
"@shopify/storefront-kit-react": "^2023.1.3",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
|
"graphql-tag": "^2.12.6",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|
|
||||||
137
src/App.tsx
137
src/App.tsx
|
|
@ -1,79 +1,90 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
||||||
|
import { ProductsPage } from './pages/Products';
|
||||||
import { Header } from './components/Header';
|
import { Header } from './components/Header';
|
||||||
import { ProductGrid } from './components/ProductGrid';
|
import { ProductGrid } from './components/ProductGrid';
|
||||||
import { ProductDetails } from './components/ProductDetails';
|
import { ProductDetails } from './components/ProductDetails';
|
||||||
import { products } from './data/products';
|
import { products } from './data/products';
|
||||||
|
|
||||||
function App() {
|
const App: React.FC = () => {
|
||||||
const [selectedProduct, setSelectedProduct] = useState<string | null>(null);
|
const [selectedProduct, setSelectedProduct] = useState<string | null>(null);
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||||
|
|
||||||
const categories = Array.from(new Set(products.map((p) => p.category)));
|
const categories = Array.from(new Set(products.map((p) => p.category)));
|
||||||
const currentProduct = products.find((p) => p.id === selectedProduct);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<Router>
|
||||||
<Header />
|
<div className="min-h-screen bg-background">
|
||||||
<main className="max-w-7xl mx-auto px-4 py-8">
|
<Header />
|
||||||
{!selectedProduct ? (
|
<main className="container mx-auto p-4">
|
||||||
<>
|
<nav className="mb-4">
|
||||||
<section className="text-center mb-12">
|
<Link to="/local-products" className="mr-4 text-primary hover:text-primary/80">
|
||||||
<h1 className="text-4xl font-bold mb-4">
|
Local Products
|
||||||
Premium Supplements for Your Fitness Journey
|
</Link>
|
||||||
</h1>
|
<Link to="/shopify-products" className="text-primary hover:text-primary/80">
|
||||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
Shopify Products
|
||||||
Discover our range of high-quality supplements designed to help you achieve your fitness goals.
|
</Link>
|
||||||
</p>
|
</nav>
|
||||||
</section>
|
<Routes>
|
||||||
|
<Route
|
||||||
<div className="mb-8">
|
path="/local-products"
|
||||||
<div className="flex flex-wrap gap-4 justify-center">
|
element={
|
||||||
<button
|
<>
|
||||||
onClick={() => setSelectedCategory(null)}
|
<div className="mb-8">
|
||||||
className={`px-4 py-2 rounded-full ${
|
<div className="flex flex-wrap gap-4 justify-center">
|
||||||
!selectedCategory
|
<button
|
||||||
? 'bg-primary text-primary-foreground'
|
onClick={() => setSelectedCategory(null)}
|
||||||
: 'bg-secondary text-secondary-foreground'
|
className={`px-4 py-2 rounded-full ${
|
||||||
}`}
|
!selectedCategory
|
||||||
>
|
? 'bg-primary text-primary-foreground'
|
||||||
All Products
|
: 'bg-secondary text-secondary-foreground'
|
||||||
</button>
|
}`}
|
||||||
{categories.map((category) => (
|
>
|
||||||
<button
|
All Products
|
||||||
key={category}
|
</button>
|
||||||
onClick={() => setSelectedCategory(category)}
|
{categories.map((category) => (
|
||||||
className={`px-4 py-2 rounded-full ${
|
<button
|
||||||
selectedCategory === category
|
key={category}
|
||||||
? 'bg-primary text-primary-foreground'
|
onClick={() => setSelectedCategory(category)}
|
||||||
: 'bg-secondary text-secondary-foreground'
|
className={`px-4 py-2 rounded-full ${
|
||||||
}`}
|
selectedCategory === category
|
||||||
>
|
? 'bg-primary text-primary-foreground'
|
||||||
{category}
|
: 'bg-secondary text-secondary-foreground'
|
||||||
</button>
|
}`}
|
||||||
))}
|
>
|
||||||
</div>
|
{category}
|
||||||
</div>
|
</button>
|
||||||
|
))}
|
||||||
<ProductGrid
|
</div>
|
||||||
products={products}
|
</div>
|
||||||
category={selectedCategory ?? undefined}
|
{selectedProduct ? (
|
||||||
onProductSelect={setSelectedProduct}
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedProduct(null)}
|
||||||
|
className="mb-8 text-primary hover:underline flex items-center gap-2"
|
||||||
|
>
|
||||||
|
← Back to Products
|
||||||
|
</button>
|
||||||
|
<ProductDetails
|
||||||
|
product={products.find((p) => p.id === selectedProduct)!}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<ProductGrid
|
||||||
|
products={products}
|
||||||
|
category={selectedCategory || undefined}
|
||||||
|
onProductSelect={setSelectedProduct}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
<Route path="/shopify-products" element={<ProductsPage />} />
|
||||||
) : (
|
</Routes>
|
||||||
<div>
|
</main>
|
||||||
<button
|
</div>
|
||||||
onClick={() => setSelectedProduct(null)}
|
</Router>
|
||||||
className="mb-8 text-primary hover:underline flex items-center gap-2"
|
|
||||||
>
|
|
||||||
← Back to Products
|
|
||||||
</button>
|
|
||||||
{currentProduct && <ProductDetails product={currentProduct} />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
55
src/components/ShopifyProductCard.tsx
Normal file
55
src/components/ShopifyProductCard.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Money, Image } from '@shopify/hydrogen-react';
|
||||||
|
import { ShoppingCart } from 'lucide-react';
|
||||||
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from './ui/card';
|
||||||
|
import { Button } from './ui/button';
|
||||||
|
import { ShopifyProduct } from '../types/shopify';
|
||||||
|
|
||||||
|
interface ShopifyProductCardProps {
|
||||||
|
product: ShopifyProduct;
|
||||||
|
onSelect: (productId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShopifyProductCard: React.FC<ShopifyProductCardProps> = ({ product, onSelect }) => {
|
||||||
|
if (!product) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="cursor-pointer" onClick={() => onSelect(product.id)}>
|
||||||
|
<CardHeader className="p-0">
|
||||||
|
{product.featuredImage && (
|
||||||
|
<Image
|
||||||
|
data={product.featuredImage}
|
||||||
|
alt={product.title}
|
||||||
|
className="w-full h-48 object-cover rounded-t-xl"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex justify-between items-start mb-2">
|
||||||
|
<CardTitle className="text-lg">{product.title}</CardTitle>
|
||||||
|
<Money
|
||||||
|
data={product.priceRange.minVariantPrice}
|
||||||
|
className="text-lg font-bold text-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground text-sm mb-4 line-clamp-2">
|
||||||
|
{product.description}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
// You'll need to implement your own add to cart logic here
|
||||||
|
console.log('Add to cart:', product.id);
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<ShoppingCart className="w-4 h-4 mr-2" />
|
||||||
|
Add to Cart
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
22
src/components/ShopifyProductGrid.tsx
Normal file
22
src/components/ShopifyProductGrid.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ShopifyProductCard } from './ShopifyProductCard';
|
||||||
|
import { ShopifyProduct } from '../types/shopify';
|
||||||
|
|
||||||
|
export const ShopifyProductGrid: React.FC<{ products: ShopifyProduct[] }> = ({ products }) => {
|
||||||
|
const handleProductSelect = (productId: string) => {
|
||||||
|
// Handle product selection - you can implement navigation to product details page
|
||||||
|
console.log('Selected product:', productId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
|
||||||
|
{products.map((product) => (
|
||||||
|
<ShopifyProductCard
|
||||||
|
key={product.id}
|
||||||
|
product={product}
|
||||||
|
onSelect={handleProductSelect}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
58
src/data/mockShopifyProducts.ts
Normal file
58
src/data/mockShopifyProducts.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { ShopifyProduct } from '../types/shopify';
|
||||||
|
|
||||||
|
export const mockProducts: ShopifyProduct[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: 'Whey Protein Isolate',
|
||||||
|
description: 'Premium whey protein isolate for optimal muscle recovery and growth.',
|
||||||
|
handle: 'whey-protein-isolate',
|
||||||
|
priceRange: {
|
||||||
|
minVariantPrice: {
|
||||||
|
amount: '49.99',
|
||||||
|
currencyCode: 'USD'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
featuredImage: {
|
||||||
|
url: 'https://images.unsplash.com/photo-1593095948071-474c5cc2989d?q=80&w=2670&auto=format&fit=crop',
|
||||||
|
altText: 'Whey Protein Container',
|
||||||
|
width: 800,
|
||||||
|
height: 800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: 'Pre-Workout Energy',
|
||||||
|
description: 'Advanced pre-workout formula for enhanced performance and focus.',
|
||||||
|
handle: 'pre-workout-energy',
|
||||||
|
priceRange: {
|
||||||
|
minVariantPrice: {
|
||||||
|
amount: '39.99',
|
||||||
|
currencyCode: 'USD'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
featuredImage: {
|
||||||
|
url: 'https://images.unsplash.com/photo-1594882645126-14020914d58d?q=80&w=2785&auto=format&fit=crop',
|
||||||
|
altText: 'Pre-workout Container',
|
||||||
|
width: 800,
|
||||||
|
height: 800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: 'BCAA Complex',
|
||||||
|
description: 'Essential branched-chain amino acids for muscle support and recovery.',
|
||||||
|
handle: 'bcaa-complex',
|
||||||
|
priceRange: {
|
||||||
|
minVariantPrice: {
|
||||||
|
amount: '29.99',
|
||||||
|
currencyCode: 'USD'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
featuredImage: {
|
||||||
|
url: 'https://images.unsplash.com/photo-1579722820308-d74e571900a9?q=80&w=2670&auto=format&fit=crop',
|
||||||
|
altText: 'BCAA Container',
|
||||||
|
width: 800,
|
||||||
|
height: 800
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
10
src/env.d.ts
vendored
Normal file
10
src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_SHOPIFY_STORE_DOMAIN: string;
|
||||||
|
readonly VITE_SHOPIFY_STOREFRONT_TOKEN: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
11
src/main.tsx
11
src/main.tsx
|
|
@ -1,10 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { ShopifyProvider } from '@shopify/hydrogen-react';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<ShopifyProvider
|
||||||
|
storeDomain={import.meta.env.VITE_SHOPIFY_STORE_DOMAIN}
|
||||||
|
storefrontToken={import.meta.env.VITE_SHOPIFY_STOREFRONT_TOKEN}
|
||||||
|
storefrontApiVersion="2024-01"
|
||||||
|
countryIsoCode="US"
|
||||||
|
languageIsoCode="EN"
|
||||||
|
>
|
||||||
|
<App />
|
||||||
|
</ShopifyProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
7
src/pages/Products.tsx
Normal file
7
src/pages/Products.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ShopifyProductGrid } from '../components/ShopifyProductGrid';
|
||||||
|
import { mockProducts } from '../data/mockShopifyProducts';
|
||||||
|
|
||||||
|
export const ProductsPage: React.FC = () => {
|
||||||
|
return <ShopifyProductGrid products={mockProducts} />;
|
||||||
|
};
|
||||||
18
src/types/shopify.ts
Normal file
18
src/types/shopify.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
export interface ShopifyProduct {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
handle: string;
|
||||||
|
priceRange: {
|
||||||
|
minVariantPrice: {
|
||||||
|
amount: string;
|
||||||
|
currencyCode: 'USD' | 'EUR' | 'GBP' | 'CAD' | 'AUD';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
featuredImage: {
|
||||||
|
url: string;
|
||||||
|
altText: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue