Compare commits

..

2 Commits

Author SHA1 Message Date
Moaz Dawalibi
5fbbc2e3dc Merge branch 'main' of https://git.point-dev.net/Karimaldeen/dm-website 2024-06-22 12:13:25 +03:00
Moaz Dawalibi
0c709c0dfd single product new design 2024-06-22 12:13:12 +03:00
13 changed files with 308 additions and 94 deletions

View File

@ -0,0 +1,28 @@
import { useTranslation } from 'react-i18next';
import { languageObject } from '../../utils/languageObject';
const ProductAdditionalInfo = (data:any) => {
const ProductAdditionalInfo = data?.data;
const { t } = useTranslation();
const infoEntries = ProductAdditionalInfo?.info ? Object.entries(ProductAdditionalInfo.info) : [];
console.log(ProductAdditionalInfo?.info);
return (
<div className='product_additional_info'>
<h4>{t("Additional Info")}</h4>
{infoEntries.length > 0 ? (
infoEntries.map(([key, value]: [any, any]) => (
<span key={key}>
<h6>{languageObject(key)}</h6>
<h5>{languageObject(value)}</h5>
</span>
))
) : (
<p>{t("Product info is empty")}</p>
)}
</div>
);
};
export default ProductAdditionalInfo;

View File

@ -0,0 +1,14 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
const ProductDescription = ({data}:any) => {
const {t} = useTranslation();
return (
<div className='product_description'>
<h4>{t("Description")}</h4>
<p>{data?.description}</p>
</div>
)
}
export default ProductDescription

View File

@ -0,0 +1,31 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import { languageObject } from '../../utils/languageObject';
const ProductInfo = (data:any) => {
const productInfo = data?.data
const {t} = useTranslation();
return (
<div className='Product_info'>
<h4>{t("Info")}</h4>
<div>
<span>
<h6>{t("name")}</h6> <h5>{languageObject(productInfo?.name)}</h5>
</span>
<span>
<h6>{t("Category")}</h6> <h5>{languageObject(productInfo?.category?.name)}</h5>
</span>
<span>
<h6>{t("Price")}</h6> <h5>{productInfo?.price}</h5>
</span>
<span>
<h6>{t("Description")}</h6> <h5>{languageObject(productInfo?.description)}</h5>
</span>
</div>
</div>
)
}
export default ProductInfo

View File

@ -0,0 +1,37 @@
import React from 'react';
import { Tabs } from 'antd';
import type { TabsProps } from 'antd';
import { useTranslation } from 'react-i18next';
import ProductDescription from './ProductDescription';
import ProductInfo from './ProductInfo';
import ProductAdditionalInfo from './ProductAdditionalInfo';
const ProductTabs = ({data}:any) => {
const {t} = useTranslation();
console.log(data?.info);
const items: TabsProps['items'] = [
{
key: '1',
label: t('product_info'),
children: <ProductInfo data={data}/>,
},
{
key: '2',
label: t('description'),
children: <ProductDescription data={data}/>,
},
{
key: '3',
label: t('additional information'),
children: <ProductAdditionalInfo data={data}/>,
},
];
return (
<Tabs className='product_tabs' defaultActiveKey="1" items={items} />
)
}
export default ProductTabs

View File

