diff --git a/component_factory/package.json b/component_factory/package.json index 19ac513..bf413a2 100755 --- a/component_factory/package.json +++ b/component_factory/package.json @@ -13,6 +13,7 @@ "formik": "^2.4.6", "next": "15.4.5", "react": "19.1.0", + "react-beautiful-dnd": "^13.1.1", "react-dom": "19.1.0", "redux": "^5.0.1", "sass": "^1.89.2", diff --git a/component_factory/src/app/simple_modal/page.tsx b/component_factory/src/app/LinkEditorModal/page.tsx old mode 100644 new mode 100755 similarity index 100% rename from component_factory/src/app/simple_modal/page.tsx rename to component_factory/src/app/LinkEditorModal/page.tsx diff --git a/component_factory/src/app/ShopifyProductCard/mockData.ts b/component_factory/src/app/ShopifyProductCard/mockData.ts new file mode 100644 index 0000000..e8a75da --- /dev/null +++ b/component_factory/src/app/ShopifyProductCard/mockData.ts @@ -0,0 +1,20 @@ +// File: InterfaceFactory/app/ShopifyProductCard/mockData.ts + +export interface ShopifyProduct { + id: string; + title: string; + description: string; + image: string; + price: number; + available: boolean; + } + + export const mockProduct: ShopifyProduct = { + id: 'prod_1', + title: 'Mock Shopify T-Shirt', + description: 'A stylish t-shirt made from 100% organic cotton. Perfect for developers.', + image: '/mock-images/shopify-tshirt.png', // You may need to add this image or use a placeholder URL + price: 29.99, + available: true, + }; + \ No newline at end of file diff --git a/component_factory/src/app/ShopifyProductCard/page.tsx b/component_factory/src/app/ShopifyProductCard/page.tsx new file mode 100644 index 0000000..01b0277 --- /dev/null +++ b/component_factory/src/app/ShopifyProductCard/page.tsx @@ -0,0 +1,49 @@ +// File: InterfaceFactory/app/ShopifyProductCard/page.tsx +'use client'; + +import React, { useEffect } from 'react'; +import { ConfigProvider, App as AntdApp } from 'antd'; +import enUS from 'antd/es/locale/en_US'; +import ShopifyProductCard from '@/components/ShopifyProductCard/ShopifyProductCard'; +import '@/app/css/ShopifyProductCard.scss'; + +import { mockProduct, ShopifyProduct } from './mockData'; + +// Minimal shape the card actually uses +type MinimalShopifyProductItem = { + id?: string; + title: string; + images: { src: string }[]; + variants: { price: string }[]; + available?: boolean; +}; + +const toShopifyProductItem = (p: ShopifyProduct): MinimalShopifyProductItem => ({ + id: p.id, + title: p.title, + images: [{ src: p.image }], // ✅ card expects images[0].src + variants: [{ price: String(p.price) }],// ✅ card expects variants[0].price (string) + available: p.available, +}); + +export default function ShopifyProductCardPage() { + // If the card uses a portal, ensure it exists (safe no-op otherwise) + useEffect(() => { + if (!document.getElementById('root-portal')) { + const el = document.createElement('div'); + el.id = 'root-portal'; + document.body.appendChild(el); + } + }, []); + + return ( + + +
+ {/* ✅ Provide the correct prop shape */} + +
+
+
+ ); +} diff --git a/component_factory/src/app/css/FanClubCard.scss b/component_factory/src/app/css/FanClubCard.scss new file mode 100644 index 0000000..271122d --- /dev/null +++ b/component_factory/src/app/css/FanClubCard.scss @@ -0,0 +1,36 @@ +@import './variables.scss'; + +.fan-club-card { + &__title { + width: 100%; + max-width: 298px; + } + &__divider { + width: 100%; + height: 1px; + background: $gray5; + max-width: 252px; + } + &__cover { + width: 166px; + height: 166px; + border-radius: 50% !important; + margin-bottom: 0; + + @include mobile { + width: 112px; + height: 112px; + } + .ant-upload { + border-radius: 50%; + } + img { + border-radius: 50% !important; + } + } + .btn-delete { + position: absolute; + top: 0; + right: 0; + } +} diff --git a/component_factory/src/app/css/ImageSkeleton.scss b/component_factory/src/app/css/ImageSkeleton.scss new file mode 100644 index 0000000..b390bf4 --- /dev/null +++ b/component_factory/src/app/css/ImageSkeleton.scss @@ -0,0 +1,15 @@ +.image-skeleton { + img { + object-fit: cover; + //object-position: top; + } + .avatar-profile__text { + font-weight: 500; + } + &--circle { + img { + border-radius: 100% !important; + } + } + } + \ No newline at end of file diff --git a/component_factory/src/app/css/ModuleEditor.scss b/component_factory/src/app/css/ModuleEditor.scss new file mode 100644 index 0000000..e39e2f7 --- /dev/null +++ b/component_factory/src/app/css/ModuleEditor.scss @@ -0,0 +1,135 @@ +@import './variables.scss'; + +@media (max-width: 768px) { + .module-editor__title--count { + position: absolute; + top: 24px; + margin-left: 0; + } + + .module-editor__title--input { + font-size: 18px; + font-weight: 600; + height: 36px; + width: 100%; + } +} + +.module-editor { + &__title { + min-width: 380px; + position: relative; + } + + &__title--count { + position: absolute; + left: 0; + top: 28px; + color: $dark !important; + opacity: 0, 5; + margin-left: 65px; + } + + &__title--input { + font-size: 20px; + font-weight: 500; + padding: 0; + caret-color: $default !important; + } + + &__title--label { + line-height: 32px !important; + } + + &__acitons { + border-radius: 16px; + border-right: none; + -webkit-border-radius: 16px; + -moz-border-radius: 16px; + -webkit-box-shadow: $boxShadow2; + -moz-box-shadow: $boxShadow2; + box-shadow: $boxShadow2; + + .ant-list-header { + width: 212px; + height: 46px; + padding: 11px 20px; + } + + .ant-list-item { + width: 212px; + height: 56px; + padding: 16px 20px; + cursor: pointer; + + &:hover { + background-color: rgba(37, 127, 252, 0.05); + } + + &:first-child { + border-radius: 16px 16px 0 0; + } + + &:last-child { + border-radius: 0 0 16px 16px; + border-end-end-radius: 16px !important; + } + } + } + + &__wrapper--with-bottom-actions { + .pannel-collapse .ant-collapse-item { + border-radius: 16px 16px 0 0 !important; + } + } +} +.module-editor-overlay { + padding-top: 0; + margin-top: -4px; + .ant-popover-inner { + border-radius: 8px; + } + + .ant-popover-inner-content { + border-radius: 8px; + padding: 0; + } +} +.ant-select-dropdown { + z-index: 99999; +} +.ant-popover-arrow { + display: none; +} + +.module-title-row { + flex-shrink: 1; + max-width: 100%; + min-width: 100px; + + button { + width: 100%; + + div { + width: 100%; + + &.ant-typography { + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + +.module-editor-bottom-actions { + width: 100%; + display: flex; + justify-content: center; + border-radius: 0 0 16px 16px; + padding: 12px 20px; + background: var(--crt-sys-color-background-surface-lowest-default); + + > button { + width: 100%; + } +} diff --git a/component_factory/src/app/css/PopoverMenu.scss b/component_factory/src/app/css/PopoverMenu.scss new file mode 100644 index 0000000..3c91e43 --- /dev/null +++ b/component_factory/src/app/css/PopoverMenu.scss @@ -0,0 +1,55 @@ +@import 'variables.scss'; + +.popover-menu-actions { + border-radius: 16px; + border-right: none; + -webkit-border-radius: 16px; + -moz-border-radius: 16px; + -webkit-box-shadow: $boxShadow2; + -moz-box-shadow: $boxShadow2; + box-shadow: $boxShadow2; + + .ant-list-item { + width: 204px; + height: 46px; + padding: 12px; + cursor: pointer; + + &:hover { + background-color: rgba(37, 127, 252, 0.05); + } + + &:first-child { + border-radius: 16px 16px 0 0; + } + + &:last-child { + border-radius: 0 0 16px 16px !important; + } + &.title { + padding-bottom: 8px; + border-bottom: none; + &:hover { + background-color: transparent; + } + } + } +} +.popover-menu-overlay { + padding-top: 0; + margin-top: -4px; + .ant-popover-inner { + border-radius: 16px; + } + + .ant-popover-inner-content { + border-radius: 16px; + padding: 0; + } +} +.ant-select-dropdown { + z-index: 99999; +} +.ant-popover-arrow { + display: none; +} diff --git a/component_factory/src/app/css/Progress.scss b/component_factory/src/app/css/Progress.scss new file mode 100644 index 0000000..f9960ed --- /dev/null +++ b/component_factory/src/app/css/Progress.scss @@ -0,0 +1,24 @@ +@import './variables.scss'; + +.progress { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .ant-progress { + &-inner { + width: 32px !important; + height: 32px !important; + + svg path { + stroke-width: 10px !important; + } + } + } + + .ant-typography { + font-size: 10px !important; + color: rgba($default, 0.5); + } +} diff --git a/component_factory/src/app/css/ShopifyProductCard.scss b/component_factory/src/app/css/ShopifyProductCard.scss new file mode 100644 index 0000000..a49ea0f --- /dev/null +++ b/component_factory/src/app/css/ShopifyProductCard.scss @@ -0,0 +1,62 @@ +@import './variables'; + +.on-demand-delete-confirm-modal { + overflow: hidden; +} + +.shopify-product-card { + overflow: hidden; + .price { + color: $grey4; + } + + &__picture { + img, + .ant-skeleton-avatar { + width: 82px !important; + height: 82px !important; + object-fit: cover; + border-right: 1px solid $grey2; + } + } + + &__info { + padding: 0 16px; + width: 334px; + &--content { + .ondemand-drag-btn { + padding-right: 0; + } + + &__status { + font-size: 12px; + line-height: 20px; + padding: 0 6px; + color: #ffffff; + display: inline-block; + border-radius: 4px; + + &--active { + background-color: $green; + } + + &--draft { + background-color: $gray8; + } + + &--unpublished { + background-color: $yellow; + } + } + } + } + &__title { + max-width: 224px; + } + &__empty { + width: 82px; + height: 82px; + background-color: $gray2; + border-right: 1px solid $grey2; + } +} diff --git a/component_factory/src/components/ImageSkeleton/ImageSkeleton.tsx b/component_factory/src/components/ImageSkeleton/ImageSkeleton.tsx new file mode 100644 index 0000000..bc2af22 --- /dev/null +++ b/component_factory/src/components/ImageSkeleton/ImageSkeleton.tsx @@ -0,0 +1,69 @@ +import { Skeleton, Avatar } from 'antd' +import React, { useEffect, useState } from 'react' +import '@/app/css/ImageSkeleton.scss' + +interface IProps { + src: string | undefined + imageClassName?: string + skeletonClassName?: string + shape?: 'square' | 'circle' + size?: number | 'small' | 'large' | 'default' | undefined + imgWidth?: string | number + name?: string +} + +const ImageSkeleton = ({ + src, + imageClassName = '', + skeletonClassName = '', + shape = 'square', + size = 'large', + imgWidth = '24', + name +}: IProps) => { + const [isLoaded, setLoaded] = useState(false) + const [isError, setError] = useState(false) + const imgEl: any = React.useRef(null) + + useEffect(() => { + if (imgEl.current?.complete) { + setLoaded(true) + } + }, []) + + return ( +
+ {src && ( + avatar setLoaded(true)} + onError={() => { + setLoaded(false) + setError(true) + }} + style={{ + width: `${imgWidth}px`, + height: `${imgWidth}px` + }} + /> + )} + {isError && name ? ( + + NAME + + ) : ( + + )} +
+ ) +} + +export default ImageSkeleton diff --git a/component_factory/src/components/PopoverMenu/ModuleEditor.tsx b/component_factory/src/components/PopoverMenu/ModuleEditor.tsx new file mode 100644 index 0000000..1d44c0d --- /dev/null +++ b/component_factory/src/components/PopoverMenu/ModuleEditor.tsx @@ -0,0 +1,100 @@ +import React, { useCallback } from "react"; +import { Button, List, Popover } from "antd"; +import { Paragraph } from "@/components/Typography/Paragraph"; +import { TypographyPresets } from "@/components/Typography/Preset"; +import "@/app/css/ModuleEditor.scss"; // use styles from the original source file + +export type PopoverMenuItem = { + key: string; + label: string; + onClick?: () => void; + className?: string; + itemClassName?: string; + labelPreset?: TypographyPresets; +}; + +interface ModuleEditorProps { + children: React.ReactNode; + data?: PopoverMenuItem[]; + title?: string; + visible?: boolean; // controlled visibility (v4: visible, v5: open) + onChangeVisible?: (value: boolean) => void; // setter for visibility +} + +const ModuleEditor = ({ + children, + data = [], + title, + visible, + onChangeVisible, +}: ModuleEditorProps) => { + const handleClickItem = useCallback( + (item: PopoverMenuItem) => () => { + // mirror original: close first, then execute the action + onChangeVisible?.(false); + item.onClick?.(); + }, + [onChangeVisible] + ); + + const renderItems = () => + data.map((item) => ( + + + {item.label} + + + )); + + // Support both antd v4 and v5 props (harmless on either) + const popProps: any = { + placement: "bottomRight", // original placement + overlayClassName: "module-editor-overlay", // original overlay class + trigger: ["click"], // original trigger + arrow: false, // v5 way to hide arrow + arrowContent: null, // v4 compatibility (noop on v5) + content: ( + + {title && ( + + + {title} + + + )} + {renderItems()} + + ), + // controlled visibility + visible, // v4 + onVisibleChange: onChangeVisible, + open: visible, // v5 + onOpenChange: onChangeVisible, + }; + + return ( + + + + ); +}; + +export default ModuleEditor; diff --git a/component_factory/src/components/PopoverMenu/PopoverMenu.tsx b/component_factory/src/components/PopoverMenu/PopoverMenu.tsx new file mode 100644 index 0000000..d3d6e12 --- /dev/null +++ b/component_factory/src/components/PopoverMenu/PopoverMenu.tsx @@ -0,0 +1,89 @@ +import { Button, List, Popover } from "antd"; +import { Paragraph } from "@/components/Typography/Paragraph"; +import { TypographyPresets } from "@/components/Typography/Preset"; +import * as React from "react"; +import { useCallback } from "react"; +import "@/app/css/PopoverMenu.scss"; + +export type PopoverMenuItem = { + key: string; + label: string; + onClick?: () => void; + className?: string; + itemClassName?: string; + labelPreset?: TypographyPresets; +}; + +interface PopoverMenuProps { + children: React.ReactNode; + data?: PopoverMenuItem[]; + title?: string; + visible?: boolean; + onChangeVisible?: (value: boolean) => void; +} + +const PopoverMenu = ({ + children, + data = [], + title, + visible, + onChangeVisible, +}: PopoverMenuProps) => { + const handleClickItem = useCallback( + (item) => () => { + onChangeVisible?.(false); + item.onClick?.(); + }, + [data] + ); + + const renderItems = () => { + return data.map((item) => ( + + + {item.label} + + + )); + }; + return ( + + {title && ( + + + {title} + + + )} + {renderItems()} + + } + trigger={["click"]} + onVisibleChange={onChangeVisible} + > + + + ); +}; + +export default PopoverMenu; diff --git a/component_factory/src/components/ShopifyProductCard/ShopifyProductCard.tsx b/component_factory/src/components/ShopifyProductCard/ShopifyProductCard.tsx new file mode 100644 index 0000000..c289722 --- /dev/null +++ b/component_factory/src/components/ShopifyProductCard/ShopifyProductCard.tsx @@ -0,0 +1,92 @@ + +import React, { useState } from 'react' +import { Button, Col, Row } from 'antd' +import ImageSkeleton from '@/components/ImageSkeleton/ImageSkeleton' +import { Text } from '@/components/Typography/Text' +import { ShopifyProductItem } from '@/models/talent/talent-profile-module.model' + +interface Props { + index?: number + item: ShopifyProductItem + showPrice?: boolean + disableDragDrop?: boolean + type: number + onEdit?: (item: ShopifyProductItem) => void + onDelete?: (item: ShopifyProductItem) => void + active?: boolean +} + +const PRODUCT_TYPE_INDIVIDUAL = 1 + +const ShopifyProductCard: React.FC = ({ + item, + showPrice = true, + disableDragDrop = false, + type, + onDelete +}) => { + const variant = item?.variants?.reduce(function (prev, curr) { + return parseFloat(prev.price) < parseFloat(curr.price) ? prev : curr + }) + + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) + + const onDeleteConfirm = () => { + typeof onDelete === 'function' && onDelete(item) + setShowDeleteConfirm(false) + } + + return ( + + {item && ( +
+
+ {item.images?.[0]?.src ? ( + + ) : ( + + + )} +
+
+ + + {item.title} + {showPrice && ( +
+ + +
+ )} + + + + + + + + + + +
+
+
+ )} +
+ ) +} + +export default ShopifyProductCard diff --git a/interface_components/Component_ProgressBar/page.tsx b/interface_components/Component_ProgressBar/page.tsx new file mode 100644 index 0000000..8a8775a --- /dev/null +++ b/interface_components/Component_ProgressBar/page.tsx @@ -0,0 +1,28 @@ +'use client' + +import React from 'react' +import { Progress as ProgressAntd, Typography } from 'antd' +import '@/app/css/Progress.scss' + +const { Text } = Typography + +interface IProps { + value: number | undefined +} + +const Progress = ({ value }: IProps) => { + return ( +
+ + {`${value || 15}%`} +
+ ) +} + +export default Progress diff --git a/interface_components_final/LinkEditorModal/page.tsx b/interface_components_final/LinkEditorModal/page.tsx new file mode 100755 index 0000000..8a02eea --- /dev/null +++ b/interface_components_final/LinkEditorModal/page.tsx @@ -0,0 +1,463 @@ +"use client"; + +import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; + +import { Upload } from "antd"; +import { Col, Row } from "antd/lib/grid"; + +import "../css/App.scss" +import "../css/base/spacing.scss" +import "../css/base/modal.scss" +import "../css/base/tabs.scss" +import "../css/base/text.scss" +import "../css/base/display.scss" +import "../css/base/form.scss" +import "../css/base/button.scss" +import "../css/onboardingFlow.scss" +import "../css/LinkEditorModal.scss"; +import "../css/ProductEditorModal.scss"; +import "../css/ModuleOndemandVideo.scss"; +import "../css/ModuleShopify.scss" + + +import Tabs from "antd/lib/tabs"; +import Button from "antd/lib/button"; +import Alert from "antd/lib/alert"; +import * as yup from "yup"; +import classNames from "classnames"; +import { Field, Form, Formik, FormikProps } from "formik"; + +import Modal from "@/components/Modal/Modal"; // Adjust this path as needed +import { Text } from "@/components/Typography/Text"; +import ImageUpload from "@/components/ImageUpload/ImageUpload"; +import { AntInput, FieldType } from "@/components/Form/FormItem"; +import { LinkItem } from "@/models/talent/talent-profile-module.model"; + +//import icon +import InfoIcon from "@/icons/Infoicon"; +import CloseOutlineIcon from "@/icons/Closeoutlineicon"; + +const initialProductValues: LinkItem = { + title: "", + url: "", + order: 0, + visible: true, +}; + +const initialSpecialOfferValues: LinkItem = { + title: "", + url: "", + order: 0, + specialOffer: { + thumbnail: "", + title: "", + storeUrl: "", + couponCode: "", + }, + visible: true, +}; +const LINK_TYPES = { + NORMAL: "NORMAL", + SPECIAL_OFFER: "SPECIAL_OFFER", +}; + +const Icon = ({ className, name, width, height }: { className?: string; name: string; width?: number; height?: number }) => { + return ( + + [icon: {name}] + + ); +}; + +const dummyPhoto = { + url: "https://via.placeholder.com/369", // static test image + fileName: "placeholder.jpg", + loading: false, +}; + +const onUploadSuccess = (file: any) => { + console.log("Upload success:", file); +}; + +const onRemove = (file: any) => { + console.log("Removed:", file); +}; + +const validateLinkSchema = yup.object().shape({ + title: yup.string().required("Please enter the link title"), + url: yup + .string() + .required("Please enter the URL") + .url("Please enter a valid URL"), +}); + +const validateSpecialOfferSchema = yup.object().shape({ + specialOffer: yup.object().shape({ + title: yup + .string() + .required("Please add the offer information") + .test("len", "Offer not more than 50 characters", (val) => { + if (val == undefined) { + return true; + } + return val.length == 0 || (val.length >= 1 && val.length <= 50); + }), + storeUrl: yup + .string() + .required("Please enter the URL") + .url("Please enter a valid URL"), + }), +}); + +export type LinkEditorModalFormValues = Pick< + LinkItem, + "thumbnail" | "url" | "title" | "specialOffer" | "order" | "visible" +>; + + +export default function Page() { + const [isModalOpen, setIsModalOpen] = useState(false); + + // Dummy state vars to eliminate TS errors + const [linkType, setLinkType] = useState(LINK_TYPES.NORMAL); + const [isEdit, setIsEdit] = useState(false); + const [showAlert, setShowAlert] = useState(true); + + const [initialValues, setInitialValues] = useState(); + + function handleSubmit(values: LinkEditorModalFormValues, formikHelpers: FormikHelpers): void | Promise { + throw new Error("Function not implemented."); + } + + const dummyOptions = [ + { label: "Option A", value: "A" }, + { label: "Option B", value: "B" }, + ]; + + const [modalTop, setModalTop] = useState(); // default safe gap + const modalRef = useRef(null); + let newTop: number; + + //CUSTOM LOGIC: HANDLE THE TOP OF THE MODAL OF DIFFERENT PLATFORMS + useEffect(() => { + const updateModalTop = () => { + const modalEl = document.querySelector('.link-editor-modal') as HTMLElement; + if (!modalEl) return; + + const modalHeight = modalEl.getBoundingClientRect().height; + const windowHeight = window.innerHeight; + + const isMobile = window.innerWidth <= 768; + + //PLATFORM LOGIC + if(!isMobile) { + if (modalHeight + 60 > windowHeight) { + newTop = 20; + setModalTop(newTop); + //console.log('DESKTOP OVERFLOW'); + + } else { + //newTop = Math.max((windowHeight - modalHeight) / 4, 30); + newTop = 20; + setModalTop(newTop); + //console.log('DESKTOP CENTERED'); + + } + } + else { + if (modalHeight + 60 > windowHeight) { + newTop = 20; + setModalTop(newTop); + //console.log('MOBILE OVERFLOW'); + } else { + //newTop = 20; + //setModalTop(newTop); + newTop = Math.max( (windowHeight / 2) - (2.4 * modalHeight), 30); + setModalTop(newTop); + //modalEl.style.removeProperty('top'); + //console.log('MOBILE CENTERED'); + } + } + + modalEl.style.top = `${newTop}px`; // 🔥 Direct DOM update + + }; + + if (isModalOpen) { + setTimeout(updateModalTop, 100); // Wait for DOM to fully render + window.addEventListener("resize", updateModalTop); + } + + return () => { + window.removeEventListener("resize", updateModalTop); + }; + }, [isModalOpen, linkType]); + + + useLayoutEffect(() => { + const adjustMargin = () => { + if (!isModalOpen) return; + + if (linkType === LINK_TYPES.SPECIAL_OFFER) { + //console.log('SPECIAL') + const form = document.querySelector(".offer-form-item") as HTMLElement; + const inputBox = form.getElementsByClassName('ant-form-item-has-success')[0] as HTMLElement; + const hasError = form.querySelector(".ant-form-item-explain-error") as HTMLElement; + + + if (inputBox) { + inputBox.style.marginBottom = hasError ? "24px" : "0px"; + } + } + + if (linkType === LINK_TYPES.NORMAL) { + //console.log('NORMAL') + const form = document.querySelector(".link-editor-form") as HTMLElement; + const inputBoxes = form?.getElementsByClassName("ant-form-item-has-success"); + const inputBox = inputBoxes?.[1] as HTMLElement; + + if (inputBox) { + inputBox.style.marginBottom = "24px"; + } + } + }; + + // Delay just until after DOM mounts, but before paint + requestAnimationFrame(() => { + adjustMargin(); + }); + + }, [linkType, isModalOpen]); + + //DEALING WITH ERROR LABELS + const [errorActivated, setErrorActivated] = useState(false); + + useEffect(() => { + if (!isModalOpen) return; + + const formRoot = document.querySelector(".offer-form-item"); + if (!formRoot) return; + + const observer = new MutationObserver(() => { + const errorLabels = formRoot.querySelectorAll(".ant-form-item-explain-error"); + + // ✅ You can react to the presence of ANY error + if (errorLabels.length > 0) { + //console.log("🚨 Error label activated"); + // Optional: do something here + setErrorActivated(true) + } else { + console.log("✅ No errors"); + } + }); + + observer.observe(formRoot, { + childList: true, + subtree: true, + }); + + return () => observer.disconnect(); + }, [isModalOpen, linkType]); + + + //TRIGGER FOR ACTIVATED ERROR + useEffect(() => { + + if(errorActivated) { + console.log('ERROR ACTIVATED'); + const form = document.querySelector(".offer-form-item") as HTMLElement; + const inputBox = form.getElementsByClassName('ant-form-item')[0] as HTMLElement; + + + if (inputBox) { + inputBox.style.marginBottom = "24px"; + } + } + + }, [errorActivated]); + + + + return ( + <> + {/** INLINE ROOT STYLING */} +