Cart: fix extras group

This commit is contained in:
2025-11-11 17:02:19 +03:00
parent 42a70affe2
commit 3d3262e5ad
6 changed files with 166 additions and 130 deletions

View File

@@ -1,119 +0,0 @@
import { Divider, message } from "antd";
import ProCheckboxGroups from "components/ProCheckboxGroups/ProCheckboxGroups";
import ProText from "components/ProText";
import { Dispatch, SetStateAction } from "react";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "redux/hooks";
import { Extra, TheExtrasGroup } from "utils/types/appTypes";
import styles from "../product.module.css";
export default function ExtraGroups({
groupsList,
selectedExtrasByGroup,
setSelectedExtrasByGroup,
}: {
groupsList: TheExtrasGroup[];
selectedExtrasByGroup: Extra[];
setSelectedExtrasByGroup: Dispatch<SetStateAction<Extra[]>>;
}) {
const { isRTL } = useAppSelector((state) => state.locale);
const { t } = useTranslation();
return (
<>
{groupsList.length > 0 && (
<div>
<Divider style={{ margin: "0 0 16px 0" }} />
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<ProText style={{ fontSize: "1.25rem" }}>
{t("menu.youMightAlsoLike")}
</ProText>
<ProText strong style={{ fontSize: "0.75rem" }}>
{t("menu.optional")}
</ProText>
</div>
{/* <ProText strong style={{ fontSize: "0.75rem" }}>
{t("menu.choose1")}
</ProText> */}
</div>
)}
{groupsList?.map((group: TheExtrasGroup) => (
<div key={group.id}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<ProText strong style={{ fontSize: "1rem" }}>
{isRTL ? group.nameAR : group.name}
</ProText>
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<ProText style={{ fontSize: "0.75rem", color: "#666" }}>
{group.force_limit_selection === 1 ? "Required" : "Optional"}
</ProText>
<ProText style={{ fontSize: "0.75rem", color: "#666" }}>
(
{selectedExtrasByGroup.filter((extra) => extra.id === group.id)
.length || 0}
/{group.limit})
</ProText>
</div>
</div>
{group.force_limit_selection === 1 && (
<ProText
style={{
fontSize: "0.75rem",
color: "red",
marginBottom: "8px",
}}
>
* You must select exactly {group.limit} option
{group.limit > 1 ? "s" : ""}
</ProText>
)}
<div className={styles.productContainer}>
<ProCheckboxGroups
options={group.extras.map((extra: any) => ({
value: extra.id.toString(),
label: isRTL ? extra.name : extra.nameAR,
price: `+${extra.price}`,
}))}
value={selectedExtrasByGroup.map((extra) => extra.id.toString())}
onChange={(values: string[]) => {
// Check if the new selection would exceed the limit
if (values.length > group.limit) {
message.error(
`You can only select up to ${group.limit} option${group.limit > 1 ? "s" : ""} from this group.`,
);
return;
}
setSelectedExtrasByGroup(
group.extras.filter((o) => values?.includes(o.id.toString())),
);
}}
/>
</div>
</div>
))}
</>
);
}

View File

@@ -0,0 +1,65 @@
import { Divider } from "antd";
import ProText from "components/ProText";
import { Dispatch, SetStateAction } from "react";
import { useTranslation } from "react-i18next";
import { TheExtrasGroup } from "utils/types/appTypes";
import ExtrasGroup from "pages/product/components/ExtrasGroup.tsx";
export default function ExtraGroupsContainer({
groupsList,
selectedExtrasByGroup,
setSelectedExtrasByGroup,
}: {
groupsList: TheExtrasGroup[];
selectedExtrasByGroup: Record<number, string[]>;
setSelectedExtrasByGroup: Dispatch<SetStateAction<Record<number, string[]>>>;
}) {
const { t } = useTranslation();
return (
<>
{groupsList.length > 0 && (
<div>
<Divider style={{ margin: "0 0 16px 0" }} />
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<ProText style={{ fontSize: "1.25rem" }}>
{t("menu.youMightAlsoLike")}
</ProText>
<ProText strong style={{ fontSize: "0.75rem" }}>
{t("menu.optional")}
</ProText>
</div>
{/* <ProText strong style={{ fontSize: "0.75rem" }}>
{t("menu.choose1")}
</ProText> */}
</div>
)}
{groupsList?.map((group: TheExtrasGroup) => (
<ExtrasGroup
group={group}
selectedExtras={selectedExtrasByGroup[group.id]}
onChange={(values) => {
setSelectedExtrasByGroup({
...selectedExtrasByGroup,
[group.id]:
group.extras
.filter((o) => values?.includes(o.id.toString()))
.map((e) => e.id.toString()) || [],
});
}}
/>
))}
</>
);
}

