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_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`;
|
||||
|
||||
export {
|
||||
BASE_URL,
|
||||
GET_LIST_RECENT_LOCATIONS_RATING_URI,
|
||||
GET_LIST_TOP_LOCATIONS,
|
||||
GET_LIST_LOCATIONS_URI,
|
||||
GET_LOCATION_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 BestLocation from "./BestLocations";
|
||||
import Discovery from "./Discovery";
|
||||
import Story from "./Stories";
|
||||
import NewsEvent from "./NewsEvents";
|
||||
|
||||
export {
|
||||
Home
|
||||
Home,
|
||||
BestLocation,
|
||||
Discovery,
|
||||
Story,
|
||||
NewsEvent
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
import { getListLocations, getListRecentLocationsRatings } from "./locations";
|
||||
import {
|
||||
getListLocationsService,
|
||||
getListRecentLocationsRatingsService,
|
||||
getListTopLocationsService
|
||||
} from "./locations";
|
||||
|
||||
export {
|
||||
getListLocations,
|
||||
getListRecentLocationsRatings
|
||||
getListLocationsService,
|
||||
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 statusCode from "./status-code";
|
||||
|
||||
@ -9,10 +9,12 @@ const initialState: any = {
|
||||
|
||||
type getListLocationsArg = {
|
||||
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 url = `${GET_LIST_LOCATIONS_URI}?page=${page}&page_size=${page_size}`
|
||||
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 url = `${GET_LIST_RECENT_LOCATIONS_RATING_URI}?page_size=${page_size}`
|
||||
try {
|
||||
@ -48,7 +50,26 @@ async function getListRecentLocationsRatings (page_size: Number) {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getListLocations,
|
||||
getListRecentLocationsRatings
|
||||
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 {
|
||||
getListLocationsService,
|
||||
getListRecentLocationsRatingsService,
|
||||
getListTopLocationsService,
|
||||
}
|
Loading…
Reference in New Issue
Block a user