216 lines
8.4 KiB
TypeScript
Executable File
216 lines
8.4 KiB
TypeScript
Executable File
import React from "preact/compat";
|
|
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 { Link, useNavigate } from "react-router-dom";
|
|
import { ReactSelectData } from "src/types/common";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
|
|
|
|
function Header() {
|
|
const [searchVal, setSearchVal] = useState('');
|
|
const [dropdown, setDropdown] = useState(false);
|
|
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 = 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()
|
|
dispatch(logout())
|
|
location.reload()
|
|
} catch (error) {
|
|
alert(error)
|
|
}
|
|
}
|
|
|
|
const onSearchSubmit = (e: React.TargetedEvent<HTMLFormElement>): void => {
|
|
e.preventDefault();
|
|
}
|
|
|
|
const onSelectedSearchOption = (v: ReactSelectData | unknown) => {
|
|
const val = v as ReactSelectData
|
|
navigate(`/location/${val.value}`)
|
|
}
|
|
|
|
const onLoadSelectOptions = async (inputValue: string) => {
|
|
try {
|
|
const results = await getSearchLocationService({
|
|
name: inputValue,
|
|
page: 1,
|
|
page_size: 7
|
|
})
|
|
const resultData = results.data.map((x: any) => {
|
|
return {
|
|
value: x.id,
|
|
label: x.name
|
|
}
|
|
})
|
|
const firstObj = {
|
|
value: 0,
|
|
label: `search: ${inputValue}`
|
|
}
|
|
const result = [firstObj, ...resultData]
|
|
setSearchResult(resultData)
|
|
return result
|
|
} catch (err) {
|
|
alert(err)
|
|
}
|
|
}
|
|
|
|
const onDropdown = (): void => {
|
|
document.body.style.overflow = "hidden"
|
|
|
|
if (dropdown) {
|
|
document.body.style.overflowX = "hidden";
|
|
document.body.style.overflowY = "scroll";
|
|
}
|
|
setDropdown(!dropdown)
|
|
}
|
|
|
|
|
|
return (
|
|
<header>
|
|
<div className="flex flex-row content">
|
|
<Link to={"/"}>
|
|
<h1 className={`title ${dropdown ? 'title-dropdown' : ""}`}>Hilingin</h1>
|
|
</Link>
|
|
<div className={'user-img self-center mr-5'} style={dropdown ? { display: 'none' } : ''}>
|
|
<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'}
|
|
/>
|
|
</Link>
|
|
{user.username &&
|
|
<div className={'profile-dropdown-img bg-secondary text-left'} style={pageState.profileMenu ? { display: 'block' } : { display: 'none' }}>
|
|
<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} 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,
|
|
IndicatorSeparator: () => null
|
|
}}
|
|
loadOptions={onLoadSelectOptions}
|
|
cacheOptions
|
|
classNames={{
|
|
control: () => "bg-secondary text-input-search",
|
|
menuList: () => "bg-secondary text-sm text-left",
|
|
}}
|
|
styles={{
|
|
singleValue: (base, _props) => ({
|
|
color: '#797979',
|
|
textTransform: 'capitalize',
|
|
...base,
|
|
}),
|
|
input: (base, _props) => ({
|
|
...base,
|
|
width: 325,
|
|
color: 'white',
|
|
padding: 0,
|
|
}),
|
|
control: (base, _props) => ({
|
|
...base,
|
|
backgroundColor: '#red',
|
|
color: 'white',
|
|
border: 0,
|
|
padding: 0,
|
|
boxShadow: 'none',
|
|
textAlign: 'left',
|
|
minHeight: 45
|
|
}),
|
|
option: (base, { isFocused }) => ({
|
|
...base,
|
|
backgroundColor: isFocused ? '#202225' : 'none',
|
|
}),
|
|
}}
|
|
onChange={(v: ReactSelectData | unknown, _) => onSelectedSearchOption(v)}
|
|
/>
|
|
</form>
|
|
<button onClick={onDropdown} className={`dropdown-menu bg-secondary ${dropdown ? 'ml-auto' : ''}`} style={{ padding: 5, borderRadius: 10 }}>
|
|
<svg xmlns="http://www.w3.org/2000/svg" height="30" fill="white" viewBox="0 -960 960 960" width="30"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z" /></svg>
|
|
</button>
|
|
</div>
|
|
<div className={`nav-container ${!dropdown ? "nav-container-disabled" : ""}`}>
|
|
{dropdown &&
|
|
<a href="/" className={`navLink ${!dropdown ? "navLink-disabled" : ""}`}>Home</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'}>
|
|
<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' }}>
|
|
<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 &&
|
|
<Link to={'/submissions'} ><div className={'p-1'}>Submissions</div></Link>
|
|
}
|
|
<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>
|
|
}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
)
|
|
}
|
|
|
|
export default Header; |