This commit is contained in:
nochill 2025-04-11 06:46:12 +07:00
parent f1fd9bcbb9
commit 8a5281a0f2
17 changed files with 356 additions and 116 deletions

View File

@ -12,6 +12,7 @@
"@floating-ui/react": "^0.26.9",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.5.0",
"class-variance-authority": "^0.7.0",
"emojibase": "^15.0.0",
"interweave": "^13.1.0",
"interweave-autolink": "^5.1.0",
@ -27,6 +28,7 @@
},
"devDependencies": {
"@preact/preset-vite": "^2.5.0",
"@types/react-redux": "^7.1.34",
"autoprefixer": "^10.4.15",
"postcss": "^8.4.28",
"tailwindcss": "^3.3.3",

View File

@ -10,13 +10,16 @@ importers:
dependencies:
'@floating-ui/react':
specifier: ^0.26.9
version: 0.26.9(react-dom@18.2.0)(react@18.2.0)
version: 0.26.9(react-dom@18.2.0(react@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)
version: 1.9.7(react-redux@8.1.3(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1))(react@18.2.0)
axios:
specifier: ^1.5.0
version: 1.6.7
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
emojibase:
specifier: ^15.0.0
version: 15.3.0
@ -25,25 +28,25 @@ importers:
version: 13.1.0(react@18.2.0)
interweave-autolink:
specifier: ^5.1.0
version: 5.1.1(interweave@13.1.0)(react@18.2.0)
version: 5.1.1(interweave@13.1.0(react@18.2.0))(react@18.2.0)
interweave-emoji:
specifier: ^7.0.0
version: 7.0.0(interweave@13.1.0)(react@18.2.0)
version: 7.0.0(interweave@13.1.0(react@18.2.0))(react@18.2.0)
preact:
specifier: ^10.16.0
version: 10.19.3
react-redux:
specifier: ^8.1.2
version: 8.1.3(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1)
version: 8.1.3(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1)
react-router-dom:
specifier: ^6.16.0
version: 6.21.3(react-dom@18.2.0)(react@18.2.0)
version: 6.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react-select:
specifier: ^5.8.0
version: 5.8.0(react-dom@18.2.0)(react@18.2.0)
version: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react-textarea-autosize:
specifier: ^8.5.3
version: 8.5.3(react@18.2.0)
version: 8.5.3(@types/react@18.2.48)(react@18.2.0)
redux-persist:
specifier: ^6.0.0
version: 6.0.0(react@18.2.0)(redux@4.2.1)
@ -52,11 +55,14 @@ importers:
version: 2.4.2(redux@4.2.1)
yet-another-react-lightbox:
specifier: ^3.12.2
version: 3.16.0(react-dom@18.2.0)(react@18.2.0)
version: 3.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
devDependencies:
'@preact/preset-vite':
specifier: ^2.5.0
version: 2.8.1(@babel/core@7.23.9)(preact@10.19.3)(vite@4.5.2)
'@types/react-redux':
specifier: ^7.1.34
version: 7.1.34
autoprefixer:
specifier: ^10.4.15
version: 10.4.17(postcss@8.4.33)
@ -486,6 +492,9 @@ packages:
'@types/prop-types@15.7.11':
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
'@types/react-redux@7.1.34':
resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==}
'@types/react-transition-group@4.4.10':
resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
@ -591,6 +600,13 @@ packages:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
class-variance-authority@0.7.0:
resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==}
clsx@2.0.0:
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
engines: {node: '>=6'}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
@ -1557,7 +1573,7 @@ snapshots:
'@emotion/memoize@0.8.1': {}
'@emotion/react@11.11.3(react@18.2.0)':
'@emotion/react@11.11.3(@types/react@18.2.48)(react@18.2.0)':
dependencies:
'@babel/runtime': 7.23.9
'@emotion/babel-plugin': 11.11.0
@ -1568,6 +1584,8 @@ snapshots:
'@emotion/weak-memoize': 0.3.1
hoist-non-react-statics: 3.3.2
react: 18.2.0
optionalDependencies:
'@types/react': 18.2.48
'@emotion/serialize@1.1.3':
dependencies:
@ -1669,15 +1687,15 @@ snapshots:
'@floating-ui/core': 1.6.0
'@floating-ui/utils': 0.2.1
'@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0)':
'@floating-ui/react-dom@2.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@floating-ui/dom': 1.6.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@floating-ui/react@0.26.9(react-dom@18.2.0)(react@18.2.0)':
'@floating-ui/react@0.26.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0(react@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)
@ -1764,14 +1782,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@reduxjs/toolkit@1.9.7(react-redux@8.1.3)(react@18.2.0)':
'@reduxjs/toolkit@1.9.7(react-redux@8.1.3(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1))(react@18.2.0)':
dependencies:
immer: 9.0.21
react: 18.2.0
react-redux: 8.1.3(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1)
redux: 4.2.1
redux-thunk: 2.4.2(redux@4.2.1)
reselect: 4.1.8
optionalDependencies:
react: 18.2.0
react-redux: 8.1.3(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1)
'@remix-run/router@1.14.2': {}
@ -1789,6 +1808,13 @@ snapshots:
'@types/prop-types@15.7.11': {}
'@types/react-redux@7.1.34':
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
'@types/react-transition-group@4.4.10':
dependencies:
'@types/react': 18.2.48
@ -1901,6 +1927,12 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
class-variance-authority@0.7.0:
dependencies:
clsx: 2.0.0
clsx@2.0.0: {}
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
@ -2116,12 +2148,12 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
interweave-autolink@5.1.1(interweave@13.1.0)(react@18.2.0):
interweave-autolink@5.1.1(interweave@13.1.0(react@18.2.0))(react@18.2.0):
dependencies:
interweave: 13.1.0(react@18.2.0)
react: 18.2.0
interweave-emoji@7.0.0(interweave@13.1.0)(react@18.2.0):
interweave-emoji@7.0.0(interweave@13.1.0(react@18.2.0))(react@18.2.0):
dependencies:
emojibase: 6.1.0
emojibase-regex: 6.0.1
@ -2288,8 +2320,9 @@ snapshots:
postcss-load-config@4.0.2(postcss@8.4.33):
dependencies:
lilconfig: 3.0.0
postcss: 8.4.33
yaml: 2.3.4
optionalDependencies:
postcss: 8.4.33
postcss-nested@6.0.1(postcss@8.4.33):
dependencies:
@ -2331,19 +2364,21 @@ snapshots:
react-is@18.2.0: {}
react-redux@8.1.3(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1):
react-redux@8.1.3(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1):
dependencies:
'@babel/runtime': 7.23.9
'@types/hoist-non-react-statics': 3.3.5
'@types/use-sync-external-store': 0.0.3
hoist-non-react-statics: 3.3.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-is: 18.2.0
redux: 4.2.1
use-sync-external-store: 1.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.2.48
react-dom: 18.2.0(react@18.2.0)
redux: 4.2.1
react-router-dom@6.21.3(react-dom@18.2.0)(react@18.2.0):
react-router-dom@6.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@remix-run/router': 1.14.2
react: 18.2.0
@ -2355,32 +2390,32 @@ snapshots:
'@remix-run/router': 1.14.2
react: 18.2.0
react-select@5.8.0(react-dom@18.2.0)(react@18.2.0):
react-select@5.8.0(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@babel/runtime': 7.23.9
'@emotion/cache': 11.11.0
'@emotion/react': 11.11.3(react@18.2.0)
'@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
'@floating-ui/dom': 1.6.0
'@types/react-transition-group': 4.4.10
memoize-one: 6.0.0
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
use-isomorphic-layout-effect: 1.1.2(react@18.2.0)
react-transition-group: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.48)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
react-textarea-autosize@8.5.3(react@18.2.0):
react-textarea-autosize@8.5.3(@types/react@18.2.48)(react@18.2.0):
dependencies:
'@babel/runtime': 7.23.9
react: 18.2.0
use-composed-ref: 1.3.0(react@18.2.0)
use-latest: 1.2.1(react@18.2.0)
use-latest: 1.2.1(@types/react@18.2.48)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0):
react-transition-group@4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@babel/runtime': 7.23.9
dom-helpers: 5.2.1
@ -2403,8 +2438,9 @@ snapshots:
redux-persist@6.0.0(react@18.2.0)(redux@4.2.1):
dependencies:
react: 18.2.0
redux: 4.2.1
optionalDependencies:
react: 18.2.0
redux-thunk@2.4.2(redux@4.2.1):
dependencies:
@ -2549,14 +2585,18 @@ snapshots:
dependencies:
react: 18.2.0
use-isomorphic-layout-effect@1.1.2(react@18.2.0):
use-isomorphic-layout-effect@1.1.2(@types/react@18.2.48)(react@18.2.0):
dependencies:
react: 18.2.0
optionalDependencies:
'@types/react': 18.2.48
use-latest@1.2.1(react@18.2.0):
use-latest@1.2.1(@types/react@18.2.48)(react@18.2.0):
dependencies:
react: 18.2.0
use-isomorphic-layout-effect: 1.1.2(react@18.2.0)
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.48)(react@18.2.0)
optionalDependencies:
'@types/react': 18.2.48
use-sync-external-store@1.2.0(react@18.2.0):
dependencies:
@ -2594,7 +2634,7 @@ snapshots:
yaml@2.3.4: {}
yet-another-react-lightbox@3.16.0(react-dom@18.2.0)(react@18.2.0):
yet-another-react-lightbox@3.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)

