dev
This commit is contained in:
parent
656a6e08da
commit
5183a90b59
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
index.html
Normal file → Executable file
0
index.html
Normal file → Executable file
2
package.json
Normal file → Executable file
2
package.json
Normal file → Executable file
@ -9,8 +9,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.9",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@types/react-redux": "^7.1.26",
|
||||
"axios": "^1.5.0",
|
||||
"emojibase": "^15.0.0",
|
||||
"interweave": "^13.1.0",
|
||||
|
50
pnpm-lock.yaml
Normal file → Executable file
50
pnpm-lock.yaml
Normal file → Executable file
@ -5,12 +5,12 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@floating-ui/react':
|
||||
specifier: ^0.26.9
|
||||
version: 0.26.9(react-dom@18.2.0)(react@18.2.0)
|
||||
'@reduxjs/toolkit':
|
||||
specifier: ^1.9.5
|
||||
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:
|
||||
specifier: ^1.5.0
|
||||
version: 1.6.7
|
||||
@ -623,6 +623,37 @@ packages:
|
||||
'@floating-ui/utils': 0.2.1
|
||||
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:
|
||||
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
|
||||
dev: false
|
||||
@ -800,15 +831,6 @@ packages:
|
||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||
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:
|
||||
resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
|
||||
dependencies:
|
||||
@ -2065,6 +2087,10 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
/tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
dev: false
|
||||
|
||||
/tailwindcss@3.4.1:
|
||||
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
0
postcss.config.js
Normal file → Executable file
0
postcss.config.js
Normal file → Executable file
0
public/vite.svg
Normal file → Executable file
0
public/vite.svg
Normal file → Executable file
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
src/actions/LogoutAction.ts
Normal file → Executable file
0
src/actions/LogoutAction.ts
Normal file → Executable file
0
src/actions/index.ts
Normal file → Executable file
0
src/actions/index.ts
Normal file → Executable file
4
src/app.css
Normal file → Executable file
4
src/app.css
Normal file → Executable file
@ -60,6 +60,10 @@
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.input-checkbox:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen and(max-width: 425px) {
|
||||
.users-score {
|
||||
font-size: .75rem;
|
||||
|
104
src/app.tsx
Normal file → Executable file
104
src/app.tsx
Normal file → Executable file
@ -14,64 +14,54 @@ import { getRoutes } from './routes';
|
||||
export function App() {
|
||||
const { routes } = getRoutes();
|
||||
return (
|
||||
<>
|
||||
<Provider store={store}>
|
||||
<PersistGate persistor={persistore}>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path='/login' element={<Login />} />
|
||||
<Route element={<DefaultLayout />}>
|
||||
{routes.map(({ path, name, element, protectedRoute }) => {
|
||||
let Element = element as any
|
||||
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>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<Provider store={store}>
|
||||
<PersistGate persistor={persistore}>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path='/login' element={<Login />} />
|
||||
<Route element={<DefaultLayout />}>
|
||||
{routes.map(({ path, name, element, protectedRoute }) => {
|
||||
let Element = element as any
|
||||
if (protectedRoute === "user") {
|
||||
return (
|
||||
<>
|
||||
<Route
|
||||
path={path}
|
||||
id={name}
|
||||
element={element}
|
||||
/>
|
||||
</>
|
||||
<Route
|
||||
path={path}
|
||||
id={name}
|
||||
element={
|
||||
<UserProtectedRoute>
|
||||
<Element />
|
||||
</UserProtectedRoute>
|
||||
}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Router>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
</>
|
||||
}
|
||||
|
||||
if (protectedRoute === "admin") {
|
||||
return (
|
||||
<Route
|
||||
path={path}
|
||||
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
0
src/assets/preact.svg
Normal file → Executable 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
0
src/components/Button/DefaultButton/index.tsx
Normal file → Executable file
0
src/components/Button/DefaultButton/style.css
Normal file → Executable file
0
src/components/Button/DefaultButton/style.css
Normal file → Executable file
14
src/components/Button/FilterButton/index.tsx
Executable file
14
src/components/Button/FilterButton/index.tsx
Executable 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;
|
20
src/components/Button/FilterButton/style.css
Executable file
20
src/components/Button/FilterButton/style.css
Executable 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
0
src/components/Button/WarningButton/index.tsx
Normal file → Executable file
0
src/components/Button/WarningButton/style.css
Normal file → Executable file
0
src/components/Button/WarningButton/style.css
Normal file → Executable file
47
src/components/Card/LocationCard/index.tsx
Executable file
47
src/components/Card/LocationCard/index.tsx
Executable 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
0
src/components/CustomInterweave/index.tsx
Normal file → Executable file
0
src/components/DropdownInput/index.tsx
Normal file → Executable file
0
src/components/DropdownInput/index.tsx
Normal file → Executable file
0
src/components/DropdownInput/style.css
Normal file → Executable file
0
src/components/DropdownInput/style.css
Normal file → Executable file
0
src/components/Footer/index.tsx
Normal file → Executable file
0
src/components/Footer/index.tsx
Normal file → Executable file
0
src/components/Footer/style.css
Normal file → Executable file
0
src/components/Footer/style.css
Normal file → Executable file
34
src/components/Header/index.tsx
Normal file → Executable file
34
src/components/Header/index.tsx
Normal file → Executable file
@ -8,6 +8,7 @@ import './style.css';
|
||||
import { logoutService } from "../../services";
|
||||
import { getSearchLocationService } from "../../services/locations";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ReactSelectData } from "src/types/common";
|
||||
|
||||
|
||||
function Header() {
|
||||
@ -40,7 +41,8 @@ function Header() {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
const onSelectedSearchOption = (val: any) => {
|
||||
const onSelectedSearchOption = (v: ReactSelectData | unknown) => {
|
||||
const val = v as ReactSelectData
|
||||
navigate(`/location/${val.value}`)
|
||||
}
|
||||
|
||||
@ -88,19 +90,19 @@ function Header() {
|
||||
/>
|
||||
</a>
|
||||
{user.username &&
|
||||
<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={'#'}><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'}>Settings</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>
|
||||
}
|
||||
</div>
|
||||
<form onSubmit={onSearchSubmit} className={`header-search-input ${dropdown ? "search-input-dropdown" : ""}`}>
|
||||
<AsyncSelect
|
||||
<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={'#'}><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'}>Settings</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>
|
||||
}
|
||||
</div>
|
||||
<form onSubmit={onSearchSubmit} className={`header-search-input ${dropdown ? "search-input-dropdown" : ""}`}>
|
||||
<AsyncSelect
|
||||
onInputChange={onInput}
|
||||
inputValue={searchVal}
|
||||
isSearchable
|
||||
@ -139,7 +141,7 @@ function Header() {
|
||||
textAlign: 'left',
|
||||
minHeight: 45
|
||||
}),
|
||||
option: (base, {isFocused}) => ({
|
||||
option: (base, { isFocused }) => ({
|
||||
...base,
|
||||
backgroundColor: isFocused ? '#202225' : 'none',
|
||||
}),
|
||||
@ -149,7 +151,7 @@ function Header() {
|
||||
// color: "white"
|
||||
// })
|
||||
}}
|
||||
onChange={onSelectedSearchOption}
|
||||
onChange={(v: ReactSelectData | unknown, _) => onSelectedSearchOption(v)}
|
||||
/>
|
||||
</form>
|
||||
<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
0
src/components/Header/style.css
Normal file → Executable file
24
src/components/Input/CheckboxInput/index.tsx
Executable file
24
src/components/Input/CheckboxInput/index.tsx
Executable 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
0
src/components/Loading/Spinner/index.tsx
Normal file → Executable file
0
src/components/Loading/Spinner/style.css
Normal file → Executable file
0
src/components/Loading/Spinner/style.css
Normal file → Executable file
0
src/components/RefOutsideClick/index.tsx
Normal file → Executable file
0
src/components/RefOutsideClick/index.tsx
Normal file → Executable file
0
src/components/Separator/Default/index.tsx
Normal file → Executable file
0
src/components/Separator/Default/index.tsx
Normal file → Executable file
0
src/components/Separator/NavigationSeparator/index.tsx
Normal file → Executable file
0
src/components/Separator/NavigationSeparator/index.tsx
Normal file → Executable file
0
src/components/Separator/NavigationSeparator/style.css
Normal file → Executable file
0
src/components/Separator/NavigationSeparator/style.css
Normal file → Executable file
0
src/components/Separator/TitleSeparator/index.tsx
Normal file → Executable file
0
src/components/Separator/TitleSeparator/index.tsx
Normal file → Executable file
0
src/components/Separator/WithAnchor/index.tsx
Normal file → Executable file
0
src/components/Separator/WithAnchor/index.tsx
Normal file → Executable file
0
src/components/Separator/WithAnchor/style.css
Normal file → Executable file
0
src/components/Separator/WithAnchor/style.css
Normal file → Executable file
12
src/components/index.ts
Normal file → Executable file
12
src/components/index.ts
Normal file → Executable file
@ -15,6 +15,12 @@ import DropdownInput from "./DropdownInput";
|
||||
|
||||
import SpinnerLoading from "./Loading/Spinner";
|
||||
|
||||
import FilterButton from "./Button/FilterButton";
|
||||
|
||||
import LocationCard from "./Card/LocationCard";
|
||||
|
||||
import CheckboxInput from "./Input/CheckboxInput";
|
||||
|
||||
export {
|
||||
Header,
|
||||
WarningButton,
|
||||
@ -30,5 +36,11 @@ export {
|
||||
CustomInterweave,
|
||||
DropdownInput,
|
||||
|
||||
CheckboxInput,
|
||||
|
||||
SpinnerLoading,
|
||||
|
||||
LocationCard,
|
||||
|
||||
FilterButton,
|
||||
}
|
0
src/constants/actions.ts
Normal file → Executable file
0
src/constants/actions.ts
Normal file → Executable file
0
src/constants/api.ts
Normal file → Executable file
0
src/constants/api.ts
Normal file → Executable file
0
src/constants/default.ts
Normal file → Executable file
0
src/constants/default.ts
Normal file → Executable file
0
src/datas/critics_users_best_pick.json
Normal file → Executable file
0
src/datas/critics_users_best_pick.json
Normal file → Executable file
0
src/datas/home.json
Normal file → Executable file
0
src/datas/home.json
Normal file → Executable file
0
src/datas/popular.json
Normal file → Executable file
0
src/datas/popular.json
Normal file → Executable file
0
src/datas/popular_user_reviews.json
Normal file → Executable file
0
src/datas/popular_user_reviews.json
Normal file → Executable file
0
src/datas/recent_news_event.json
Normal file → Executable file
0
src/datas/recent_news_event.json
Normal file → Executable file
0
src/domains/LocationInfo.ts
Normal file → Executable file
0
src/domains/LocationInfo.ts
Normal file → Executable file
0
src/domains/NewsEvent.ts
Normal file → Executable file
0
src/domains/NewsEvent.ts
Normal file → Executable file
0
src/domains/Province.ts
Normal file → Executable file
0
src/domains/Province.ts
Normal file → Executable file
0
src/domains/Regency.ts
Normal file → Executable file
0
src/domains/Regency.ts
Normal file → Executable file
0
src/domains/Region.ts
Normal file → Executable file
0
src/domains/Region.ts
Normal file → Executable file
0
src/domains/User.ts
Normal file → Executable file
0
src/domains/User.ts
Normal file → Executable file
0
src/domains/index.ts
Normal file → Executable file
0
src/domains/index.ts
Normal file → Executable file
0
src/features/auth/authSlice/authSlice.ts
Normal file → Executable file
0
src/features/auth/authSlice/authSlice.ts
Normal file → Executable file
0
src/features/index.ts
Normal file → Executable file
0
src/features/index.ts
Normal file → Executable file
0
src/fonts/Lato-Black.ttf
Normal file → Executable file
0
src/fonts/Lato-Black.ttf
Normal file → Executable file
0
src/fonts/Lato-BlackItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-BlackItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-Bold.ttf
Normal file → Executable file
0
src/fonts/Lato-Bold.ttf
Normal file → Executable file
0
src/fonts/Lato-BoldItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-BoldItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-Italic.ttf
Normal file → Executable file
0
src/fonts/Lato-Italic.ttf
Normal file → Executable file
0
src/fonts/Lato-Light.ttf
Normal file → Executable file
0
src/fonts/Lato-Light.ttf
Normal file → Executable file
0
src/fonts/Lato-LightItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-LightItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-Regular.ttf
Normal file → Executable file
0
src/fonts/Lato-Regular.ttf
Normal file → Executable file
0
src/fonts/Lato-Thin.ttf
Normal file → Executable file
0
src/fonts/Lato-Thin.ttf
Normal file → Executable file
0
src/fonts/Lato-ThinItalic.ttf
Normal file → Executable file
0
src/fonts/Lato-ThinItalic.ttf
Normal file → Executable file
0
src/index.css
Normal file → Executable file
0
src/index.css
Normal file → Executable file
0
src/lato.css
Normal file → Executable file
0
src/lato.css
Normal file → Executable file
0
src/layouts/Default/Default.tsx
Normal file → Executable file
0
src/layouts/Default/Default.tsx
Normal file → Executable file
0
src/layouts/index.ts
Normal file → Executable file
0
src/layouts/index.ts
Normal file → Executable file
0
src/main.tsx
Normal file → Executable file
0
src/main.tsx
Normal file → Executable file
0
src/pages/AddLocation/index.tsx
Normal file → Executable file
0
src/pages/AddLocation/index.tsx
Normal file → Executable file
0
src/pages/AddLocation/style.css
Normal file → Executable file
0
src/pages/AddLocation/style.css
Normal file → Executable file
0
src/pages/AddLocation/types.ts
Normal file → Executable file
0
src/pages/AddLocation/types.ts
Normal file → Executable file
209
src/pages/BestLocations/index.tsx
Normal file → Executable file
209
src/pages/BestLocations/index.tsx
Normal file → Executable file
@ -2,8 +2,8 @@ import { TargetedEvent } from "preact/compat";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { getListTopLocationsService } from "../../services";
|
||||
import { DefaultSeparator } from "../../components";
|
||||
import { NullValueRes } from "../../types/common";
|
||||
import './style.css';
|
||||
import { useClick, useFloating, useInteractions } from "@floating-ui/react";
|
||||
|
||||
interface TopLocation {
|
||||
row_number: Number,
|
||||
@ -33,7 +33,7 @@ const REGIONS = [
|
||||
'Sulawesi',
|
||||
];
|
||||
|
||||
const REVIEWERS_TYPE =[
|
||||
const REVIEWERS_TYPE = [
|
||||
'All',
|
||||
'Critics',
|
||||
'Users'
|
||||
@ -47,7 +47,7 @@ const MIN_REVIEWS = [
|
||||
]
|
||||
|
||||
function BestLocation() {
|
||||
const [page, setPage] = useState<number>(1);
|
||||
const [page, _setPage] = useState<number>(1);
|
||||
const [topLocations, setTopLocations] = useState<Array<TopLocation>>([])
|
||||
const [pageState, setPageState] = useState({
|
||||
filterScoreType: 'all',
|
||||
@ -58,10 +58,10 @@ function BestLocation() {
|
||||
|
||||
async function getTopLocations() {
|
||||
try {
|
||||
const res = await getListTopLocationsService({
|
||||
page: page, page_size: 20,
|
||||
order_by: pageState.filterScoreTypeidx,
|
||||
region_type: pageState.filterRegionType
|
||||
const res = await getListTopLocationsService({
|
||||
page: page, page_size: 20,
|
||||
order_by: pageState.filterScoreTypeidx,
|
||||
region_type: pageState.filterRegionType
|
||||
})
|
||||
setTopLocations(res.data)
|
||||
|
||||
@ -72,133 +72,130 @@ function BestLocation() {
|
||||
|
||||
function onChangeReviewType(e: TargetedEvent<HTMLAnchorElement>, reviewer_type: string, i: number) {
|
||||
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) {
|
||||
e.preventDefault();
|
||||
setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type})
|
||||
setPageState({ ...pageState, filterRegionTypeName: region_name, filterRegionType: type })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTopLocations()
|
||||
}, [pageState])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'content main-content mt-3'}>
|
||||
<section name={"Top locations header"}>
|
||||
<h1 className={'text-3xl mb-5 font-bold'}>Top Locations</h1>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Regions</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>{pageState.filterRegionTypeName}</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content text-sm'}>
|
||||
{REGIONS.map((x, index) => (
|
||||
<a onClick={(e) => onChangeRegionType(e, x, index) } className={'block pt-1'}>{x}</a>
|
||||
))}
|
||||
</div>
|
||||
<div className={'content main-content mt-3'}>
|
||||
<section name={"Top locations header"}>
|
||||
<h1 className={'text-3xl mb-5 font-bold'}>Top Locations</h1>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Regions</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>{pageState.filterRegionTypeName}</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content text-sm'}>
|
||||
{REGIONS.map((x, index) => (
|
||||
<a onClick={(e) => onChangeRegionType(e, x, index)} className={'block pt-1'}>{x}</a>
|
||||
))}
|
||||
</div>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Min. Reviews</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>All</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content-min-reviews text-sm'}>
|
||||
{MIN_REVIEWS.map(x => (
|
||||
<a className={'block pt-1'}>{x}</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Min. Reviews</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>All</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content-min-reviews text-sm'}>
|
||||
{MIN_REVIEWS.map(x => (
|
||||
<a className={'block pt-1'}>{x}</a>
|
||||
))}
|
||||
</div>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Tags</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>All</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content text-sm'}>
|
||||
{/* {REGIONS.map(x => (
|
||||
</div>
|
||||
<div className={'regions-dropdown text-xs pr-3 inline-block'}>
|
||||
<p className={'inline-block'}>Tags</p>
|
||||
<a style={{ cursor: 'pointer' }}>
|
||||
<p className={'ml-2 inline-block'}>All</p>
|
||||
<svg style={{ display: 'inline-block' }} fill='white' xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z" /></svg>
|
||||
</a>
|
||||
<div className={'dropdown-content text-sm'}>
|
||||
{/* {REGIONS.map(x => (
|
||||
<a className={'block pt-1'}>{x}</a>
|
||||
))} */}
|
||||
</div>
|
||||
</div>
|
||||
<DefaultSeparator />
|
||||
</section>
|
||||
</div>
|
||||
<DefaultSeparator />
|
||||
</section>
|
||||
|
||||
<section name={'Main content'}>
|
||||
<div className={'flex flex-row pt-10 pb-10'}>
|
||||
<div className={'mr-5'} style={{ flex: 1}}>
|
||||
{topLocations.map(x => (
|
||||
<>
|
||||
{/* UNCOMMENT....IF THERES A PURPOSE FOR RIGHT THREE DOTS */}
|
||||
{/* <div style={{ float: 'right', cursor: 'pointer' }}>
|
||||
<section name={'Main content'}>
|
||||
<div className={'flex flex-row pt-10 pb-10'}>
|
||||
<div className={'mr-5'} style={{ flex: 1 }}>
|
||||
{topLocations.map(x => (
|
||||
<>
|
||||
{/* UNCOMMENT....IF THERES A PURPOSE FOR RIGHT THREE DOTS */}
|
||||
{/* <div style={{ float: 'right', cursor: 'pointer' }}>
|
||||
<a className={'text-xl'}>
|
||||
...
|
||||
</a>
|
||||
</div> */}
|
||||
<div className={'mb-2 best-locations-title'}>
|
||||
<a className={'text-xl'} href={`/location/${x.id}`}>{x.row_number}.{x.name}</a>
|
||||
</div>
|
||||
<div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}>
|
||||
<a href={`/location/${x.id}`} >
|
||||
<img src={x.thumbnail ? x.thumbnail : ""} loading={'lazy'} style={{ width: '100%', objectFit: 'cover', height: '100%', aspectRatio: '1/1' }} />
|
||||
</a>
|
||||
</div>
|
||||
<div className={'text-md font-bold'}>{x.regency_name}</div>
|
||||
<div className={'text-xs mb-2'}>{x.address}</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>
|
||||
<div className={'mt-4'}>
|
||||
<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={"flex flex-row items-center p-2"}>
|
||||
<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>
|
||||
<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>
|
||||
<div className={'mb-2 best-locations-title'}>
|
||||
<a className={'text-xl'} href={`/location/${x.id}`}>{x.row_number}.{x.name}</a>
|
||||
</div>
|
||||
<div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}>
|
||||
<a href={`/location/${x.id}`} >
|
||||
<img src={x.thumbnail ? x.thumbnail : ""} loading={'lazy'} style={{ width: '100%', objectFit: 'cover', height: '100%', aspectRatio: '1/1' }} />
|
||||
</a>
|
||||
</div>
|
||||
<div className={'text-md font-bold'}>{x.regency_name}</div>
|
||||
<div className={'text-xs mb-2'}>{x.address}</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>
|
||||
<div className={'mt-4'}>
|
||||
<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={"flex flex-row items-center p-2"}>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<p className={'text-xs users-score'}>{x.critic_count} reviews</p>
|
||||
</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 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>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
0
src/pages/BestLocations/style.css
Normal file → Executable file
0
src/pages/BestLocations/style.css
Normal file → Executable file
304
src/pages/Discovery/index.tsx
Normal file → Executable file
304
src/pages/Discovery/index.tsx
Normal file → Executable 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() {
|
||||
return(
|
||||
<>
|
||||
<h1>Discovery</h1>
|
||||
</>
|
||||
|
||||
interface DiscoveryState {
|
||||
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
112
src/pages/Discovery/style.css
Executable 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
341
src/pages/Home/index.tsx
Normal file → Executable file
@ -1,4 +1,4 @@
|
||||
import { SeparatorWithAnchor } from '../../components';
|
||||
import { LocationCard, SeparatorWithAnchor } from '../../components';
|
||||
import news from '../../datas/recent_news_event.json';
|
||||
import popular from '../../datas/popular.json';
|
||||
import popular_user_review from '../../datas/popular_user_reviews.json';
|
||||
@ -23,32 +23,32 @@ function Home() {
|
||||
// const [isLoading, setIsLoading] = useState<boolean>(true)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
|
||||
async function getRecentLocations() {
|
||||
try {
|
||||
const locations = await getListRecentLocationsRatingsService(12)
|
||||
setRecentLocations(locations.data)
|
||||
// setIsLoading(false)
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function getCrititsBestLocations() {
|
||||
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)
|
||||
}catch(err) {
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function getUsersBestLocations() {
|
||||
try {
|
||||
const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 3, region_type: 0})
|
||||
try {
|
||||
const res = await getListTopLocationsService({ page: 1, page_size: 6, order_by: 3, region_type: 0 })
|
||||
setTopUsersLocations(res.data)
|
||||
}catch(err) {
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
@ -61,181 +61,184 @@ function Home() {
|
||||
getRecentLocations()
|
||||
getCrititsBestLocations()
|
||||
getUsersBestLocations()
|
||||
},[])
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<div className="content main-content mt-3">
|
||||
<div className="content main-content mt-3">
|
||||
|
||||
{/* RECENTLY ADDED SECTION */}
|
||||
<section about={"Recently added places"} className={'mt-3'}>
|
||||
<SeparatorWithAnchor pageLink='#' pageName='recently added' secondLink='#' />
|
||||
{recentLocations.map((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%' }} />
|
||||
{/* RECENTLY ADDED SECTION */}
|
||||
<section about={"Recently added places"} className={'mt-3'}>
|
||||
<SeparatorWithAnchor pageLink='#' pageName='recently added' secondLink='#' />
|
||||
{recentLocations.map((x) => (
|
||||
<LocationCard
|
||||
containerClass='recently-added-section-card'
|
||||
onCardClick={onNavigateToDetail}
|
||||
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>
|
||||
</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>
|
||||
<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.rating}%`, backgroundColor: 'green' }} />
|
||||
<div style={{ height: 4, width: `${x.critic_score}%`, 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 style={{ clear: 'both' }} />
|
||||
</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 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 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>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
0
src/pages/Home/style.css
Normal file → Executable file
0
src/pages/Home/style.css
Normal file → Executable file
0
src/pages/LocationDetail/index.css
Normal file → Executable file
0
src/pages/LocationDetail/index.css
Normal file → Executable file
784
src/pages/LocationDetail/index.tsx
Normal file → Executable file
784
src/pages/LocationDetail/index.tsx
Normal file → Executable file
@ -3,10 +3,10 @@ import { ChangeEvent, TargetedEvent } from 'preact/compat';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import Lightbox from 'yet-another-react-lightbox';
|
||||
import useCallbackState from '../../types/state-callback';
|
||||
import {
|
||||
EmptyLocationDetailResponse,
|
||||
LocationDetailResponse,
|
||||
LocationResponse,
|
||||
import {
|
||||
EmptyLocationDetailResponse,
|
||||
LocationDetailResponse,
|
||||
LocationResponse,
|
||||
emptyLocationResponse,
|
||||
CurrentUserLocationReviews,
|
||||
} from './types';
|
||||
@ -33,7 +33,7 @@ function LocationDetail() {
|
||||
const [locationImages, setLocationImages] = useState<LocationResponse>(emptyLocationResponse())
|
||||
const [currentUserReview, setCurrentUserReview] = useState<CurrentUserLocationReviews>()
|
||||
const [lightboxOpen, setLightboxOpen] = useState<boolean>(false)
|
||||
const[updatePage, setUpdatePage] = useState<boolean>(true)
|
||||
const [updatePage, setUpdatePage] = useState<boolean>(true)
|
||||
const [pageState, setPageState] = useState({
|
||||
critic_filter_name: 'highest rated',
|
||||
critic_filter_type: 0,
|
||||
@ -61,7 +61,7 @@ function LocationDetail() {
|
||||
try {
|
||||
const res = await getLocationService(Number(id))
|
||||
setLocationDetail(res.data, (val) => {
|
||||
if(val.detail.thumbnail) {
|
||||
if (val.detail.thumbnail) {
|
||||
getImage(val.detail.thumbnail)
|
||||
}
|
||||
})
|
||||
@ -87,17 +87,17 @@ function LocationDetail() {
|
||||
}
|
||||
|
||||
async function getCurrentUserLocationReview(): Promise<void> {
|
||||
try {
|
||||
const res = await getCurrentUserLocationReviewService(Number(id))
|
||||
setCurrentUserReview(res.data)
|
||||
setPageState({ ...pageState, enable_post: false})
|
||||
} catch (error) {
|
||||
let err = error as IHttpResponse;
|
||||
if(err.status == 404 || err.status == 401 ) {
|
||||
return
|
||||
try {
|
||||
const res = await getCurrentUserLocationReviewService(Number(id))
|
||||
setCurrentUserReview(res.data)
|
||||
setPageState({ ...pageState, enable_post: false })
|
||||
} catch (error) {
|
||||
let err = error as IHttpResponse;
|
||||
if (err.status == 404 || err.status == 401) {
|
||||
return
|
||||
}
|
||||
alert(err.error.response.data.message)
|
||||
}
|
||||
alert(err.error.response.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
function handleTextAreaChange(e: ChangeEvent<HTMLTextAreaElement>): void {
|
||||
@ -176,7 +176,7 @@ function LocationDetail() {
|
||||
console.log(err)
|
||||
const str = handleAxiosError(err)
|
||||
alert(str)
|
||||
setPageState({ ...pageState, on_submit_loading: false })
|
||||
setPageState({ ...pageState, on_submit_loading: false })
|
||||
}
|
||||
|
||||
}
|
||||
@ -191,476 +191,372 @@ function LocationDetail() {
|
||||
useEffect(() => {
|
||||
getCurrentUserLocationReview()
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(updatePage) {
|
||||
if (updatePage || id) {
|
||||
getLocationDetail()
|
||||
}
|
||||
}, [updatePage, id])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'content main-content mt-3'}>
|
||||
<section name={"HEADER LINK"}>
|
||||
<div className={'header-link text-tertiary'}>
|
||||
<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' }}>CRITIC REVIEWS</a>
|
||||
<a className={'ml-4'} style={{ display: 'inline-block' }}>COMMENTS</a>
|
||||
</div>
|
||||
</section>
|
||||
<div className={'content main-content mt-3'}>
|
||||
<section name={"HEADER LINK"}>
|
||||
<div className={'header-link text-tertiary'}>
|
||||
<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' }}>CRITIC REVIEWS</a>
|
||||
<a className={'ml-4'} style={{ display: 'inline-block' }}>COMMENTS</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section name={'LOCATION HEADER'}>
|
||||
<div className={'pb-5'} style={{ borderBottom: '1px solid #38444d' }}>
|
||||
<div className={'font-bold mt-5 text-2xl'}>
|
||||
<h1>{locationDetail?.detail.name}</h1>
|
||||
<section name={'LOCATION HEADER'}>
|
||||
<div className={'pb-5'} style={{ borderBottom: '1px solid #38444d' }}>
|
||||
<div className={'font-bold mt-5 text-2xl'}>
|
||||
<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>
|
||||
{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 }}>
|
||||
<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 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>
|
||||
:
|
||||
<>
|
||||
<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>
|
||||
}
|
||||
{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 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 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' }} />
|
||||
{pageState.on_submit_loading ?
|
||||
<SpinnerLoading />
|
||||
:
|
||||
<span className={'text-xxs p-1 text-area-button'} style={pageState.enable_post ? '' : { display: 'none' }}>
|
||||
<a href={'#'} onClick={handleSubmitReview}>
|
||||
POST
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
<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 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>
|
||||
{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>
|
||||
<div style={{ clear: 'both' }} />
|
||||
|
||||
<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 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>
|
||||
{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>
|
||||
:
|
||||
<>
|
||||
<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 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'}
|
||||
/>
|
||||
<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 ?
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ display: 'inline-block', verticalAlign: 'top' }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
|
||||
<a>
|
||||
<span>{x.username}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 15, lineHeight: '24px', margin: '5px 75px 1px' }}>
|
||||
<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>
|
||||
{pageState.on_submit_loading ?
|
||||
<SpinnerLoading />
|
||||
:
|
||||
<span className={'text-xxs p-1 text-area-button'} style={pageState.enable_post ? '' : { display: 'none'}}>
|
||||
<a href={'#'} onClick={handleSubmitReview}>
|
||||
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'}
|
||||
/>
|
||||
<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={{ display: 'inline-block', verticalAlign: 'top' }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
|
||||
<a>
|
||||
<span>{x.username}</span>
|
||||
</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 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>
|
||||
))}
|
||||
</>
|
||||
|
||||
:
|
||||
<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>
|
||||
</>
|
||||
|
||||
}
|
||||
</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 ?
|
||||
<>
|
||||
{locationDetail.users_review.map(x => (
|
||||
<div style={{ padding: '15px 0' }}>
|
||||
<div className={'mr-5'} style={{ width: 45, float: 'left' }}>
|
||||
<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'}
|
||||
/>
|
||||
{locationDetail.users_review.map(x => (
|
||||
<div style={{ padding: '15px 0' }}>
|
||||
<div className={'mr-5'} style={{ width: 45, float: 'left' }}>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
|
||||
<a>
|
||||
<span>{x.username}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontWeight: 700, fontSize: 16, lineHeight: 'initial' }}>
|
||||
<a>
|
||||
<span>{x.username}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'inline-block'}>
|
||||
<div className={'text-sm text-center'} >{x.score}</div>
|
||||
<div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}>
|
||||
<div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}>
|
||||
<CustomInterweave
|
||||
content={x.comments}
|
||||
/>
|
||||
</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 className={'inline-block'}>
|
||||
<div className={'text-sm text-center'} >{x.score}</div>
|
||||
<div style={{ height: 4, width: 25, position: 'relative', backgroundColor: '#d8d8d8' }}>
|
||||
<div style={{ height: 4, backgroundColor: '#85ce73', width: `${x.score}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 15, lineHeight: '24px', margin: '10px 65px 1px', wordWrap: 'break-word' }}>
|
||||
<CustomInterweave
|
||||
content={x.comments}
|
||||
/>
|
||||
</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 className={'review-more-button text-center text-sm mt-5'}>
|
||||
<a style={{ borderRadius: 15, padding: '8px 32px', border: '1px solid #d8d8d8' }}>
|
||||
More
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<span className={'text-sm italic'}>No users review to display</span>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
{/* <div name={'USERS REVIEW'} style={{ margin: '50px 0', textAlign: 'left' }}>
|
||||
<SeparatorWithAnchor pageName={"Popular User's review"} pageLink='#' secondLink='#' />
|
||||
<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'}
|
||||
/>
|
||||
<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 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={''} 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>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<span className={'text-sm italic'}>No users review to display</span>
|
||||
</>
|
||||
}
|
||||
</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={'text-center text-md pt-5 pb-5'}>
|
||||
Added on: 28 May 1988
|
||||
<div className={'mb-5'}>
|
||||
CONTRUBITION
|
||||
<DefaultSeparator />
|
||||
anoeantoeh aoenthaoe aoenth aot
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Lightbox
|
||||
open={lightboxOpen}
|
||||
close={() => setLightboxOpen(false)}
|
||||
slides={locationImages?.images}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
<div style={{ clear: 'both' }} />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className={'text-center text-md pt-5 pb-5'}>
|
||||
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
0
src/pages/LocationDetail/types.ts
Normal file → Executable file
0
src/pages/Login/index.css
Normal file → Executable file
0
src/pages/Login/index.css
Normal file → Executable file
0
src/pages/Login/index.tsx
Normal file → Executable file
0
src/pages/Login/index.tsx
Normal file → Executable file
0
src/pages/NewsEvent/index.tsx
Normal file → Executable file
0
src/pages/NewsEvent/index.tsx
Normal file → Executable file
0
src/pages/NewsEvent/style.css
Normal file → Executable file
0
src/pages/NewsEvent/style.css
Normal file → Executable file
0
src/pages/NotFound/index.tsx
Normal file → Executable file
0
src/pages/NotFound/index.tsx
Normal file → Executable file
2
src/pages/Stories/index.tsx
Normal file → Executable file
2
src/pages/Stories/index.tsx
Normal file → Executable file
@ -1,8 +1,6 @@
|
||||
function Story() {
|
||||
return(
|
||||
<>
|
||||
<h1>Best PLaces</h1>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
|
0
src/pages/Submissions/index.tsx
Normal file → Executable file
0
src/pages/Submissions/index.tsx
Normal file → Executable file
0
src/pages/UserFeed/index.tsx
Normal file → Executable file
0
src/pages/UserFeed/index.tsx
Normal file → Executable file
4
src/pages/UserProfile/index.tsx
Normal file → Executable file
4
src/pages/UserProfile/index.tsx
Normal file → Executable 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>
|
||||
|
0
src/pages/UserProfile/style.css
Normal file → Executable file
0
src/pages/UserProfile/style.css
Normal file → Executable file
0
src/pages/UserSettings/index.tsx
Normal file → Executable file
0
src/pages/UserSettings/index.tsx
Normal file → Executable file
0
src/pages/UserSettings/styles.css
Normal file → Executable file
0
src/pages/UserSettings/styles.css
Normal file → Executable file
0
src/pages/index.tsx
Normal file → Executable file
0
src/pages/index.tsx
Normal file → Executable file
0
src/reducers/index.ts
Normal file → Executable file
0
src/reducers/index.ts
Normal file → Executable file
0
src/routes/ProtectedRoute.tsx
Normal file → Executable file
0
src/routes/ProtectedRoute.tsx
Normal file → Executable file
0
src/routes/index.tsx
Normal file → Executable file
0
src/routes/index.tsx
Normal file → Executable file
0
src/services/auth.ts
Normal file → Executable file
0
src/services/auth.ts
Normal file → Executable file
0
src/services/config.ts
Normal file → Executable file
0
src/services/config.ts
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user