This commit is contained in:
NCanggoro 2024-05-22 11:25:20 +07:00
parent 656a6e08da
commit 5183a90b59
121 changed files with 1271 additions and 817 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

0
TODO Normal file → Executable file
View File

0
index.html Normal file → Executable file
View File

2
package.json Normal file → Executable file
View File

@ -9,8 +9,8 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@floating-ui/react": "^0.26.9",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"@types/react-redux": "^7.1.26",
"axios": "^1.5.0", "axios": "^1.5.0",
"emojibase": "^15.0.0", "emojibase": "^15.0.0",
"interweave": "^13.1.0", "interweave": "^13.1.0",

50
pnpm-lock.yaml Normal file → Executable file
View File

@ -5,12 +5,12 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@floating-ui/react':
specifier: ^0.26.9
version: 0.26.9(react-dom@18.2.0)(react@18.2.0)
'@reduxjs/toolkit': '@reduxjs/toolkit':
specifier: ^1.9.5 specifier: ^1.9.5
version: 1.9.7(react-redux@8.1.3)(react@18.2.0) version: 1.9.7(react-redux@8.1.3)(react@18.2.0)
'@types/react-redux':
specifier: ^7.1.26
version: 7.1.33
axios: axios:
specifier: ^1.5.0 specifier: ^1.5.0
version: 1.6.7 version: 1.6.7
@ -623,6 +623,37 @@ packages:
'@floating-ui/utils': 0.2.1 '@floating-ui/utils': 0.2.1
dev: false dev: false
/@floating-ui/dom@1.6.3:
resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==}
dependencies:
'@floating-ui/core': 1.6.0
'@floating-ui/utils': 0.2.1
dev: false
/@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/dom': 1.6.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@floating-ui/react@0.26.9(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-p86wynZJVEkEq2BBjY/8p2g3biQ6TlgT4o/3KgFKyTWoJLU1GZ8wpctwRqtkEl2tseYA+kw7dBAIDFcednfI5w==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
'@floating-ui/utils': 0.2.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
tabbable: 6.2.0
dev: false
/@floating-ui/utils@0.2.1: /@floating-ui/utils@0.2.1:
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
dev: false dev: false
@ -800,15 +831,6 @@ packages:
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
dev: false dev: false
/@types/react-redux@7.1.33:
resolution: {integrity: sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==}
dependencies:
'@types/hoist-non-react-statics': 3.3.5
'@types/react': 18.2.48
hoist-non-react-statics: 3.3.2
redux: 4.2.1
dev: false
/@types/react-transition-group@4.4.10: /@types/react-transition-group@4.4.10:
resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
dependencies: dependencies:
@ -2065,6 +2087,10 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
/tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
dev: false
/tailwindcss@3.4.1: /tailwindcss@3.4.1:
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}

0
postcss.config.js Normal file → Executable file
View File

0
public/vite.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
src/actions/LogoutAction.ts Normal file → Executable file
View File

0
src/actions/index.ts Normal file → Executable file
View File

4
src/app.css Normal file → Executable file
View File