View File

@ -2,7 +2,7 @@ import { JSXInternal } from "node_modules/preact/src/jsx";
import { LocationInfo } from "../../../domains";
interface ComponentProps {
onCardClick: (id: Number) => void,
onCardClick: (id: number) => void,
data: LocationInfo,
containerClass?: string,
containerStyle?: JSXInternal.CSSProperties

View File

@ -1,14 +1,14 @@
import React from "preact/compat";
import { useState } from "preact/hooks";
import { useSelector, useDispatch } from "react-redux";
import { useEffect, useState } from "preact/hooks";
import { UserRootState } from "../../store/type";
import { logout } from '../../actions';
import AsyncSelect from 'react-select/async';
import './style.css';
import { logoutService } from "../../services";
import { getSearchLocationService } from "../../services/locations";
import { useNavigate } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { ReactSelectData } from "src/types/common";
import { useDispatch, useSelector } from "react-redux";
function Header() {
@ -17,16 +17,37 @@ function Header() {
const [pageState, setPageState] = useState({
profileMenu: false
})
const [searchResult, setSearchResult] = useState([])
const dispatch = useDispatch();
const navigate = useNavigate();
const user = useSelector((state: UserRootState) => state.auth)
const onInput = (val: string): void => {
// const val = e.target as HTMLInputElement;
const onInput = async (val: any) => {
setSearchVal(val.toLowerCase())
}
useEffect(() => {
try {
setTimeout(async () => {
const results = await getSearchLocationService({
name: searchVal,
page: 1,
page_size: 7
})
const resultData = results.data.map((x: any) => {
return {
value: x.id,
label: x.name
}
})
setSearchResult(resultData)
}, 900)
} catch (err) {
alert(err)
}
}, [searchVal])
const handleLogout = async (): Promise<void> => {
try {
await logoutService()
@ -59,7 +80,13 @@ function Header() {
label: x.name
}
})
return resultData
const firstObj = {
value: 0,
label: `search: ${inputValue}`
}
const result = [firstObj, ...resultData]
setSearchResult(resultData)
return result
} catch (err) {
alert(err)
}
@ -75,39 +102,41 @@ function Header() {
setDropdown(!dropdown)
}
return (
<header>
<div className="flex flex-row content">
<a href={"/"}>
<Link to={"/"}>
<h1 className={`title ${dropdown ? 'title-dropdown' : ""}`}>Hilingin</h1>
</a>
</Link>
<div className={'user-img self-center mr-5'} style={dropdown ? { display: 'none' } : ''}>
<a href={user.username ? '#' : '/login'} onClick={() => user.username ? setPageState({ ...pageState, profileMenu: !pageState.profileMenu }) : ''}>
<Link to={user.username ? '#' : '/login'} onClick={() => user.username ? setPageState({ ...pageState, profileMenu: !pageState.profileMenu }) : ''}>
<img
loading={'lazy'}
style={{ width: 40, borderRadius: 15 }}
src={'https://cdn.discordapp.com/attachments/743422487882104837/1153985664849805392/421-4212617_person-placeholder-image-transparent-hd-png-download.png'}
/>
</a>
</Link>
{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>
<Link to={'/user/profile'}><div className={'p-2'}>Profile</div></Link>
<Link to={'#'}><div className={'p-2'}>Feed</div></Link>
<Link to={'/add-location'}><div className={'p-2'}>Add location</div></Link>
<Link to={'/add-location'}><div className={'p-2'}>Settings</div></Link>
<Link to={'#'} onClick={handleLogout}><div className={'p-2'}>Logout</div></Link>
{/* <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" : ""}`}>
<form onSubmit={onSearchSubmit} style={{ marginTop: 'auto', marginBottom: 'auto' }} className={`header-search-input ${dropdown ? "search-input-dropdown" : ""}`}>
<AsyncSelect
onInputChange={onInput}
inputValue={searchVal}
isSearchable
isClearable
menuIsOpen={searchVal.length > 1}
placeholder={"Candi Borobudur, Tunjungan Plaza, ...."}
components={{
DropdownIndicator: () => null,
NoOptionsMessage: () => null,
@ -145,11 +174,6 @@ function Header() {
...base,
backgroundColor: isFocused ? '#202225' : 'none',
}),
// container: (base, props) => ({
// ...base,
// border: 0,
// color: "white"
// })
}}
onChange={(v: ReactSelectData | unknown, _) => onSelectedSearchOption(v)}
/>
@ -162,23 +186,23 @@ function Header() {
{dropdown &&
<a href="/" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Home</a>
}
<a href="/best-places" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Top Places</a>
<a href="/discover" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Discover</a>
<a href="/stories" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Stories</a>
<a href="/news-events" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>News / Events</a>
<a href="#" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Forum</a>
<Link to="/best-places" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Top Places</Link>
<Link to="/discover" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Discover</Link>
<Link to="/stories" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Stories</Link>
<Link to="/news-events" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>News / Events</Link>
<Link to="#" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Forum</Link>
<div className={'profile-container'}>
<a href={user.username ? '#' : '/login'} onClick={() => user.username ? setPageState({ ...pageState, profileMenu: !pageState.profileMenu }) : ''} className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>{user.username ? user.username : 'Sign in'}</a>
<Link to={user.username ? '#' : '/login'} onClick={() => user.username ? setPageState({ ...pageState, profileMenu: !pageState.profileMenu }) : ''} className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>{user.username ? user.username : 'Sign in'}</Link>
{user && screen.width > 600 &&
<div className={'profile-dropdown bg-secondary ml-6'} style={pageState.profileMenu ? { display: 'block' } : { display: 'none' }}>
<a href={'/add-location'}><div className={'p-1'}>Add location</div></a>
<a href={'/user/profile'}><div className={'p-1'}>Profile</div></a>
<a href={'#'}><div className={'p-1'}>Feed</div></a>
<a href={'/user/settings'}><div className={'p-1'}>Settings</div></a>
<Link to={'/add-location'} onClick={() => setPageState({ ...pageState, profileMenu: !pageState.profileMenu })}><div className={'p-1'}>Add location</div></Link>
<Link to={'/user/profile'}><div className={'p-1'}>Profile</div></Link>
<Link to={'#'}><div className={'p-1'}>Feed</div></Link>
<Link to={'/user/settings'}><div className={'p-1'}>Settings</div></Link>
{user.is_admin &&
<a href={'/submissions'} ><div className={'p-1'}>Submissions</div></a>
<Link to={'/submissions'} ><div className={'p-1'}>Submissions</div></Link>
}
<a href={'#'} onClick={handleLogout}><div className={'p-1'}>Logout</div></a>
<Link to={'#'} onClick={handleLogout}><div className={'p-1'}>Logout</div></Link>
{/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */}
{/* <div className={'p-2'}><a href={'#'}>Halo</a></div> */}
</div>

View File

@ -0,0 +1,12 @@
import { JSXInternal } from "node_modules/preact/src/jsx";
import "../style.css"
type DefaultLoadingAnimationProps = {
style: JSXInternal.CSSProperties
}
const DefaultLoadingAnimation = (props: DefaultLoadingAnimationProps) => (
<div style={props.style} class="loader"></div>
)
export default DefaultLoadingAnimation;

View File

@ -0,0 +1,21 @@
.loader {
width: 30px;
padding: 8px;
aspect-ratio: 1;
border-radius: 50%;
background: #a8adb3;
--_m:
conic-gradient(#0000 10%, #000),
linear-gradient(#000 0 0) content-box;
-webkit-mask: var(--_m);
mask: var(--_m);
-webkit-mask-composite: source-out;
mask-composite: subtract;
animation: l3 1s infinite linear;
}
@keyframes l3 {
to {
transform: rotate(1turn)
}
}

113
src/components/Select/Default.tsx Executable file
View File

@ -0,0 +1,113 @@
// import { cx } from "class-variance-authority";
// import Downshift from "downshift";
// import "../Header/style.css";
// type DefaultSelectProps = {
// data: any,
// onInput: (val: string) => void;
// inputValue: string
// }
// function DefaultSelect(props: DefaultSelectProps) {
// const items = [
// { author: 'Harper Lee', title: 'To Kill a Mockingbird' },
// { author: 'Lev Tolstoy', title: 'War and Peace' },
// { author: 'Fyodor Dostoyevsy', title: 'The Idiot' },
// { author: 'Oscar Wilde', title: 'A Picture of Dorian Gray' },
// { author: 'George Orwell', title: '1984' },
// { author: 'Jane Austen', title: 'Pride and Prejudice' },
// { author: 'Marcus Aurelius', title: 'Meditations' },
// { author: 'Fyodor Dostoevsky', title: 'The Brothers Karamazov' },
// { author: 'Lev Tolstoy', title: 'Anna Karenina' },
// { author: 'Fyodor Dostoevsky', title: 'Crime and Punishment' },
// ];
// return (
// <Downshift
// onChange={selection =>
// alert(
// selection
// ? `You selected "${selection.title}" by ${selection.author}. Great Choice!`
// : 'Selection Cleared',
// )
// }
// itemToString={item => (item ? item.title : '')}
// >
// {({
// getInputProps,
// getItemProps,
// getLabelProps,
// getMenuProps,
// getToggleButtonProps,
// isOpen,
// inputValue,
// highlightedIndex,
// selectedItem,
// closeMenu,
// }) => {
// inputValue = props.inputValue
// return (
// <div>
// <div className="w-72 flex flex-col gap-1">
// <div style={{ borderRadius: 2 }}>
// <input
// placeholder="Candi Borobudur, Tunjungan Plaza ..."
// style={{ minHeight: 45 }}
// className="w-full bg-secondary p-1.5"
// {...getInputProps({
// onChange: val => props.onInput(val),
// onKeyDown: event => {
// if (event.key === 'ArrowUp' && highlightedIndex === 0) {
// // If at the top, close the menu
// event.preventDefault();
// closeMenu();
// }
// if(event.key === 'Enter') {
// alert(props.inputValue)
// }
// },
// })}
// />
// </div>
// </div>
// <ul
// className={`absolute w-72 bg-secondary mt-1 shadow-md max-h-80 overflow-scroll p-0 z-10 ${!(isOpen && items.length) && 'hidden'
// }`}
// {...getMenuProps()}
// >
// {isOpen
// ? items
// .filter(
// item =>
// !inputValue ||
// item.title.toLowerCase().includes(inputValue.toLowerCase()) ||
// item.author.toLowerCase().includes(inputValue.toLowerCase()),
// )
// .map((item, index) => (
// <li
// className={cx(
// highlightedIndex === index && 'bg-tertiary',
// 'py-2 px-3 flex flex-col',
// )}
// {...getItemProps({
// key: item.title,
// index,
// item,
// })}
// >
// <span>{item.title}</span>
// <span className="text-sm text-gray-700">{item.author}</span>
// </li>
// ))
// : null}
// </ul>
// </div>
// )
// }}
// </Downshift>
// );
// }
// export default DefaultSelect;

View File

@ -1,11 +1,11 @@
export type LocationInfo = {
id: Number,
id: number,
name: string,
thumbnail: string | null,
regency_name: String,
province_name: String,
critic_score: Number,
critic_count: Number,
user_score: Number,
user_count: Number
critic_score: number,
critic_count: number,
user_score: number,
user_count: number
}

View File

@ -10,6 +10,7 @@ import './style.css';
import { createLocationService } from "../../services/locations";
import { useSelector } from "react-redux";
import { UserRootState } from "../../store/type";
import DefaultLoadingAnimation from "../../components/LoadingAnimation/Default";
function AddLocation() {
const [recentLocations, setRecentLocations] = useState<Array<LocationInfo>>()
@ -24,6 +25,7 @@ function AddLocation() {
regency: emptyRegency(),
thumbnails: [],
})
const [submitLoading, setSubmitLoading ] = useState<boolean>(false)
const user = useSelector((state: UserRootState) => state.auth)
@ -33,7 +35,7 @@ function AddLocation() {
async function getRecentLocations() {
try {
const locations = await getListRecentLocationsRatingsService(9)
const locations = await getListRecentLocationsRatingsService(10, 1)
setRecentLocations(locations.data)
// setIsLoading(false)
} catch (error) {
@ -61,6 +63,8 @@ function AddLocation() {
async function onSubmitForm(e: TargetedEvent) {
e.preventDefault();
setSubmitLoading(true)
if(form.regency.regency_name === '') {
setPageState({ regency_form_error: true })
return
@ -86,8 +90,8 @@ function AddLocation() {
try {
await createLocationService(formData)
alert("Location Added")
setSubmitLoading(false)
alert("Location Added\nModerator will check the submission because we don't trust you")
} catch(error) {
console.log(error)
}
@ -214,8 +218,11 @@ function AddLocation() {
{form.thumbnails.length > 0 &&
<a className={'block mt-2'} onClick={onDeleteAllThumbnails}>Delete All Thumbnails</a>
}
<input type={'submit'} value={'Submit'} className={'block p-1 text-sm text-primary mt-4'} style={{ backgroundColor: '#a8adb3', letterSpacing: .5, width: 75 }} />
{submitLoading ?
<DefaultLoadingAnimation style={{ marginTop: 20 }} />
:
<input type={'submit'} value={'Submit'} className={'block p-1 text-sm text-primary mt-4'} style={{ backgroundColor: '#a8adb3', letterSpacing: .5, width: 75 }} />
}
</form>
<span className={'mt-5 text-sm font-bold block'}>NOTE: LOCATION SUBMISSION MAY BE EDITED BY MODERATOR SO DON'T PUT STUPID ASS THUMBNAILS YOU 1 CENT DOORKNOB</span>
</div>

View File

@ -4,6 +4,7 @@ import { getListTopLocationsService } from "../../services";
import { DefaultSeparator } from "../../components";
import './style.css';
import { useClick, useFloating, useInteractions } from "@floating-ui/react";
import { Link } from "react-router-dom";
interface TopLocation {
row_number: Number,
@ -139,12 +140,12 @@ function BestLocation() {
</a>
</div> */}
<div className={'mb-2 best-locations-title'}>
<a className={'text-xl'} href={`/location/${x.id}`}>{x.row_number}.{x.name}</a>
<Link className={'text-xl'} to={`/location/${x.id}`}>{x.row_number}.{x.name}</Link>
</div>
<div style={{ maxWidth: 200, maxHeight: 200, margin: '0 30px 30px 10px', float: 'left' }}>
<a href={`/location/${x.id}`} >
<Link to={`/location/${x.id}`} >
<img src={x.thumbnail ? x.thumbnail : ""} loading={'lazy'} style={{ width: '100%', objectFit: 'cover', height: '100%', aspectRatio: '1/1' }} />
</a>
</Link>
</div>
<div className={'text-md font-bold'}>{x.regency_name}</div>
<div className={'text-xs mb-2'}>{x.address}</div>

View File

@ -5,10 +5,10 @@ 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 { LocationType } from "../../types/common";
import { enumKeys } from "../../utils";
import { getSearchLocationService } from "../../services/locations";
import { useNavigate } from "react-router-dom";
import './style.css';
interface floatFilterArgs<T> {
@ -129,6 +129,7 @@ function Discovery() {
locationType: []
});
const [isFloatFilterOpen, setFloatFilterOpen] = useState(false)
const navigate = useNavigate()
useEffect(() => {
getLocationType()
@ -147,8 +148,8 @@ function Discovery() {
async function getRecentLocations() {
try {
const locations = await getListRecentLocationsRatingsService(15)
setData((prevState) => ({ ...prevState, locations: locations.data.locations }))
const locations = await getListRecentLocationsRatingsService(15, 1)
setData((prevState) => ({ ...prevState, locations: locations.data }))
} catch (error) {
console.log(error)
}
@ -232,6 +233,21 @@ function Discovery() {
}
function onClickLocation(id: number) {
navigate(`/location/${id}`)
}
function onDeleteByCityFilter(value: any) {
const dataRegencies = data.searchRegencies as (Regency & { isSelected?: boolean })[];
const regencyIdx = dataRegencies.findIndex(x => x.id == value.id);
dataRegencies[regencyIdx].isSelected = !dataRegencies[regencyIdx].isSelected
setData({
...data,
regencies: dataRegencies,
searchRegencies: dataRegencies
})
}
return (
<div className="content main-content mt-3">
<section name="header">
@ -252,6 +268,7 @@ function Discovery() {
{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>
<button onClick={() => onDeleteByCityFilter(x)} style={{ color: "white"}}>x</button>
</div>
))}
</div>
@ -291,7 +308,7 @@ function Discovery() {
{data.locations.map(x => (
<LocationCard
containerClass='card-list-container'
onCardClick={(id) => console.log(id)}
onCardClick={onClickLocation}
data={x}
/>
))}

View File

@ -27,8 +27,8 @@ function Home() {
async function getRecentLocations() {
try {
const locations = await getListRecentLocationsRatingsService(12)
setRecentLocations(locations.data.locations)
const locations = await getListRecentLocationsRatingsService(12, 1)
setRecentLocations(locations.data)
// setIsLoading(false)
} catch (error) {
console.log(error)

View File

@ -10,6 +10,7 @@ import { IHttpResponse } from "../../../src/types/common";
import { useSelector } from "react-redux";
import { UserRootState } from "src/store/type";
import "./style.css"
import { Link } from "react-router-dom";
function NewsEvent() {
const [news, setNews] = useState<Array<News>>([]);
@ -170,7 +171,7 @@ function NewsEvent() {
</div>
<div className={'submitted-info'} style={{ display: 'inline-block' }}>
1d ago by
<a> {x.submitted_by}</a>
<Link to={'#'}> {x.submitted_by}</Link>
</div>
</div>

View File

@ -5,6 +5,7 @@ import "./style.css"
import { useEffect, useState } from "preact/compat";
import { getUserStatsService } from "../../services";
import { CustomInterweave, SeparatorWithAnchor } from "../../components";
import { Link } from "react-router-dom";
interface UserStats {
followers: number,
@ -105,31 +106,31 @@ function UserProfile() {
<section name={"profile-navigation"}>
<div className={'bg-secondary profile-nav'}>
<a>
<Link to='#'>
<div>
summary
</div>
</a>
<a>
</Link>
<Link to='#'>
<div>
reviews
</div>
</a>
<a>
</Link>
<Link to='#'>
<div>
likes
</div>
</a>
<a>
</Link>
<Link to='#'>
<div>
stories
</div>
</a>
<a>
</Link>
<Link to='#'>
<div>
tags
</div>
</a>
</Link>
</div>
</section>

View File

@ -36,7 +36,7 @@ function UserSettings() {
const [form, setForm] = useState<UserInfo>({
about: user.about,
website: user.website,
social_media: user.social_media
social_media: user.social_media ?? []
})
@ -218,7 +218,7 @@ function UserSettings() {
value={form.about}
style={{ border: 'none', overflow: 'auto', outline: 'none', boxShadow: 'none', width: '100%', minHeight: 100, overflowY: 'hidden' }}
/>
<div className={'mt-2 mb-1'}>
<div className={'mt-2 mb-5'}>
<h2 className={'font-bold text-sm mb-1'}>WEBSITE</h2>
<input className={'bg-secondary text-sm'} placeholder={'www.mywebsite.com'} style={{ width: '100%', borderRadius: 7, padding: '5px 10px' }} type={'text'} onChange={onChangeFormInput} name={'website'} value={form.website} />
</div>

View File

@ -45,9 +45,9 @@ async function getListLocationsService({ page, page_size }: GetListLocationsArg)
}
}
async function getListRecentLocationsRatingsService(page_size: Number) {
async function getListRecentLocationsRatingsService(page_size: number, page: number) {
const newState = { ...initialState };
const url = `${GET_LIST_RECENT_LOCATIONS_RATING_URI}?page_size=${page_size}`
const url = `${GET_LIST_RECENT_LOCATIONS_RATING_URI}?page_size=${page_size}&page=${page}`
try {
const response = await client({ method: 'GET', url: url })
switch (response.request.status) {

View File

@ -1,10 +1,11 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}