View File

@@ -0,0 +1,87 @@
import ProText from "components/ProText.tsx";
import styles from "pages/product/product.module.css";
import ProCheckboxGroups from "components/ProCheckboxGroups/ProCheckboxGroups.tsx";
import { message } from "antd";
import { TheExtrasGroup } from "utils/types/appTypes.ts";
import { useAppSelector } from "redux/hooks.ts";
type Props = {
group: TheExtrasGroup;
selectedExtras: Array<string>;
onChange: (values: Array<string>) => void;
};
export default function ExtrasGroup({
group,
selectedExtras,
onChange,
}: Props) {
const { isRTL } = useAppSelector((state) => state.locale);
return (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<ProText strong style={{ fontSize: "1rem" }}>
{isRTL ? group.nameAR : group.name}
</ProText>
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<ProText style={{ fontSize: "0.75rem", color: "#666" }}>
{group.force_limit_selection === 1 ? "Required" : "Optional"}
</ProText>
<ProText style={{ fontSize: "0.75rem", color: "#666" }}>
({selectedExtras?.length || 0}/{group.limit})
</ProText>
</div>
</div>
{group.force_limit_selection === 1 && (
<ProText
style={{
fontSize: "0.75rem",
color: "red",
marginBottom: "8px",
}}
>
* You must select exactly {group.limit} option
{group.limit > 1 ? "s" : ""}
</ProText>
)}
<div className={styles.productContainer}>
<ProCheckboxGroups
options={group.extras.map((extra: any) => ({
value: extra.id.toString(),
label: isRTL ? extra.name : extra.nameAR,
price: `+${extra.price}`,
}))}
value={selectedExtras}
onChange={(values: string[]) => {
// Check if the new selection would exceed the limit
if (values.length > group.limit) {
message.error(
`You can only select up to ${group.limit} option${group.limit > 1 ? "s" : ""} from this group.`,
);
return;
}
onChange(
group.extras
.filter((o) => values?.includes(o.id.toString()))
.map((e) => e.id.toString()) || [],
);
}}
/>
</div>
</div>
);
}

View File

@@ -23,7 +23,7 @@ export default function ProductFooter({
isValid?: boolean;
selectedExtras: Extra[];
selectedVariant?: Variant;
selectedGroups: Extra[];
selectedGroups: Record<number, string[]>;
quantity: number;
onClose?: () => void;
}) {
@@ -69,7 +69,11 @@ export default function ProductFooter({
comment: specialRequest,
variant: selectedVariant,
extras: selectedExtras,
extrasgroup: selectedGroups,
extrasgroupnew: Object.keys(selectedGroups).map((key) => ({
groupid: key,
extrasid: selectedGroups[Number(key)],
})),
extrasgroup: [""],
isHasLoyalty: product?.isHasLoyalty,
no_of_stamps_give: product?.no_of_stamps_give,
},

View File

@@ -13,7 +13,7 @@ import { colors } from "ThemeConstants";
import { Extra, Product } from "utils/types/appTypes";
import BackButton from "../menu/components/BackButton";
import ExtraComponent from "./components/Extra";
import ExtraGroups from "./components/ExtraGroups";
import ExtraGroupsContainer from "pages/product/components/ExtraGroupsContainer.tsx";
import ProductFooter from "./components/ProductFooter";
import Variants from "./components/Variants";
import styles from "./product.module.css";
@@ -40,9 +40,9 @@ export default function ProductDetailPage({
const [selectedExtras, setSelectedExtras] = useState<Extra[]>([]);
// State for selected extras by group
const [selectedExtrasByGroup, setSelectedExtrasByGroup] = useState<Extra[]>(
[],
);
const [selectedExtrasByGroup, setSelectedExtrasByGroup] = useState<
Record<number, Array<string>>
>([]);
// Determine variant levels based on options array length
const variantLevels = useMemo(() => {
@@ -126,9 +126,7 @@ export default function ProductDetailPage({
const allRequiredExtraGroupsSatisfied = product.theExtrasGroups.every(
(group) => {
if (group.force_limit_selection === 1) {
const selectedCount =
selectedExtrasByGroup.filter((extra) => extra.id === group.id)
.length || 0;
const selectedCount = selectedExtrasByGroup[group.id]?.length || 0;
return selectedCount === group.limit;
}
return true; // Optional groups are always satisfied
@@ -311,7 +309,7 @@ export default function ProductDetailPage({
)}
{product.theExtrasGroups.length > 0 && (
<ExtraGroups
<ExtraGroupsContainer
groupsList={product.theExtrasGroups}
selectedExtrasByGroup={selectedExtrasByGroup}
setSelectedExtrasByGroup={setSelectedExtrasByGroup}