add best locationsA

This commit is contained in:
NCanggoro 2023-09-17 16:29:53 +07:00
parent 819527ca20
commit 5cdf8624d6
9 changed files with 326 additions and 11 deletions

View File

@ -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

View 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;

View 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;
}

View File

@ -0,0 +1,10 @@
function Discovery() {
return(
<>
<h1>Best PLaces</h1>
</>
)
}
export default Discovery;

View File

@ -0,0 +1,10 @@
function NewsEvent() {
return(
<>
<h1>Best PLaces</h1>
</>
)
}
export default NewsEvent;

View File

@ -0,0 +1,10 @@
function Story() {
return(
<>
<h1>Best PLaces</h1>
</>
)
}
export default Story;

View File

@ -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
}

View File

@ -1,6 +1,11 @@
import { getListLocations, getListRecentLocationsRatings } from "./locations";
import {
getListLocationsService,
getListRecentLocationsRatingsService,
getListTopLocationsService
} from "./locations";
export {
getListLocations,
getListRecentLocationsRatings
getListLocationsService,
getListRecentLocationsRatingsService,
getListTopLocationsService,
}

View File

@ -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) {
}
}
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}&region_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 {
getListLocations,
getListRecentLocationsRatings
getListLocationsService,
getListRecentLocationsRatingsService,
getListTopLocationsService,
}