@ -60,6 +60,10 @@
line-height: 1.25rem; line-height: 1.25rem;
} }
.input-checkbox:hover {
cursor: pointer;
}
@media screen and(max-width: 425px) { @media screen and(max-width: 425px) {
.users-score { .users-score {
font-size: .75rem; font-size: .75rem;

104
src/app.tsx Normal file → Executable file
View File

@ -14,64 +14,54 @@ import { getRoutes } from './routes';
export function App() { export function App() {
const { routes } = getRoutes(); const { routes } = getRoutes();
return ( return (
<> <Provider store={store}>
<Provider store={store}> <PersistGate persistor={persistore}>
<PersistGate persistor={persistore}> <Router>
<Router> <Routes>
<Routes> <Route path='/login' element={<Login />} />
<Route path='/login' element={<Login />} /> <Route element={<DefaultLayout />}>
<Route element={<DefaultLayout />}> {routes.map(({ path, name, element, protectedRoute }) => {
{routes.map(({ path, name, element, protectedRoute }) => { let Element = element as any
let Element = element as any if (protectedRoute === "user") {
if (protectedRoute === "user") {
return (
<>
<Route
path={path}
id={name}
element={
<UserProtectedRoute>
<Element />
</UserProtectedRoute>
}
/>
</>
)
}
if (protectedRoute === "admin") {
return (
<>
<>
<Route
path={path}
id={name}
element={
<AdminProtectedRoute>
<Element />
</AdminProtectedRoute>
}
/>
</>
</>
)
}
return ( return (
<> <Route
<Route path={path}
path={path} id={name}
id={name} element={
element={element} <UserProtectedRoute>
/> <Element />
</> </UserProtectedRoute>
}
/>
) )
})} }
<Route path="*" element={<NotFound />} />
</Route> if (protectedRoute === "admin") {
</Routes> return (
</Router> <Route
</PersistGate> path={path}
</Provider> id={name}
</> element={
<AdminProtectedRoute>
<Element />
</AdminProtectedRoute>
}
/>
)
}
return (
<Route
path={path}
id={name}
element={element}
/>
)
})}
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Router>
</PersistGate>
</Provider>
) )
} }

0
src/assets/preact.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

0
src/components/Button/DefaultButton/index.tsx Normal file → Executable file
View File

0
src/components/Button/DefaultButton/style.css Normal file → Executable file
View File

View File

@ -0,0 +1,14 @@
import "./style.css";
interface ComponentProps {
label: string
onClick: () => void;
}
const FilterButton = (props: ComponentProps) => (
<button onClick={props.onClick} className="button-container">
<div className="button-label">{props.label}</div>
</button>
)
export default FilterButton;

View File

@ -0,0 +1,20 @@
.button-label {
text-align: center;
/* color: #85CE73; */
}
.button-container {
align-self: stretch;
padding-left: 12px;
padding-right: 12px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 6px;
border: 1px #58874d solid;
color: #85CE73;
}
.button-container:hover {
background-color: #58874d;
color: #FFF;
}

0
src/components/Button/WarningButton/index.tsx Normal file → Executable file
View File

0
src/components/Button/WarningButton/style.css Normal file → Executable file
View File

View File

@ -0,0 +1,47 @@
import { JSXInternal } from "node_modules/preact/src/jsx";
import { LocationInfo } from "../../../domains";
interface ComponentProps {
onCardClick: (id: Number) => void,
data: LocationInfo,
containerClass?: string,
containerStyle?: JSXInternal.CSSProperties
}
const LocationCard = (props: ComponentProps) => (
<div className={props.containerClass} style={props.containerStyle}>
<a onClick={() => props.onCardClick(props.data.id)}>
<div className={'border-secondary recently-img-container'}>
<img alt={props.data.name} src={props.data.thumbnail ? props.data.thumbnail : ''} loading="lazy" style={{ width: '100%', height: '100%' }} />
</div>
</a>
<div className={"border-primary pb-2 location-container text-sm mb-2 mt-2"}>
<p className={'location-title'}>{props.data.name}</p>
<p className={'text-xs mt-1'}>{props.data.regency_name}, {props.data.province_name}</p>
</div>
{props.data.critic_count !== 0 &&
<div className={"flex flex-row items-center mb-3"}>
<div className={'mr-3 users-score-bar'}>
<p className={'text-sm text-center'}>{props.data.critic_score}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${props.data.critic_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<p className={"users-score"}>critic score ({props.data.critic_count})</p>
</div>
}
{props.data.user_score !== 0 &&
<div className={"flex flex-row items-center"}>
<div className={'mr-3 users-score-bar'}>
<p className={'text-sm text-center'}>{props.data.user_score}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${props.data.user_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<p className={'users-score'}>user score ({props.data.user_count})</p>
</div>
}
</div>
)
export default LocationCard;

0
src/components/CustomInterweave/index.tsx Normal file → Executable file
View File

0
src/components/DropdownInput/index.tsx Normal file → Executable file
View File

0
src/components/DropdownInput/style.css Normal file → Executable file
View File

0
src/components/Footer/index.tsx Normal file → Executable file
View File

0
src/components/Footer/style.css Normal file → Executable file
View File

34
src/components/Header/index.tsx Normal file → Executable file
View File

@ -8,6 +8,7 @@ import './style.css';
import { logoutService } from "../../services"; import { logoutService } from "../../services";
import { getSearchLocationService } from "../../services/locations"; import { getSearchLocationService } from "../../services/locations";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { ReactSelectData } from "src/types/common";
function Header() { function Header() {
@ -40,7 +41,8 @@ function Header() {
e.preventDefault(); e.preventDefault();
} }
const onSelectedSearchOption = (val: any) => { const onSelectedSearchOption = (v: ReactSelectData | unknown) => {
const val = v as ReactSelectData
navigate(`/location/${val.value}`) navigate(`/location/${val.value}`)
} }
@ -88,19 +90,19 @@ function Header() {
/> />
</a> </a>
{user.username && {user.username &&
<div className={'profile-dropdown-img bg-secondary text-left'} style={pageState.profileMenu ? { display: 'block' } : { display: 'none' }}> <div className={'profile-dropdown-img bg-secondary text-left'} style={pageState.profileMenu ? { display: 'block' } : { display: 'none' }}>
<a href={'/user/profile'}><div className={'p-2'}>Profile</div></a> <a href={'/user/profile'}><div className={'p-2'}>Profile</div></a>
<a href={'#'}><div className={'p-2'}>Feed</div></a> <a href={'#'}><div className={'p-2'}>Feed</div></a>
<a href={'/add-location'}><div className={'p-2'}>Add location</div></a> <a href={'/add-location'}><div className={'p-2'}>Add location</div></a>
<a href={'/add-location'}><div className={'p-2'}>Settings</div></a> <a href={'/add-location'}><div className={'p-2'}>Settings</div></a>
<a href={'#'} onClick={handleLogout}><div className={'p-2'}>Logout</div></a> <a href={'#'} onClick={handleLogout}><div className={'p-2'}>Logout</div></a>
{/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */} {/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */}
{/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */} {/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */}
</div> </div>
} }
</div> </div>
<form onSubmit={onSearchSubmit} className={`header-search-input ${dropdown ? "search-input-dropdown" : ""}`}> <form onSubmit={onSearchSubmit} className={`header-search-input ${dropdown ? "search-input-dropdown" : ""}`}>
<AsyncSelect <AsyncSelect
onInputChange={onInput} onInputChange={onInput}
inputValue={searchVal} inputValue={searchVal}
isSearchable isSearchable
@ -139,7 +141,7 @@ function Header() {
textAlign: 'left', textAlign: 'left',
minHeight: 45 minHeight: 45
}), }),
option: (base, {isFocused}) => ({ option: (base, { isFocused }) => ({
...base, ...base,
backgroundColor: isFocused ? '#202225' : 'none', backgroundColor: isFocused ? '#202225' : 'none',
}), }),
@ -149,7 +151,7 @@ function Header() {
// color: "white" // color: "white"
// }) // })
}} }}
onChange={onSelectedSearchOption} onChange={(v: ReactSelectData | unknown, _) => onSelectedSearchOption(v)}
/> />
</form> </form>
<button onClick={onDropdown} className={`dropdown-menu bg-secondary ${dropdown ? 'ml-auto' : ''}`} style={{ padding: 5, borderRadius: 10 }}> <button onClick={onDropdown} className={`dropdown-menu bg-secondary ${dropdown ? 'ml-auto' : ''}`} style={{ padding: 5, borderRadius: 10 }}>

0
src/components/Header/style.css Normal file → Executable file
View File

View File

@ -0,0 +1,24 @@
import { capitalize } from "../../../types/common"
interface ComponentProps {
value: string,
isSelected?: boolean
onChange: () => void,
inputName: string
}
const CheckboxInput = (props: ComponentProps) => (
<div style="align-self: stretch; justify-content: flex-start; align-items: center; gap: 12px; display: inline-flex">
<input checked={props.isSelected}
type={"checkbox"}
style={{ width: 16, height: 16 }}
className={"input-checkbox"}
value={props.value}
name={props.inputName}
onChange={props.onChange}
/>
<div style="color: white; font-size: 14px; line-height: 21px; letter-spacing: 0.56px; word-wrap: break-word">{capitalize(props.value)}</div>
</div>
)
export default CheckboxInput

0
src/components/Loading/Spinner/index.tsx Normal file → Executable file
View File

0
src/components/Loading/Spinner/style.css Normal file → Executable file
View File

0
src/components/RefOutsideClick/index.tsx Normal file → Executable file
View File

0
src/components/Separator/Default/index.tsx Normal file → Executable file
View File

0
src/components/Separator/NavigationSeparator/index.tsx Normal file → Executable file
View File

0
src/components/Separator/NavigationSeparator/style.css Normal file → Executable file
View File

0
src/components/Separator/TitleSeparator/index.tsx Normal file → Executable file
View File

0
src/components/Separator/WithAnchor/index.tsx Normal file → Executable file
View File

0
src/components/Separator/WithAnchor/style.css Normal file → Executable file
View File

12
src/components/index.ts Normal file → Executable file
View File

@ -15,6 +15,12 @@ import DropdownInput from "./DropdownInput";
import SpinnerLoading from "./Loading/Spinner"; import SpinnerLoading from "./Loading/Spinner";
import FilterButton from "./Button/FilterButton";
import LocationCard from "./Card/LocationCard";
import CheckboxInput from "./Input/CheckboxInput";
export { export {
Header, Header,
WarningButton, WarningButton,
@ -30,5 +36,11 @@ export {
CustomInterweave, CustomInterweave,
DropdownInput, DropdownInput,
CheckboxInput,
SpinnerLoading, SpinnerLoading,
LocationCard,
FilterButton,
} }

0
src/constants/actions.ts Normal file → Executable file
View File

0
src/constants/api.ts Normal file → Executable file
View File

0
src/constants/default.ts Normal file → Executable file
View File

0
src/datas/critics_users_best_pick.json Normal file → Executable file
View File

0
src/datas/home.json Normal file → Executable file
View File

0
src/datas/popular.json Normal file → Executable file
View File

0
src/datas/popular_user_reviews.json Normal file → Executable file
View File

0
src/datas/recent_news_event.json Normal file → Executable file
View File

0
src/domains/LocationInfo.ts Normal file → Executable file
View File

0
src/domains/NewsEvent.ts Normal file → Executable file
View File

0
src/domains/Province.ts Normal file → Executable file
View File

0
src/domains/Regency.ts Normal file → Executable file
View File

0
src/domains/Region.ts Normal file → Executable file
View File

0
src/domains/User.ts Normal file → Executable file
View File

0
src/domains/index.ts Normal file → Executable file
View File

0
src/features/auth/authSlice/authSlice.ts Normal file → Executable file
View File

0
src/features/index.ts Normal file → Executable file
View File

0
src/fonts/Lato-Black.ttf Normal file → Executable file
View File

0
src/fonts/Lato-BlackItalic.ttf Normal file → Executable file
View File

0
src/fonts/Lato-Bold.ttf Normal file → Executable file
View File

0
src/fonts/Lato-BoldItalic.ttf Normal file → Executable file
View File

0
src/fonts/Lato-Italic.ttf Normal file → Executable file
View File

0
src/fonts/Lato-Light.ttf Normal file → Executable file
View File

0
src/fonts/Lato-LightItalic.ttf Normal file → Executable file
View File

0
src/fonts/Lato-Regular.ttf Normal file → Executable file
View File

0
src/fonts/Lato-Thin.ttf Normal file → Executable file
View File

0
src/fonts/Lato-ThinItalic.ttf Normal file → Executable file
View File

0
src/index.css Normal file → Executable file
View File

0
src/lato.css Normal file → Executable file
View File

0
src/layouts/Default/Default.tsx Normal file → Executable file
View File

0
src/layouts/index.ts Normal file → Executable file
View File

0
src/main.tsx Normal file → Executable file
View File

0
src/pages/AddLocation/index.tsx Normal file → Executable file
View File

0
src/pages/AddLocation/style.css Normal file → Executable file
View File

0
src/pages/AddLocation/types.ts Normal file → Executable file
View File

209
src/pages/BestLocations/index.tsx Normal file → Executable file
View File

@ -2,8 +2,8 @@ import { TargetedEvent } from "preact/compat";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { getListTopLocationsService } from "../../services"; import { getListTopLocationsService } from "../../services";
import { DefaultSeparator } from "../../components"; import { DefaultSeparator } from "../../components";
import { NullValueRes } from "../../types/common";
import './style.css'; import './style.css';
import { useClick, useFloating, useInteractions } from "@floating-ui/react";
interface TopLocation { interface TopLocation {
row_number: Number, row_number: Number,
@ -33,7 +33,7 @@ const REGIONS = [
'Sulawesi', 'Sulawesi',
]; ];
const REVIEWERS_TYPE =[ const REVIEWERS_TYPE = [
'All', 'All',
'Critics', 'Critics',
'Users' 'Users'
@ -47,7 +47,7 @@ const MIN_REVIEWS = [
] ]
function BestLocation() { function BestLocation() {
const [page, setPage] = useState<number>(1); const [page, _setPage] = useState<number>(1);
const [topLocations, setTopLocations] = useState<Array<TopLocation>>([]) const [topLocations, setTopLocations] = useState<Array<TopLocation>>([])
const [pageState, setPageState] = useState({ const [pageState, setPageState] = useState({
filterScoreType: 'all', filterScoreType: 'all',
@ -58,10 +58,10 @@ function BestLocation() {
async function getTopLocations() { async function getTopLocations() {
try { try {
const res = await getListTopLocationsService({ const res = await getListTopLocationsService({
page: page, page_size: 20, page: page, page_size: 20,
order_by: pageState.filterScoreTypeidx, order_by: pageState.filterScoreTypeidx,
region_type: pageState.filterRegionType region_type: pageState.filterRegionType
}) })
setTopLocations(res.data) setTopLocations(res.data)
@ -72,133 +72,130 @@ function BestLocation() {
function onChangeReviewType(e: TargetedEvent<HTMLAnchorElement>, reviewer_type: string, i: number) { function onChangeReviewType(e: TargetedEvent<HTMLAnchorElement>, reviewer_type: string, i: number) {
e.preventDefault() e.preventDefault()
setPageState({...pageState, filterScoreType: reviewer_type, filterScoreTypeidx: i }) setPageState({ ...pageState, filterScoreType: reviewer_type, filterScoreTypeidx: i })
} }
function onChangeRegionType(e: TargetedEvent<HTMLAnchorElement>, region_name: string, type: number) { function onChangeRegionType(e: TargetedEvent<HTMLAnchorElement>, region_name: string, type: number) {
e.preventDefault(); e.preventDefault();
setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type}) setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type })
} }
useEffect(() => { useEffect(() => {
getTopLocations() getTopLocations()
}, [pageState]) }, [pageState])
return ( return (
<> <div className={'content main-content mt-3'}>
<div className={'content main-content mt-3'}> <section name={"Top locations header"}>
<section name={"Top locations header"}> <h1 className={'text-3xl mb-5 font-bold'}>Top Locations</h1>
<h1 className={'text-3xl mb-5 font-bold'}>Top Locations</h1> <div className={'regions-dropdown text-xs pr-3 inline-block'}>
<div className={'regions-dropdown text-xs pr-3 inline-block'}> <p className={'inline-block'}>Regions</p>
<p className={'inline-block'}>Regions</p> <a style={{ cursor: 'pointer' }}>
<a style={{ cursor: 'pointer' }}> <p className={'ml-2 inline-block'}>{pageState.filterRegionTypeName}</p>
<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>
<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>
</a> <div className={'dropdown-content text-sm'}>
<div className={'dropdown-content text-sm'}> {REGIONS.map((x, index) => (
{REGIONS.map((x, index) => ( <a onClick={(e) => onChangeRegionType(e, x, index)} className={'block pt-1'}>{x}</a>
<a onClick={(e) => onChangeRegionType(e, x, index) } className={'block pt-1'}>{x}</a> ))}
))}
</div>
</div> </div>
<div className={'regions-dropdown text-xs pr-3 inline-block'}> </div>
<p className={'inline-block'}>Min. Reviews</p> <div className={'regions-dropdown text-xs pr-3 inline-block'}>
<a style={{ cursor: 'pointer' }}> <p className={'inline-block'}>Min. Reviews</p>
<p className={'ml-2 inline-block'}>All</p> <a style={{ cursor: 'pointer' }}>
<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> <p className={'ml-2 inline-block'}>All</p>
</a> <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>
<div className={'dropdown-content-min-reviews text-sm'}> </a>
{MIN_REVIEWS.map(x => ( <div className={'dropdown-content-min-reviews text-sm'}>
<a className={'block pt-1'}>{x}</a> {MIN_REVIEWS.map(x => (
))} <a className={'block pt-1'}>{x}</a>
</div> ))}
</div> </div>
<div className={'regions-dropdown text-xs pr-3 inline-block'}> </div>
<p className={'inline-block'}>Tags</p> <div className={'regions-dropdown text-xs pr-3 inline-block'}>
<a style={{ cursor: 'pointer' }}> <p className={'inline-block'}>Tags</p>
<p className={'ml-2 inline-block'}>All</p> <a style={{ cursor: 'pointer' }}>
<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> <p className={'ml-2 inline-block'}>All</p>
</a> <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>
<div className={'dropdown-content text-sm'}> </a>
{/* {REGIONS.map(x => ( <div className={'dropdown-content text-sm'}>
{/* {REGIONS.map(x => (
<a className={'block pt-1'}>{x}</a> <a className={'block pt-1'}>{x}</a>
))} */} ))} */}
</div>
</div> </div>
<DefaultSeparator /> </div>
</section> <DefaultSeparator />
</section>
<section name={'Main content'}> <section name={'Main content'}>
<div className={'flex flex-row pt-10 pb-10'}> <div className={'flex flex-row pt-10 pb-10'}>
<div className={'mr-5'} style={{ flex: 1}}> <div className={'mr-5'} style={{ flex: 1 }}>
{topLocations.map(x => ( {topLocations.map(x => (
<> <>
{/* UNCOMMENT....IF THERES A PURPOSE FOR RIGHT THREE DOTS */} {/* UNCOMMENT....IF THERES A PURPOSE FOR RIGHT THREE DOTS */}
{/* <div style={{ float: 'right', cursor: 'pointer' }}> {/* <div style={{ float: 'right', cursor: 'pointer' }}>
<a className={'text-xl'}> <a className={'text-xl'}>
... ...
</a> </a>
</div> */} </div> */}
<div className={'mb-2 best-locations-title'}> <div className={'mb-2 best-locations-title'}>
<a className={'text-xl'} href={`/location/${x.id}`}>{x.row_number}.{x.name}</a> <a className={'text-xl'} href={`/location/${x.id}`}>{x.row_number}.{x.name}</a>
</div> </div>
<div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}> <div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}>
<a href={`/location/${x.id}`} > <a href={`/location/${x.id}`} >
<img src={x.thumbnail ? x.thumbnail : ""} loading={'lazy'} style={{ width: '100%', objectFit: 'cover', height: '100%', aspectRatio: '1/1' }} /> <img src={x.thumbnail ? x.thumbnail : ""} loading={'lazy'} style={{ width: '100%', objectFit: 'cover', height: '100%', aspectRatio: '1/1' }} />
</a> </a>
</div> </div>
<div className={'text-md font-bold'}>{x.regency_name}</div> <div className={'text-md font-bold'}>{x.regency_name}</div>
<div className={'text-xs mb-2'}>{x.address}</div> <div className={'text-xs mb-2'}>{x.address}</div>
<div>$$$ (IDR 1000-12000)</div> <div>$$$ (IDR 1000-12000)</div>
<a href={x.google_maps_link} target={'_'}><div className={'text-sm mt-2 items-center'}><svg style={{ display: 'inline-block', marginBottom: 3}} xmlns="http://www.w3.org/2000/svg" height="12" fill={'white'} viewBox="0 -960 960 960" width="12 "><path d="M480-480q33 0 56.5-23.5T560-560q0-33-23.5-56.5T480-640q-33 0-56.5 23.5T400-560q0 33 23.5 56.5T480-480Zm0 294q122-112 181-203.5T720-552q0-109-69.5-178.5T480-800q-101 0-170.5 69.5T240-552q0 71 59 162.5T480-186Zm0 106Q319-217 239.5-334.5T160-552q0-150 96.5-239T480-880q127 0 223.5 89T800-552q0 100-79.5 217.5T480-80Zm0-480Z"/></svg>Maps Location</div></a> <a href={x.google_maps_link} target={'_'}><div className={'text-sm mt-2 items-center'}><svg style={{ display: 'inline-block', marginBottom: 3 }} xmlns="http://www.w3.org/2000/svg" height="12" fill={'white'} viewBox="0 -960 960 960" width="12 "><path d="M480-480q33 0 56.5-23.5T560-560q0-33-23.5-56.5T480-640q-33 0-56.5 23.5T400-560q0 33 23.5 56.5T480-480Zm0 294q122-112 181-203.5T720-552q0-109-69.5-178.5T480-800q-101 0-170.5 69.5T240-552q0 71 59 162.5T480-186Zm0 106Q319-217 239.5-334.5T160-552q0-150 96.5-239T480-880q127 0 223.5 89T800-552q0 100-79.5 217.5T480-80Zm0-480Z" /></svg>Maps Location</div></a>
<div className={'mt-4'}> <div className={'mt-4'}>
<div className={'text-xs bg-secondary'} style={{ width: 160, display: 'inline-block', borderRadius: 5 }}> <div className={'text-xs bg-secondary'} style={{ width: 160, display: 'inline-block', borderRadius: 5 }}>
<div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopRightRadius: 5, borderTopLeftRadius: 5}}>CRITICS SCORE</div> <div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopRightRadius: 5, borderTopLeftRadius: 5 }}>CRITICS SCORE</div>
<div className={"flex flex-row items-center p-2"}> <div className={"flex flex-row items-center p-2"}>
<div className={'mr-3 users-score-bar'}> <div className={'mr-3 users-score-bar'}>
<p className={`text-xl text-center ${x.critic_score !== 0 ? 'font-bold' : ''}`}>{x.critic_score !== 0 ? Number(x.critic_score) / Number(x.critic_count) * 10 : "N/A"}</p> <p className={`text-xl text-center ${x.critic_score !== 0 ? 'font-bold' : ''}`}>{x.critic_score !== 0 ? Number(x.critic_score) / Number(x.critic_count) * 10 : "N/A"}</p>
<div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}> <div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${x.critic_count !== 0 ? Number(x.critic_score) / Number(x.critic_count) * 10 : 0}%`, backgroundColor: 'green' }} /> <div style={{ height: 4, width: ` ${x.critic_count !== 0 ? Number(x.critic_score) / Number(x.critic_count) * 10 : 0}%`, backgroundColor: 'green' }} />
</div>
</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: 5 }}>
<div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopLeftRadius: 5, borderTopRightRadius: 5}}>USERS SCORE</div>
<div className={"flex flex-row items-center p-2"}>
<div className={'mr-3 users-score-bar'}>
<p className={`text-xl text-center ${x.user_score !== 0 ? 'font-bold' : ''}`}>{x.user_score !== 0 ? x.user_score : "N/A" }</p>
<div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${x.user_score !== 0 ? x.user_score : 0}%`, backgroundColor: 'green' }} />
</div>
</div>
<p className={'text-xs users-score'}>{x.user_count} reviews</p>
</div> </div>
<p className={'text-xs users-score'}>{x.critic_count} reviews</p>
</div> </div>
</div> </div>
<div style={{ clear: 'both'}}/> <div className={'text-xs bg-secondary ml-3'} style={{ width: 160, display: 'inline-block', borderRadius: 5 }}>
</> <div className={'text-center p-1 bg-tertiary text-primary'} style={{ borderTopLeftRadius: 5, borderTopRightRadius: 5 }}>USERS SCORE</div>
<div className={"flex flex-row items-center p-2"}>
<div className={'mr-3 users-score-bar'}>
<p className={`text-xl text-center ${x.user_score !== 0 ? 'font-bold' : ''}`}>{x.user_score !== 0 ? x.user_score : "N/A"}</p>
<div className={"mt-1"} style={{ height: 4, width: 40, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${x.user_score !== 0 ? x.user_score : 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 right-filter'}>
{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 className={'p-4 bg-secondary'} style={{ minWidth: 300}}>
<div className={'h-30 bg-primary p-4 right-filter'}>
{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> </div>
</section> </div>
</div> </section>
</> </div>
) )
} }

0
src/pages/BestLocations/style.css Normal file → Executable file
View File

304
src/pages/Discovery/index.tsx Normal file → Executable file
View File

@ -1,8 +1,304 @@
import { useEffect, useState } from "preact/hooks";
import { CheckboxInput, DefaultSeparator, FilterButton, LocationCard } from "../../components";
import { useClick, useFloating, useInteractions, Side, AlignedPlacement, flip, shift, useDismiss } from "@floating-ui/react";
import { JSXInternal } from "node_modules/preact/src/jsx";
import { getListRecentLocationsRatingsService, getRegenciesService, } from "../../services";
import { LocationInfo, Regency } from "../../domains";
import { ChangeEvent, TargetedEvent } from "preact/compat";
import { LocationType, capitalize } from "../../types/common";
import './style.css';
import { enumKeys } from "../../utils";
import { getSearchLocationService } from "../../services/locations";
interface floatFilterArgs<T> {
isOpen: boolean,
placement: Side | AlignedPlacement,
outsidePress?: () => void,
onChecked: (e: TargetedEvent<HTMLInputElement, Event>, id: number) => void,
onClearFilter?: () => void,
onInputChange: (val: string) => void,
listData?: T[];
}
function FloatFilter(props: floatFilterArgs<Regency & { isSelected?: boolean }>) {
const [floatState, setFloatState] = useState({
searchValue: "",
})
const { refs, floatingStyles, context } = useFloating({
placement: props.placement,
open: props.isOpen,
middleware: [
flip({ fallbackAxisSideDirection: "end" }),
shift()
]
});
const click = useClick(context);
const dismiss = useDismiss(context, {
outsidePress: () => {
props.outsidePress && props.outsidePress()
return false
}
});
const { getReferenceProps, getFloatingProps } = useInteractions([
click,
dismiss
]);
const style: JSXInternal.CSSProperties = {
...floatingStyles,
top: 210,
left: 'none',
marginLeft: 150,
width: 355,
zIndex: 1,
}
function onInputChange(e: ChangeEvent<HTMLInputElement>): void {
const input = e.target as HTMLInputElement;
setFloatState({ searchValue: input.value.toLowerCase() })
props.onInputChange(input.value)
}
if (props.isOpen) return (
<div
ref={refs.setFloating} {...getReferenceProps()}
style={style}
className={'bg-secondary float-filter-scroll'}
{...getFloatingProps()}
>
<div style={{ padding: 10 }}>
<input
type="text"
value={floatState.searchValue}
onChange={onInputChange}
placeholder={"Search ..."}
className={'bg-quartenary text-sm input-text mb-3'}
/>
</div>
<div style={{ overflowY: 'scroll', height: 315, padding: 10 }} className={'float-filter-scroll'}>
{props.listData ?
<ul>
{props.listData.map(x => (
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 5 }}>
<input
type={"checkbox"}
id={x.id.toString()}
checked={x.isSelected}
value={x.id}
name={x.regency_name}
className={'input-checkbox'}
style={{ marginRight: 10, width: 18, height: 18 }}
onChange={(e) => props.onChecked(e, x.id)}
/>
<label htmlFor={x.id.toString()}>{x.regency_name}</label>
</div>
))}
</ul>
: null}
</div>
<div style={{ display: 'flex', justifyContent: 'space-around', paddingBottom: 10, paddingTop: 10 }}>
<button onClick={props.onClearFilter}>Clear</button>
<button onClick={props.outsidePress} >Close</button>
</div>
</div>
)
return null
}
function Discovery() { function Discovery() {
return(
<> interface DiscoveryState {
<h1>Discovery</h1> filterQ: string,
</> regencies: any[],
searchRegencies: any[],
locations: LocationInfo[],
locationType: Array<{value: string, isSelected?: boolean}>
}
const [data, setData] = useState<DiscoveryState>({
filterQ: '',
regencies: [],
searchRegencies: [],
locations: [],
locationType: []
});
const [isFloatFilterOpen, setFloatFilterOpen] = useState(false)
useEffect(() => {
getLocationType()
getRecentLocations()
getRegion()
}, [])
async function getRegion() {
try {
const res = await getRegenciesService();
setData((prevState) => ({ ...prevState, regencies: res.data, searchRegencies: res.data }))
} catch (err) {
alert(err)
}
}
async function getRecentLocations() {
try {
const locations = await getListRecentLocationsRatingsService(20)
setData((prevState) => ({ ...prevState, locations: locations.data }))
} catch (error) {
console.log(error)
}
}
function onCheckedRegencyFilter(_val: any, id: number) {
// const value = val as HTMLInputElement;
const dataRegencies = data.searchRegencies as (Regency & { isSelected?: boolean })[];
const regencyIdx = dataRegencies.findIndex(x => x.id == id);
dataRegencies[regencyIdx].isSelected = !dataRegencies[regencyIdx].isSelected
setData({
...data,
regencies: dataRegencies,
searchRegencies: dataRegencies
})
}
function onClearFilter() {
const dataRegencies = data.searchRegencies as (Regency & { isSelected?: boolean })[];
const regencies = dataRegencies.map(x => ({ ...x, isSelected: false }))
setData({
...data,
regencies: regencies,
searchRegencies: regencies
})
}
function onInputChange(search: string) {
const dataRegencies = data.regencies as (Regency & { isSelected?: boolean })[];
// console.log(dataRegencies.filter(x => x.regency_name.toLowerCase().includes(search)))
setData({
...data,
searchRegencies: dataRegencies.filter(x => x.regency_name.toLowerCase().includes(search))
})
}
function onClickedTypeLocations(idx: number) {
const locType = data.locationType
locType[idx].isSelected = !locType[idx].isSelected
setData({
...data,
locationType: locType
})
}
function onApplyFilter() {
const dataRegencies = data.regencies as (Regency & { isSelected?: boolean})[];
const selectedRegencies = dataRegencies.filter(x => x.isSelected)
const selectedLocType = data.locationType.filter(x => x.isSelected)
let regenciesQ = "";
let locTypeQ = ""
if(selectedRegencies.length > 0) {
regenciesQ = selectedRegencies.map(x => x.id).join(" OR ").replace(/\s+/g, '%20')
}
if(selectedLocType.length > 0) {
const onJoin = selectedRegencies.length > 0 ? " AND " : " OR "
locTypeQ = selectedLocType.map(x => x.value).join(onJoin).replace(/\s+/g, '%20')
if(selectedRegencies.length > 0) locTypeQ = `%20AND%20${locTypeQ}`
}
const searchQ = `${regenciesQ}${locTypeQ}`
}
function getLocationType() {
const type: Array<{value: string, isSelected?: boolean}> = []
for (const lt of enumKeys(LocationType)) {
type.push({value: LocationType[lt]});
}
setData((prevState) => ({ ...prevState, locationType: type }))
}
return (
<div className="content main-content mt-3">
<section name="header">
<h1 className={'text-3xl mb-5 font-bold'}>Discovery</h1>
<DefaultSeparator />
</section>
<section name="content">
<div style="padding-bottom: 24px; justify-content: flex-start; align-items: flex-start; display: inline-flex">
{/* FILTER */}
<section name="discovery filter">
<div className="filter-container">
<div style="align-self: stretch; padding-left: 12px; padding-right: 12px; padding-top: 8px; padding-bottom: 8px; background: #36383B; border-radius: 8px; justify-content: flex-start; align-items: center; gap: 10px; display: inline-flex">
<div style="flex: 1 1 0; color: white; font-size: 16px; line-height: 24px; letter-spacing: 0.64px; word-wrap: break-word">Filter</div>
</div>
<div style="align-self: stretch; padding-left: 12px; padding-right: 12px; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 16px; display: flex">
<div style="align-self: stretch; color: white; font-size: 14px; font-weight: 700; line-height: 21px; letter-spacing: 0.56px; word-wrap: break-word">Kota / Kabupaten </div>
<div style="align-self: stretch; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 12px; display: flex">
{data.regencies.filter(x => x.isSelected).map(x => (
<div style="align-self: stretch; justify-content: flex-start; align-items: center; gap: 12px; display: inline-flex">
<div style="flex: 1 1 0; color: white; font-size: 14px; font-family: Poppins; font-weight: 500; line-height: 21px; letter-spacing: 0.56px; word-wrap: break-word">{x.regency_name}</div>
</div>
))}
</div>
<div style="align-self: stretch; padding: 8px; border-radius: 6px; justify-content: center; align-items: center; gap: 4px; display: inline-flex">
<button onClick={() => setFloatFilterOpen(!isFloatFilterOpen)} style="text-align: center; color: #85CE73; font-size: 14px; font-weight: 600; line-height: 21px; letter-spacing: 0.10px; word-wrap: break-word">Lihat Selengkapnya</button>
<FloatFilter
isOpen={isFloatFilterOpen}
placement="right"
listData={data.searchRegencies}
outsidePress={() => setFloatFilterOpen(false)}
onClearFilter={onClearFilter}
onChecked={onCheckedRegencyFilter}
onInputChange={onInputChange}
/>
</div>
</div>
<div style="align-self: stretch; height: 2px; background: #36383B; border-radius: 2px"></div>
<div style="align-self: stretch; height: 190px; padding-left: 12px; padding-right: 12px; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 16px; display: flex">
<div style="align-self: stretch; color: white; font-size: 14px; font-family: Poppins; font-weight: 700; line-height: 21px; letter-spacing: 0.56px; word-wrap: break-word">Tipe</div>
<div style="align-self: stretch; height: 153px; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 12px; display: flex">
{data.locationType.map((x, i) => (
<CheckboxInput
inputName="location_type"
onChange={() => onClickedTypeLocations(i)}
value={x.value}
isSelected={x.isSelected}
/>
))}
</div>
</div>
<div style="align-self: stretch; height: 2px; background: #36383B; border-radius: 2px"></div>
<FilterButton onClick={onApplyFilter} label="Apply" />
</div>
</section>
{/* FILTER END */}
<div>
{data.locations.map(x => (
<LocationCard
containerClass='card-list-container'
onCardClick={(id) => console.log(id)}
data={x}
/>
))}
</div>
</div>
</section>
</div>
) )
} }

112
src/pages/Discovery/style.css Executable file
View File

@ -0,0 +1,112 @@
.float-filter-scroll::-webkit-scrollbar-track {
background: #e0e0e0;
}
/* Customizing the scrollbar handle */
.float-filter-scroll::-webkit-scrollbar-thumb {
background: #888;
}
.float-filter-scroll::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Customizing the scrollbar width */
.float-filter-scroll::-webkit-scrollbar {
width: 12px;
}
/* For Firefox */
.float-filter-scroll {
scrollbar-width: thin;
scrollbar-color: #a8adb3 #2f3136;
}
.card-list-container {
padding: 10px 1% 15px;
display: inline-block;
margin: 0 0 15px;
vertical-align: top;
width: 20%;
}
.filter-container {
width: 195px;
flex-direction: column;
justify-content:
flex-start;
align-items: flex-start;
gap: 24px;
display: inline-flex;
margin-right: 20px;
}
@media screen and (max-width: 1300px) {
.card-list-container {
width: 24.3%;
}
}
@media screen and (max-width: 1135px) {
.card-list-container {
width: 25%;
}
}
@media screen and (max-width: 1100px) {
.card-list-container {
width: 33%;
}
}
@media screen and (max-width: 920px) {
.card-list-container {
width: 33%;
}
.filter-container {
display: none;
}
}
@media screen and (max-width: 625px) {
.card-list-container {
width: 50%;
}
}
/*
@media screen and (max-width: 425px) {
.news-card {
width: 100%;
}
.news-link {
font-size: 0.75rem;
line-height: 1rem;
}
.recently-img-container {
height: 120px;
width: 100%;
}
.critics-users-image {
height: 60px;
width: 60px;
}
.location-province {
font-size: 10px !important;
}
.trending-image-container {
overflow: hidden;
border-radius: 7px;
height: 100px;
}
.top-location-container .location-title {
font-size: 0.685rem;
}
} */

341
src/pages/Home/index.tsx Normal file → Executable file
View File

@ -1,4 +1,4 @@
import { SeparatorWithAnchor } from '../../components'; import { LocationCard, SeparatorWithAnchor } from '../../components';
import news from '../../datas/recent_news_event.json'; import news from '../../datas/recent_news_event.json';
import popular from '../../datas/popular.json'; import popular from '../../datas/popular.json';
import popular_user_review from '../../datas/popular_user_reviews.json'; import popular_user_review from '../../datas/popular_user_reviews.json';
@ -23,32 +23,32 @@ function Home() {
// const [isLoading, setIsLoading] = useState<boolean>(true) // const [isLoading, setIsLoading] = useState<boolean>(true)
const navigate = useNavigate() const navigate = useNavigate()
async function getRecentLocations() { async function getRecentLocations() {
try { try {
const locations = await getListRecentLocationsRatingsService(12) const locations = await getListRecentLocationsRatingsService(12)
setRecentLocations(locations.data) setRecentLocations(locations.data)
// setIsLoading(false) // setIsLoading(false)
} catch(error) { } catch (error) {
console.log(error) console.log(error)
} }
} }
async function getCrititsBestLocations() { async function getCrititsBestLocations() {
try { try {
const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 2, region_type: 0}) const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 2, region_type: 0 })
setTopCriticsLocations(res.data) setTopCriticsLocations(res.data)
}catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
async function getUsersBestLocations() { async function getUsersBestLocations() {
try { try {
const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 3, region_type: 0}) const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 3, region_type: 0 })
setTopUsersLocations(res.data) setTopUsersLocations(res.data)
}catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
@ -61,181 +61,184 @@ function Home() {
getRecentLocations() getRecentLocations()
getCrititsBestLocations() getCrititsBestLocations()
getUsersBestLocations() getUsersBestLocations()
},[]) }, [])
return ( return (
<> <div className="content main-content mt-3">
<div className="content main-content mt-3">
{/* RECENTLY ADDED SECTION */} {/* RECENTLY ADDED SECTION */}
<section about={"Recently added places"} className={'mt-3'}> <section about={"Recently added places"} className={'mt-3'}>
<SeparatorWithAnchor pageLink='#' pageName='recently added' secondLink='#' /> <SeparatorWithAnchor pageLink='#' pageName='recently added' secondLink='#' />
{recentLocations.map((x) => ( {recentLocations.map((x) => (
<div className={"recently-added-section-card"}> <LocationCard
<a onClick={() => onNavigateToDetail(x.id)}> containerClass='recently-added-section-card'
<div className={'border-secondary recently-img-container'}> onCardClick={onNavigateToDetail}
<img alt={x.name} src={x.thumbnail ? x.thumbnail : ''} loading="lazy" style={{ width: '100%', height: '100%' }} /> data={x}
/>
// <div className={"recently-added-section-card"}>
// <a onClick={() => onNavigateToDetail(x.id)}>
// <div className={'border-secondary recently-img-container'}>
// <img alt={x.name} src={x.thumbnail ? x.thumbnail : ''} loading="lazy" style={{ width: '100%', height: '100%' }} />
// </div>
// </a>
// <div className={"border-primary pb-2 location-container text-sm mb-2 mt-2"}>
// <p className={'location-title'}>{x.name}</p>
// <p className={'text-xs mt-1'}>{x.regency_name}, {x.province_name}</p>
// </div>
// {x.critic_count !== 0 &&
// <div className={"flex flex-row items-center mb-3"}>
// <div className={'mr-3 users-score-bar'}>
// <p className={'text-sm text-center'}>{x.critic_score}</p>
// <div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
// <div style={{ height: 4, width: `${x.critic_score}%`, backgroundColor: 'green' }} />
// </div>
// </div>
// <p className={"users-score"}>critic score ({x.critic_count})</p>
// </div>
// }
// {x.user_score !== 0 &&
// <div className={"flex flex-row items-center"}>
// <div className={'mr-3 users-score-bar'}>
// <p className={'text-sm text-center'}>{x.user_score}</p>
// <div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
// <div style={{ height: 4, width: ` ${x.user_score}%`, backgroundColor: 'green' }} />
// </div>
// </div>
// <p className={'users-score'}>user score ({x.user_count})</p>
// </div>
// }
// </div>
))}
</section>
{/* END RECENTLY ADDED SECTION */}
{/* START RECENT NEWS / EVENT SECTION */}
{/* USE OPEN GRAPH PARSER TO READ OG DATA FROM HTML */}
{/* https://github.com/dyatlov/go-opengraph */}
<section className={"mt-10"}>
<SeparatorWithAnchor pageLink='#' pageName='Recent News / Event' secondLink='#' />
<div className={"mt-5"}>
{news.data.map((x: News) => (
<div class={"text-sm news-card"}>
<div className={"image-news-container"}>
<img src={x.thumbnail} loading={'lazy'} className={"news-img"} />
</div>
<a className={'news-link'} target="_blank" href={x.link}>{x.link.split("/")[2].replace(/www\./, '')}</a>
<p className={'mt-2 mb-2'}>{x.header}</p>
<div className={"flex flex-row user-engagement"}>
<svg xmlns="http://www.w3.org/2000/svg" fill={"gray"} height="18" viewBox="0 -960 960 960" width="18"><path d="m480-120-58-52q-101-91-167-157T150-447.5Q111-500 95.5-544T80-634q0-94 63-157t157-63q52 0 99 22t81 62q34-40 81-62t99-22q94 0 157 63t63 157q0 46-15.5 90T810-447.5Q771-395 705-329T538-172l-58 52Zm0-108q96-86 158-147.5t98-107q36-45.5 50-81t14-70.5q0-60-40-100t-100-40q-47 0-87 26.5T518-680h-76q-15-41-55-67.5T300-774q-60 0-100 40t-40 100q0 35 14 70.5t50 81q36 45.5 98 107T480-228Zm0-273Z" /></svg>
<p className={"mr-3 ml-1"}>{x.likes_count}</p>
<svg style={{ marginTop: 2 }} stroke="currentColor" fill="white" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M256 32C114.6 32 0 125.1 0 240c0 49.6 21.4 95 57 130.7C44.5 421.1 2.7 466 2.2 466.5c-2.2 2.3-2.8 5.7-1.5 8.7S4.8 480 8 480c66.3 0 116-31.8 140.6-51.4 32.7 12.3 69 19.4 107.4 19.4 141.4 0 256-93.1 256-208S397.4 32 256 32zM128 272c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 0c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 0c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path></svg>
<p className={"ml-1"}>{x.comments_count}</p>
</div>
</div>
))
}
</div>
</section>
{/* END RECENT NEWS / EVENT SECTION */}
{/* LOCATION CRITICS BEST AND USERS BEST SECTION */}
<section className={"mt-10"}>
<SeparatorWithAnchor pageLink='#' pageName={"Popular user reviews"} secondLink='#' />
<div>
{popular_user_review.data.map((x) => (
<div className={'text-sm reviews-container'}>
<div style={{ float: 'left', width: 120, margin: '0 12px 10px 0' }}>
<img src={x.thumbnail} loading={'lazy'} style={{ width: '100%' }} />
</div>
<p className={"text-sm location-titles"}>{x.place_name}</p>
<p className={'text-xs mb-2'}>{x.location}</p>
<p className={'text-sm'}>{x.username}</p>
<div style={{ display: 'inline-block', overflow: 'hidden', verticalAlign: 'middle' }}>
<p className={'text-xs ml-2 mt-1'}>{x.rating}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${x.rating}%`, backgroundColor: 'green' }} />
</div>
</div>
<div style={{ clear: 'both' }} className={'mb-3'} />
<div className={'text-sm'}>
{x.message}
<a className={'text-tertiary ml-2'} style={{ cursor: 'pointer' }}>read more</a>
</div>
</div>
))
}
</div>
</section>
{/* START LOCATION CRITICS BEST AND USERS BEST SECTION */}
<section className={"mt-10 mb-10"}>
<div class={"grid grid-cols-4 lg:gap-12"}>
<div className={'col-span-4 lg:col-span-2 trending-section'}>
<SeparatorWithAnchor pageLink='#' pageName={"Trending Now"} secondLink='#' />
<div className={'grid grid-cols-4 md:grid-cols-3'}>
{popular.data.map((x) => (
<div className={"m-2 text-sm col-span-2 md:col-span-1"}>
<div className={"mb-2 trending-image-container"}>
<img src={x.thumbnail} loading={"lazy"} style={{ width: '100%', height: '100%' }} />
</div >
<p className={"location-title"}>{x.name}</p>
<p className={"text-xs location-title"}>{x.location}</p>
<div>
</div>
</div>
))}
</div>
</div>
<div className={'col-span-2 lg:col-span-1'}>
<SeparatorWithAnchor pageLink='#' pageName={"Critic's Best"} />
{topCriticsLocations.map((x) => (
<div className={"pt-2 text-sm top-location-container"}>
<div className={'mr-2 critics-users-image'}>
<img
src={x.thumbnail ? x.thumbnail : 'https://i.ytimg.com/vi/0DY1WSk8B9o/maxresdefault.jpg'}
loading={'lazy'}
style={{ height: '100%', width: '100%', borderRadius: 3 }}
/>
</div> </div>
</a>
<div className={"border-primary pb-2 location-container text-sm mb-2 mt-2"}>
<p className={'location-title'}>{x.name}</p> <p className={'location-title'}>{x.name}</p>
<p className={'text-xs mt-1'}>{x.regency_name}, {x.province_name}</p> <p className={'text-xs location-province location-title'}>{x.regency_name}</p>
</div> <div className={'critics-users-rating-container'} style={{ display: 'inline-block' }}>
{ x.critic_count !== 0 && <p className={'text-xs ml-2'}>{x.critic_score} <span className={'text-gray'}>({x.critic_count})</span></p>
<div className={"flex flex-row items-center mb-3"}>
<div className={'mr-3 users-score-bar'}>
<p className={'text-sm text-center'}>{x.critic_score}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d"}}>
<div style={{ height: 4, width: `${x.critic_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<p className={"users-score"}>critic score ({x.critic_count})</p>
</div>
}
{ x.user_score !== 0 &&
<div className={"flex flex-row items-center"}>
<div className={'mr-3 users-score-bar'}>
<p className={'text-sm text-center'}>{x.user_score}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${x.user_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<p className={'users-score'}>user score ({x.user_count})</p>
</div>
}
</div>
))}
</section>
{/* END RECENTLY ADDED SECTION */}
{/* START RECENT NEWS / EVENT SECTION */}
{/* USE OPEN GRAPH PARSER TO READ OG DATA FROM HTML */}
{/* https://github.com/dyatlov/go-opengraph */}
<section className={"mt-10"}>
<SeparatorWithAnchor pageLink='#' pageName='Recent News / Event' secondLink='#' />
<div className={"mt-5"}>
{news.data.map((x: News) => (
<div class={"text-sm news-card"}>
<div className={"image-news-container"}>
<img src={x.thumbnail} loading={'lazy'} className={"news-img"}/>
</div>
<a className={'news-link'} target="_blank" href={x.link}>{x.link.split("/")[2].replace(/www\./, '')}</a>
<p className={'mt-2 mb-2'}>{x.header}</p>
<div className={"flex flex-row user-engagement"}>
<svg xmlns="http://www.w3.org/2000/svg" fill={"gray"} height="18" viewBox="0 -960 960 960" width="18"><path d="m480-120-58-52q-101-91-167-157T150-447.5Q111-500 95.5-544T80-634q0-94 63-157t157-63q52 0 99 22t81 62q34-40 81-62t99-22q94 0 157 63t63 157q0 46-15.5 90T810-447.5Q771-395 705-329T538-172l-58 52Zm0-108q96-86 158-147.5t98-107q36-45.5 50-81t14-70.5q0-60-40-100t-100-40q-47 0-87 26.5T518-680h-76q-15-41-55-67.5T300-774q-60 0-100 40t-40 100q0 35 14 70.5t50 81q36 45.5 98 107T480-228Zm0-273Z" /></svg>
<p className={"mr-3 ml-1"}>{x.likes_count}</p>
<svg style={{ marginTop: 2}} stroke="currentColor" fill="white" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M256 32C114.6 32 0 125.1 0 240c0 49.6 21.4 95 57 130.7C44.5 421.1 2.7 466 2.2 466.5c-2.2 2.3-2.8 5.7-1.5 8.7S4.8 480 8 480c66.3 0 116-31.8 140.6-51.4 32.7 12.3 69 19.4 107.4 19.4 141.4 0 256-93.1 256-208S397.4 32 256 32zM128 272c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 0c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 0c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path></svg>
<p className={"ml-1"}>{x.comments_count}</p>
</div>
</div>
))
}
</div>
</section>
{/* END RECENT NEWS / EVENT SECTION */}
{/* LOCATION CRITICS BEST AND USERS BEST SECTION */}
<section className={"mt-10"}>
<SeparatorWithAnchor pageLink='#' pageName={"Popular user reviews"} secondLink='#' />
<div>
{popular_user_review.data.map((x) => (
<div className={'text-sm reviews-container'}>
<div style={{ float: 'left', width: 120, margin: '0 12px 10px 0' }}>
<img src={x.thumbnail} loading={'lazy'} style={{ width: '100%' }} />
</div>
<p className={"text-sm location-titles"}>{x.place_name}</p>
<p className={'text-xs mb-2'}>{x.location}</p>
<p className={'text-sm'}>{x.username}</p>
<div style={{ display: 'inline-block', overflow: 'hidden', verticalAlign: 'middle' }}>
<p className={'text-xs ml-2 mt-1'}>{x.rating}</p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}> <div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${x.rating}%`, backgroundColor: 'green' }} /> <div style={{ height: 4, width: `${x.critic_score}%`, backgroundColor: 'green' }} />
</div> </div>
</div> </div>
<div style={{ clear: 'both'}} className={'mb-3'}/> <div style={{ clear: 'both' }} />
<div className={'text-sm'}>
{x.message}
<a className={'text-tertiary ml-2'} style={{ cursor: 'pointer'}}>read more</a>
</div>
</div> </div>
)) ))
} }
</div> </div>
</section> <div className={'col-span-2 lg:col-span-1'}>
<SeparatorWithAnchor pageLink='#' pageName={"User's Best"} />
{topUsersLocations.map((x) => (
{/* START LOCATION CRITICS BEST AND USERS BEST SECTION */} <div className={"pt-2 text-sm top-location-container"}>
<section className={"mt-10 mb-10"}> <div className={'mr-2 critics-users-image'}>
<div class={"grid grid-cols-4 lg:gap-12"}> <img
<div className={'col-span-4 lg:col-span-2 trending-section'}> src={x.thumbnail ? x.thumbnail : 'https://i.ytimg.com/vi/0DY1WSk8B9o/maxresdefault.jpg'}
<SeparatorWithAnchor pageLink='#' pageName={"Trending Now"} secondLink='#' /> style={{ height: '100%', width: '100%', borderRadius: 3 }}
<div className={'grid grid-cols-4 md:grid-cols-3'}> />
{popular.data.map((x) => ( </div>
<div className={"m-2 text-sm col-span-2 md:col-span-1"}> <p className={'location-title'}>{x.name}</p>
<div className={"mb-2 trending-image-container"}> <p className={'text-xs location-province location-title'}>{x.regency_name}</p>
<img src={x.thumbnail} loading={"lazy"} style={{ width: '100%', height: '100%' }} /> <div className={'critics-users-rating-container'} style={{ display: 'inline-block' }}>
</div > <p className={'text-xs ml-2'}>{x.user_score} <span className={'text-xs text-gray'}>({x.user_count})</span></p>
<p className={"location-title"}>{x.name}</p> <div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<p className={"text-xs location-title"}>{x.location}</p> <div style={{ height: 4, width: `${x.user_score}%`, backgroundColor: 'green' }} />
<div>
</div>
</div> </div>
))} </div>
<div style={{ clear: 'both' }} />
</div> </div>
</div> ))
<div className={'col-span-2 lg:col-span-1'}> }
<SeparatorWithAnchor pageLink='#' pageName={"Critic's Best"} />
{topCriticsLocations.map((x) => (
<div className={"pt-2 text-sm top-location-container"}>
<div className={'mr-2 critics-users-image'}>
<img
src={x.thumbnail ? x.thumbnail : 'https://i.ytimg.com/vi/0DY1WSk8B9o/maxresdefault.jpg'}
loading={'lazy'}
style={{ height: '100%', width: '100%', borderRadius: 3}}
/>
</div>
<p className={'location-title'}>{x.name}</p>
<p className={'text-xs location-province location-title'}>{x.regency_name}</p>
<div className={'critics-users-rating-container'} style={{ display: 'inline-block' }}>
<p className={'text-xs ml-2'}>{x.critic_score} <span className={'text-gray'}>({x.critic_count})</span></p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${x.critic_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<div style={{ clear: 'both'}} />
</div>
))
}
</div>
<div className={'col-span-2 lg:col-span-1'}>
<SeparatorWithAnchor pageLink='#' pageName={"User's Best"} />
{topUsersLocations.map((x) => (
<div className={"pt-2 text-sm top-location-container"}>
<div className={'mr-2 critics-users-image'}>
<img
src={x.thumbnail ? x.thumbnail : 'https://i.ytimg.com/vi/0DY1WSk8B9o/maxresdefault.jpg'}
style={{ height: '100%', width: '100%', borderRadius: 3}}
/>
</div>
<p className={'location-title'}>{x.name}</p>
<p className={'text-xs location-province location-title'}>{x.regency_name}</p>
<div className={'critics-users-rating-container'} style={{ display: 'inline-block' }}>
<p className={'text-xs ml-2'}>{x.user_score} <span className={'text-xs text-gray'}>({x.user_count})</span></p>
<div style={{ height: 4, width: 30, backgroundColor: "#72767d"}}>
<div style={{ height: 4, width: `${x.user_score}%`, backgroundColor: 'green' }} />
</div>
</div>
<div style={{ clear: 'both'}} />
</div>
))
}
</div>
</div> </div>
</section> </div>
</div> </section>
</> </div>
) )
} }

0
src/pages/Home/style.css Normal file → Executable file
View File

0
src/pages/LocationDetail/index.css Normal file → Executable file
View File

784
src/pages/LocationDetail/index.tsx Normal file → Executable file
View File

@ -3,10 +3,10 @@ import { ChangeEvent, TargetedEvent } from 'preact/compat';
import { useEffect, useRef, useState } from 'preact/hooks'; import { useEffect, useRef, useState } from 'preact/hooks';
import Lightbox from 'yet-another-react-lightbox'; import Lightbox from 'yet-another-react-lightbox';
import useCallbackState from '../../types/state-callback'; import useCallbackState from '../../types/state-callback';
import { import {
EmptyLocationDetailResponse, EmptyLocationDetailResponse,
LocationDetailResponse, LocationDetailResponse,
LocationResponse, LocationResponse,
emptyLocationResponse, emptyLocationResponse,
CurrentUserLocationReviews, CurrentUserLocationReviews,
} from './types'; } from './types';
@ -33,7 +33,7 @@ function LocationDetail() {
const [locationImages, setLocationImages] = useState<LocationResponse>(emptyLocationResponse()) const [locationImages, setLocationImages] = useState<LocationResponse>(emptyLocationResponse())
const [currentUserReview, setCurrentUserReview] = useState<CurrentUserLocationReviews>() const [currentUserReview, setCurrentUserReview] = useState<CurrentUserLocationReviews>()
const [lightboxOpen, setLightboxOpen] = useState<boolean>(false) const [lightboxOpen, setLightboxOpen] = useState<boolean>(false)
const[updatePage, setUpdatePage] = useState<boolean>(true) const [updatePage, setUpdatePage] = useState<boolean>(true)
const [pageState, setPageState] = useState({ const [pageState, setPageState] = useState({
critic_filter_name: 'highest rated', critic_filter_name: 'highest rated',
critic_filter_type: 0, critic_filter_type: 0,
@ -61,7 +61,7 @@ function LocationDetail() {
try { try {
const res = await getLocationService(Number(id)) const res = await getLocationService(Number(id))
setLocationDetail(res.data, (val) => { setLocationDetail(res.data, (val) => {
if(val.detail.thumbnail) { if (val.detail.thumbnail) {
getImage(val.detail.thumbnail) getImage(val.detail.thumbnail)
} }
}) })
@ -87,17 +87,17 @@ function LocationDetail() {
} }
async function getCurrentUserLocationReview(): Promise<void> { async function getCurrentUserLocationReview(): Promise<void> {
try { try {
const res = await getCurrentUserLocationReviewService(Number(id)) const res = await getCurrentUserLocationReviewService(Number(id))
setCurrentUserReview(res.data) setCurrentUserReview(res.data)
setPageState({ ...pageState, enable_post: false}) setPageState({ ...pageState, enable_post: false })
} catch (error) { } catch (error) {
let err = error as IHttpResponse; let err = error as IHttpResponse;
if(err.status == 404 || err.status == 401 ) { if (err.status == 404 || err.status == 401) {
return return
}
alert(err.error.response.data.message)
} }
alert(err.error.response.data.message)
}
} }
function handleTextAreaChange(e: ChangeEvent<HTMLTextAreaElement>): void { function handleTextAreaChange(e: ChangeEvent<HTMLTextAreaElement>): void {
@ -176,7 +176,7 @@ function LocationDetail() {
console.log(err) console.log(err)
const str = handleAxiosError(err) const str = handleAxiosError(err)
alert(str) alert(str)
setPageState({ ...pageState, on_submit_loading: false }) setPageState({ ...pageState, on_submit_loading: false })
} }
} }
@ -191,476 +191,372 @@ function LocationDetail() {
useEffect(() => { useEffect(() => {
getCurrentUserLocationReview() getCurrentUserLocationReview()
}, []) }, [])
useEffect(() => { useEffect(() => {
if(updatePage) { if (updatePage || id) {
getLocationDetail() getLocationDetail()
} }
}, [updatePage, id]) }, [updatePage, id])
return ( return (
<> <div className={'content main-content mt-3'}>
<div className={'content main-content mt-3'}> <section name={"HEADER LINK"}>
<section name={"HEADER LINK"}> <div className={'header-link text-tertiary'}>
<div className={'header-link text-tertiary'}> <a style={{ display: 'inline-block' }}>OVERVIEW</a>
<a style={{ display: 'inline-block' }}>OVERVIEW</a> <a className={'ml-4'} style={{ display: 'inline-block' }}>USER REVIEWS</a>
<a className={'ml-4'} style={{ display: 'inline-block' }}>USER REVIEWS</a> <a className={'ml-4'} style={{ display: 'inline-block' }}>CRITIC REVIEWS</a>
<a className={'ml-4'} style={{ display: 'inline-block' }}>CRITIC REVIEWS</a> <a className={'ml-4'} style={{ display: 'inline-block' }}>COMMENTS</a>
<a className={'ml-4'} style={{ display: 'inline-block' }}>COMMENTS</a> </div>
</div> </section>
</section>
<section name={'LOCATION HEADER'}> <section name={'LOCATION HEADER'}>
<div className={'pb-5'} style={{ borderBottom: '1px solid #38444d' }}> <div className={'pb-5'} style={{ borderBottom: '1px solid #38444d' }}>
<div className={'font-bold mt-5 text-2xl'}> <div className={'font-bold mt-5 text-2xl'}>
<h1>{locationDetail?.detail.name}</h1> <h1>{locationDetail?.detail.name}</h1>
</div>
{isLoading ?
<div className={'mt-3'} style={{ width: 250, height: 250, backgroundColor: 'gray', float: 'left' }} />
:
<div className={'inline-block'} style={{ maxWidth: 320 }}>
<a
onClick={() => setLightboxOpen(true)}
className={'mt-3'}
style={{ display: 'grid', position: 'relative', gridTemplateColumns: 'repeat(12,1fr)', cursor: 'zoom-in' }}
>{Number(locationImages?.total_image) > 0 &&
<div class="image-stack__item image-stack__item--top">
<img src={locationDetail.detail.thumbnail ? locationDetail.detail.thumbnail : ""} alt="" style={{ aspectRatio: '1/1' }} />
{locationImages?.images.length > 1 &&
<div className={'text-xs p-2 bg-primary'} style={{ position: 'absolute', bottom: 0, right: 0 }}>
Total images ({locationImages?.images.length})
</div>
}
</div>
}
{locationImages?.images.length > 1 &&
<div class="image-stack__item image-stack__item--middle">
<img src={locationImages?.images[0].src} alt="" style={{ aspectRatio: '1/1' }} />
</div>
}
<div class="image-stack__item image-stack__item--bottom" style={Number(locationImages?.total_image) > 1 ? {} : { gridColumn: '13/1' }}>
<img src={Number(locationImages?.total_image) > 1 ? locationImages?.images[1].src.toString() : locationDetail.detail.thumbnail!} alt="" style={{ aspectRatio: '1/1' }} />
</div>
</a>
</div> </div>
{isLoading ? }
<div className={'mt-3'} style={{ width: 250, height: 250, backgroundColor: 'gray', float: 'left' }} /> <div className={'inline-block'} style={{ verticalAlign: 'top', padding: '0 2%', width: '30%', minWidth: 310 }}>
<div className={'p-4 bg-secondary mt-3'} style={{ width: '100%', height: 120, borderTopLeftRadius: 10, borderTopRightRadius: 10 }}>
<div className={'font-bold ml-1 text-xs'}>CRITICS SCORE</div>
<div className={'text-4xl text-center mt-2 mr-4'} style={{ width: 95, float: 'left' }}>
{locationDetail.detail.critic_count !== 0 ? Math.floor(Number(locationDetail.detail.critic_score) / Number(locationDetail.detail.critic_count)) : "NR"}
<div className={"items-center p-2"}>
<div className={'mr-3 users-score-bar'}>
<div className={"mt-1"} style={{ height: 4, width: 80, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${locationDetail.detail.critic_count !== 0 ? Number(locationDetail.detail.critic_score) : 0}%`, backgroundColor: 'green' }} />
</div>
</div>
</div>
</div>
{locationDetail.detail.critic_count !== 0 &&
<div className={'ml-14 text-sm'}>
Based on {locationDetail.detail.critic_count} reviews
</div>
}
</div>
<div className={'p-4 bg-secondary mt-1 inline-block'} style={{ width: '100%', height: 120, borderBottomLeftRadius: 10, borderBottomRightRadius: 10 }}>
<div className={'font-bold ml-2 text-xs'}>USERS SCORE</div>
<div className={'text-4xl text-center mt-2 mr-4'} style={{ width: 95, float: 'left' }}>
{locationDetail.detail.user_count !== 0 ? Math.floor(Number(locationDetail.detail.user_score) / Number(locationDetail.detail.user_count)) : "NR"}
<div className={"items-center p-2"}>
<div className={'mr-3 users-score-bar'}>
<div className={"mt-1"} style={{ height: 4, width: 80, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${locationDetail.detail.user_count !== 0 ? Number(locationDetail.detail.user_score) / Number(locationDetail.detail.user_count) : 0}%`, backgroundColor: 'green' }} />
</div>
</div>
</div>
</div>
{locationDetail.detail.user_count !== 0 &&
<div className={'ml-14 text-sm'}>
Based on {locationDetail.detail.user_count} reviews
</div>
}
</div>
</div>
<div className={'inline-block mt-3 bg-primary text-sm location-detail-container'}>
<div className={'pb-1'} style={{ borderBottom: '1px solid #38444d' }}>
<h2 className={'inline-block font-bold text-xs'} style={{ letterSpacing: .9 }}>DETAILS</h2>
<div className={''} style={{ float: 'right', fontSize: 10, letterSpacing: .9 }}>
<a href="#">SUBMIT CORRECTION</a>
</div>
</div>
<div className={'mt-2 capitalize'}>
<span>address: </span> {locationDetail.detail.address} {locationDetail.detail.regency_name}
</div>
<div className={'mt-1 capitalize'}>
<span>province: </span> <a href={'#'}> {locationDetail.detail.province_name}</a>
</div>
<div className={'mt-1 capitalize'}>
<span>region: </span> <a href={'#'}> {locationDetail.detail.region_name}</a>
</div>
<div className={'mt-1 capitalize'}>
<span>average cost: </span> IDR 25.0000
</div>
<div className={'mt-1 text-md'}>
<a href={locationDetail.detail.google_maps_link.toString()} style={{ borderBottom: '1px solid white' }} target={'_'}> Maps Location</a>
</div>
<div className={'mt-1'}>
<span>Tags: </span>
</div>
{locationDetail.tags.map(x => (
<div className={'p-1 text-xs tags-box mr-1'}>
<a href={'#'}>{x}</a>
</div>
))
}
<div className={'p-1 text-xs tags-box'}>
<a href={'#'}>+ add tags</a>
</div>
</div>
</div>
</section>
<section name={'REVIEWS SECTION'}>
<div className={'mt-5'} style={{ tableLayout: 'fixed', display: 'table', width: '100%' }}>
<div className={'wideLeft'} style={{ textAlign: 'left', paddingRight: 20, maxWidth: 1096, minWidth: 680, display: 'table-cell', position: 'relative', verticalAlign: 'top', width: '100%', boxSizing: 'border-box' }}>
{!user.username ?
<div className={'bg-secondary text-center p-3'} style={{ width: '100%' }}><a href={'#'} onClick={handleSignInNavigation} style={{ borderBottom: '1px solid white' }}>SIGN IN</a> TO REVIEW</div>
: :
<div className={'inline-block'} style={{ maxWidth: 320 }}> <div name="REVIEW INPUT TEXTAREA" className={'reviewContainer p-4'} style={{ backgroundColor: '#2f3136' }}>
<a <div className={'review-box-content'}>
onClick={() => setLightboxOpen(true)}
className={'mt-3'} <div className={'userImage mr-3'} style={{ width: 55, float: 'left' }}>
style={{ display: 'grid', position: 'relative', gridTemplateColumns: 'repeat(12,1fr)', cursor: 'zoom-in' }} <a href={'#'}>
>{Number(locationImages?.total_image) > 0 && <img loading={'lazy'} src={user.avatar_picture != '' ? user.avatar_picture.toString() : DEFAULT_AVATAR_IMG} style={{ aspectRatio: '1/1' }} />
<div class="image-stack__item image-stack__item--top"> </a>
<img src={locationDetail.detail.thumbnail ? locationDetail.detail.thumbnail : ""} alt="" style={{ aspectRatio: '1/1' }} /> </div>
{locationImages?.images.length > 1 &&
<div className={'text-xs p-2 bg-primary'} style={{ position: 'absolute', bottom: 0, right: 0 }}> <div style={{ display: 'block' }}>
Total images ({locationImages?.images.length}) <a href={'#'}>{user.username}</a>
</div>
<div className={'ratingInput'} style={currentUserReview ? { margin: '0 0 10px' } : { margin: '5px 0 10px' }}>
{currentUserReview ?
<div style={{ display: 'inline-block' }}>
<p className={'ml-2'}>{currentUserReview.score}</p>
<div style={{ height: 4, width: 35, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${currentUserReview.score}%`, backgroundColor: 'green' }} />
</div>
</div> </div>
:
<>
<input
type={'text'}
pattern={"\d*"}
style={{ fontSize: 12, backgroundColor: '#40444b', textAlign: 'center', width: 40, height: 20, lineHeight: 18, border: '1px solid #38444d' }}
maxLength={3}
value={reviewValue.score_input}
onChange={handleScoreInputChange}
placeholder={"0-100"}
autoComplete={'off'}
/>
<div className={'inline-block text-xs ml-2 text-tertiary'}>/ score</div>
{pageState.is_score_rating_panic_msg &&
<div className={'inline-block text-xs ml-2 text-error'}>{pageState.is_score_rating_panic_msg}</div>
}
</>
}
<div style={{ clear: 'both' }} />
</div>
<div className={'mt-3'} style={{ width: '100%' }}>
{currentUserReview ?
<CustomInterweave
content={currentUserReview.comments}
/>
:
<ReactTextareaAutosize
onChange={handleTextAreaChange}
ref={textAreaRef}
className={'p-2 text-area text-sm'}
value={reviewValue.review_textArea}
/>
} }
</div> </div>
}
{locationImages?.images.length > 1 && <div style={{ textAlign: 'right', width: "100%" }}>
<div class="image-stack__item image-stack__item--middle"> <div style={{ display: 'inline-block', fontSize: 11, verticalAlign: 'middle', margin: '0 10px 0 0', letterSpacing: .5 }}>
<img src={locationImages?.images[0].src} alt="" style={{ aspectRatio: '1/1' }} /> <a>Review Guidelines</a>
</div> </div>
} {pageState.on_submit_loading ?
<div class="image-stack__item image-stack__item--bottom" style={Number(locationImages?.total_image) > 1 ? {} : { gridColumn: '13/1' }}> <SpinnerLoading />
<img src={Number(locationImages?.total_image) > 1 ? locationImages?.images[1].src.toString() : locationDetail.detail.thumbnail!} alt="" style={{ aspectRatio: '1/1' }} /> :
<span className={'text-xxs p-1 text-area-button'} style={pageState.enable_post ? '' : { display: 'none' }}>
<a href={'#'} onClick={handleSubmitReview}>
POST
</a>
</span>
}
</div> </div>
</a> </div>
</div> </div>
} }
<div className={'inline-block'} style={{ verticalAlign: 'top', padding: '0 2%', width: '30%', minWidth: 310 }}> <div name={'CRTICITS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}>
<div className={'p-4 bg-secondary mt-3'} style={{ width: '100%', height: 120, borderTopLeftRadius: 10, borderTopRightRadius: 10 }}> <SeparatorWithAnchor pageName={"critic's review"} pageLink='#' />
<div className={'font-bold ml-1 text-xs'}>CRITICS SCORE</div> {locationDetail.critics_review.length > 0 ?
<div className={'text-4xl text-center mt-2 mr-4'} style={{ width: 95, float: 'left' }}> <>
{locationDetail.detail.critic_count !== 0 ? Math.floor(Number(locationDetail.detail.critic_score) / Number(locationDetail.detail.critic_count)) : "NR"} <div className={'criticSortFilter'}>
<div className={"items-center p-2"}> <div className={'inline-block text-sm'}>Sort by: </div>
<div className={'mr-3 users-score-bar'}> <a className={'dropdownLabel'} onClick={() => setPageState({ ...pageState, show_sort: !pageState.show_sort })}>
<div className={"mt-1"} style={{ height: 4, width: 80, backgroundColor: "#72767d" }}> <p className={'ml-2 inline-block capitalize text-sm'}>{pageState.critic_filter_name}</p>
<div style={{ height: 4, width: ` ${locationDetail.detail.critic_count !== 0 ? Number(locationDetail.detail.critic_score) : 0}%`, backgroundColor: 'green' }} /> <svg style={{ display: 'inline-block' }} fill={"currentColor"} 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>
</div> </a>
<div className={'dropdown-content text-sm bg-secondary'} style={pageState.show_sort ? { display: 'block' } : ''}>
{SORT_TYPE.map((x, index) => (
<a onClick={(e) => onChangeCriticsSort(e, x, index)} className={'block pt-1 capitalize'}>{x}</a>
))}
</div> </div>
</div> </div>
</div> <div style={{ clear: 'both' }} />
{locationDetail.detail.critic_count !== 0 &&
<div className={'ml-14 text-sm'}>
Based on {locationDetail.detail.critic_count} reviews
</div>
}
</div>
<div className={'p-4 bg-secondary mt-1 inline-block'} style={{ width: '100%', height: 120, borderBottomLeftRadius: 10, borderBottomRightRadius: 10 }}>
<div className={'font-bold ml-2 text-xs'}>USERS SCORE</div>
<div className={'text-4xl text-center mt-2 mr-4'} style={{ width: 95, float: 'left' }}>
{locationDetail.detail.user_count !== 0 ? Math.floor(Number(locationDetail.detail.user_score) / Number(locationDetail.detail.user_count)) : "NR"}
<div className={"items-center p-2"}>
<div className={'mr-3 users-score-bar'}>
<div className={"mt-1"} style={{ height: 4, width: 80, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: ` ${locationDetail.detail.user_count !== 0 ? Number(locationDetail.detail.user_score) / Number(locationDetail.detail.user_count) : 0}%`, backgroundColor: 'green' }} />
</div>
</div>
</div>
</div>
{locationDetail.detail.user_count !== 0 &&
<div className={'ml-14 text-sm'}>
Based on {locationDetail.detail.user_count} reviews
</div>
}
</div>
</div>
<div className={'inline-block mt-3 bg-primary text-sm location-detail-container'}>
<div className={'pb-1'} style={{ borderBottom: '1px solid #38444d' }}>
<h2 className={'inline-block font-bold text-xs'} style={{ letterSpacing: .9 }}>DETAILS</h2>
<div className={''} style={{ float: 'right', fontSize: 10, letterSpacing: .9 }}>
<a href="#">SUBMIT CORRECTION</a>
</div>
</div>
<div className={'mt-2 capitalize'}>
<span>address: </span> {locationDetail.detail.address} {locationDetail.detail.regency_name}
</div>
<div className={'mt-1 capitalize'}>
<span>province: </span> <a href={'#'}> {locationDetail.detail.province_name}</a>
</div>
<div className={'mt-1 capitalize'}>
<span>region: </span> <a href={'#'}> {locationDetail.detail.region_name}</a>
</div>
<div className={'mt-1 capitalize'}>
<span>average cost: </span> IDR 25.0000
</div>
<div className={'mt-1 text-md'}>
<a href={locationDetail.detail.google_maps_link.toString()} style={{ borderBottom: '1px solid white' }} target={'_'}> Maps Location</a>
</div>
<div className={'mt-1'}>
<span>Tags: </span>
</div>
{locationDetail.tags.map(x => (
<div className={'p-1 text-xs tags-box mr-1'}>
<a href={'#'}>{x}</a>
</div>
))
}
<div className={'p-1 text-xs tags-box'}>
<a href={'#'}>+ add tags</a>
</div>
</div>
</div>
</section>
<section name={'REVIEWS SECTION'}> {locationDetail.critics_review.map(x => (
<div className={'mt-5'} style={{ tableLayout: 'fixed', display: 'table', width: '100%' }}> <div className={''} style={{ padding: '15px 0' }}>
<div className={'wideLeft'} style={{ textAlign: 'left', paddingRight: 20, maxWidth: 1096, minWidth: 680, display: 'table-cell', position: 'relative', verticalAlign: 'top', width: '100%', boxSizing: 'border-box' }}> <div style={{ float: 'left' }}>
{!user.username ? <div style={{ fontSize: 20, marginRight: 20, textAlign: 'center', width: 55, marginBottom: 3 }}>
<div className={'bg-secondary text-center p-3'} style={{ width: '100%' }}><a href={'#'} onClick={handleSignInNavigation} style={{ borderBottom: '1px solid white' }}>SIGN IN</a> TO REVIEW</div> {x.score}
:
<div name="REVIEW INPUT TEXTAREA" className={'reviewContainer p-4'} style={{ backgroundColor: '#2f3136' }}>
<div className={'review-box-content'}>
<div className={'userImage mr-3'} style={{ width: 55, float: 'left' }}>
<a href={'#'}>
<img loading={'lazy'} src={user.avatar_picture != '' ? user.avatar_picture.toString() : DEFAULT_AVATAR_IMG} style={{ aspectRatio: '1/1' }} />
</a>
</div>
<div style={{ display: 'block' }}>
<a href={'#'}>{user.username}</a>
</div>
<div className={'ratingInput'} style={currentUserReview ? { margin: '0 0 10px' } : { margin: '5px 0 10px' }}>
{currentUserReview ?
<div style={{ display: 'inline-block' }}>
<p className={'ml-2'}>{currentUserReview.score}</p>
<div style={{ height: 4, width: 35, backgroundColor: "#72767d" }}>
<div style={{ height: 4, width: `${currentUserReview.score}%`, backgroundColor: 'green' }} />
</div>
</div> </div>
: <div style={{ height: 4, width: 55, position: 'relative', backgroundColor: '#d8d8d8' }}>
<> <div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} />
<input </div>
type={'text'} </div>
pattern={"\d*"} <div className={'mr-3'} style={{ display: 'inline-block', width: 40 }}>
style={{ fontSize: 12, backgroundColor: '#40444b', textAlign: 'center', width: 40, height: 20, lineHeight: 18, border: '1px solid #38444d' }} <a href="#">
maxLength={3} <img
value={reviewValue.score_input} loading={'lazy'}
onChange={handleScoreInputChange} style={{ width: '100%' }}
placeholder={"0-100"} src={x.user_avatar ? x.user_avatar : 'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
autoComplete={'off'}
/> />
<div className={'inline-block text-xs ml-2 text-tertiary'}>/ score</div> </a>
{pageState.is_score_rating_panic_msg && </div>
<div className={'inline-block text-xs ml-2 text-error'}>{pageState.is_score_rating_panic_msg}</div> <div style={{ display: 'inline-block', verticalAlign: 'top' }}>
} <div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
</> <a>
<span>{x.username}</span>
} </a>
<div style={{ clear: 'both' }} /> </div>
</div> </div>
<div style={{ fontSize: 15, lineHeight: '24px', margin: '5px 75px 1px' }}>
<div className={'mt-3'} style={{ width: '100%' }}>
{currentUserReview ?
<CustomInterweave <CustomInterweave
content={currentUserReview.comments} content={x.comments}
/> />
:
<ReactTextareaAutosize
onChange={handleTextAreaChange}
ref={textAreaRef}
className={'p-2 text-area text-sm'}
value={reviewValue.review_textArea}
/>
}
</div>
<div style={{ textAlign: 'right', width: "100%" }}>
<div style={{ display: 'inline-block', fontSize: 11, verticalAlign: 'middle', margin: '0 10px 0 0', letterSpacing: .5 }}>
<a>Review Guidelines</a>
</div> </div>
{pageState.on_submit_loading ? <div className={'reviewLinks'} style={{ marginLeft: 72 }}>
<SpinnerLoading /> <div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
: <a className={'text-sm'} href={'#'}>
<span className={'text-xxs p-1 text-area-button'} style={pageState.enable_post ? '' : { display: 'none'}}> <svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<a href={'#'} onClick={handleSubmitReview}> <div className={'inline-block'}>Video</div>
POST
</a>
</span>
}
</div>
</div>
</div>
}
<div name={'CRTICITS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}>
<SeparatorWithAnchor pageName={"critic's review"} pageLink='#' />
{locationDetail.critics_review.length > 0 ?
<>
<div className={'criticSortFilter'}>
<div className={'inline-block text-sm'}>Sort by: </div>
<a className={'dropdownLabel'} onClick={() => setPageState({ ...pageState, show_sort: !pageState.show_sort })}>
<p className={'ml-2 inline-block capitalize text-sm'}>{pageState.critic_filter_name}</p>
<svg style={{ display: 'inline-block' }} fill={"currentColor"} 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 bg-secondary'} style={pageState.show_sort ? { display: 'block' } : ''}>
{SORT_TYPE.map((x, index) => (
<a onClick={(e) => onChangeCriticsSort(e, x, index)} className={'block pt-1 capitalize'}>{x}</a>
))}
</div>
</div>
<div style={{ clear: 'both' }} />
{locationDetail.critics_review.map(x => (
<div className={''} style={{ padding: '15px 0' }}>
<div style={{ float: 'left' }}>
<div style={{ fontSize: 20, marginRight: 20, textAlign: 'center', width: 55, marginBottom: 3 }}>
{x.score}
</div>
<div style={{ height: 4, width: 55, position: 'relative', backgroundColor: '#d8d8d8' }}>
<div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} />
</div>
</div>
<div className={'mr-3'} style={{ display: 'inline-block', width: 40 }}>
<a href="#">
<img
loading={'lazy'}
style={{ width: '100%' }}
src={x.user_avatar ? x.user_avatar : 'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
/>
</a> </a>
</div> </div>
<div style={{ display: 'inline-block', verticalAlign: 'top' }}> <div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}> <a className={'text-sm'} href={'#'}>
<a> <svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<span>{x.username}</span> <div className={'inline-block'}>Instagram</div>
</a> </a>
</div>
</div>
<div style={{ fontSize: 15, lineHeight: '24px', margin: '5px 75px 1px' }}>
<CustomInterweave
content={x.comments}
/>
</div>
<div className={'reviewLinks'} style={{ marginLeft: 72 }}>
<div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Video</div>
</a>
</div>
<div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Instagram</div>
</a>
</div>
</div> </div>
</div> </div>
))} </div>
))}
</> </>
: :
<span className={'text-sm italic'}>No Critics review to display</span>
}
</div>
<div name={'USERS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}>
<SeparatorWithAnchor pageName={"User's review"} pageLink='#' secondLink={locationDetail.users_review.length > 0 ? '#' : ''} />
{locationDetail.users_review.length > 0 ?
<> <>
<span className={'text-sm italic'}>No Critics review to display</span> {locationDetail.users_review.map(x => (
</> <div style={{ padding: '15px 0' }}>
<div className={'mr-5'} style={{ width: 45, float: 'left' }}>
} <a href="#">
</div> <img
loading={'lazy'}
<div name={'USERS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}> style={{ width: '100%' }}
<SeparatorWithAnchor pageName={"User's review"} pageLink='#' secondLink={locationDetail.users_review.length > 0 ? '#' : ''} /> src={x.user_avatar ? x.user_avatar : 'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
{ locationDetail.users_review.length > 0 ? />
<> </a>
{locationDetail.users_review.map(x => ( </div>
<div style={{ padding: '15px 0' }}> <div>
<div className={'mr-5'} style={{ width: 45, float: 'left' }}> <div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
<a href="#"> <a>
<img <span>{x.username}</span>
loading={'lazy'}
style={{ width: '100%' }}
src={x.user_avatar ? x.user_avatar : 'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
/>
</a> </a>
</div> </div>
<div> </div>
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}> <div className={'inline-block'}>
<a> <div className={'text-sm text-center'} >{x.score}</div>
<span>{x.username}</span> <div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}>
</a> <div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} />
</div> </div>
</div> </div>
<div className={'inline-block'}> <div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}>
<div className={'text-sm text-center'} >{x.score}</div> <CustomInterweave
<div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}> content={x.comments}
<div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} /> />
</div> </div>
</div> <div className={'reviewLinks'} style={{ marginLeft: 63 }}>
<div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}> <div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<CustomInterweave <a className={'text-sm'} href={'#'}>
content={x.comments} <svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
/> <div className={'inline-block'}>Video</div>
</div> </a>
<div className={'reviewLinks'} style={{ marginLeft: 63 }}> </div>
<div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}> <div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}> <a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg> <svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Video</div> <div className={'inline-block'}>Instagram</div>
</a> </a>
</div>
<div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Instagram</div>
</a>
</div>
</div> </div>
</div> </div>
))}
<div className={'review-more-button text-center text-sm mt-5'}>
<a style={{ borderRadius: 15, padding: '8px 32px', border: '1px solid #d8d8d8' }}>
More
</a>
</div> </div>
</> ))}
:
<>
<span className={'text-sm italic'}>No users review to display</span>
</>
}
</div>
<div className={'review-more-button text-center text-sm mt-5'}>
{/* <div name={'USERS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}> <a style={{ borderRadius: 15, padding: '8px 32px', border: '1px solid #d8d8d8' }}>
<SeparatorWithAnchor pageName={"Popular User's review"} pageLink='#' secondLink='#' /> More
<div className={''} style={{ padding: '15px 0' }}>
<div className={'mr-5'} style={{ width: 45, float: 'left' }}>
<a href="#">
<img
loading={'lazy'}
style={{ width: '100%' }}
src={'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
/>
</a> </a>
</div> </div>
<div> </>
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}> :
<a> <>
<span>Benito Mussolini</span> <span className={'text-sm italic'}>No users review to display</span>
</a> </>
</div> }
</div>
<div className={'inline-block'}>
<div className={'text-sm text-center'} >80</div>
<div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}>
<div style={{ height: 4, backgroundColor: '#85ce73', width: '90%' }} />
</div>
</div>
<div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}>
<p>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.</p>
</div>
<div className={'reviewLinks'} style={{ marginLeft: 63 }}>
<div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Video</div>
</a>
</div>
<div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Instagram</div>
</a>
</div>
</div>
</div>
<div className={''} style={{ padding: '15px 0', borderTop: '1px solid #38444d' }}>
<div className={'mr-5'} style={{ width: 45, float: 'left' }}>
<a href="#">
<img
loading={'lazy'}
style={{ width: '100%' }}
src={'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
/>
</a>
</div>
<div>
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
<a>
<span>Benito Mussolini</span>
</a>
</div>
</div>
<div className={'inline-block'}>
<div className={'text-sm text-center'} >80</div>
<div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}>
<div style={{ height: 4, backgroundColor: '#85ce73', width: '90%' }} />
</div>
</div>
<div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}>
<p>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.</p>
</div>
<div className={'reviewLinks'} style={{ marginLeft: 63 }}>
<div className={'mr-2'} style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Video</div>
</a>
</div>
<div style={{ minWidth: 55, display: 'inline-block', verticalAlign: 'middle' }}>
<a className={'text-sm'} href={'#'}>
<svg className={'inline-block mr-1'} fill={'currentColor'} xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z" /></svg>
<div className={'inline-block'}>Instagram</div>
</a>
</div>
</div>
</div>
<div className={'review-more-button text-center text-sm mt-5'}>
<a style={{ borderRadius: 15, padding: '8px 32px', border: '1px solid #d8d8d8' }}>
More
</a>
</div>
</div> */}
<div className={'mb-5'}>
CONTRUBITION
<DefaultSeparator />
anoeantoeh aoenthaoe aoenth aot
</div>
</div> </div>
{/* {screen.width >= 1024 &&
<div className={'bg-secondary'} style={{ display: 'table-cell', position: 'relative', verticalAlign: 'top', width: 330, textAlign: 'left', padding: 15, boxSizing: 'border-box', height: 1080 }}>
// ADD USER DISTRIBUTION SOMETHING LIKE THIS
// https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_user_rating
Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit cumque aliquam doloribus in reiciendis? Laborum, ea assumenda, tempora dolore placeat aspernatur, cumque totam sequi debitis dolor nam eligendi suscipit aliquid?
</div>
} */}
</div>
<div style={{ clear: 'both' }} />
</section>
<section> <div className={'mb-5'}>
<div className={'text-center text-md pt-5 pb-5'}> CONTRUBITION
Added on: 28 May 1988 <DefaultSeparator />
anoeantoeh aoenthaoe aoenth aot
</div>
</div> </div>
</section> </div>
<Lightbox <div style={{ clear: 'both' }} />
open={lightboxOpen} </section>
close={() => setLightboxOpen(false)}
slides={locationImages?.images} <section>
/> <div className={'text-center text-md pt-5 pb-5'}>
</div> Added on: 28 May 1988
</> </div>
</section>
<Lightbox
open={lightboxOpen}
close={() => setLightboxOpen(false)}
slides={locationImages?.images}
/>
</div>
) )
} }

