diff --git a/src/pages/BestLocations/index.tsx b/src/pages/BestLocations/index.tsx
index 979a618..30b8c37 100644
--- a/src/pages/BestLocations/index.tsx
+++ b/src/pages/BestLocations/index.tsx
@@ -3,8 +3,6 @@ import { getListTopLocationsService } from "../../services";
import { DefaultSeparator } from "../../components";
import { TargetedEvent } from "preact/compat";
import './style.css';
-import { useNavigate } from "react-router-dom";
-
interface TopLocation {
row_number: Number,
@@ -57,8 +55,6 @@ function BestLocation() {
filterRegionType: 0,
})
- const navigate = useNavigate()
-
async function getTopLocations() {
try {
const res = await getListTopLocationsService({ page: page, page_size: 20, order_by: pageState.filterScoreTypeidx, region_type: pageState.filterRegionType })
@@ -79,18 +75,6 @@ function BestLocation() {
setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type})
}
- function onNavigateToDetail(
- id: Number,
- critic_count: Number,
- critic_score: Number,
- user_count: Number,
- user_score: Number,
- ) {
-
- navigate(`/location/${id}`, { state: { user_score: user_score, user_count: user_count, critic_score: critic_score, critic_count: critic_count }})
-
- }
-
useEffect(() => {
getTopLocations()
}, [pageState])
@@ -152,10 +136,10 @@ function BestLocation() {
*/}
diff --git a/src/pages/LocationDetail/index.css b/src/pages/LocationDetail/index.css
index d0d80c5..de75f8b 100644
--- a/src/pages/LocationDetail/index.css
+++ b/src/pages/LocationDetail/index.css
@@ -67,6 +67,11 @@ img {
outline: none;
}
+.text-area-button {
+ background-color: gray;
+ letter-spacing: 1px;
+}
+
.criticSortFilter {
float: right;
}
diff --git a/src/pages/LocationDetail/index.tsx b/src/pages/LocationDetail/index.tsx
index a9413ed..a5f73ff 100644
--- a/src/pages/LocationDetail/index.tsx
+++ b/src/pages/LocationDetail/index.tsx
@@ -1,24 +1,43 @@
-import { useLocation, useParams } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import { ChangeEvent, TargetedEvent } from 'preact/compat';
import { useEffect, useRef, useState } from 'preact/hooks';
-import './index.css';
import Lightbox from 'yet-another-react-lightbox';
import useCallbackState from '../../types/state-callback';
-import { EmptyLocationDetailResponse, LocationDetailResponse, LocationResponse, emptyLocationResponse } from './types';
-import { useAutosizeTextArea } from '../../utils';
-import { getImagesByLocationService, getLocationService } from "../../services";
-import { DefaultSeparator, SeparatorWithAnchor, CustomInterweave } from '../../components';
+import {
+ EmptyLocationDetailResponse,
+ LocationDetailResponse,
+ LocationResponse,
+ emptyLocationResponse,
+ CurrentUserLocationReviews,
+} from './types';
+import { handleAxiosError, useAutosizeTextArea } from '../../utils';
+import { getImagesByLocationService, getLocationService, postReviewLocation } from "../../services";
+import { DefaultSeparator, SeparatorWithAnchor, CustomInterweave, SpinnerLoading } from '../../components';
+import { useSelector } from 'react-redux';
+import { UserRootState } from '../../store/type';
+import { DEFAULT_AVATAR_IMG } from '../../constants/default';
+import './index.css';
+import { AxiosError } from 'axios';
+
+const SORT_TYPE = [
+ 'highest rated',
+ 'lowest rated',
+ 'newest',
+ 'oldest'
+]
function LocationDetail() {
const [locationDetail, setLocationDetail] = useCallbackState(EmptyLocationDetailResponse)
const [locationImages, setLocationImages] = useState(emptyLocationResponse())
+ const [currentUserReview, setCurrentUserReview] = useState()
const [lightboxOpen, setLightboxOpen] = useState(false)
const [pageState, setPageState] = useState({
critic_filter_name: 'highest rated',
critic_filter_type: 0,
show_sort: false,
+ enable_post: true,
+ on_submit_loading: false,
is_score_rating_panic_msg: '',
- temp: ''
})
const [reviewValue, setReviewValue] = useState({
review_textArea: '',
@@ -26,28 +45,27 @@ function LocationDetail() {
})
const [isLoading, setIsLoading] = useState(true)
+ const navigate = useNavigate();
+ const user = useSelector((state: UserRootState) => state.auth)
+
const textAreaRef = useRef(null);
useAutosizeTextArea(textAreaRef.current, reviewValue.review_textArea);
- const { state } = useLocation();
const { id } = useParams()
- const SORT_TYPE = [
- 'highest rated',
- 'lowest rated',
- 'newest',
- 'oldest'
- ]
-
async function getLocationDetail(): Promise {
try {
const res = await getLocationService(Number(id))
setLocationDetail(res.data, (val) => {
getImage(val.detail.thumbnail.String.toString())
})
- } catch (err) {
- console.log(err)
+ } catch (error) {
+ let err = error as AxiosError;
+ if (err.response?.status == 404) {
+ navigate("/")
+ }
+ alert(error)
}
}
@@ -90,22 +108,63 @@ function LocationDetail() {
setPageState({ ...pageState, show_sort: false, critic_filter_name: sort_name, critic_filter_type: sort_type })
}
- function handleSubmitReview(e: TargetedEvent) {
+ async function handleSubmitReview(e: TargetedEvent) {
+ e.preventDefault();
+ setPageState({ ...pageState, on_submit_loading: true })
+
+ if (isNaN(Number(reviewValue.score_input))) {
+ setPageState({ ...pageState, is_score_rating_panic_msg: "SCORE MUST BE A NUMBER" })
+ return
+ }
+
+ if (Number(reviewValue.score_input) > 100) {
+ setPageState({ ...pageState, is_score_rating_panic_msg: "SCORE MUST BE LESS OR EQUAL THAN 100" })
+ return
+ }
+
+ if (reviewValue.score_input === '') {
+ setPageState({ ...pageState, is_score_rating_panic_msg: "SCORE MUSTN'T BE EMPTY" })
+ return
+ }
+
+ try {
+ const { data } = await postReviewLocation({
+ is_hided: false,
+ location_id: Number(id),
+ score: Number(reviewValue.score_input),
+ submitted_by: Number(user.id),
+ is_from_critic: user.is_critics,
+ comments: reviewValue.review_textArea,
+ })
+
+ setPageState({ ...pageState, enable_post: false, on_submit_loading: false })
+ setReviewValue({ review_textArea: '', score_input: '' })
+ setCurrentUserReview({
+ id: data.id,
+ comments: data.comments,
+ is_from_critic: data.is_from_critic,
+ is_hided: data.is_hided,
+ location_id: data.location_id,
+ score: data.score,
+ submitted_by: data.submitted_by,
+ created_at: data.created_at,
+ updated_at: data.updated_at
+ })
+ } catch (error) {
+ let err = error as AxiosError;
+ console.log(err)
+ const str = handleAxiosError(err)
+ alert(str)
+ setPageState({ ...pageState, on_submit_loading: false })
+ }
+
+ }
+
+ function handleSignInNavigation(e: TargetedEvent) {
e.preventDefault();
- if(Number(reviewValue.score_input) > 100) {
- setPageState({ ...pageState, is_score_rating_panic_msg: "SCORE MUST BE LESS OR EQUAL THAN 100"})
- return
- }
-
- if(reviewValue.score_input === '') {
- setPageState({ ...pageState, is_score_rating_panic_msg: "SCORE MUSTN'T BE EMPTY"})
- return
- }
- const temp = reviewValue.review_textArea.replace(/\n/g, " ")
-
- setPageState({ ...pageState, temp: temp })
+ navigate('/login', { state: { from: `/location/${id}` } })
}
useEffect(() => {
@@ -162,36 +221,36 @@ function LocationDetail() {
CRITICS SCORE
- {state.critic_count !== 0 ? state.critic_score : "NR"}
+ {locationDetail.detail.critic_count !== 0 ? Math.floor(Number(locationDetail.detail.critic_score) / Number(locationDetail.detail.critic_count)) : "NR"}
- {state.critic_count !== 0 &&
+ {locationDetail.detail.critic_count !== 0 &&
- Based on {state.critic_count} reviews
+ Based on {locationDetail.detail.critic_count} reviews
}
USERS SCORE
-
- {state.user_count !== 0 ? state.user_score : "NR"}
+
+ {locationDetail.detail.user_count !== 0 ? Math.floor(Number(locationDetail.detail.user_score) / Number(locationDetail.detail.user_count)) : "NR"}
- {state.user_count !== 0 &&
-
- Based on {state.user_count} reviews
+ {locationDetail.detail.user_count !== 0 &&
+
+ Based on {locationDetail.detail.user_count} reviews
}
@@ -237,67 +296,87 @@ function LocationDetail() {
+ {!user.username ?
+
+ :
+
+
- {/*
*/}
-
-
+
-
+
-
+
+ {currentUserReview ?
+
+ {console.log(currentUserReview)}
+
{currentUserReview.score}
+
+
+ :
+ <>
+
+
/ score
+ {pageState.is_score_rating_panic_msg &&
+
{pageState.is_score_rating_panic_msg}
+ }
+ >
-
-
-
-
/ score
- {pageState.is_score_rating_panic_msg &&
-
{pageState.is_score_rating_panic_msg}
+ }
+
+
+
+
+ {currentUserReview ?
+
+ :
+
}
-
-
-
-
-
-
-
-
-
+ }
@@ -314,236 +393,121 @@ function LocationDetail() {
-
-
-
- 90
+ {locationDetail.critics_review.map(x => (
+
+
-
-
+
+
-
-
-
-
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro nihil dolor delectus ex minima aliquid quidem veniam officiis temporibus ipsum ea incidunt voluptatum a, repellat illum, cumque consequatur saepe assumenda.
-
-
-
-
-
-
-
-
-
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro nihil dolor delectus ex minima aliquid quidem veniam officiis temporibus ipsum ea incidunt voluptatum a, repellat illum, cumque consequatur saepe assumenda.
-
-
-
-
-
-
-
-
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro nihil dolor delectus ex minima aliquid quidem veniam officiis temporibus ipsum ea incidunt voluptatum a, repellat illum, cumque consequatur saepe assumenda.
-
-
-
-
+ ))}
-
+
0 ? '#' : ''} />
+ { locationDetail.users_review.length > 0 ?
+ <>
+ {locationDetail.users_review.map(x => (
+
+ ))}
-
-
-
-
-
-
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro nihil dolor delectus ex minima aliquid quidem veniam officiis temporibus ipsum ea incidunt voluptatum a, repellat illum, cumque consequatur saepe assumenda.
-
-
-
-
-
-
-
-
-
-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro nihil dolor delectus ex minima aliquid quidem veniam officiis temporibus ipsum ea incidunt voluptatum a, repellat illum, cumque consequatur saepe assumenda.
-
-
-
-
-
+ >
+ :
+ <>
+
No users review to display
+ >
+ }
- */}
CONTRUBITION
@@ -645,7 +609,7 @@ function LocationDetail() {
} */}
-
+
diff --git a/src/pages/LocationDetail/types.ts b/src/pages/LocationDetail/types.ts
index 3143f8b..f6d35c5 100644
--- a/src/pages/LocationDetail/types.ts
+++ b/src/pages/LocationDetail/types.ts
@@ -4,13 +4,16 @@ export interface ILocationDetail {
id: Number,
name: String,
address: String,
+ regency_name: String,
+ province_name: String,
+ region_name: String,
google_maps_link: String,
thumbnail: NullValueRes<"String", String>,
submitted_by: Number,
- regency_name: String,
- province_name: String,
- region_name: String,
- submitted_by_user: String
+ critic_score: Number,
+ critic_count: Number,
+ user_score: Number,
+ user_count: Number
}
export function emptyLocationDetail(): ILocationDetail {
@@ -24,19 +27,38 @@ export function emptyLocationDetail(): ILocationDetail {
regency_name: '',
region_name: '',
submitted_by: 0,
- submitted_by_user: ''
+ critic_score: 0,
+ critic_count: 0,
+ user_score: 0,
+ user_count: 0,
}
}
+export interface LocationReviewsResponse {
+ id: number,
+ score: number,
+ comments: string,
+ user_id: number,
+ username: string,
+ user_avatar: NullValueRes<"String", string>,
+ created_at: string,
+ updated_at: string
+}
+
export interface LocationDetailResponse {
detail: ILocationDetail,
tags: Array
+ users_review: Array,
+ critics_review: Array
}
+
export function EmptyLocationDetailResponse(): LocationDetailResponse {
return {
detail: emptyLocationDetail(),
- tags: []
+ tags: [],
+ critics_review: Array(),
+ users_review: Array()
}
}
@@ -47,15 +69,6 @@ export interface LocationImage extends SlideImage {
uploaded_by: String
}
-export function emptyLocationImage(): LocationImage {
- return {
- id: 0,
- src: '',
- created_at: '',
- uploaded_by: ''
- }
-}
-
export interface LocationResponse {
total_image: Number,
images: Array
@@ -64,6 +77,18 @@ export interface LocationResponse {
export function emptyLocationResponse(): LocationResponse {
return {
total_image: 0,
- images: [emptyLocationImage()]
+ images: Array()
}
+}
+
+export type CurrentUserLocationReviews = {
+ id: Number,
+ comments: string,
+ is_from_critic: boolean,
+ is_hided: boolean,
+ location_id: Number,
+ score: Number,
+ submitted_by: Number,
+ created_at: NullValueRes<"Time", string>,
+ updated_at: NullValueRes<"Time", string>,
}
\ No newline at end of file
diff --git a/src/services/locations.ts b/src/services/locations.ts
index 1177ee7..3f700b4 100644
--- a/src/services/locations.ts
+++ b/src/services/locations.ts
@@ -80,7 +80,7 @@ async function getLocationService(id: Number) {
return newState;
}
} catch (error) {
- console.log(error)
+ throw(error)
}
}
diff --git a/src/services/review.ts b/src/services/review.ts
new file mode 100644
index 0000000..4bbfc58
--- /dev/null
+++ b/src/services/review.ts
@@ -0,0 +1,34 @@
+import { AxiosError } from "axios"
+import { client } from "./config";
+import { POST_REVIEW_LOCATION_URI } from "../constants/api";
+
+const initialState: IEmptyResponseState = {
+ data: null,
+ error: AxiosError
+}
+
+interface postReviewLocationReq {
+ submitted_by: number,
+ comments: string,
+ score: number,
+ is_from_critic: boolean,
+ is_hided: boolean,
+ location_id: number
+}
+
+async function postReviewLocation(req: postReviewLocationReq) {
+ const newState = { ...initialState };
+ try {
+ const response = await client({ method: 'POST', url: POST_REVIEW_LOCATION_URI, data: req, withCredentials: true})
+ newState.data = response.data
+ newState.error = null
+ return newState
+ } catch (error) {
+ newState.error = error
+ throw(error)
+ }
+}
+
+export {
+ postReviewLocation
+}
\ No newline at end of file
diff --git a/src/types/common.ts b/src/types/common.ts
index 12044c5..9d0fc2e 100644
--- a/src/types/common.ts
+++ b/src/types/common.ts
@@ -8,6 +8,6 @@ interface GetRequestPagination {
interface IEmptyResponseState {
- data: unknown,
+ data: any,
error: any,
};
\ No newline at end of file