summaryrefslogtreecommitdiff
path: root/src-migrate/utils
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2025-05-31 02:33:14 +0000
committerIT Fixcomart <it@fixcomart.co.id>2025-05-31 02:33:14 +0000
commit2a1dea70b8f0062fe8eebeb7139a7b77a24e220b (patch)
treee7a5db13b2655cbdfb1c81859e240652fdc87bbb /src-migrate/utils
parent0b2e31247d4fe7eb1432079979478a0cfc38d049 (diff)
parent2732c04b36f98a25895826b28003b1e2c56ad952 (diff)
Merged in fix_responsive_cart (pull request #413)
<miqdad> Fix unresponsive cart
Diffstat (limited to 'src-migrate/utils')
-rw-r--r--src-migrate/utils/cart.js455
-rw-r--r--src-migrate/utils/checkBoxState.js90
2 files changed, 545 insertions, 0 deletions
diff --git a/src-migrate/utils/cart.js b/src-migrate/utils/cart.js
new file mode 100644
index 00000000..4bdee49a
--- /dev/null
+++ b/src-migrate/utils/cart.js
@@ -0,0 +1,455 @@
+// cart-cookie-utils.js
+import Cookies from 'js-cookie';
+import checkboxUpdateState from './checkBoxState';
+
+// Constants
+const CART_ITEMS_COOKIE = 'cart_data';
+const SELECTED_ITEMS_COOKIE = 'cart_selected_items';
+const COOKIE_EXPIRY_DAYS = 7; // Cookie akan berlaku selama 7 hari
+
+/**
+ * Mengambil data cart lengkap dari cookie
+ * @returns {Object} Object dengan key cart_id dan value cart item data lengkap
+ */
+export const getCartDataFromCookie = () => {
+ try {
+ const storedData = Cookies.get(CART_ITEMS_COOKIE);
+ return storedData ? JSON.parse(storedData) : {};
+ } catch (error) {
+ console.error('Error reading cart data from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Menyimpan data cart lengkap ke cookie
+ * @param {Object} cartData Object dengan key cart_id dan value cart item data lengkap
+ */
+export const setCartDataToCookie = (cartData) => {
+ try {
+ Cookies.set(CART_ITEMS_COOKIE, JSON.stringify(cartData), {
+ expires: COOKIE_EXPIRY_DAYS,
+ path: '/',
+ sameSite: 'strict',
+ });
+ } catch (error) {
+ console.error('Error saving cart data to cookie:', error);
+ }
+};
+
+/**
+ * Mengambil state selected items dari cookie
+ * @returns {Object} Object dengan key product id dan value boolean selected status
+ */
+export const getSelectedItemsFromCookie = () => {
+ try {
+ const storedItems = Cookies.get(SELECTED_ITEMS_COOKIE);
+ return storedItems ? JSON.parse(storedItems) : {};
+ } catch (error) {
+ console.error('Error reading selected items from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Menyimpan state selected items ke cookie
+ * @param {Object} items Object dengan key product id dan value boolean selected status
+ */
+export const setSelectedItemsToCookie = (items) => {
+ try {
+ Cookies.set(SELECTED_ITEMS_COOKIE, JSON.stringify(items), {
+ expires: COOKIE_EXPIRY_DAYS,
+ path: '/',
+ sameSite: 'strict',
+ });
+ } catch (error) {
+ console.error('Error saving selected items to cookie:', error);
+ }
+};
+
+/**
+ * Transform cart items dari format API ke format yang lebih simpel untuk disimpan di cookie
+ * @param {Array} cartItems Array cart items dari API
+ * @returns {Object} Object dengan key cart_id dan value cart item data
+ */
+export const transformCartItemsForCookie = (cartItems) => {
+ if (!cartItems || !Array.isArray(cartItems)) return {};
+
+ const cartData = {};
+
+ cartItems.forEach((item) => {
+ // Skip items yang tidak memiliki cart_id
+ if (!item.cart_id) return;
+
+ cartData[item.cart_id] = {
+ id: item.id,
+ cart_id: item.cart_id,
+ cart_type: item.cart_type,
+ product: item.product_id
+ ? {
+ id: item.product_id,
+ name: item.product_name || '',
+ }
+ : null,
+ program_line: item.program_line_id
+ ? {
+ id: item.program_line_id,
+ name: item.program_line_name || '',
+ }
+ : null,
+ quantity: item.quantity,
+ selected: item.selected,
+ price: item.price,
+ package_price: item.package_price,
+ source: item.source || 'add_to_cart',
+ };
+ });
+
+ return cartData;
+};
+
+/**
+ * Sinkronisasi cart data dan selected items dari server dengan cookie
+ * @param {Object} cart Cart object dari API
+ * @returns {Object} Object yang berisi updated cartData dan selectedItems
+ */
+export const syncCartWithCookie = (cart) => {
+ try {
+ if (!cart || !cart.products) return { needsUpdate: false };
+
+ // Transform data API ke cookie
+ const serverCartData = transformCartItemsForCookie(cart.products);
+
+ // Ambil data lama dari cookie
+ const existingCartData = getCartDataFromCookie();
+
+ // Ambil selected status dari cookie
+ const selectedItems = getSelectedItemsFromCookie();
+
+ // Gabungkan data cart, (prioritize data server)
+ const mergedCartData = { ...existingCartData, ...serverCartData };
+
+ // Periksa apakah ada perbedaan status selected
+ let needsUpdate = false;
+
+ // Update selected status berdasarkan cookie jika ada
+ for (const cartId in mergedCartData) {
+ const item = mergedCartData[cartId];
+ if (item.id && selectedItems[item.id] !== undefined) {
+ // Jika status di cookie berbeda dengan di cart
+ if (item.selected !== selectedItems[item.id]) {
+ needsUpdate = true;
+ item.selected = selectedItems[item.id];
+ }
+ } else if (item.id) {
+ selectedItems[item.id] = item.selected;
+ }
+ }
+
+ // Simpan ke cookie
+ setCartDataToCookie(mergedCartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return {
+ cartData: mergedCartData,
+ selectedItems,
+ needsUpdate,
+ };
+ } catch (error) {
+ console.error('Error syncing cart with cookie:', error);
+ return { needsUpdate: false };
+ }
+};
+
+/**
+ * Update selected status item di cookie
+ * @param {number} productId ID produk
+ * @param {boolean} isSelected Status selected baru
+ * @param {boolean} notifyUpdate Whether to notify checkbox update state (default: true)
+ */
+export const updateSelectedItemInCookie = (
+ productId,
+ isSelected,
+ notifyUpdate = true
+) => {
+ try {
+ // Notify checkbox update state if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.startUpdate();
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+ selectedItems[productId] = isSelected;
+ setSelectedItemsToCookie(selectedItems);
+
+ // Update juga di cart data
+ const cartData = getCartDataFromCookie();
+
+ for (const cartId in cartData) {
+ const item = cartData[cartId];
+ if (item.id === productId) {
+ item.selected = isSelected;
+ }
+ }
+
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error updating selected item in cookie:', error);
+ return {};
+ } finally {
+ // End update notification if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.endUpdate();
+ }
+ }
+};
+
+/**
+ * Set semua item menjadi selected atau unselected di cookie
+ * @param {Array} productIds Array product IDs
+ * @param {boolean} isSelected Status selected baru
+ * @param {boolean} notifyUpdate Whether to notify checkbox update state (default: true)
+ */
+export const setAllSelectedInCookie = (
+ productIds,
+ isSelected,
+ notifyUpdate = true
+) => {
+ try {
+ // Notify checkbox update state if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.startUpdate();
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+
+ productIds.forEach((id) => {
+ if (id) selectedItems[id] = isSelected;
+ });
+
+ setSelectedItemsToCookie(selectedItems);
+
+ // Update juga di cart data
+ const cartData = getCartDataFromCookie();
+
+ for (const cartId in cartData) {
+ if (productIds.includes(cartData[cartId].id)) {
+ cartData[cartId].selected = isSelected;
+ }
+ }
+
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error setting all selected in cookie:', error);
+ return {};
+ } finally {
+ // End update notification if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.endUpdate();
+ }
+ }
+};
+
+/**
+ * Hapus item dari cookie
+ * @param {Array} cartIds Array cart IDs untuk dihapus
+ */
+export const removeCartItemsFromCookie = (cartIds) => {
+ try {
+ const cartData = getCartDataFromCookie();
+ const selectedItems = getSelectedItemsFromCookie();
+ const productIdsToRemove = [];
+
+ // Hapus item dari cartData dan catat product IDs
+ cartIds.forEach((cartId) => {
+ if (cartData[cartId]) {
+ if (cartData[cartId].id) {
+ productIdsToRemove.push(cartData[cartId].id);
+ }
+ delete cartData[cartId];
+ }
+ });
+
+ // Hapus dari selectedItems
+ productIdsToRemove.forEach((productId) => {
+ if (selectedItems[productId] !== undefined) {
+ delete selectedItems[productId];
+ }
+ });
+
+ // Simpan kembali ke cookie
+ setCartDataToCookie(cartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return { cartData, selectedItems };
+ } catch (error) {
+ console.error('Error removing cart items from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+
+/**
+ * Force reset semua selected items ke unselected state
+ */
+export const forceResetAllSelectedItems = () => {
+ try {
+ checkboxUpdateState.startUpdate();
+
+ const cartData = getCartDataFromCookie();
+ const selectedItems = {};
+
+ // Reset semua selected status di cartData
+ for (const cartId in cartData) {
+ cartData[cartId].selected = false;
+ if (cartData[cartId].id) {
+ selectedItems[cartData[cartId].id] = false;
+ }
+ }
+
+ // Simpan kembali ke cookie
+ setCartDataToCookie(cartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return { cartData, selectedItems };
+ } catch (error) {
+ console.error('Error resetting all selected items:', error);
+ return {};
+ } finally {
+ checkboxUpdateState.endUpdate();
+ }
+};
+
+/**
+ * Sync selected items between cookie and cart data
+ * @param {Array} cartProducts Products array from cart
+ */
+export const syncSelectedItemsWithCookie = (cartProducts) => {
+ try {
+ if (!cartProducts || !Array.isArray(cartProducts)) {
+ return { items: {}, needsUpdate: false };
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+ let needsUpdate = false;
+
+ // Check if we need to update any items based on cookie values
+ cartProducts.forEach((product) => {
+ if (product.id && selectedItems[product.id] !== undefined) {
+ if (product.selected !== selectedItems[product.id]) {
+ needsUpdate = true;
+ }
+ } else if (product.id) {
+ // If not in cookie, add with current value
+ selectedItems[product.id] = product.selected;
+ }
+ });
+
+ // Update the cookie with the latest values
+ setSelectedItemsToCookie(selectedItems);
+
+ return { items: selectedItems, needsUpdate };
+ } catch (error) {
+ console.error('Error syncing selected items with cookie:', error);
+ return { items: {}, needsUpdate: false };
+ }
+};
+
+// Export the checkbox update state for use in components
+export { checkboxUpdateState };
+
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+export const removeSelectedItemsFromCookie = (productIds) => {
+ try {
+ const selectedItems = getSelectedItemsFromCookie();
+ const cartData = getCartDataFromCookie();
+ const cartIdsToRemove = [];
+
+ // Find cart IDs that match the product IDs
+ for (const cartId in cartData) {
+ if (productIds.includes(cartData[cartId].id)) {
+ cartIdsToRemove.push(cartId);
+ }
+ }
+
+ // Remove from selectedItems
+ productIds.forEach((productId) => {
+ if (selectedItems[productId] !== undefined) {
+ delete selectedItems[productId];
+ }
+ });
+
+ // Remove from cartData
+ cartIdsToRemove.forEach((cartId) => {
+ delete cartData[cartId];
+ });
+
+ // Save both cookies
+ setSelectedItemsToCookie(selectedItems);
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error removing selected items from cookie:', error);
+ return {};
+ }
+};
+
+class QuantityUpdateState {
+ constructor() {
+ this.updateItems = new Set();
+ this.listeners = new Set();
+ }
+
+ startUpdate(itemId) {
+ this.updateItems.add(itemId);
+ this.notifyListeners();
+ }
+
+ endUpdate(itemId) {
+ this.updateItems.delete(itemId);
+ this.notifyListeners();
+ }
+
+ isAnyQuantityUpdating() {
+ return this.updateItems.size > 0;
+ }
+
+ isItemUpdating(itemId) {
+ return this.updateItems.has(itemId);
+ }
+
+ addListener(callback) {
+ this.listeners.add(callback);
+ }
+
+ removeListener(callback) {
+ this.listeners.delete(callback);
+ }
+
+ notifyListeners() {
+ const isUpdating = this.isAnyQuantityUpdating();
+ this.listeners.forEach(callback => callback(isUpdating));
+ }
+}
+
+export const quantityUpdateState = new QuantityUpdateState(); \ No newline at end of file
diff --git a/src-migrate/utils/checkBoxState.js b/src-migrate/utils/checkBoxState.js
new file mode 100644
index 00000000..9568c321
--- /dev/null
+++ b/src-migrate/utils/checkBoxState.js
@@ -0,0 +1,90 @@
+/**
+ * State manager for checkbox updates
+ * Tracks global and individual checkbox update states
+ */
+class CheckboxUpdateState {
+ constructor() {
+ this.updateCount = 0;
+ this.listeners = new Set();
+ this.updatingCheckboxIds = new Set();
+ }
+
+ // Global update state (for buttons quotation and checkout)
+ isUpdating() {
+ return this.updateCount > 0;
+ }
+
+ // Individual checkbox state
+ isCheckboxUpdating(itemId) {
+ return this.updatingCheckboxIds.has(String(itemId));
+ }
+
+ // Start update
+ startUpdate(itemId = null) {
+ this.updateCount++;
+
+ if (itemId !== null) {
+ this.updatingCheckboxIds.add(String(itemId));
+ }
+
+ this.notifyListeners();
+ return this.updateCount;
+ }
+
+ // End update
+ endUpdate(itemId = null) {
+ this.updateCount = Math.max(0, this.updateCount - 1);
+
+ if (itemId !== null) {
+ this.updatingCheckboxIds.delete(String(itemId));
+ }
+
+ this.notifyListeners();
+ return this.updateCount;
+ }
+
+ // Reset all states
+ reset() {
+ this.updateCount = 0;
+ this.updatingCheckboxIds.clear();
+ this.notifyListeners();
+ }
+
+ // Listener management
+ addListener(callback) {
+ if (typeof callback === 'function') {
+ this.listeners.add(callback);
+ // Immediate callback with current state
+ callback(this.isUpdating());
+ }
+ }
+
+ removeListener(callback) {
+ this.listeners.delete(callback);
+ }
+
+ // Debug helpers
+ getUpdateCount() {
+ return this.updateCount;
+ }
+
+ getUpdatingCheckboxIds() {
+ return [...this.updatingCheckboxIds];
+ }
+
+ // Private method to notify listeners
+ notifyListeners() {
+ const isUpdating = this.isUpdating();
+
+ this.listeners.forEach((listener) => {
+ try {
+ listener(isUpdating);
+ } catch (error) {
+ console.error('Checkbox update state listener error:', error);
+ }
+ });
+ }
+}
+
+const checkboxUpdateState = new CheckboxUpdateState();
+export default checkboxUpdateState;