0
src/pages/LocationDetail/types.ts Normal file → Executable file
View File

0
src/pages/Login/index.css Normal file → Executable file
View File

0
src/pages/Login/index.tsx Normal file → Executable file
View File

0
src/pages/NewsEvent/index.tsx Normal file → Executable file
View File

0
src/pages/NewsEvent/style.css Normal file → Executable file
View File

0
src/pages/NotFound/index.tsx Normal file → Executable file
View File

2
src/pages/Stories/index.tsx Normal file → Executable file
View File

@ -1,8 +1,6 @@
function Story() { function Story() {
return( return(
<>
<h1>Best PLaces</h1> <h1>Best PLaces</h1>
</>
) )
} }

0
src/pages/Submissions/index.tsx Normal file → Executable file
View File

0
src/pages/UserFeed/index.tsx Normal file → Executable file
View File

4
src/pages/UserProfile/index.tsx Normal file → Executable file
View File

@ -188,9 +188,7 @@ function UserProfile() {
))} ))}
</> </>
: :
<> <span className={'text-sm italic'}>No users review to display</span>
<span className={'text-sm italic'}>No users review to display</span>
</>
} }
</div> </div>
</div> </div>

0
src/pages/UserProfile/style.css Normal file → Executable file
View File

0
src/pages/UserSettings/index.tsx Normal file → Executable file
View File

0
src/pages/UserSettings/styles.css Normal file → Executable file
View File

0
src/pages/index.tsx Normal file → Executable file
View File

0
src/reducers/index.ts Normal file → Executable file
View File

0
src/routes/ProtectedRoute.tsx Normal file → Executable file
View File

0
src/routes/index.tsx Normal file → Executable file
View File

0
src/services/auth.ts Normal file → Executable file
View File

0
src/services/config.ts Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More