diff --git a/src/components/Card/RatingsCard/index.tsx b/src/components/Card/RatingsCard/index.tsx index 115dcad..8df76cf 100644 --- a/src/components/Card/RatingsCard/index.tsx +++ b/src/components/Card/RatingsCard/index.tsx @@ -2,18 +2,20 @@ import { CleanlinessIcon } from '../../Icons/CleanlinessIcon'; import { FacilityIcon } from '../../Icons/FacilityIcon'; import { RestaurantIcon } from '../../Icons/RestaurantIcon'; import { ServiceIcon } from '../../Icons/ServiceIcon'; +import { ComfortIcon } from '../../Icons/ComfortIcon'; +import { LocationType } from '../../../types/common'; interface RatingData { score: number; count: number; } -interface DetailRatings { - environment: number; - cleanliness: number; - price: number; - facility: number; -} +type DetailRatings = [ + { label: string; value: number | null }, + { label: string; value: number | null }, + { label: string; value: number | null }, + { label: string; value: number | null }, +]; interface RatingsCardProps { data: T; @@ -65,16 +67,17 @@ const RatingsCard = ({ {criticDetails && (
{([ - { label: 'Taste', value: criticDetails.environment, icon: }, - { label: 'Cleanliness', value: criticDetails.cleanliness, icon: }, - { label: 'Service', value: criticDetails.price, icon: }, - { label: 'Facilities', value: criticDetails.facility, icon: }, - ] as const).map((item) => ( + //@ts-ignore + { ...criticDetails[0], icon: data.detail.location_type === LocationType.Culinary ? : }, + { ...criticDetails[1], icon: }, + { ...criticDetails[2], icon: }, + { ...criticDetails[3], icon: }, + ]).map((item) => (
{item.icon}
{item.label}
-
{item.value}
+
{item.value ?? '-'}
Based on {formatCount(criticData.count)} reviews
@@ -99,16 +102,17 @@ const RatingsCard = ({ {userDetails && (
{([ - { label: 'Taste', value: userDetails.environment, icon: }, - { label: 'Cleanliness', value: userDetails.cleanliness, icon: }, - { label: 'Service', value: userDetails.price, icon: }, - { label: 'Facilities', value: userDetails.facility, icon: }, - ] as const).map((item) => ( + //@ts-ignore + { ...userDetails[0], icon: data.detail.location_type === LocationType.Culinary ? : }, + { ...userDetails[1], icon: }, + { ...userDetails[2], icon: }, + { ...userDetails[3], icon: }, + ]).map((item) => (
{item.icon}
{item.label}
-
{item.value}
+
{item.value ?? '-'}
Based on {formatCount(userData.count)} reviews
diff --git a/src/components/Icons/ComfortIcon/index.tsx b/src/components/Icons/ComfortIcon/index.tsx new file mode 100644 index 0000000..d617346 --- /dev/null +++ b/src/components/Icons/ComfortIcon/index.tsx @@ -0,0 +1,43 @@ +interface ComfortIconProps { + width?: number; + height?: number; + fill?: string; + className?: string; + bold?: boolean; + strokeWidth?: number; +} + +export function ComfortIcon({ width = 30, height = 30, fill = 'white', className, bold = false, strokeWidth = 0.6 }: ComfortIconProps) { + const stroke = bold ? fill : 'none'; + const sw = bold ? strokeWidth : 0; + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/Img/FallbackImage.tsx b/src/components/Img/FallbackImage.tsx index 9d8cc65..d1e2861 100644 --- a/src/components/Img/FallbackImage.tsx +++ b/src/components/Img/FallbackImage.tsx @@ -1,19 +1,21 @@ interface FallbackImageProps { thumbnail: string locationType?: string - style: React.CSSProperties + style?: React.CSSProperties alt?: string + className?: string } const fallbackThumbnailSrc = 'https://otherstuff.nochill.in/public/upload/misty-forest-black-white.webp'; const restaurantThumbnailSrc = 'https://otherstuff.nochill.in/restaorunta.webp'; -const FallbackImage = ({ thumbnail, locationType, style, alt }: FallbackImageProps) => ( +const FallbackImage = ({ thumbnail, locationType, style, alt, className }: FallbackImageProps) => ( {alt} { if (locationType === 'restaurant' || locationType === 'culinary') { e.currentTarget.src = restaurantThumbnailSrc; diff --git a/src/constants/api.ts b/src/constants/api.ts index c819110..9cefbd2 100755 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -23,6 +23,7 @@ const GET_LIST_TRENDING_LOCATIONS_URI = `${BASE_URL}/locations/trending`; const GET_LIST_RECENT_LOCATIONS_RATING_URI = `${BASE_URL}/locations/recent`; const GET_LOCATION_URI = `${BASE_URL}/location`; const GET_LOCATION_TAGS_URI = `${BASE_URL}/location/tags`; +const GET_LOCATION_REVIEW_GRADES_URI = `${BASE_URL}/location/grades`; const POST_CREATE_LOCATION = `${BASE_URL}/location`; const POST_LOCATION_VISIT_URI = `${BASE_URL}/location`; @@ -51,6 +52,7 @@ export { GET_LOCATION_URI, GET_SEARCH_LOCATIONS_URI, GET_LOCATION_TAGS_URI, + GET_LOCATION_REVIEW_GRADES_URI, GET_IMAGES_BY_LOCATION_URI, GET_CURRENT_USER_REVIEW_LOCATION_URI, PATCH_USER_AVATAR, diff --git a/src/pages/LocationDetail/index.tsx b/src/pages/LocationDetail/index.tsx index e0ae219..534687e 100755 --- a/src/pages/LocationDetail/index.tsx +++ b/src/pages/LocationDetail/index.tsx @@ -11,8 +11,9 @@ import { CurrentUserLocationReviews, } from './types'; import { handleApiError, useAutosizeTextArea } from '../../utils'; -import { getCurrentUserLocationReviewService, getImagesByLocationService, getLocationService, postReviewLocation, postReviewImages } from "../../services"; -import { recordLocationVisitService } from "../../services/locations"; +import { getCurrentUserLocationReviewService, getImagesByLocationService, getLocationService, postReviewLocation, postReviewImages, LocationReviewGrades } from "../../services"; +import { recordLocationVisitService, getLocationReviewGradesService } from "../../services/locations"; +import { LocationType } from '../../types/common'; import { DefaultSeparator, SeparatorWithAnchor, CustomInterweave, SpinnerLoading, ReviewCard, ReviewCardFull } from '../../components'; import RatingsCard from '../../components/Card/RatingsCard'; import { useSelector } from 'react-redux'; @@ -24,6 +25,7 @@ import ReactTextareaAutosize from 'react-textarea-autosize'; import { MenuIcon } from '../../../src/components/Icons/MenuIcon'; import ScheduleCard from '../../components/Card/ScheduleCard'; import FacilitiesCard from '../../components/Card/FacilitiesCard'; +import FallbackImage from '../../components/Img/FallbackImage'; const SORT_TYPE = [ @@ -37,6 +39,7 @@ function LocationDetail() { const [locationDetail, setLocationDetail] = useCallbackState(EmptyLocationDetailResponse) const [locationImages, setLocationImages] = useState(emptyLocationResponse()) const [currentUserReview, setCurrentUserReview] = useState() + const [reviewGrades, setReviewGrades] = useState(null) const [lightboxOpen, setLightboxOpen] = useState(false) const [updatePage, setUpdatePage] = useState(true) const [pageState, setPageState] = useState({ @@ -127,6 +130,12 @@ function LocationDetail() { } } + async function getLocationReviewGrades(): Promise { + const { data, error } = await getLocationReviewGradesService(Number(id)) + if (error) return + setReviewGrades(data) + } + async function getImage(thumbnail?: String): Promise { try { const res = await getImagesByLocationService({ page: 1, page_size: 15, location_id: Number(id) }) @@ -261,6 +270,12 @@ function LocationDetail() { } }, [updatePage, id]) + useEffect(() => { + if (id) { + getLocationReviewGrades() + } + }, [id]) + return (
@@ -289,11 +304,17 @@ function LocationDetail() { className={`relative overflow-hidden cursor-zoom-in group/tile ${className}`} onClick={() => openAt(index)} > - + {/* + /> */}
Photo {index + 1} @@ -406,18 +427,26 @@ function LocationDetail() { score: Number(data.detail.user_score), count: Number(data.detail.user_count) })} - getCriticDetails={() => ({ - environment: 85, - cleanliness: 90, - price: 75, - facility: 80 - })} - getUserDetails={() => ({ - environment: 82, - cleanliness: 88, - price: 70, - facility: 78 - })} + getCriticDetails={reviewGrades ? () => { + const isCulinary = locationDetail.detail.location_type === LocationType.Culinary + const g = reviewGrades.critics_grades + return [ + { label: isCulinary ? 'Taste' : 'Comfort', value: isCulinary ? g.avg_taste_grade : g.avg_comfort_grade }, + { label: 'Cleanliness', value: g.avg_cleanliness_grade }, + { label: 'Service', value: g.avg_service_grade }, + { label: 'Facilities', value: g.avg_facilities_grade }, + ] + } : undefined} + getUserDetails={reviewGrades ? () => { + const isCulinary = locationDetail.detail.location_type === LocationType.Culinary + const g = reviewGrades.users_grades + return [ + { label: isCulinary ? 'Taste' : 'Comfort', value: isCulinary ? g.avg_taste_grade : g.avg_comfort_grade }, + { label: 'Cleanliness', value: g.avg_cleanliness_grade }, + { label: 'Service', value: g.avg_service_grade }, + { label: 'Facilities', value: g.avg_facilities_grade }, + ] + } : undefined} />
diff --git a/src/pages/LocationDetail/types.ts b/src/pages/LocationDetail/types.ts index abb39da..8fdea07 100755 --- a/src/pages/LocationDetail/types.ts +++ b/src/pages/LocationDetail/types.ts @@ -3,13 +3,13 @@ import { SlideImage } from "yet-another-react-lightbox" export interface ILocationDetail { id: number, - name: String, - address: String, - regency_name: String, - province_name: String, - location_type: String, - region_name: String, - google_maps_link: String, + name: string, + address: string, + regency_name: string, + province_name: string, + location_type: string, + region_name: string, + google_maps_link: string, thumbnail: string | null, submitted_by: number, critic_score: number, @@ -52,7 +52,7 @@ export interface LocationReviewsResponse { export interface LocationDetailResponse { detail: ILocationDetail, - tags: Array + tags: Array users_review: Array, critics_review: Array } diff --git a/src/services/index.ts b/src/services/index.ts index b8c50c9..3a0af74 100755 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,10 +1,12 @@ -import { +import { getListLocationsService, getListRecentLocationsRatingsService, getListTopLocationsService, getLocationService, getLocationTagsService, + getLocationReviewGradesService, } from "./locations"; +export type { LocationReviewGrades, LocationReviewGradesGroup } from "./locations"; import { getImagesByLocationService } from "./images" import { createAccountService, loginService, logoutService } from "./auth"; import { postReviewLocation, postReviewImages, getCurrentUserLocationReviewService } from "./review"; @@ -28,6 +30,7 @@ export { getListTopLocationsService, getLocationService, getLocationTagsService, + getLocationReviewGradesService, getImagesByLocationService, postReviewLocation, diff --git a/src/services/locations.ts b/src/services/locations.ts index 701ede6..3852b5b 100755 --- a/src/services/locations.ts +++ b/src/services/locations.ts @@ -7,6 +7,7 @@ import { GET_LIST_TRENDING_LOCATIONS_URI, GET_LOCATION_TAGS_URI, GET_LOCATION_URI, + GET_LOCATION_REVIEW_GRADES_URI, GET_SEARCH_LOCATIONS_URI, POST_CREATE_LOCATION, POST_LOCATION_VISIT_URI, @@ -173,7 +174,6 @@ async function getListTopLocationsService(params: GetListLocationsArg) { async function getLocationService(params: GetLocationArg) { try { const data = await fetchLocation(params) - console.log(data) return { data, error: null } } catch (error) { throw error @@ -277,6 +277,39 @@ export const recordLocationVisitService = async (locationId: number): Promise => { + const url = `${GET_LOCATION_REVIEW_GRADES_URI}/${locationId}` + const response = await client({ method: 'GET', url }) + return response.data +} + +export const getLocationReviewGradesService = async (locationId: number) => { + try { + const data = await fetchLocationReviewGrades(locationId) + return { data, error: null } + } catch (error: any) { + return { data: null, error, status: error?.status } + } +} + export const getMenuItemsService = async (locationId: number) => { try { const data = await fetchMenuItems(locationId)