Mengerjakan QC Kak Inno & Kak Ayu Tanggal 16 Oktober
Fix Search
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
import searchState, { debouncedFetch } from '@/app/api/[[...slugs]]/_lib/search/searchState';
|
||||
import { Box, Center, Loader, Modal, Text, TextInput } from '@mantine/core';
|
||||
import { Box, Center, Loader, Popover, Text, TextInput } from '@mantine/core';
|
||||
import { IconX } from '@tabler/icons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSnapshot } from 'valtio';
|
||||
@@ -8,14 +9,14 @@ import getDetailUrl from './searchUrl';
|
||||
|
||||
export default function GlobalSearch() {
|
||||
const snap = useSnapshot(searchState);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
// Toggle modal when there's a query
|
||||
// buka popover saat ada query
|
||||
useEffect(() => {
|
||||
setIsOpen(!!snap.query);
|
||||
setOpened(!!snap.query);
|
||||
}, [snap.query]);
|
||||
|
||||
// Infinite scroll
|
||||
// infinite scroll
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const bottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
|
||||
@@ -25,88 +26,119 @@ export default function GlobalSearch() {
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, [snap.loading]);
|
||||
|
||||
return (
|
||||
<Box style={{ position: 'relative', width: '100%' }}>
|
||||
<TextInput
|
||||
placeholder="Cari apapun..."
|
||||
value={snap.query}
|
||||
onChange={(e) => {
|
||||
searchState.query = e.currentTarget.value;
|
||||
debouncedFetch();
|
||||
}}
|
||||
radius="xl"
|
||||
rightSection={
|
||||
snap.query ? (
|
||||
<IconX
|
||||
size={16}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
searchState.query = '';
|
||||
searchState.results = [];
|
||||
}}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
const handleSelect = async (e: React.MouseEvent, item: any) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
{/* Modal for search results */}
|
||||
<Modal
|
||||
opened={isOpen && !!snap.query}
|
||||
onClose={() => {
|
||||
searchState.query = '';
|
||||
searchState.results = [];
|
||||
const url = getDetailUrl(item);
|
||||
if (!url) return;
|
||||
|
||||
// Immediately close the search dropdown
|
||||
setOpened(false);
|
||||
searchState.results = []; // Clear results immediately
|
||||
searchState.loading = false;
|
||||
|
||||
// Use window.location for navigation to ensure full page reload
|
||||
window.location.href = url;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box pos="relative">
|
||||
<Popover
|
||||
opened={opened && !!snap.query}
|
||||
onChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
// Clear search state when popover is closed
|
||||
searchState.query = '';
|
||||
searchState.results = [];
|
||||
searchState.page = 1;
|
||||
searchState.nextPage = null;
|
||||
}
|
||||
setOpened(isOpen);
|
||||
}}
|
||||
withCloseButton={false}
|
||||
size="lg"
|
||||
padding={0}
|
||||
width="target"
|
||||
position="bottom"
|
||||
shadow="md"
|
||||
withinPortal
|
||||
radius="md"
|
||||
style={{ position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 1000 }}
|
||||
zIndex={1000} // Add this line to ensure it appears above other elements
|
||||
styles={{
|
||||
content: { // Changed from 'modal' to 'content'
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||
borderRadius: '0.5rem',
|
||||
maxHeight: '400px',
|
||||
overflow: 'hidden',
|
||||
dropdown: {
|
||||
zIndex: 1000, // Add this to ensure the dropdown appears above other elements
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box style={{ maxHeight: '400px', overflowY: 'auto' }}>
|
||||
{snap.results.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
p="sm"
|
||||
style={{
|
||||
borderBottom: '1px solid #eee',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.2s',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: '100%',
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.background = '#f5f5f5')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
|
||||
onClick={() => {
|
||||
const url = getDetailUrl(item);
|
||||
window.location.href = url;
|
||||
}}
|
||||
>
|
||||
<Text size="sm" fw={500}>
|
||||
{item.judul || item.namaPasar || item.nama || item.name}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
dari modul: {item.type}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
{snap.loading && (
|
||||
<Popover.Target>
|
||||
<TextInput
|
||||
placeholder="Cari apapun..."
|
||||
value={snap.query}
|
||||
onChange={(e) => {
|
||||
searchState.query = e.currentTarget.value;
|
||||
debouncedFetch();
|
||||
}}
|
||||
radius="xl"
|
||||
size="md"
|
||||
rightSection={
|
||||
snap.query ? (
|
||||
<IconX
|
||||
size={16}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
searchState.query = '';
|
||||
searchState.results = [];
|
||||
searchState.page = 1;
|
||||
searchState.nextPage = null;
|
||||
setOpened(false);
|
||||
}}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Popover.Target>
|
||||
|
||||
<Popover.Dropdown
|
||||
p={0}
|
||||
style={{
|
||||
maxHeight: 350,
|
||||
overflowY: 'auto',
|
||||
borderRadius: 12,
|
||||
zIndex: 1000, // Add this line to ensure dropdown stays above other elements
|
||||
position: 'relative', // Add this to contain child elements
|
||||
}}
|
||||
>
|
||||
{snap.results.length > 0 ? (
|
||||
snap.results.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
p="sm"
|
||||
className="search-result-item" // Add this class
|
||||
style={{
|
||||
borderBottom: '1px solid #eee',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.2s',
|
||||
position: 'relative', // Add this
|
||||
zIndex: 1, // Add this to ensure proper stacking context
|
||||
backgroundColor: 'white', // Ensure background is set
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.background = '#f7f7f7')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
|
||||
onClick={(e) => handleSelect(e, item)} // Pass the event here
|
||||
>
|
||||
<Text size="sm" fw={500}>
|
||||
{item.judul || item.namaPasar || item.nama || item.name}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
dari modul: {item.type}
|
||||
</Text>
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<Center py="md">
|
||||
<Loader size="sm" />
|
||||
{snap.loading ? <Loader size="sm" /> : <Text fz="sm">Tidak ada hasil</Text>}
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
</Modal>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user