@ -13,20 +13,20 @@ const ContactTab = ({className=""}: {className?: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const {mutate,isSuccess,isLoading} = useAddSupportMessage() const {mutate,isSuccess,isLoading} = useAddSupportMessage()
const SoicalInfo: { icon: any; text: string }[] = [ const SoicalInfo: { icon: any; text: string }[] = [
{ {
icon: <IoMailOutline/>, icon: <IoMailOutline/>,
text: "info@yourdomain.com", text: "info@yourdomain.com",
}, },
{ {
icon: <FiPhone/> , icon: <FiPhone/> ,
text: "+1 (378) 400-1234", text: "+1 (378) 400-1234",
}, },
{ {
icon: <IoEarthOutline/>, icon: <IoEarthOutline/>,
text: "www.yourdomain.com", text: "www.yourdomain.com",
}, },
]; ];
const SoicalIcons: { icon: any; }[] = [ const SoicalIcons: { icon: any; }[] = [
{ {

View File

@ -1,16 +1,10 @@
import React, { useRef, useState } from 'react' import React, { useState } from 'react'
import { FaChevronRight, FaInstagram, FaLinkedinIn, FaTwitter } from "react-icons/fa6";
import { Button, Form } from 'react-bootstrap';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { IoMailOutline,IoEarthOutline } from "react-icons/io5";
import { FiPhone } from "react-icons/fi";
import { FaFacebookF } from "react-icons/fa";
import ContactTab from '../../Components/Setting/Tabs/ContactTab'; import ContactTab from '../../Components/Setting/Tabs/ContactTab';
import HeaderLink from '../../Components/Ui/HeaderLink'; import HeaderLink from '../../Components/Ui/HeaderLink';
const Contact = () => { const Contact = () => {
const form = useRef<any>(null);
const { t } = useTranslation(); const { t } = useTranslation();
const sendEmail = (e: React.FormEvent<HTMLFormElement>) => { const sendEmail = (e: React.FormEvent<HTMLFormElement>) => {
@ -35,35 +29,6 @@ const Contact = () => {
const [Message , setMessage] = useState('') const [Message , setMessage] = useState('')
const SoicalInfo: { icon: any; text: string }[] = [
{
icon: <IoMailOutline/>,
text: "info@yourdomain.com",
},
{
icon: <FiPhone/> ,
text: "+1 (378) 400-1234",
},
{
icon: <IoEarthOutline/>,
text: "www.yourdomain.com",
},
];
const SoicalIcons: { icon: any; }[] = [
{
icon: <FaFacebookF/>,
},
{
icon: <FaTwitter /> ,
},
{
icon: <FaLinkedinIn />,
},
{
icon: <FaInstagram />,
},
];
return ( return (
<div className='contact_container'> <div className='contact_container'>

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { IoIosArrowForward } from "react-icons/io"; import { IoIosArrowForward } from "react-icons/io";
import { Currency } from "../../Layout/app/Const"; import { Currency } from "../../Layout/app/Const";
@ -12,15 +12,29 @@ import { languageObject } from "../../utils/languageObject";
import { jsonObjectToArray } from "../../utils/jsonObjectToArray"; import { jsonObjectToArray } from "../../utils/jsonObjectToArray";
import HeaderLink from "../../Components/Ui/HeaderLink"; import HeaderLink from "../../Components/Ui/HeaderLink";
import { BaseURL } from "../../api/utils/config"; import { BaseURL } from "../../api/utils/config";
import ProductTabs from "../../Components/Products/ProductsTabs";
import { IoHeartOutline } from "react-icons/io5";
import { useAddFavorite, useDeleteFavorite } from "../../api/Favorite";
import { useQueryClient } from "react-query";
import { toast } from "react-toastify";
import { FaCartPlus } from "react-icons/fa";
import { useCartState } from "../../state/CartState";
import useAuthState from "../../state/AuthState";
const Page = () => { const Page = () => {
const [t] = useTranslation(); const [t] = useTranslation();
const {product_id} = useParams() const {product_id} = useParams()
const { Cart, setCart } = useCartState();
const { isAuthenticated } = useAuthState();
const { mutate: addToFavorite, isSuccess: Add } = useAddFavorite();
const { mutate: deleteToFavorite, isSuccess: Delete } = useDeleteFavorite();
const {data,isLoading} = useGetSingleBaseProduct({ const {data,isLoading} = useGetSingleBaseProduct({
show:product_id show:product_id
}) })
const product = data?.data?.product as Product || {} const product = data?.data?.product as Product || {}
console.log(product);
const [MainImage, setMainImage] = useState(product?.main_photo); const [MainImage, setMainImage] = useState(product?.main_photo);
@ -28,8 +42,51 @@ const Page = () => {
const handelImage = (item: any) => { const handelImage = (item: any) => {
// setMainImage(item); // setMainImage(item);
}; };
console.log(product?.images);
const queryClient = useQueryClient();
const handelChangeFavorite = (item: Product) => {
if (!isAuthenticated) {
toast.error("sorry you need to be authenticated");
return;
}
if (item?.favorite) {
deleteToFavorite({
id: item?.id,
});
} else {
addToFavorite({
id: item?.id,
});
}
queryClient.invalidateQueries('mainProduct');
};
useEffect(() => {
if (Add) {
toast.success("added to favorite successfully");
queryClient.invalidateQueries("mainProduct");
}
}, [Add]);
useEffect(() => {
if (Delete) {
toast.success("removed from favorite successfully");
queryClient.invalidateQueries("mainProduct");
}
}, [Delete]);
const handelAddToCart = (item: Product) => {
if (!isAuthenticated) {
toast.error("sorry you need to be authenticated");
return;
}
setCart(item);
toast.success(`${languageObject(item?.name)}` + " added to cart");
};
const info = jsonObjectToArray(product?.info) const info = jsonObjectToArray(product?.info)
if(isLoading){ if(isLoading){
@ -43,13 +100,22 @@ const Page = () => {
<main> <main>
<div className="Product_left"> <div className="Product_left">
<span> <span>
<div className="fav_icon">
<IoHeartOutline
onClick={() => handelChangeFavorite(product)}
className={product?.favorite ? "" : "not_favorite"}
/>
<FaCartPlus
onClick={() => handelAddToCart(product)}
/>
{/* {t("add to cart")} */}
</div>
<CustomImage src={MainImage ?? product?.main_photo} alt="" /> <CustomImage src={MainImage ?? product?.main_photo} alt="" />
<div className="gallery_product"> <div className="gallery_product">
{product?.images?.map((item,index)=>{ {product?.images?.map((item,index)=>{
return ( return (
<span key={index} onClick={() => handelImage(item?.path)} > <span key={index} onClick={() => handelImage(item?.path)} >
<CustomImage <CustomImage
src={item?.path} src={item?.path}
alt="" alt=""
/> />
@ -68,20 +134,8 @@ const Page = () => {
</div> </div>
<div className="Product_Right"> <div className="Product_Right">
<h1> {languageObject(product?.name)} </h1> <ProductTabs data={product}/>
<div> </div>
<span>
<h6>{t("Category")}</h6> <h5>{languageObject(product?.category?.name)}</h5>
</span>
<span>
<h6>{t("Price")}</h6> <h5>{product?.price}</h5>
</span>
<span>
<h6>{t("Description")}</h6> <h5>{languageObject(product?.description)}</h5>
</span>
</div>
</div>
</main> </main>
<Similar category_id={product?.category?.id} /> <Similar category_id={product?.category?.id} />
</div> </div>

View File

@ -57,3 +57,6 @@ svg {
} }
} }
} }
//

View File

@ -9,6 +9,7 @@
--bg: white; --bg: white;
--bg2: #dcdcdc; --bg2: #dcdcdc;
--white: white; --white: white;
--whiteOpacity: #f6f6f6;
--shadow: rgba(0, 0, 0, 0.15); --shadow: rgba(0, 0, 0, 0.15);
--gray: rgb(207, 210, 214); --gray: rgb(207, 210, 214);
--whiteGray: #717171; --whiteGray: #717171;

View File

@ -21,6 +21,16 @@
.Product_left { .Product_left {
display: flex; display: flex;
width: 45%; width: 45%;
.fav_icon{
svg{
font-size: 25px;
transition: ease-in-out .3s;
margin-inline: 5px;
&:hover{
color: var(--primary);
}
}
}
> span { > span {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -33,7 +43,6 @@
display: flex; display: flex;
gap: 20px; gap: 20px;
overflow-x: scroll; overflow-x: scroll;
// max-width: 40vw;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
@ -50,34 +59,79 @@
} }
} }
.Product_Right { .Product_info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 5px;
align-items: flex-start; align-items: flex-start;
padding-top: 2%;
h1 { h1 {
font-size: 4vw; font-size: 4vw;
} }
> div { > div {
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 10px;
> span { > span {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 20px; gap: 20px;
width: 80%; width: 80%;
padding: 10px 15px 0px 15px;
&:nth-child(odd){
background: var(--whiteOpacity) !important;
}
h6 { h6 {
color: gray; color: var(--whiteGray);
position: relative; position: relative;
min-width: 80px; width: 50%;
font-size: 12px;
}
h5{
height: 100%;
color: var(--text);
font-size: 12px;
} }
} }
} }
} }
} }
.Product_Right{
width: 50%;
}
.product_tab{
width: 100%;
}
.product_description{
width: 100%;
p{
padding-inline: 15px;
width: 95%;
background: var(--whiteOpacity);
}
}
.product_additional_info{
> span {
display: flex;
align-items: flex-start;
gap: 20px;
width: 100%;
padding: 10px 15px 0px 15px;
&:nth-child(even){
background: var(--whiteOpacity) !important;
}
h6 {
color: var(--text);
position: relative;
width: 50%;
font-size: 10px;
}
h5{
color: var(--text);
font-size: 10px;
}
}
}
.Products { .Products {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -89,23 +143,25 @@
> header { > header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
h1{
font-size: 20px;
}
} }
.ProductCards { .ProductCards {
max-width: 90vw; max-width: 90vw;
margin-inline: auto; margin-inline: auto;
display: flex; display: flex;
// gap: 40px;
// overflow-x: scroll;
// &::-webkit-scrollbar {
// display: none;
// }
} }
} }
@media screen and (max-width:850px) {
@media screen and (max-width:700px) { .ant-tabs .ant-tabs-tab-btn{
font-size: 14px !important;
}
.ant-tabs >.ant-tabs-nav .ant-tabs-nav-wrap,.ant-tabs >div>.ant-tabs-nav .ant-tabs-nav-wrap{
justify-content: center !important;align-items: center !important;
}
.Product { .Product {
display: flex; display: flex;
min-height: 100vh; min-height: 100vh;
@ -121,24 +177,20 @@
gap: 10%; gap: 10%;
} }
.Product_left { .Product_left {
display: flex; display: flex;justify-content: center;align-items: center;
width: 90%; width: 100%;
margin-inline: auto; margin-inline: auto;
> span { > span {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
> img { > img {
width: 60%; width: 50%;
margin-inline: auto; margin-inline: auto;
} }
.gallery_product { .gallery_product {
display: flex; display: flex;
gap: 20px; gap: 20px;
// overflow-x: scroll;
// &::-webkit-scrollbar {
// display: none;
// }
span { span {
padding: 10px 10px; padding: 10px 10px;
border: 2px solid rgba(128, 128, 128, 0.1); border: 2px solid rgba(128, 128, 128, 0.1);
@ -180,4 +232,10 @@
} }
} }
} }
}
@media screen and (max-width:450px) {
.ant-tabs >.ant-tabs-nav .ant-tabs-nav-wrap,.ant-tabs >div>.ant-tabs-nav .ant-tabs-nav-wrap{
justify-content: flex-start !important;align-items: flex-start !important;
}
} }

View File

@ -391,6 +391,14 @@
"Track, return or purchase items":"تتبع أو إرجاع أو شراء العناصر", "Track, return or purchase items":"تتبع أو إرجاع أو شراء العناصر",
"Order History":"تاريخ الطلب", "Order History":"تاريخ الطلب",
"Please try using other keywords to find the product name":"يرجى محاولة استخدام كلمات رئيسية أخرى للعثور على اسم المنتج", "Please try using other keywords to find the product name":"يرجى محاولة استخدام كلمات رئيسية أخرى للعثور على اسم المنتج",
"There are no suitable products":"لا توجد منتجات مناسبة" "There are no suitable products":"لا توجد منتجات مناسبة",
"product_info":"معلومات المنتج",
"additional information":"معلومات إضافية",
"description":"الوصف",
"Info":"معلومات",
"Additional Info":"معلومات إضافية",
"Product info is empty":"معلومات المنتج فارغة",
"":""
} }

View File

@ -377,5 +377,13 @@
"Track, return or purchase items":"跟踪、退货或购买物品", "Track, return or purchase items":"跟踪、退货或购买物品",
"Order History":"订单历史", "Order History":"订单历史",
"Please try using other keywords to find the product name":"请尝试使用其他关键字来查找产品名称", "Please try using other keywords to find the product name":"请尝试使用其他关键字来查找产品名称",
"There are no suitable products":"没有合适的产品" "There are no suitable products":"没有合适的产品",
"product_info":"产品信息",
"additional information":"附加信息",
"description":"描述",
"Info":"信息",
"Additional Info":"附加信息",
"Product info is empty":"产品信息为空",
"":""
} }

View File

@ -387,7 +387,14 @@
"Track, return or purchase items":"Track, return or purchase items", "Track, return or purchase items":"Track, return or purchase items",
"Order History":"Order History", "Order History":"Order History",
"Please try using other keywords to find the product name":"Please try using other keywords to find the product name", "Please try using other keywords to find the product name":"Please try using other keywords to find the product name",
"There are no suitable products":"There are no suitable products" "There are no suitable products":"There are no suitable products",
"product_info":"Product info",
"additional information":"Additional information",
"description":"Description",
"Info":"Info",
"Additional Info":"Additional Info",
"Product info is empty":"Product info is empty"
} }