Files
shoptime/com.twin.app.shoptime/src/components/TItemCard/TItemCard.jsx
2025-09-09 17:05:00 +09:00

278 lines
7.2 KiB
JavaScript

import React, { memo, useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import Spottable from "@enact/spotlight/Spottable";
import defaultLogoImg from "../../../assets/images/ic-tab-partners-default@3x.png";
import defaultimgHorizontal from "../../../assets/images/img-thumb-empty-hor@3x.png";
import defaultImageItem from "../../../assets/images/img-thumb-empty-product@3x.png";
import defaultimgVertical from "../../../assets/images/img-thumb-empty-ver@3x.png";
import IcLiveShow from "../../../assets/images/tag/tag-liveshow.png";
import usePriceInfo from "../../hooks/usePriceInfo";
import { $L, removeSpecificTags } from "../../utils/helperMethods";
import CustomImage from "../CustomImage/CustomImage";
import css from "./TItemCard.module.less";
import { sendLogTotalRecommend } from "../../actions/logActions";
const SpottableComponent = Spottable("div");
const TYPES = {
vertical: "vertical",
horizontal: "horizontal",
videoShow: "videoShow",
};
const IMAGETYPES = {
imgHorizontal: "imgHorizontal",
imgVertical: "imgVertical",
};
const STRING_CONF = {
SOLD_OUT: "SOLD OUT",
};
export const removeDotAndColon = (string) => {
return /[.:]/.test(string) ? string.replace(/[.:]/g, "") : string;
};
export default memo(function TItemCard({
children,
className,
disabled,
imageAlt,
imageSource,
imgType = IMAGETYPES.imgHorizontal,
logo,
logoDisplay = false,
isBestSeller = false,
isLive = false,
onBlur,
onClick,
onFocus,
onError,
offerInfo,
priceInfo,
productId,
productName,
catNm,
rank,
soldoutFlag,
spotlightId,
nonPosition = false,
type = TYPES.vertical,
firstLabel,
label,
lastLabel,
contextName,
messageId,
order,
patnerName,
brandName,
shelfId,
shelfLocation,
shelfTitle,
contentTitle,
category,
curationId,
curationTitle,
nowProductId,
nowCategory,
nowProductTitle,
contentId,
...rest
}) {
const dispatch = useDispatch();
const [defaultImage, setDefaultImage] = useState(null);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
useEffect(() => {
if (!imageSource) {
if (type === "videoShow") {
setDefaultImage(
imgType === IMAGETYPES.imgHorizontal
? defaultimgHorizontal
: defaultimgVertical
);
} else {
setDefaultImage(defaultImageItem);
}
}
}, [imageSource, type, imgType]);
const { originalPrice, discountedPrice, discountRate } =
usePriceInfo(priceInfo) || {};
const _onBlur = useCallback(() => {
if (onBlur) {
onBlur();
}
}, [onBlur]);
const _onClick = useCallback(
(e) => {
if (disabled) {
e.stopPropagation();
return;
}
if (onClick) {
onClick(e);
if (contextName && messageId) {
const params = {
contextName: contextName,
messageId: messageId,
shelfLocation: shelfLocation,
shelfId: shelfId,
shelfTitle: shelfTitle,
productId: productId,
productTitle: productName,
nowProductId: nowProductId,
nowCategory: nowCategory,
nowProductTitle: nowProductTitle,
partner: patnerName,
brand: brandName,
price: originalPrice,
discount: discountRate,
location: order,
category: category ? category : catNm,
contentTitle: contentTitle,
curationId: curationId,
curationTitle: curationTitle,
};
dispatch(sendLogTotalRecommend(params));
}
}
},
[onClick, disabled, contextName, messageId]
);
const _onFocus = useCallback(() => {
if (onFocus) {
onFocus();
}
}, [onFocus]);
const addDefaultImg = useCallback(
(e) => {
// console.log("###titemcard", e);
},
[onError]
);
const prdtNum = label ? label : "";
const discountLabel = discountRate ? discountRate + " discount," : "";
const discountpriceLabel = discountRate
? "Sale price " + discountedPrice + ", "
: "";
const priceLabel =
originalPrice && parseFloat(originalPrice.replace("$", "")) === 0
? offerInfo
? " " + offerInfo
: ""
: originalPrice
? " Original price " + originalPrice + ", "
: "";
const lastLabeltext = lastLabel ? lastLabel : "";
const firstLabeltext = firstLabel ? firstLabel + " " : "";
const soldOutText = soldoutFlag === "Y" ? "Sold Out " : "";
const ariaLabel =
soldOutText +
firstLabeltext +
discountLabel +
productName +
discountpriceLabel +
priceLabel +
prdtNum +
lastLabeltext;
const productNameDangerousHTML = useMemo(() => {
const sanitizedString = removeSpecificTags(productName);
return sanitizedString;
}, [productName]);
return (
<SpottableComponent
className={classNames(
css[type],
nonPosition && css.nonPosition,
type === "videoShow" && css[imgType],
className && className
)}
onBlur={_onBlur}
onClick={_onClick}
onFocus={_onFocus}
spotlightId={spotlightId ?? "spotlightId-" + removeDotAndColon(productId)}
aria-label={ariaLabel}
role=""
{...rest}
>
<div className={css.imageWrap}>
<CustomImage
alt={imageAlt}
delay={0}
src={imageSource}
fallbackSrc={
type === "videoShow"
? imgType === IMAGETYPES.imgHorizontal
? defaultimgHorizontal
: defaultimgVertical
: defaultImageItem
}
/>
{priceInfo &&
discountRate &&
Number(discountRate.replace("%", "")) > 4 && (
<span>{discountRate}</span>
)}
{soldoutFlag && soldoutFlag === "Y" && (
<div
className={classNames(css.soldout, countryCode === "DE" && css.de)}
>
{$L(STRING_CONF.SOLD_OUT)}
</div>
)}
</div>
<div className={classNames(css.descWrap, catNm && css.hstNmWrap)}>
{logo && (
<div className={css.logo}>
<CustomImage src={logo} fallbackSrc={defaultLogoImg} />
</div>
)}
<div className={css.title}>
<h3
className={css.productNameTitle}
dangerouslySetInnerHTML={{ __html: productNameDangerousHTML }}
/>
</div>
{priceInfo ? (
<p className={css.priceInfo}>
{parseFloat(originalPrice.replace(/[^0-9.-]+/g, "")) === 0 ? (
<strong>{offerInfo}</strong>
) : discountRate ? (
discountedPrice
) : (
originalPrice
)}
{discountRate && <span>{originalPrice}</span>}
</p>
) : (
<p className={css.offerInfo}>{offerInfo}</p>
)}
</div>
{isBestSeller && rank && (
<div className={css.bestSeller}>
<span>{rank}</span>
</div>
)}
{isLive && <img className={css.liveTag} src={IcLiveShow} alt="" />}
</SpottableComponent>
);
});
export { IMAGETYPES, TYPES };