Commit 12f74c1b by jaymehta

.

1 parent 64714aa2
import { EditTwoTone, EyeTwoTone, SearchOutlined } from "@ant-design/icons";
import { Button, Input, Space, Table, Tag } from "antd";
import React, { Fragment, useEffect, useRef, useState } from "react";
import { useSelector } from "react-redux";
import ReviewsListing from "./ReviewsListing";
const Reviews = () => {
const searchInput = useRef(null);
const [searchText, setSearchText] = useState("");
const [searchedColumn, setSearchedColumn] = useState("");
const [columns, setcolumns] = useState([]);
const [data, setdata] = useState([]);
const [showReviews, setshowReviews] = useState(false);
const handleReset = clearFilters => {
clearFilters();
setSearchText("");
};
const handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
setSearchText(selectedKeys[0]);
setSearchedColumn(dataIndex);
};
const { allActivitiesData } = useSelector(state => state.allActivitiesData);
console.log("allActivitiesData", allActivitiesData);
const getColumnSearchProps = dataIndex => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => (
<div
style={{
padding: 8
}}
onKeyDown={e => e.stopPropagation()}
>
<Input
ref={searchInput}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
style={{
marginBottom: 8,
display: "block"
}}
/>
<Space>
<Button
type="primary"
onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{
width: 90
}}
>
Search
</Button>
<Button
onClick={() => clearFilters && handleReset(clearFilters)}
size="small"
style={{
width: 90
}}
>
Reset
</Button>
<Button
type="link"
size="small"
onClick={() => {
confirm({
closeDropdown: false
});
setSearchText(selectedKeys[0]);
setSearchedColumn(dataIndex);
}}
>
Filter
</Button>
<Button
type="link"
size="small"
onClick={() => {
close();
}}
>
close
</Button>
</Space>
</div>
),
filterIcon: filtered => (
<SearchOutlined
style={{
color: filtered ? "#1677ff" : undefined
}}
/>
),
onFilter: (value, record) => record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownOpenChange: visible => {
if (visible) {
setTimeout(() => searchInput.current?.select(), 100);
}
},
render: text =>
searchedColumn === dataIndex ? (
<Highlighter
highlightStyle={{
backgroundColor: "#ffc069",
padding: 0
}}
searchWords={[searchText]}
autoEscape
textToHighlight={text ? text.toString() : ""}
/>
) : (
text
)
});
useEffect(() => {
setdata(
allActivitiesData &&
allActivitiesData.data.length > 0 &&
allActivitiesData.data.map(item => {
return {
key: item.id,
name: item.attributes.name,
reviewsCount: item.attributes.reviews.data.length
// tags: ["nice", "developer"]
};
})
);
setcolumns([
{
title: "Activities",
dataIndex: "name",
key: "name",
...getColumnSearchProps("name"),
render: text => <a>{text}</a>
},
{
title: "Reviews count",
dataIndex: "reviewsCount",
key: "reviewsCount"
},
{
title: "See reviews",
render: (_, record) => (
<Space size="middle">
{/* <a>Invite {record.name}</a> */}
<EyeTwoTone
style={{ fontSize: "22px" }}
onClick={() => {
setshowReviews(true)
// router.push(`/vendor/activities/${record.key}`);
// console.log(record);
}}
/>
</Space>
),
width: "10%"
}
]);
}, []);
return (
<Fragment>
<div className="row">{!showReviews ? <Table columns={columns} dataSource={data} /> : <ReviewsListing/>}</div>
</Fragment>
);
};
export default Reviews;
import React from "react";
import { Accordion } from "react-bootstrap";
const ReviewsListing = () => {
return (
<div className="">
<Accordion className="accordion-filter" defaultActiveKey="0" flush>
<Accordion.Item eventKey="0">
<Accordion.Header>Review 1</Accordion.Header>
<Accordion.Body>
<div>Review 1</div>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="1">
<Accordion.Header>Review 1</Accordion.Header>
<Accordion.Body>
<div>Review 1</div>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="2">
<Accordion.Header>Review 1</Accordion.Header>
<Accordion.Body>
<div>Review 1</div>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="3">
<Accordion.Header>Review 1</Accordion.Header>
<Accordion.Body>
<div>Review 1</div>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="4">
<Accordion.Header>Review 1</Accordion.Header>
<Accordion.Body>
<div>Review 1</div>
</Accordion.Body>
</Accordion.Item>
</Accordion>
</div>
);
};
export default ReviewsListing;
import Image from "next/image";
import React from "react";
import { useRouter } from "next/router";
import React, { useState } from "react";
import { Button, Modal } from "react-bootstrap";
import { useSelector } from "react-redux";
import StarRatings from "react-star-ratings";
import { postReviewEndUser } from "../../redux/actions/reviewsAction";
const GuestReviews = ({ activityById }) => {
const [rating, setRating] = useState(0);
const [comments, setcomments] = useState();
const [readMoreText, setreadMoreText] = useState();
const [showModal, setshowModal] = useState(false);
const { endUser } = useSelector(state => state.endUser);
const { reviews } = useSelector(state => state.reviews);
const router = useRouter();
console.log("reviews", reviews);
const handleRatingChange = newRating => {
setRating(newRating);
};
const GuestReviews = ({activityById}) => {
const guestReviewsData = [
{
name: "Andrea",
month: "October 2023",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
},
{
name: "Andrea",
month: "October 2023",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
},
{
name: "Andrea",
month: "October 2023",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
},
{
name: "Andrea",
month: "October 2023",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
}
];
return (
<>
<section className="guest-reviews-session">
......@@ -36,9 +32,9 @@ const GuestReviews = ({activityById}) => {
<div className="head01">
<div className="title">Guest reviews</div>
</div>
<a href="" className="view-all-reviews-btn">
{/* <a href="" className="view-all-reviews-btn">
View All Reviews
</a>
</a> */}
</div>
</div>
</div>
......@@ -46,60 +42,117 @@ const GuestReviews = ({activityById}) => {
<div className="col-12">
<div className="product-reviews">
<span className="rating">8.8</span>
<span>Fabulous </span>
<span className="review">1,365 Reviews</span>
<span>Fabulous</span>
<span className="review">{` ${reviews.length} reviews`}</span>
</div>
</div>
</div>
<div className="row">
{guestReviewsData &&
guestReviewsData.map(data => {
{reviews &&
reviews.map(data => {
return (
<div className="col-md-6">
<div className="guest-reviews-detail">
<div className="head">
<div className="name">{data.name}</div>
<div className="month">{data.month}</div>
</div>
<div className="star">
<a href="">
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span>
</a>
<a href="">
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span>
</a>
<a href="">
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span>
</a>
<a href="">
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span>
</a>
<a href="">
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span>
</a>
</div>
<div className="name">{data.attributes.endUser.data.attributes.name}</div>
<div className="month">{data.attributes.createdAt}</div>
</div>
<StarRatings
className="col-3 mx-2"
rating={data.attributes.rating}
starRatedColor="yellow" // Set the rated color to yellow
starHoverColor="yellow" // Set the hover color to yellow
changeRating={() => {}}
numberOfStars={5}
name="rating"
starDimension="20px" // Set star width and height
/>
<div className="review-content">
{data.description}
<a href="">Read More</a>
"{data.attributes.comments.length > 80 ? `${data.attributes.comments.slice(0, 80)}...` : data.attributes.comments}
{data.attributes.comments.length > 80 && (
<a
onClick={() => {
setreadMoreText(data);
setshowModal(true);
}}
>
Read More
</a>
)}
</div>
{/* View All */}
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
{endUser && (
<div className="row">
<div className="head01">
<div className="title">Post a review:</div>
</div>
<div>
<label className=" mb-3 ">Rating: </label>{" "}
<StarRatings
className="col-3 mx-2"
rating={rating}
starRatedColor="yellow" // Set the rated color to yellow
starHoverColor="yellow" // Set the hover color to yellow
changeRating={handleRatingChange}
numberOfStars={5}
name="rating"
starDimension="20px" // Set star width and height
/>
</div>
<textarea
// style={{ width: "50%" }}
rows={4}
className="p-2 mx-2 col-md-6"
placeholder="Comments"
name="comments"
value={comments}
onChange={e => {
e.preventDefault();
// console.log(e.target.value);4
setcomments(e.target.value);
// setrejectionReasonText(e.target.value);
}}
/>
<div className="view-all-btn my-3">
<Button
disabled={rating == 0 || comments == ""}
variant="primary"
onClick={async e => {
e.preventDefault();
const res = await postReviewEndUser({ endUserId: endUser.id, activityId: router.query.id, comments, rating });
console.log("res", res);
setRating(0);
setcomments("");
}}
>
Submit
</Button>
</div>
</div>
)}
</div>
</div>
</div>
{readMoreText && (
<Modal
centered
show={showModal}
onHide={() => {
setshowModal();
setreadMoreText(null);
}}
>
<Modal.Header closeButton>Review from {readMoreText.attributes.endUser.data.attributes.name}</Modal.Header>
<Modal.Body>
<div>{readMoreText.attributes.comments}</div>
</Modal.Body>
</Modal>
)}
</section>
</>
);
......
......@@ -13,9 +13,12 @@ import "swiper/css/pagination";
import "swiper/css/navigation";
import { cleanImage } from "../../services/imageHandling.js";
import { useRouter } from "next/router.js";
const BrowseExperiences = ({allActivitiesData}) => {
import WishlistComponent from "../detail/WIshlistComponent.js";
import { useSelector } from "react-redux";
const BrowseExperiences = ({ allActivitiesData }) => {
const { endUser } = useSelector(state => state.endUser);
const router = useRouter()
const router = useRouter();
return (
<>
......@@ -68,12 +71,7 @@ const BrowseExperiences = ({allActivitiesData}) => {
allActivitiesData.data.map(data => {
return (
<SwiperSlide key={data?.id}>
<motion.div
variants={zoomIn("left", 0.3)}
initial={"hidden"}
whileInView={"show"}
viewport={{ once: false, amount: 0.2 }}
>
<motion.div variants={zoomIn("left", 0.3)} initial={"hidden"} whileInView={"show"} viewport={{ once: false, amount: 0.2 }}>
<div className="browse-experiences-item">
<a href="/detail" className="img-wrapper">
<span className="image-container">
......@@ -92,9 +90,10 @@ const BrowseExperiences = ({allActivitiesData}) => {
</span>
</div>
<div className="wishlist">
<span className="image-container">
{endUser && <WishlistComponent activityId={data.id} userId={endUser.id} />}
{/* <span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/wishlist.svg" />
</span>
</span> */}
</div>
</div>
</div>
......@@ -109,9 +108,14 @@ const BrowseExperiences = ({allActivitiesData}) => {
<div className="">Includes taxes & Fees</div>
</div>
<div className="explore-now">
<Button onClick={()=> {
router.push(`/activities/${data.id}`)
}} variant="primary">Explore Now</Button>
<Button
onClick={() => {
router.push(`/activities/${data.id}`);
}}
variant="primary"
>
Explore Now
</Button>
</div>
</div>
</div>
......
......@@ -41,6 +41,12 @@ const Sidebar = () => {
<span>Gift Card</span>
</a>
</li>
<li className={router.pathname === "/admin/reviews" ? "active" : ""}>
<a href="/admin/reviews">
<Image alt="" width={22} height={15} src="/images/admin/icon-gift.svg" />
<span>Reviews</span>
</a>
</li>
</ul>
</div>
);
......
......@@ -25,6 +25,7 @@ import { ArrowLeftOutlined, CheckCircleTwoTone, PlusOutlined } from "@ant-design
import { useRouter } from "next/router";
import ImageUploadPopUp from "./ImageUploadPopUp";
import { loadUser } from "../../redux/actions/userActions";
import UploadImageComponent from "./UploadImageComponent";
dayjs.extend(customParseFormat);
const { RangePicker } = DatePicker;
......@@ -334,7 +335,7 @@ const ActivityDetails = ({ isUpdate }) => {
// dispatch(updateActivityById(8))
// }, [])
// console.log("masterDays", masterDays);
console.log("activityById", activityById);
console.log("activityPeriodState", activityPeriodState);
return (
<Fragment>
......@@ -1104,10 +1105,13 @@ const ActivityDetails = ({ isUpdate }) => {
</div>
</div>
</div>
{/* const [showImageUploadModal, setshowImageUploadModal] = useState(false); */}
{/* <ImageUploadPopUp isUpdate={isUpdate} setimagesArrayComponent={setimagesArrayComponent} />
{console.log("setimagesArrayComponent", imagesArrayComponent)} */}
{/* <UploadImageComponent /> */}
<ImageUploadPopUp
isUpdate={isUpdate}
setimagesArrayComponent={setimagesArrayComponent}
populatedImages={activityById?.data.attributes.imagesComponent}
/>
{console.log("setimagesArrayComponent", imagesArrayComponent)}
<br />
<>
{/* <button
......
......@@ -12,11 +12,13 @@ const getBase64 = file =>
reader.onerror = error => reject(error);
});
const ImageUploadPopUp = ({ isUpdate, setimagesArrayComponent }) => {
const ImageUploadPopUp = ({ isUpdate, setimagesArrayComponent, populatedImages }) => {
// const { loadedUser } = useSelector(state => state.loadedUser);
const [session, setSession] = useState();
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [fileList, setFileList] = useState([]);
const [uploading, setUploading] = useState(false);
useEffect(() => {
const fetchSession = async () => {
......@@ -25,9 +27,22 @@ const ImageUploadPopUp = ({ isUpdate, setimagesArrayComponent }) => {
fetchSession();
// dispatch(getLoggedInVendor());
}, []);
const [fileList, setFileList] = useState([]);
const [uploading, setUploading] = useState(false);
// console.log("session", session);
useEffect(() => {
setFileList(
populatedImages &&
populatedImages.map(item => {
return {
uid: item.id,
name: "image.png",
status: "done",
url: item.imageUrl
};
})
);
}, []);
console.log("fileList", fileList);
let formData = new FormData();
const handleUpload = async () => {
fileList.forEach(file => {
......@@ -73,11 +88,12 @@ const ImageUploadPopUp = ({ isUpdate, setimagesArrayComponent }) => {
setPreviewOpen(true);
};
const handleImageChange = info => {
console.log("info", info);
setFileList(info.fileList);
};
return (
<div>
<Upload listType="picture-card" fileList={fileList} onChange={handleImageChange}>
<Upload showUploadList={{ showRemoveIcon: false }} listType="picture-card" fileList={fileList} onChange={handleImageChange} onPreview={handlePreview}>
{fileList.length >= 5 ? null : (
<div>
<PlusOutlined />
......
import { PlusOutlined } from "@ant-design/icons";
import React from "react";
const UploadImageComponent = () => {
return (
<div>
<div className="upload-file">
{/* <button> */}
<input className="upload-file-input" type="file" id={`termsConditionsFile`} name={`termsConditionsFile`} />
{/* <PlusOutlined /> */}
{/* </button> */}
{/* <label className="custom-file-label" htmlFor={`termsConditionsFile`}>
Upload
</label> */}
</div>
</div>
);
};
export default UploadImageComponent;
......@@ -6,6 +6,7 @@ import { getActivitiesByFilters, getActivityById } from "../../redux/actions/act
import { loadUser } from "../../redux/actions/userActions";
import { wrapper } from "../../redux/store";
import { getFaqs } from "../../redux/actions/faqsAction";
import { getReviewsAction } from "../../redux/actions/reviewsAction";
const ActivityDetailPage = () => {
const dispatch = useDispatch();
......@@ -26,6 +27,7 @@ export default ActivityDetailPage;
export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => {
await store.dispatch(getActivityById(query.id));
await store.dispatch(getFaqs());
await store.dispatch(getReviewsAction({activityId: query.id}))
// await store.dispatch(getActivitiesByFilters({category: query.category}))
return {
......
import React from "react";
import Reviews from "../../../components/admin/Reviews";
import Sidebar from "../../../components/layout/AdminDashboardSidebar";
import Layout from "../../../components/layout/Layout";
import { getActivitiesByFilters } from "../../../redux/actions/activityAction";
import { wrapper } from "../../../redux/store";
const ReviewsPage = () => {
return (
<div>
<Layout>
<div className="sidebarContainer">
<Sidebar />
<div className="content">
<Reviews />
</div>
</div>
</Layout>
</div>
);
};
export default ReviewsPage;
/** For server side rendering */
export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => {
// await store.dispatch(loadUser());
await store.dispatch(getActivitiesByFilters({}));
// await store.dispatch(getAllVendors());
// await store.dispatch(loadUser());
// await store.dispatch(getActivitiesByVendor());
// await store.dispatch(getAllCategories());
// await store.dispatch(getAllSubCategories());
return {
props: {}
};
});
......@@ -362,7 +362,7 @@ export const getActivitiesByFilters =
// $and: [{ fromDate: { $gte: "2024-05-05" } }, { toDate: { $lte: toDate } }]
// $or: subCategory
},
populate: ["masterMonths", "subCategory", "subCategory.category", "timeSlots", "masterPincode", "vendor", "image", "category"]
populate: ["masterMonths", "subCategory", "subCategory.category", "timeSlots", "masterPincode", "vendor", "image", "category", "reviews"]
// sort: ["pricePerPerson:asc"]
};
if (category) {
......
import axios from "axios";
import { getSession } from "next-auth/react";
import qs from "qs";
import { GET_REVIEWS_FAIL, GET_REVIEWS_REQUEST, GET_REVIEWS_SUCCESS } from "../constants/reviewsConstants";
export const postReviewEndUser = async ({ endUserId, activityId, comments, rating }) => {
try {
const session = await getSession();
console.log("session", session);
if (!session) {
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.jwt}`
}
};
const data = {
endUser: endUserId,
experience: activityId,
comments,
rating
};
console.log("data", data);
const response = await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/reviews?`, { data }, config);
return response.data;
} catch (error) {}
};
export const getReviewsAction =
({ activityId, endUserId }) =>
async dispatch => {
try {
dispatch({
type: GET_REVIEWS_REQUEST,
loading: true
});
const config = {
headers: {
"Content-Type": "application/json"
// Authorization: `Bearer ${session.jwt}`
}
};
const query = {
filters: {},
populate: ["endUser", "experience"]
};
if (activityId) {
query.filters["experience"] = { id: { $eq: activityId } };
}
if (endUserId) {
query.filters["endUser"] = { id: { $eq: endUserId } };
}
const queryString = qs.stringify(query, {
encodeValuesOnly: true
});
const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/reviews?${queryString}`, config);
dispatch({
type: GET_REVIEWS_SUCCESS,
payload: response.data,
loading: false
});
} catch (error) {
dispatch({
type: GET_REVIEWS_FAIL,
payload: error.response.data
});
}
};
export const GET_REVIEWS_REQUEST = "GET_REVIEWS_REQUEST"
export const GET_REVIEWS_SUCCESS = "GET_REVIEWS_SUCCESS"
export const GET_REVIEWS_FAIL = "GET_REVIEWS_FAIL"
export const CLEAR_ERRORS = "CLEAR_ERRORS";
\ No newline at end of file
......@@ -11,6 +11,7 @@ import { getAllTestimonialReducer } from "./testimonialReducer";
import { blogReducer, blogsReducer } from "./blogReducer";
import { getAllHomeBannerReducer } from "./homeBannerReducer";
import { getAllFaqsReducer } from "./faqsReducer";
import { reviewsReducer } from "./reviewsReducers";
const reducers = combineReducers({
townships: townshipsReducer,
......@@ -44,6 +45,7 @@ const reducers = combineReducers({
activityFilters: setActivityFilterReducer,
wishlists: getWishlistsReducer,
faqs: getAllFaqsReducer,
reviews: reviewsReducer,
});
export default reducers;
import { GET_REVIEWS_FAIL, GET_REVIEWS_REQUEST, GET_REVIEWS_SUCCESS, CLEAR_ERRORS } from "../constants/reviewsConstants";
// Room details reducer.
export const reviewsReducer = (state = { reviews: [] }, action) => {
switch (action.type) {
case GET_REVIEWS_REQUEST:
return {
loading: true
};
case GET_REVIEWS_SUCCESS:
return {
loading: false,
reviews: action.payload.data
};
case GET_REVIEWS_FAIL:
return {
error: action.payload.error.message
};
case CLEAR_ERRORS:
return {
...state,
error: null
};
default:
return state;
}
};
......@@ -3540,7 +3540,39 @@ img:hover {
line-height: 40px;
font-weight: 400;
}
/* .form-container .upload-files{
display: inline-block;
width: 102px;
height: 102px;
margin-block: 0 8px;
margin-inline: 0 8px;
vertical-align: top;
padding: 8px;
border: 1px solid #d9d9d9;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
}*/
.form-container .upload-file-input{
/* height: 100%;
width: 100%;
border: 0;
padding: 0;
padding-top: 2rem; */
height: inherit;
}
/* ::-webkit-file-upload-button {
display: none;
}
::file-selector-button {
display: none;
} */
.form-container .upload-file .anticon.anticon-plus{
position: absolute;
}
@media (min-width: 992px) {
.navbar-expand-lg .navbar-nav .nav-link {
margin: 0 2rem;
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!