Compare commits
No commits in common. "73a522be604c5c975858c7c1d4a2d4d186ae5548" and "e390f7892621544578d4f2bcf1648a0764e89882" have entirely different histories.
73a522be60
...
e390f78926
@ -1,64 +1,64 @@
|
||||
.cart-overview {
|
||||
padding: 8px 4px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.cart-summary {
|
||||
background-color: #f7f9fc;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.count-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.food-count, .drink-count {
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
margin-top: 10px;
|
||||
color: #2c7be5;
|
||||
}
|
||||
|
||||
.cart-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.calculate-change-button, .clear-cart-button {
|
||||
.clear-cart-button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding: 12px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #ff5252;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.calculate-change-button {
|
||||
background-color: #2c7be5;
|
||||
color: white;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #2c7be5;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.clear-cart-button {
|
||||
background-color: #ff5252;
|
||||
color: white;
|
||||
.cart-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.calculate-change-button:active {
|
||||
background-color: #1b5eb5;
|
||||
background-color: #1b5eb5;
|
||||
}
|
||||
|
||||
.clear-cart-button:active {
|
||||
@ -68,51 +68,49 @@
|
||||
.cart-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #f7f9fc;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
background-color: white;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
gap: 5px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.item-option {
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-price {
|
||||
font-weight: bold;
|
||||
color: #2c7be5;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.item-quantity {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.item-quantity button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background-color: #2c7be5;
|
||||
@ -128,8 +126,7 @@
|
||||
|
||||
.empty-cart {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #888;
|
||||
padding: 30px;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@ -15,9 +15,6 @@ const ProductDetails: React.FC<ProductDetailsProps> = ({ product, category }) =>
|
||||
const { addToCart } = useCart();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Check if all options have the same price
|
||||
const hasSamePrice = product.options.every(option => option.price === product.options[0].price);
|
||||
|
||||
const handleAddToCart = () => {
|
||||
addToCart({
|
||||
productId: product.id,
|
||||
@ -29,10 +26,14 @@ const ProductDetails: React.FC<ProductDetailsProps> = ({ product, category }) =>
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const incrementQuantity = () => setQuantity(prev => prev + 1);
|
||||
const decrementQuantity = () => setQuantity(prev => Math.max(1, prev - 1));
|
||||
|
||||
// Check if all options have the same price
|
||||
const hasSamePrice = product.options.every(option => option.price === product.options[0].price);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="product-details"
|
||||
>
|
||||
<div className="product-details">
|
||||
<h2>{product.name}</h2>
|
||||
|
||||
<div className="options-list">
|
||||
@ -42,24 +43,24 @@ const ProductDetails: React.FC<ProductDetailsProps> = ({ product, category }) =>
|
||||
key={option.name}
|
||||
className={`option-item ${selectedOption.name === option.name ? 'selected' : ''}`}
|
||||
onClick={() => setSelectedOption(option)}
|
||||
style={(option.color || product.color) ?
|
||||
{ backgroundColor: `${option.color || product.color}60` } : {}}
|
||||
>
|
||||
<span>{option.name}</span>
|
||||
{!hasSamePrice && <span className="option-price">{option.price.toFixed(2)} €</span>}
|
||||
</div>
|
||||
))}
|
||||
{hasSamePrice && (
|
||||
<div className="uniform-price">Preis: {product.options[0].price.toFixed(2)} €</div>
|
||||
<div className="uniform-price">
|
||||
Preis: {product.options[0].price.toFixed(2)} €
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="quantity-controls">
|
||||
<h3>Menge:</h3>
|
||||
<div className="quantity-buttons">
|
||||
<button onClick={() => setQuantity(prev => Math.max(1, prev - 1))}>-</button>
|
||||
<button onClick={decrementQuantity}>-</button>
|
||||
<span>{quantity}</span>
|
||||
<button onClick={() => setQuantity(prev => prev + 1)}>+</button>
|
||||
<button onClick={incrementQuantity}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,35 +1,34 @@
|
||||
.product-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 6px;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.product-item:active {
|
||||
transform: scale(0.99);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-weight: 600;
|
||||
font-weight: bold;
|
||||
color: #2c7be5;
|
||||
font-size: 0.95rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ const ProductList: React.FC<ProductListProps> = ({ products, category }) => {
|
||||
to={`/product/${category}/${product.id}`}
|
||||
key={product.id}
|
||||
className="product-item"
|
||||
style={product.color ? { backgroundColor: `${product.color}80` } : {}}
|
||||
>
|
||||
<div className="product-name">{product.name}</div>
|
||||
<div className="product-price">{product.basePrice.toFixed(2)} €</div>
|
||||
|
||||
@ -4,10 +4,9 @@
|
||||
"id": "f1",
|
||||
"name": "Hendl",
|
||||
"basePrice": 9.50,
|
||||
"color": "#FFEBCD",
|
||||
"options": [
|
||||
{"name": "Halbes Hendl", "price": 9.50},
|
||||
{"name": "Ganzes Hendl", "price": 16.00, "color": "#FFDAB9"},
|
||||
{"name": "Ganzes Hendl", "price": 16.00},
|
||||
{"name": "Hendl mit Semmel", "price": 11.00}
|
||||
]
|
||||
},
|
||||
@ -15,18 +14,16 @@
|
||||
"id": "f2",
|
||||
"name": "Schnitzelsemmel",
|
||||
"basePrice": 5.80,
|
||||
"color": "#FFEFC1",
|
||||
"options": [
|
||||
{"name": "Klassisch", "price": 5.80},
|
||||
{"name": "Mit Salat", "price": 6.50, "color": "#E8F5E9"},
|
||||
{"name": "Mit Käse", "price": 6.80, "color": "#FFF9C4"}
|
||||
{"name": "Mit Salat", "price": 6.50},
|
||||
{"name": "Mit Käse", "price": 6.80}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "f3",
|
||||
"name": "Pommes",
|
||||
"basePrice": 3.50,
|
||||
"color": "#FFD700",
|
||||
"options": [
|
||||
{"name": "Klein", "price": 3.50},
|
||||
{"name": "Groß", "price": 4.50},
|
||||
@ -37,7 +34,6 @@
|
||||
"id": "f4",
|
||||
"name": "Grillwurst",
|
||||
"basePrice": 4.20,
|
||||
"color": "#FFCCCB",
|
||||
"options": [
|
||||
{"name": "Bratwurst", "price": 4.20},
|
||||
{"name": "Käsekrainer", "price": 4.50},
|
||||
@ -48,7 +44,6 @@
|
||||
"id": "f5",
|
||||
"name": "Leberkässemmel",
|
||||
"basePrice": 4.00,
|
||||
"color": "#FFE4E1",
|
||||
"options": [
|
||||
{"name": "Klassisch", "price": 4.00},
|
||||
{"name": "Mit Gurkerl", "price": 4.30},
|
||||
@ -59,7 +54,6 @@
|
||||
"id": "f6",
|
||||
"name": "Langos",
|
||||
"basePrice": 4.80,
|
||||
"color": "#DEB887",
|
||||
"options": [
|
||||
{"name": "Mit Knoblauch", "price": 4.80},
|
||||
{"name": "Mit Käse", "price": 5.50},
|
||||
@ -72,18 +66,16 @@
|
||||
"id": "d1",
|
||||
"name": "Bier",
|
||||
"basePrice": 4.20,
|
||||
"color": "#F0E68C",
|
||||
"options": [
|
||||
{"name": "Krügerl (0,5L)", "price": 4.20, "color": "#F5DEB3"},
|
||||
{"name": "Krügerl (0,5L)", "price": 4.20},
|
||||
{"name": "Seidl (0,3L)", "price": 3.50},
|
||||
{"name": "Radler (0,5L)", "price": 4.20, "color": "#FAFAD2"}
|
||||
{"name": "Radler (0,5L)", "price": 4.20}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d2",
|
||||
"name": "Cola",
|
||||
"basePrice": 3.50,
|
||||
"color": "#D2B48C",
|
||||
"options": [
|
||||
{"name": "Cola Rot (0,5L)", "price": 3.50},
|
||||
{"name": "Cola Weiß (0,5L)", "price": 3.50},
|
||||
@ -95,7 +87,6 @@
|
||||
"id": "d3",
|
||||
"name": "Softdrinks",
|
||||
"basePrice": 3.50,
|
||||
"color": "#FFDAB9",
|
||||
"options": [
|
||||
{"name": "Fanta (0,5L)", "price": 3.50},
|
||||
{"name": "Sprite (0,5L)", "price": 3.50},
|
||||
@ -107,7 +98,6 @@
|
||||
"id": "d4",
|
||||
"name": "Mineral",
|
||||
"basePrice": 2.80,
|
||||
"color": "#E0FFFF",
|
||||
"options": [
|
||||
{"name": "Prickelnd (0,5L)", "price": 2.80},
|
||||
{"name": "Still (0,5L)", "price": 2.80},
|
||||
@ -119,7 +109,6 @@
|
||||
"id": "d5",
|
||||
"name": "Gespritzt",
|
||||
"basePrice": 3.00,
|
||||
"color": "#FFDAB9",
|
||||
"options": [
|
||||
{"name": "Apfelsaft g'spritzt", "price": 3.00},
|
||||
{"name": "Johannisbeer g'spritzt", "price": 3.20},
|
||||
@ -131,7 +120,6 @@
|
||||
"id": "d6",
|
||||
"name": "Spritzer",
|
||||
"basePrice": 3.80,
|
||||
"color": "#FFE4E1",
|
||||
"options": [
|
||||
{"name": "Weißer Spritzer", "price": 3.80},
|
||||
{"name": "Aperol Spritzer", "price": 4.50},
|
||||
@ -143,7 +131,6 @@
|
||||
"id": "d7",
|
||||
"name": "Schnaps",
|
||||
"basePrice": 3.00,
|
||||
"color": "#E6E6FA",
|
||||
"options": [
|
||||
{"name": "Marille (2cl)", "price": 3.00},
|
||||
{"name": "Williams (2cl)", "price": 3.00},
|
||||
|
||||
@ -1,61 +1,37 @@
|
||||
.home-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #f9fafb;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
background-color: #2c7be5;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.app-header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 8px 0;
|
||||
font-size: 1.2rem;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #2c7be5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.category-tabs button {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
border: none;
|
||||
font-size: 1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.category-tabs button.active {
|
||||
@ -63,22 +39,14 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.products-container {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
.products-section, .cart-section {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.cart-container {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main-container {
|
||||
max-width: 800px;
|
||||
}
|
||||
.products-section h2, .cart-section h2 {
|
||||
margin: 0 10px 10px 10px;
|
||||
font-size: 1.3rem;
|
||||
color: #333;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid #2c7be5;
|
||||
}
|
||||
|
||||
@ -9,11 +9,23 @@ const HomePage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<'food' | 'drinks'>('drinks');
|
||||
|
||||
useEffect(() => {
|
||||
import('../data/products.json')
|
||||
.then(data => {
|
||||
setProductsData(data);
|
||||
})
|
||||
.catch(error => console.error('Failed to load products:', error));
|
||||
const loadProducts = async () => {
|
||||
try {
|
||||
const data = await import('../data/products.json');
|
||||
|
||||
// Sort both food and drinks by name
|
||||
const sortedData = {
|
||||
food: [...data.food].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
drinks: [...data.drinks].sort((a, b) => a.name.localeCompare(b.name))
|
||||
};
|
||||
|
||||
setProductsData(sortedData);
|
||||
} catch (error) {
|
||||
console.error('Failed to load products:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadProducts();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@ -22,34 +34,31 @@ const HomePage: React.FC = () => {
|
||||
<h1>Preis Rechner</h1>
|
||||
</header>
|
||||
|
||||
<div className="main-container">
|
||||
<div className="products-container">
|
||||
<div className="category-tabs">
|
||||
<button
|
||||
className={activeTab === 'drinks' ? 'active' : ''}
|
||||
onClick={() => setActiveTab('drinks')}
|
||||
>
|
||||
Getränke
|
||||
</button>
|
||||
<button
|
||||
className={activeTab === 'food' ? 'active' : ''}
|
||||
onClick={() => setActiveTab('food')}
|
||||
>
|
||||
Essen
|
||||
</button>
|
||||
</div>
|
||||
<div className="category-tabs">
|
||||
<button
|
||||
className={activeTab === 'drinks' ? 'active' : ''}
|
||||
onClick={() => setActiveTab('drinks')}
|
||||
>
|
||||
Trinken
|
||||
</button>
|
||||
<button
|
||||
className={activeTab === 'food' ? 'active' : ''}
|
||||
onClick={() => setActiveTab('food')}
|
||||
>
|
||||
Essen
|
||||
</button>
|
||||
|
||||
<h2 className="section-title">{activeTab === 'food' ? 'Essen' : 'Getränke'}</h2>
|
||||
<ProductList
|
||||
products={productsData[activeTab]}
|
||||
category={activeTab}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cart-container">
|
||||
<h2 className="section-title">Warenkorb</h2>
|
||||
<CartOverview />
|
||||
</div>
|
||||
<div className="products-section">
|
||||
<h2>{activeTab === 'food' ? 'Essen' : 'Getränke'}</h2>
|
||||
{activeTab === 'food' && <ProductList products={productsData.food} category="food" />}
|
||||
{activeTab === 'drinks' && <ProductList products={productsData.drinks} category="drinks" />}
|
||||
</div>
|
||||
|
||||
<div className="cart-section">
|
||||
<h2>Warenkorb</h2>
|
||||
<CartOverview />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
export type ProductOption = {
|
||||
name: string;
|
||||
price: number;
|
||||
color?: string; // Optional color for this specific option
|
||||
};
|
||||
|
||||
export type Product = {
|
||||
@ -9,7 +8,6 @@ export type Product = {
|
||||
name: string;
|
||||
basePrice: number;
|
||||
options: ProductOption[];
|
||||
color?: string; // Optional color code for background highlight
|
||||
};
|
||||
|
||||
export type ProductsData = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user