diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json index 9702b48..621ed59 100644 --- a/src/assets/locals/ar.json +++ b/src/assets/locals/ar.json @@ -366,7 +366,10 @@ "areYouSureYouWantToCancelThisOrder?": "هل أنت متأكد أنك تريد إلغاء هذا الطلب؟", "keepOrder": "الاحتفاظ بالطلب", "thisActionCannotBeUndone": "هذا الإجراء لا يمكن التراجع عنه.", - "createOrderFailed": "فشل إنشاء الطلب" + "createOrderFailed": "فشل إنشاء الطلب", + "howWasYourExperienceWithFascanoRestaurant": "كيف كانت تجربتك مع مطعم فاسكانو؟", + "rateOrder": "تقييم الطلب", + "submitRating": "إرسال التقييم" }, "orderTypes": { "dine-in": "في المطعم", diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json index 2848704..fe4970f 100644 --- a/src/assets/locals/en.json +++ b/src/assets/locals/en.json @@ -377,7 +377,10 @@ "areYouSureYouWantToCancelThisOrder?": "Are you sure you want to cancel this order?", "keepOrder": "Keep Order", "thisActionCannotBeUndone": "This action cannot be undone.", - "createOrderFailed": "Create Order Failed" + "createOrderFailed": "Create Order Failed", + "howWasYourExperienceWithFascanoRestaurant": "How was your experience with Fascano Restaurant?", + "rateOrder": "Rate Order", + "submitRating": "Submit Rating" }, "orderTypes": { "dine-in": "Dine In", diff --git a/src/components/CustomBottomSheet/CustomBottomSheet.module.css b/src/components/CustomBottomSheet/CustomBottomSheet.module.css index ece3d23..e5b3cdf 100644 --- a/src/components/CustomBottomSheet/CustomBottomSheet.module.css +++ b/src/components/CustomBottomSheet/CustomBottomSheet.module.css @@ -1,33 +1,49 @@ .homeServiceCard { - width: 100%; - height: 48px; - display: flex; - justify-content: flex-start; - padding: 12px 18px !important; - row-gap: 10px; - transition: all 0.3s ease; - border-radius: 50px; - background-color: rgba(234, 31, 34, 0.04); - color: #ea1f22; - } - - .homeServiceCard :global(.ant-card-body) { - padding: 0px !important; - text-align: start; - width: 100%; - } - - .serviceIcon path { - stroke: #ea1f22; - } + width: 100%; + height: 48px; + display: flex; + justify-content: flex-start; + padding: 12px 18px !important; + row-gap: 10px; + transition: all 0.3s ease; + border-radius: 50px; + background-color: rgba(234, 31, 34, 0.04); + color: #ea1f22; +} - .nextIcon { - width: 24px; - height: 24px; - } +.homeServiceCard :global(.ant-card-body) { + padding: 0px !important; + text-align: start; + width: 100%; +} - .backIcon { - width: 24px; - height: 24px; - } - \ No newline at end of file +.rateServiceCard{ + width: 100%; + height: 48px; + display: flex; + justify-content: center; + padding: 12px 18px !important; + row-gap: 10px; + transition: all 0.3s ease; + border-radius: 50px; +} + +.rateServiceCard :global(.ant-card-body) { + padding: 0px !important; + text-align: start; + width: 100%; +} + +.serviceIcon path { + stroke: #ea1f22; +} + +.nextIcon { + width: 24px; + height: 24px; +} + +.backIcon { + width: 24px; + height: 24px; +} diff --git a/src/components/CustomBottomSheet/RateBottomSheet.tsx b/src/components/CustomBottomSheet/RateBottomSheet.tsx new file mode 100644 index 0000000..3ee8b37 --- /dev/null +++ b/src/components/CustomBottomSheet/RateBottomSheet.tsx @@ -0,0 +1,239 @@ +// import { useGlobals } from "../../hooks/useGlobals"; +import { Button, Card, message } from "antd"; +import BackIcon from "components/Icons/BackIcon"; +import NextIcon from "components/Icons/NextIcon"; +import RateIcon from "components/Icons/order/RateIcon"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useParams } from "react-router-dom"; +import { useGetUserDetailsQuery, useRateOrderMutation } from "redux/api/others"; +import { useAppSelector } from "redux/hooks"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import ProText from "../ProText"; +import ProTitle from "../ProTitle"; +import styles from "./CustomBottomSheet.module.css"; +import { colors } from "ThemeConstants"; + +export function RateBottomSheet() { + const { t } = useTranslation(); + const [isOpen, setIsOpen] = useState(false); + const [rating, setRating] = useState(0); + const { isRTL } = useAppSelector((state) => state.locale); + const { orderId } = useParams(); + + const [rateOrder] = useRateOrderMutation(); + const { data: getUserDetails } = useGetUserDetailsQuery(undefined, { + skip: !isOpen, + }); + const handleSubmitRating = () => { + setIsOpen(false); + rateOrder({ + orderID: orderId || "", + rating: rating, + comment: "This is a test comment", + userID: getUserDetails?.id.toString() || "", + }).then((res: any) => { + if (res.error) { + message.error(res.error.data.message); + } else { + message.success(res.data.message); + } + }); + }; + + const handleStarClick = ( + starIndex: number, + event: React.MouseEvent, + ) => { + const starElement = event.currentTarget; + const rect = starElement.getBoundingClientRect(); + const clickX = event.clientX - rect.left; + const starWidth = rect.width; + + // Calculate which part of the star was clicked + // Divide star into 4 parts: 0-25% = 0.25, 25-50% = 0.5, 50-75% = 0.75, 75-100% = 1.0 + const clickPercentage = clickX / starWidth; + let starValue = 0; + + if (clickPercentage <= 0.25) { + starValue = 0.25; + } else if (clickPercentage <= 0.5) { + starValue = 0.5; + } else if (clickPercentage <= 0.75) { + starValue = 0.75; + } else { + starValue = 1.0; + } + + const newRating = starIndex + starValue; + setRating(Math.min(newRating, 5)); // Cap at 5 + }; + + return ( + <> + setIsOpen(true)}> +
+
+ + {t("order.rateOrder")} + +
+
+
+ + setIsOpen(false)} + title={t("order.rateOrder")} + showCloseButton={false} + initialSnap={1} + height={450} + snapPoints={[450]} + > +
+ + + + {t("order.howWasYourExperienceWithFascanoRestaurant")} + + +
+ {[0, 1, 2, 3, 4].map((starIndex) => { + const starValue = starIndex + 1; + const isFilled = rating >= starValue; + const isPartialFilled = rating > starIndex && rating < starValue; + const fillPercentage = isPartialFilled + ? ((rating - starIndex) * 100) / 1 + : isFilled + ? 100 + : 0; + + return ( +
handleStarClick(starIndex, e)} + style={{ + position: "relative", + cursor: "pointer", + width: 40, + height: 40, + display: "inline-block", + }} + > + {/* Gray star background */} + + + + {/* Golden star overlay with overflow for partial fill */} +
+ + + +
+
+ ); + })} +
+ +
+ +
+
+
+ + ); +} diff --git a/src/components/Icons/order/RateIcon.tsx b/src/components/Icons/order/RateIcon.tsx new file mode 100644 index 0000000..e8137a9 --- /dev/null +++ b/src/components/Icons/order/RateIcon.tsx @@ -0,0 +1,758 @@ +interface RateIconType { + className?: string; + onClick?: () => void; +} + +const RateIcon = ({ className, onClick }: RateIconType) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default RateIcon; diff --git a/src/pages/order/page.tsx b/src/pages/order/page.tsx index c2ca95b..196b738 100644 --- a/src/pages/order/page.tsx +++ b/src/pages/order/page.tsx @@ -19,6 +19,7 @@ import Stepper from "./components/Stepper"; import styles from "./order.module.css"; import BackIcon from "components/Icons/BackIcon"; import NextIcon from "components/Icons/NextIcon"; +import { RateBottomSheet } from "components/CustomBottomSheet/RateBottomSheet"; export default function OrderPage() { const { t } = useTranslation(); @@ -220,6 +221,8 @@ export default function OrderPage() { + + diff --git a/src/utils/types/appTypes.ts b/src/utils/types/appTypes.ts index 688a336..20b66a8 100644 --- a/src/utils/types/appTypes.ts +++ b/src/utils/types/appTypes.ts @@ -332,11 +332,27 @@ export interface CartItem { uniqueId?: string; } -export interface User { - id: string; +export interface UserType { + id: number; + username: string; email: string; - name: string; - role: "admin" | "user"; + mobilenumber: string; + device_token: string; + password: string; + redeem_point: number; + customer_image: string; + login_taken: string; + device: string; + social_id: string; + otp_number: number; + otp_status: number; + created_datetime: string; + user_uuid: string; + is_deleted: number; + thawani_customer_id: string; + birth_date: string; + gender: string; + country_id: number; } export type Locale = "en" | "ar";