import React, { Component } from "react"; import Button from "@mui/material/Button"; import ButtonGroup from "@mui/material/ButtonGroup"; import IconButton from "@mui/material/IconButton"; import Typography from "@mui/material/Typography"; import Box from "@mui/material/Box"; import Tooltip from "@mui/material/Tooltip"; import TextField from "@mui/material/TextField"; import AddIcon from "@mui/icons-material/Add"; import RemoveIcon from "@mui/icons-material/Remove"; import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; import DeleteIcon from "@mui/icons-material/Delete"; import NotificationsIcon from "@mui/icons-material/Notifications"; import NotificationsActiveIcon from "@mui/icons-material/NotificationsActive"; import CircularProgress from "@mui/material/CircularProgress"; import { withI18n } from "../i18n/withTranslation.js"; import { PUSH_SUBSCRIPTIONS_CHANGED_EVENT, emitPushSubscriptionsChanged, isPushApiSupported, fetchPushConfiguration, registerPushServiceWorker, ensurePushSubscription, articlePushStatus, articlePushSubscribe, articlePushUnsubscribe, parseSubscribedStatus, parseSuccess, } from "../utils/articlePush.js"; if (!Array.isArray(window.cart)) window.cart = []; class AddToCartButton extends Component { constructor(props) { super(props); if (!Array.isArray(window.cart)) window.cart = []; this.state = { quantity: window.cart.find((i) => i.id === this.props.id) ? window.cart.find((i) => i.id === this.props.id).quantity : 0, isEditing: false, editValue: "", pushInteractive: false, pushSubscribed: false, pushBusy: false, pushError: null, }; } kArtikelNumber = () => { const { id } = this.props; const n = typeof id === "number" ? id : parseInt(id, 10); return Number.isFinite(n) && n > 0 ? n : null; }; refreshIncomingPushStatus = async () => { const { available, incoming } = this.props; if (available || !incoming) { this.setState({ pushInteractive: false, pushSubscribed: false, pushError: null, }); return; } const kArtikel = this.kArtikelNumber(); if (!kArtikel || !isPushApiSupported()) { this.setState({ pushInteractive: false }); return; } try { const cfg = await fetchPushConfiguration(); if (!cfg.configured || !cfg.publicKey) { this.setState({ pushInteractive: false }); return; } await registerPushServiceWorker(); const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.getSubscription(); if (!subscription) { this.setState({ pushInteractive: true, pushSubscribed: false, pushError: null, }); return; } const statusData = await articlePushStatus(kArtikel, subscription.endpoint); this.setState({ pushInteractive: true, pushSubscribed: parseSubscribedStatus(statusData), pushError: null, }); } catch (e) { console.warn("AddToCartButton: incoming push init failed", e); this.setState({ pushInteractive: false }); } }; handleIncomingPushClick = async () => { if (!this.state.pushInteractive || this.state.pushBusy) return; const kArtikel = this.kArtikelNumber(); if (!kArtikel) return; const t = this.props.t; this.setState({ pushBusy: true, pushError: null }); try { if (this.state.pushSubscribed) { const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.getSubscription(); if (!subscription) { this.setState({ pushSubscribed: false, pushBusy: false }); return; } const res = await articlePushUnsubscribe(subscription.endpoint, kArtikel); if (parseSuccess(res)) { this.setState({ pushSubscribed: false }); emitPushSubscriptionsChanged(); } else { this.setState({ pushError: res?.message || res?.error || (t ? t("productDialogs.pushNotifyError") : ""), }); } } else { const perm = await Notification.requestPermission(); if (perm !== "granted") { this.setState({ pushError: t ? t("productDialogs.pushNotifyPermissionDenied") : "", pushBusy: false, }); return; } const cfg = await fetchPushConfiguration(); if (!cfg.configured || !cfg.publicKey) { this.setState({ pushError: t ? t("productDialogs.pushNotifyServerDisabled") : "", pushBusy: false, }); return; } await registerPushServiceWorker(); const subscription = await ensurePushSubscription(cfg.publicKey); const res = await articlePushSubscribe(kArtikel, subscription); if (parseSuccess(res)) { this.setState({ pushSubscribed: true }); emitPushSubscriptionsChanged(); } else { this.setState({ pushError: res?.message || res?.error || (t ? t("productDialogs.pushNotifyError") : ""), }); } } } catch (e) { console.error("AddToCartButton: incoming push", e); this.setState({ pushError: e.message || (t ? t("productDialogs.pushNotifyError") : ""), }); } finally { this.setState({ pushBusy: false }); } }; componentDidMount() { this.cart = () => { if (!Array.isArray(window.cart)) window.cart = []; const item = window.cart.find((i) => i.id === this.props.id); const newQuantity = item ? item.quantity : 0; if (this.state.quantity !== newQuantity) this.setState({ quantity: newQuantity }); }; this.onPushSubscriptionsChanged = () => { this.refreshIncomingPushStatus(); }; window.addEventListener("cart", this.cart); window.addEventListener( PUSH_SUBSCRIPTIONS_CHANGED_EVENT, this.onPushSubscriptionsChanged ); this.refreshIncomingPushStatus(); } componentDidUpdate(prevProps) { if ( prevProps.available !== this.props.available || prevProps.incoming !== this.props.incoming || prevProps.id !== this.props.id ) { this.refreshIncomingPushStatus(); } } componentWillUnmount() { window.removeEventListener("cart", this.cart); window.removeEventListener( PUSH_SUBSCRIPTIONS_CHANGED_EVENT, this.onPushSubscriptionsChanged ); } handleIncrement = () => { if (!window.cart) window.cart = []; const idx = window.cart.findIndex((item) => item.id === this.props.id); if (idx === -1) { window.cart.push({ id: this.props.id, name: this.props.name, seoName: this.props.seoName, pictureList: this.props.pictureList, price: this.props.price, fGrundPreis: this.props.fGrundPreis, cGrundEinheit: this.props.cGrundEinheit, quantity: 1, weight: this.props.weight, vat: this.props.vat, versandklasse: this.props.versandklasse, availableSupplier: this.props.availableSupplier, komponenten: this.props.komponenten, available: this.props.available }); } else { window.cart[idx].quantity++; } window.dispatchEvent(new CustomEvent("cart")); }; handleDecrement = () => { if (!window.cart) window.cart = []; const idx = window.cart.findIndex((item) => item.id === this.props.id); if (idx !== -1) { if (window.cart[idx].quantity > 1) { window.cart[idx].quantity--; } else { window.cart.splice(idx, 1); } window.dispatchEvent(new CustomEvent("cart")); } }; handleClearCart = () => { if (!window.cart) window.cart = []; const idx = window.cart.findIndex((item) => item.id === this.props.id); if (idx !== -1) { window.cart.splice(idx, 1); window.dispatchEvent(new CustomEvent("cart")); } }; handleEditStart = () => { this.setState({ isEditing: true, editValue: this.state.quantity > 0 ? this.state.quantity.toString() : "", }); }; handleEditChange = (event) => { // Only allow numbers const value = event.target.value.replace(/[^0-9]/g, ""); this.setState({ editValue: value }); }; handleEditComplete = () => { let newQuantity = parseInt(this.state.editValue, 10); if (isNaN(newQuantity) || newQuantity < 0) { newQuantity = 0; } if (!window.cart) window.cart = []; const idx = window.cart.findIndex((item) => item.id === this.props.id); if (idx !== -1) { window.cart[idx].quantity = newQuantity; window.dispatchEvent( new CustomEvent("cart", { detail: { id: this.props.id, quantity: newQuantity }, }) ); } this.setState({ isEditing: false }); }; handleKeyPress = (event) => { if (event.key === "Enter") { this.handleEditComplete(); } }; toggleCart = () => { // Dispatch an event that Header.js can listen for to toggle the cart window.dispatchEvent(new CustomEvent("toggle-cart")); }; render() { const { quantity, isEditing, editValue, pushInteractive, pushSubscribed, pushBusy, pushError, } = this.state; const { available, size, incoming, availableSupplier } = this.props; // Button is disabled if product is not available if (!available) { if (incoming) { const dateStr = new Date(incoming).toLocaleDateString("de-DE", { year: "numeric", month: "long", day: "numeric", }); const dateLabel = this.props.t ? this.props.t("cart.availableFrom", { date: dateStr }) : `Ab ${dateStr}`; return ( {pushError && ( {pushError} )} ); } // If availableSupplier is 1, handle both quantity cases if (availableSupplier === 1) { // If no items in cart, show simple "Add to Cart" button with yellowish green if (quantity === 0) { return ( ); } // If items are in cart, show quantity controls with yellowish green if (quantity > 0) { return ( {isEditing ? ( e.target.select()} size="small" variant="standard" inputProps={{ style: { textAlign: "center", width: "30px", fontSize: "14px", padding: "2px", fontWeight: "bold", }, "aria-label": "quantity", }} sx={{ my: -0.5 }} /> ) : ( {quantity} )} {this.props.cartButton && ( )} ); } } return ( ); } // If no items in cart, show simple "Add to Cart" button if (quantity === 0) { return ( ); } // If items are in cart, show quantity controls return ( {isEditing ? ( e.target.select()} size="small" variant="standard" inputProps={{ style: { textAlign: "center", width: "30px", fontSize: "14px", padding: "2px", fontWeight: "bold", }, "aria-label": "quantity", }} sx={{ my: -0.5 }} /> ) : ( {quantity} )} {this.props.cartButton && ( )} ); } } export default withI18n()(AddToCartButton);