add best locationsA
This commit is contained in:
parent
819527ca20
commit
5cdf8624d6
@ -4,12 +4,14 @@ const SIGNUP_URI = `${BASE_URL}/user/signup`
|
|||||||
|
|
||||||
|
|
||||||
const GET_LIST_LOCATIONS_URI = `${BASE_URL}/locations`;
|
const GET_LIST_LOCATIONS_URI = `${BASE_URL}/locations`;
|
||||||
const GET_LIST_RECENT_LOCATIONS_RATING_URI = `${BASE_URL}/recent-locations/ratings`
|
const GET_LIST_TOP_LOCATIONS = `${BASE_URL}/locations/top-ratings`
|
||||||
|
const GET_LIST_RECENT_LOCATIONS_RATING_URI = `${BASE_URL}/locations/recent`
|
||||||
const GET_LOCATION_URI = `${BASE_URL}/location`;
|
const GET_LOCATION_URI = `${BASE_URL}/location`;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BASE_URL,
|
BASE_URL,
|
||||||
GET_LIST_RECENT_LOCATIONS_RATING_URI,
|
GET_LIST_RECENT_LOCATIONS_RATING_URI,
|
||||||
|
GET_LIST_TOP_LOCATIONS,
|
||||||
GET_LIST_LOCATIONS_URI,
|
GET_LIST_LOCATIONS_URI,
|
||||||
GET_LOCATION_URI,
|
GET_LOCATION_URI,
|
||||||
SIGNUP_URI
|
SIGNUP_URI
|
||||||
|
198
src/pages/BestLocations/index.tsx
Normal file
198
src/pages/BestLocations/index.tsx
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { getListTopLocationsService } from "../../services";
|
||||||
|
import { DefaultSeparator } from "../../components";
|
||||||
|
import { TargetedEvent } from "preact/compat";
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
|
|
||||||
|
interface TopLocation {
|
||||||
|
row_number: Number,
|
||||||
|
id: Number,
|
||||||
|
name: String,
|
||||||
|
thumbnail: NullValueRes<"String", String>,
|
||||||
|
address: String,
|
||||||
|
google_maps_link: string,
|
||||||
|
regency_name: string,
|
||||||
|
critic_score: NullValueRes<"Int32", Number>,
|
||||||
|
critic_count: Number,
|
||||||
|
user_score: NullValueRes<"Int32", Number>,
|
||||||
|
user_count: Number,
|
||||||
|
critic_bayes: Number,
|
||||||
|
user_bayes: Number,
|
||||||
|
avg_bayes: Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
const REGIONS = [
|
||||||
|
'All',
|
||||||
|
'Sumatera',
|
||||||
|
'Jawa',
|
||||||
|
'Kalimantan',
|
||||||
|
'Nusa Tenggara',
|
||||||
|
'Maluku',
|
||||||
|
'Papua',
|
||||||
|
'Sulawesi',
|
||||||
|
];
|
||||||
|
|
||||||
|
const REVIEWERS_TYPE =[
|
||||||
|
'All',
|
||||||
|
'Critics',
|
||||||
|
'Users'
|
||||||
|
]
|
||||||
|
|
||||||
|
const MIN_REVIEWS = [
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
11
|
||||||
|
]
|
||||||
|
|
||||||
|
function BestLocation() {
|
||||||
|
const [page, setPage] = useState<number>(1);
|
||||||
|
const [topLocations, setTopLocations] = useState<Array<TopLocation>>([])
|
||||||
|
const [pageState, setPageState] = useState({
|
||||||
|
filterScoreType: 'all',
|
||||||
|
filterScoreTypeidx: 1,
|
||||||
|
filterRegionTypeName: 'All',
|
||||||
|
filterRegionType: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getTopLocations() {
|
||||||
|
try {
|
||||||
|
const res = await getListTopLocationsService({ page: page, page_size: 20, order_by: pageState.filterScoreTypeidx, region_type: pageState.filterRegionType })
|
||||||
|
setTopLocations(res.data)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeReviewType(e: TargetedEvent<HTMLAnchorElement>, reviewer_type: string, i: number) {
|
||||||
|
e.preventDefault()
|
||||||
|
setPageState({...pageState, filterScoreType: reviewer_type, filterScoreTypeidx: i })
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeRegionType(e: TargetedEvent<HTMLAnchorElement>, region_name: string, type: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getTopLocations()
|
||||||
|
}, [pageState])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'content main-content mt-3'}>
|
||||||
|
<section name={"Top locations header"}>
|
||||||
|
<h1 className={'text-3xl mb-5 font-bold'}>Top Locations</h1>
|
||||||
|
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||||
|
<p className={'inline-block'}>Regions</p>
|
||||||
|
<a style={{ cursor: 'pointer' }}>
|
||||||
|
<p className={'ml-2 inline-block'}>{pageState.filterRegionTypeName}</p>
|
||||||
|
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||||
|
</a>
|
||||||
|
<div className={'dropdown-content text-sm'}>
|
||||||
|
{REGIONS.map((x, index) => (
|
||||||
|
<a onClick={(e) => onChangeRegionType(e, x, index) } className={'block pt-1'}>{x}</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||||
|
<p className={'inline-block'}>Min. Reviews</p>
|
||||||
|
<a style={{ cursor: 'pointer' }}>
|
||||||
|
<p className={'ml-2 inline-block'}>All</p>
|
||||||
|
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||||
|
</a>
|
||||||
|
<div className={'dropdown-content-min-reviews text-sm'}>
|
||||||
|
{MIN_REVIEWS.map(x => (
|
||||||
|
<a className={'block pt-1'}>{x}</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||||
|
<p className={'inline-block'}>Tags</p>
|
||||||
|
<a style={{ cursor: 'pointer' }}>
|
||||||
|
<p className={'ml-2 inline-block'}>All</p>
|
||||||
|
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||||
|
</a>
|
||||||
|
<div className={'dropdown-content text-sm'}>
|
||||||
|
{/* {REGIONS.map(x => (
|
||||||
|
<a className={'block pt-1'}>{x}</a>
|
||||||
|
))} */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DefaultSeparator />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section name={'Main content'}>
|
||||||
|
<div className={'flex flex-row pt-10 pb-10'}>
|
||||||
|
<div className={'mr-5'} style={{ flex: 1}}>
|
||||||
|
{topLocations.map(x => (
|
||||||
|
<>
|
||||||
|
<div style={{ float: 'right', cursor: 'pointer' }}>
|
||||||
|
<a className={'text-xl'}>
|
||||||
|
...
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className={'mb-2'}>
|
||||||
|
<p className={'text-xl'}>{x.row_number}.{x.name}</p>
|
||||||
|
</div>
|
||||||
|
<div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}>
|
||||||
|
<img src={x.thumbnail.String.toString()} style={{ width: '100%', objectFit: 'cover', height: 200 }} />
|
||||||
|
</div>
|
||||||
|
<div>{x.address}</div>
|
||||||
|
<div>$$$ (IDR 1000-12000)</div>
|
||||||
|
<div><a href={x.google_maps_link} target={'_'}> Maps </a></div>
|
||||||
|
<div className={'mt-6'}>
|
||||||
|
<div className={'text-xs bg-secondary'} style={{ width: 160, display: 'inline-block', borderRadius: 7 }}>
|
||||||
|
<div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopRightRadius: 7, borderTopLeftRadius: 7}}>CRITICS SCORE</div>
|
||||||
|
<div className={"flex flex-row items-center p-3"}>
|
||||||
|
<div className={'mr-3 users-score-bar'}>
|
||||||
|
<p className={`text-xl text-center ${x.critic_score.Valid ? 'font-bold' : ''}`}>{x.critic_score.Valid ? Number(x.critic_score.Int32) / Number(x.critic_count) * 10 : "N/A"}</p>
|
||||||
|
<div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}>
|
||||||
|
<div style={{ height: 4, width: ` ${x.critic_score.Valid ? Number(x.critic_score.Int32) / Number(x.critic_count) * 10 : 0}%`, backgroundColor: 'green' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={'text-xs users-score'}>{x.critic_count} reviews</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'text-xs bg-secondary ml-3'} style={{ width: 160, display: 'inline-block', borderRadius: 7 }}>
|
||||||
|
<div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopLeftRadius: 7, borderTopRightRadius: 7}}>USERS SCORE</div>
|
||||||
|
<div className={"flex flex-row items-center p-3"}>
|
||||||
|
<div className={'mr-3 users-score-bar'}>
|
||||||
|
<p className={`text-xl text-center ${x.user_score.Valid ? 'font-bold' : ''}`}>{x.user_score.Valid ? x.user_score.Int32 : "N/A" }</p>
|
||||||
|
<div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}>
|
||||||
|
<div style={{ height: 4, width: ` ${x.user_score.Valid ? x.user_score.Int32 : 0}%`, backgroundColor: 'green' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={'text-xs users-score'}>{x.user_count} reviews</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ clear: 'both'}}/>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className={'p-4 bg-secondary'}style={{ minWidth: 300}}>
|
||||||
|
<div className={'h-30 bg-primary p-4'}>
|
||||||
|
{REVIEWERS_TYPE.map((x, idx) => (
|
||||||
|
<a
|
||||||
|
onClick={(e) => onChangeReviewType(e, x.toLowerCase(), idx+1)}
|
||||||
|
href={'#'}
|
||||||
|
style={ pageState.filterScoreType == x.toLowerCase() ? { pointerEvents: 'none'} : ''}
|
||||||
|
>
|
||||||
|
<div className={`pt-1 pb-1 ${pageState.filterScoreType == x.toLowerCase() ? 'pl-1 bg-tertiary selected-reviewer-filter' : ''}`}>{x} Score</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BestLocation;
|
51
src/pages/BestLocations/style.css
Normal file
51
src/pages/BestLocations/style.css
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
.dropdown-content {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #202225;
|
||||||
|
min-width: 120px;
|
||||||
|
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content-min-reviews {
|
||||||
|
margin-left: 60px;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #202225;
|
||||||
|
min-width: 70px;
|
||||||
|
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regions-dropdown:hover .dropdown-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regions-dropdown:hover .dropdown-content-min-reviews {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content a {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content a:hover {
|
||||||
|
background-color: rgb(73, 73, 73);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content-min-reviews a {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content-min-reviews a:hover {
|
||||||
|
background-color: rgb(73, 73, 73);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a .selected-reviewer-filter:hover{
|
||||||
|
color: white;
|
||||||
|
cursor: default;
|
||||||
|
}
|
10
src/pages/Discovery/index.tsx
Normal file
10
src/pages/Discovery/index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function Discovery() {
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<h1>Best PLaces</h1>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Discovery;
|
10
src/pages/NewsEvents/index.tsx
Normal file
10
src/pages/NewsEvents/index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function NewsEvent() {
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<h1>Best PLaces</h1>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewsEvent;
|
10
src/pages/Stories/index.tsx
Normal file
10
src/pages/Stories/index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function Story() {
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<h1>Best PLaces</h1>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Story;
|
@ -1,5 +1,13 @@
|
|||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
|
import BestLocation from "./BestLocations";
|
||||||
|
import Discovery from "./Discovery";
|
||||||
|
import Story from "./Stories";
|
||||||
|
import NewsEvent from "./NewsEvents";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Home
|
Home,
|
||||||
|
BestLocation,
|
||||||
|
Discovery,
|
||||||
|
Story,
|
||||||
|
NewsEvent
|
||||||
}
|
}
|
@ -1,6 +1,11 @@
|
|||||||
import { getListLocations, getListRecentLocationsRatings } from "./locations";
|
import {
|
||||||
|
getListLocationsService,
|
||||||
|
getListRecentLocationsRatingsService,
|
||||||
|
getListTopLocationsService
|
||||||
|
} from "./locations";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getListLocations,
|
getListLocationsService,
|
||||||
getListRecentLocationsRatings
|
getListRecentLocationsRatingsService,
|
||||||
|
getListTopLocationsService,
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { GET_LIST_LOCATIONS_URI, GET_LIST_RECENT_LOCATIONS_RATING_URI } from "../constants/api";
|
import { GET_LIST_LOCATIONS_URI, GET_LIST_RECENT_LOCATIONS_RATING_URI, GET_LIST_TOP_LOCATIONS } from "../constants/api";
|
||||||
import { client } from "./config";
|
import { client } from "./config";
|
||||||
import statusCode from "./status-code";
|
import statusCode from "./status-code";
|
||||||
|
|
||||||
@ -9,10 +9,12 @@ const initialState: any = {
|
|||||||
|
|
||||||
type getListLocationsArg = {
|
type getListLocationsArg = {
|
||||||
page: number,
|
page: number,
|
||||||
page_size: number
|
page_size: number,
|
||||||
|
order_by?: number,
|
||||||
|
region_type?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getListLocations ({ page, page_size}: getListLocationsArg) {
|
async function getListLocationsService ({ page, page_size}: getListLocationsArg) {
|
||||||
const newState = {...initialState};
|
const newState = {...initialState};
|
||||||
const url = `${GET_LIST_LOCATIONS_URI}?page=${page}&page_size=${page_size}`
|
const url = `${GET_LIST_LOCATIONS_URI}?page=${page}&page_size=${page_size}`
|
||||||
try {
|
try {
|
||||||
@ -30,7 +32,7 @@ async function getListLocations ({ page, page_size}: getListLocationsArg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getListRecentLocationsRatings (page_size: Number) {
|
async function getListRecentLocationsRatingsService (page_size: Number) {
|
||||||
const newState = {...initialState};
|
const newState = {...initialState};
|
||||||
const url = `${GET_LIST_RECENT_LOCATIONS_RATING_URI}?page_size=${page_size}`
|
const url = `${GET_LIST_RECENT_LOCATIONS_RATING_URI}?page_size=${page_size}`
|
||||||
try {
|
try {
|
||||||
@ -48,7 +50,26 @@ async function getListRecentLocationsRatings (page_size: Number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getListTopLocationsService({ page, page_size, order_by, region_type}: getListLocationsArg) {
|
||||||
|
const newState = {...initialState};
|
||||||
|
const url = `${GET_LIST_TOP_LOCATIONS}?page=${page}&page_size=${page_size}&order_by=${order_by}®ion_type=${region_type}`
|
||||||
|
try {
|
||||||
|
const response = await client({method: 'GET', url: url})
|
||||||
|
switch (response.request.status) {
|
||||||
|
case statusCode.OK:
|
||||||
|
newState.data = response.data;
|
||||||
|
return newState;
|
||||||
|
default:
|
||||||
|
newState.error = response.data;
|
||||||
|
return newState
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getListLocations,
|
getListLocationsService,
|
||||||
getListRecentLocationsRatings
|
getListRecentLocationsRatingsService,
|
||||||
|
getListTopLocationsService,
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user