source: interfejs/product-detail.html

main
Last change on this file was 69f2a41, checked in by Klimentina Efremova <klimentina08642@…>, 7 months ago

Initial commit

  • Property mode set to 100644
File size: 16.7 KB
Line 
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Product Details - Handcraft Marketplace</title>
7 <link rel="stylesheet" href="style.css">
8</head>
9<body>
10<header>
11 <div class="logo-container">
12 <img src="../images/administrative/logo.jpg" alt="Handcraft Marketplace Logo" onerror="this.style.display='none'">
13 <h1>🎨 Handcraft Marketplace</h1>
14 </div>
15 <nav>
16 <ul>
17 <li><a href="index.html">Home</a></li>
18 <li><a href="products.html">Products</a></li>
19 <li><a href="login.html">Login</a></li>
20 <li><a href="register.html">Register</a></li>
21 </ul>
22 </nav>
23</header>
24
25<main>
26 <div class="breadcrumb">
27 <a href="index.html">Home</a> &gt;
28 <a href="products.html">Products</a> &gt;
29 <span id="product-category">Loading...</span>
30 </div>
31
32 <div id="product-detail-container" class="product-detail">
33 <div class="loading">Loading product details...</div>
34 </div>
35
36 <div id="reviews-section" class="dashboard-section" style="margin-top: 2rem; display: none;">
37 <h3>Customer Reviews</h3>
38 <div id="reviews-list" class="reviews-list">
39 <div class="loading">Loading reviews...</div>
40 </div>
41 </div>
42
43 <div id="related-products" class="related-products" style="margin-top: 2rem; display: none;">
44 <h3>Related Products</h3>
45 <div class="product-grid" id="related-products-grid">
46 <div class="loading">Loading related products...</div>
47 </div>
48 </div>
49</main>
50
51<div id="login-prompt-modal" class="modal" style="display: none;">
52 <div class="modal-content">
53 <span class="close">&times;</span>
54 <h2>Login Required</h2>
55 <p>Please login or register to add items to your cart.</p>
56 <div style="display: flex; gap: 1rem; justify-content: center; margin-top: 2rem;">
57 <a href="login.html" class="btn-primary">Login</a>
58 <a href="register.html" class="btn-secondary">Register</a>
59 </div>
60 </div>
61</div>
62
63<footer>
64 <div class="footer-content">
65 <div class="footer-section">
66 <h3>Handcraft Marketplace</h3>
67 <p>Connecting artisans with buyers worldwide</p>
68 </div>
69 <div class="footer-section">
70 <h3>Quick Links</h3>
71 <ul>
72 <li><a href="index.html">Home</a></li>
73 <li><a href="products.html">Products</a></li>
74 </ul>
75 </div>
76 <div class="footer-section">
77 <h3>Contact</h3>
78 <p>Email: support@handcraft.com</p>
79 </div>
80 </div>
81 <div class="footer-bottom">
82 <p>&copy; 2025 Handcraft Marketplace. All rights reserved.</p>
83 </div>
84</footer>
85
86<script src="script.js"></script>
87<script>
88 document.addEventListener('DOMContentLoaded', () => {
89 const urlParams = new URLSearchParams(window.location.search);
90 const productCode = urlParams.get('code') || urlParams.get('id');
91
92 if (!productCode) {
93 window.location.href = 'products.html';
94 return;
95 }
96
97 loadProductDetails(productCode);
98 loadProductReviews(productCode);
99 loadRelatedProducts(productCode);
100 });
101
102 async function loadProductDetails(productCode) {
103 try {
104 const response = await fetch(`/api/product-by-code?code=${productCode}`);
105 const data = await response.json();
106
107 if (data.success) {
108 const product = data.product;
109 document.getElementById('product-category').textContent = product.category_name || 'Product';
110
111 const container = document.getElementById('product-detail-container');
112
113 // Get images
114 const images = product.images || [];
115 const mainImage = images.length > 0 ? images[0].image : '../images/product-placeholder.jpg';
116
117 // Get colors
118 const colors = product.colors || [];
119
120 // Calculate total reviews and average rating
121 const avgRating = product.avg_rating || 0;
122 const totalReviews = product.review_count || 0;
123
124 container.innerHTML = `
125 <div class="product-gallery">
126 <div class="main-image">
127 <img src="${mainImage}" alt="${product.description}"
128 onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22400%22 height=%22400%22 viewBox=%220 0 400 400%22><rect width=%22400%22 height=%22400%22 fill=%22%23f0f0f0%22/><text x=%22150%22 y=%22200%22 font-family=%22Arial%22 font-size=%2224%22 fill=%22%23999%22>No Image</text></svg>'">
129 </div>
130 ${images.length > 1 ? `
131 <div class="thumbnail-grid">
132 ${images.map(img => `
133 <img src="${img.image}" class="thumbnail" onclick="updateMainImage(this.src)"
134 onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2280%22 height=%2280%22 viewBox=%220 0 80 80%22><rect width=%2280%22 height=%2280%22 fill=%22%23f0f0f0%22/></svg>'">
135 `).join('')}
136 </div>
137 ` : ''}
138 </div>
139
140 <div class="product-info">
141 <h1 class="product-title">${product.description}</h1>
142
143 <div class="product-meta">
144 <span class="product-store">Sold by: ${product.store_name || 'Unknown Store'}</span>
145 <span class="product-code">Code: ${product.code}</span>
146 </div>
147
148 <div class="product-rating">
149 <span class="rating-stars">${'★'.repeat(Math.floor(avgRating))}${'☆'.repeat(5 - Math.floor(avgRating))}</span>
150 <span class="rating-count">(${totalReviews} reviews)</span>
151 </div>
152
153 <div class="product-price-large">$${parseFloat(product.price).toFixed(2)}</div>
154
155 <div class="product-availability-large">
156 ${product.availability > 0 ?
157 `<span class="in-stock">✓ In Stock (${product.availability} available)</span>` :
158 '<span class="out-of-stock">✗ Out of Stock</span>'}
159 </div>
160
161 <div class="product-details-grid">
162 <div class="detail-item">
163 <strong>Weight:</strong> ${product.weight} kg
164 </div>
165 <div class="detail-item">
166 <strong>Dimensions:</strong> ${product.dimensions || 'N/A'}
167 </div>
168 <div class="detail-item">
169 <strong>Production Time:</strong> ${product.production_time || '1'} day(s)
170 </div>
171 <div class="detail-item">
172 <strong>Category:</strong> ${product.category_name || 'General'}
173 </div>
174 </div>
175
176 ${colors.length > 0 ? `
177 <div class="product-colors">
178 <strong>Available Colors:</strong>
179 <div class="color-list">
180 ${colors.map(c => `
181 <span class="color-tag" style="background-color: ${c.color.toLowerCase()}; color: white; padding: 0.25rem 0.5rem; border-radius: 3px; margin-right: 0.5rem; display: inline-block; margin-bottom: 0.5rem;">
182 ${c.color}
183 </span>
184 `).join('')}
185 </div>
186 </div>
187 ` : ''}
188
189 <div class="product-actions">
190 <div class="quantity-selector">
191 <label for="quantity">Quantity:</label>
192 <input type="number" id="quantity" min="1" max="${product.availability}" value="1" ${product.availability === 0 ? 'disabled' : ''}>
193 </div>
194
195 <button id="add-to-cart" class="btn-primary btn-large" ${product.availability === 0 ? 'disabled' : ''}>
196 Add to Cart
197 </button>
198
199 <button id="buy-now" class="btn-secondary btn-large" ${product.availability === 0 ? 'disabled' : ''}>
200 Buy Now
201 </button>
202 </div>
203
204 <div class="product-description">
205 <h3>Description</h3>
206 <p>${product.description}</p>
207 </div>
208
209 <div class="store-info">
210 <h3>Store Information</h3>
211 <p><strong>Store:</strong> ${product.store_name || 'Unknown Store'}</p>
212 <p><strong>Store Email:</strong> ${product.store_email || 'N/A'}</p>
213 <p><strong>Store Rating:</strong> ${product.store_rating || '0.0'}/5.0</p>
214 <a href="#" class="btn-small" onclick="viewStore('${product.store_id}')">Visit Store</a>
215 </div>
216 </div>
217 `;
218
219 // Add event listeners
220 document.getElementById('add-to-cart')?.addEventListener('click', () => addToCart(product));
221 document.getElementById('buy-now')?.addEventListener('click', () => buyNow(product));
222
223 document.getElementById('reviews-section').style.display = 'block';
224 document.getElementById('related-products').style.display = 'block';
225 } else {
226 document.getElementById('product-detail-container').innerHTML = '<p class="error">Product not found</p>';
227 }
228 } catch (error) {
229 console.error('Error loading product:', error);
230 document.getElementById('product-detail-container').innerHTML = '<p class="error">Error loading product details</p>';
231 }
232 }
233
234 async function loadProductReviews(productCode) {
235 // This would need a separate API endpoint
236 setTimeout(() => {
237 document.getElementById('reviews-list').innerHTML = `
238 <div class="review-card">
239 <div class="review-header">
240 <span class="review-author">John Doe</span>
241 <span class="review-rating">★★★★★</span>
242 <span class="review-date">${new Date().toLocaleDateString()}</span>
243 </div>
244 <p class="review-comment">Excellent product! Very happy with my purchase.</p>
245 </div>
246 <div class="review-card">
247 <div class="review-header">
248 <span class="review-author">Jane Smith</span>
249 <span class="review-rating">★★★★☆</span>
250 <span class="review-date">${new Date(Date.now() - 86400000).toLocaleDateString()}</span>
251 </div>
252 <p class="review-comment">Good quality, fast shipping. Would recommend.</p>
253 </div>
254 `;
255 }, 500);
256 }
257
258 async function loadRelatedProducts(productCode) {
259 try {
260 const response = await fetch('/api/products');
261 const data = await response.json();
262
263 if (data.success) {
264 // Get random products excluding current one
265 const otherProducts = (data.products || [])
266 .filter(p => p.code !== productCode)
267 .slice(0, 4);
268
269 const grid = document.getElementById('related-products-grid');
270
271 if (otherProducts.length > 0) {
272 grid.innerHTML = otherProducts.map(product => `
273 <div class="product-card">
274 <div class="product-image">
275 <img src="../images/product-placeholder.jpg" alt="${product.description.substring(0, 30)}"
276 onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%22 height=%22100%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 fill=%22%23f0f0f0%22/></svg>'">
277 </div>
278 <div class="product-info">
279 <h3>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</h3>
280 <p class="product-price">$${parseFloat(product.price).toFixed(2)}</p>
281 <a href="product-detail.html?code=${product.code}" class="btn-small">View</a>
282 </div>
283 </div>
284 `).join('');
285 } else {
286 grid.innerHTML = '<p>No related products found</p>';
287 }
288 }
289 } catch (error) {
290 console.error('Error loading related products:', error);
291 }
292 }
293
294 window.updateMainImage = (src) => {
295 document.querySelector('.main-image img').src = src;
296 };
297
298 async function addToCart(product) {
299 const auth = await checkAuth();
300
301 if (!auth.success) {
302 document.getElementById('login-prompt-modal').style.display = 'block';
303 return;
304 }
305
306 if (auth.user.userType !== 'client') {
307 alert('Only clients can purchase products');
308 return;
309 }
310
311 const quantity = parseInt(document.getElementById('quantity').value);
312
313 // Get existing cart from session storage
314 let cart = JSON.parse(sessionStorage.getItem('cart') || '[]');
315
316 // Check if product already in cart
317 const existingItem = cart.find(item => item.code === product.code);
318
319 if (existingItem) {
320 existingItem.quantity += quantity;
321 } else {
322 cart.push({
323 code: product.code,
324 description: product.description,
325 price: product.price,
326 quantity: quantity,
327 storeId: product.store_id,
328 storeName: product.store_name,
329 image: product.images && product.images.length > 0 ? product.images[0].image : null
330 });
331 }
332
333 sessionStorage.setItem('cart', JSON.stringify(cart));
334
335 alert(`Added ${quantity} item(s) to cart!`);
336 }
337
338 async function buyNow(product) {
339 const auth = await checkAuth();
340
341 if (!auth.success) {
342 document.getElementById('login-prompt-modal').style.display = 'block';
343 return;
344 }
345
346 if (auth.user.userType !== 'client') {
347 alert('Only clients can purchase products');
348 return;
349 }
350
351 const quantity = parseInt(document.getElementById('quantity').value);
352
353 // Create a single-item cart and go to checkout
354 const cart = [{
355 code: product.code,
356 description: product.description,
357 price: product.price,
358 quantity: quantity,
359 storeId: product.store_id,
360 storeName: product.store_name
361 }];
362
363 sessionStorage.setItem('cart', JSON.stringify(cart));
364 window.location.href = 'checkout.html';
365 }
366
367 window.viewStore = (storeId) => {
368 alert(`View store ${storeId} - Feature coming soon`);
369 };
370
371 // Close modal functionality
372 document.querySelectorAll('.modal .close').forEach(closeBtn => {
373 closeBtn.addEventListener('click', () => {
374 closeBtn.closest('.modal').style.display = 'none';
375 });
376 });
377
378 window.addEventListener('click', (event) => {
379 if (event.target.classList.contains('modal')) {
380 event.target.style.display = 'none';
381 }
382 });
383</script>
384</body>
385</html>
Note: See TracBrowser for help on using the repository browser.