1- import { useEffect , useState , useMemo , useCallback , useRef } from "react" ;
1+ import { useEffect , useState } from "react" ;
22import AsyncSelect from "react-select/async" ;
33import { usePathname , useRouter , useSearchParams } from "next/navigation" ;
44import axios from "axios" ;
5+ import { AND_SEPARATION , OR_SEPARATION } from "@/lib/constants" ;
6+ import debounce from "debounce" ;
7+
8+ const WITH_COMPANIES = "with_companies" ;
59
610export default function Company ( { inputStyles } ) {
711 const router = useRouter ( ) ;
812 const pathname = usePathname ( ) ;
913 const searchParams = useSearchParams ( ) ;
10- const current = useMemo (
11- ( ) => new URLSearchParams ( Array . from ( searchParams . entries ( ) ) ) ,
12- [ searchParams ] ,
13- ) ;
14+ const current = new URLSearchParams ( Array . from ( searchParams . entries ( ) ) ) ;
15+
1416 const isQueryParams = searchParams . get ( "query" ) ;
17+ const defaultToggleSeparation = searchParams
18+ . get ( WITH_COMPANIES )
19+ ?. includes ( "|" )
20+ ? OR_SEPARATION
21+ : AND_SEPARATION ;
1522
1623 const [ company , setCompany ] = useState ( [ ] ) ;
24+ const [ toggleSeparation , setToggleSeparation ] = useState (
25+ defaultToggleSeparation ,
26+ ) ;
1727
18- const timerRef = useRef ( null ) ;
19- const companiesLoadOptions = useCallback ( ( inputValue , callback ) => {
20- const fetchDataWithDelay = async ( ) => {
21- // Delay pengambilan data selama 500ms setelah pengguna berhenti mengetik
22- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
23-
24- // Lakukan pengambilan data setelah delay
25- axios
26- . get ( `/api/search/company` , { params : { query : inputValue } } )
27- . then ( ( { data } ) => {
28- const options = data . results . map ( ( company ) => ( {
29- value : company . id ,
30- label : company . name ,
31- } ) ) ;
32- const filteredOptions = options . filter ( ( option ) =>
33- option . label . toLowerCase ( ) . includes ( inputValue . toLowerCase ( ) ) ,
34- ) ;
35- callback ( filteredOptions ) ;
36- } ) ;
37- } ;
28+ const separation = toggleSeparation === AND_SEPARATION ? "," : "|" ;
29+
30+ const companiesLoadOptions = debounce ( async ( inputValue , callback ) => {
31+ const { data } = await axios . get ( `/api/search/company` , {
32+ params : { query : inputValue } ,
33+ } ) ;
3834
39- // Hapus pemanggilan sebelumnya jika ada
40- clearTimeout ( timerRef . current ) ;
35+ const options = data . results . map ( ( company ) => ( {
36+ value : company . id ,
37+ label : company . name ,
38+ } ) ) ;
4139
42- // Set timer untuk memanggil fetchDataWithDelay setelah delay
43- timerRef . current = setTimeout ( ( ) => {
44- fetchDataWithDelay ( ) ;
45- } , 1000 ) ;
46- } , [ ] ) ;
40+ const filteredOptions = options . filter ( ( option ) =>
41+ option . label . toLowerCase ( ) . includes ( inputValue . toLowerCase ( ) ) ,
42+ ) ;
43+
44+ callback ( filteredOptions ) ;
45+ } , 1000 ) ;
4746
4847 const handleCompanyChange = ( selectedOption ) => {
4948 const value = selectedOption . map ( ( option ) => option . value ) ;
5049
5150 if ( value . length === 0 ) {
52- current . delete ( "with_companies" ) ;
51+ current . delete ( WITH_COMPANIES ) ;
5352 } else {
54- current . set ( "with_companies" , value ) ;
53+ current . set ( WITH_COMPANIES , value . join ( separation ) ) ;
5554 }
5655
57- const search = current . toString ( ) ;
56+ router . push ( `${ pathname } ?${ current . toString ( ) } ` ) ;
57+ } ;
58+
59+ const handleSeparator = ( separator ) => {
60+ setToggleSeparation ( separator ) ;
61+
62+ if ( searchParams . get ( WITH_COMPANIES ) ) {
63+ const params = searchParams . get ( WITH_COMPANIES ) ;
5864
59- const query = search ? `?${ search } ` : "" ;
65+ const separation = separator === AND_SEPARATION ? "," : "|" ;
66+ const newSeparator = params . includes ( "|" ) ? "," : "|" ;
67+ if ( newSeparator !== separation ) return ;
6068
61- router . push ( `${ pathname } ${ query } ` ) ;
69+ const updatedParams = params . replace ( / [ \| , ] / g, newSeparator ) ;
70+
71+ current . set ( WITH_COMPANIES , updatedParams ) ;
72+ router . push ( `${ pathname } ?${ current . toString ( ) } ` ) ;
73+ }
6274 } ;
6375
6476 useEffect ( ( ) => {
6577 // Company
66- if ( searchParams . get ( "with_companies" ) ) {
67- const companyParams = searchParams . get ( "with_companies" ) . split ( "," ) ;
68- const fetchPromises = companyParams . map ( ( companyId ) => {
69- return axios . get ( `/api/company/${ companyId } ` ) . then ( ( { data } ) => data ) ;
70- } ) ;
78+ if ( searchParams . get ( WITH_COMPANIES ) ) {
79+ const params = searchParams . get ( WITH_COMPANIES ) ;
80+ const splitted = params . split ( separation ) ;
7181
72- Promise . all ( fetchPromises )
82+ Promise . all (
83+ splitted . map ( ( companyId ) =>
84+ axios . get ( `/api/company/${ companyId } ` ) . then ( ( { data } ) => data ) ,
85+ ) ,
86+ )
7387 . then ( ( responses ) => {
7488 const uniqueCompany = [ ...new Set ( responses ) ] ; // Remove duplicates if any
7589 const searchCompany = uniqueCompany . map ( ( company ) => ( {
@@ -84,11 +98,37 @@ export default function Company({ inputStyles }) {
8498 } else {
8599 setCompany ( null ) ;
86100 }
87- } , [ searchParams ] ) ;
101+ } , [ searchParams , separation ] ) ;
88102
89103 return (
90104 < section className = { `flex flex-col gap-1` } >
91- < span className = { `font-medium` } > Company</ span >
105+ < div className = { `flex items-center justify-between` } >
106+ < span className = { `font-medium` } > Company</ span >
107+
108+ < div className = { `flex rounded-full bg-base-100 p-1` } >
109+ < button
110+ onClick = { ( ) => handleSeparator ( AND_SEPARATION ) }
111+ className = { `btn btn-ghost btn-xs rounded-full ${
112+ toggleSeparation === AND_SEPARATION
113+ ? "bg-white text-base-100 hover:bg-white hover:bg-opacity-50"
114+ : ""
115+ } `}
116+ >
117+ AND
118+ </ button >
119+ < button
120+ onClick = { ( ) => handleSeparator ( OR_SEPARATION ) }
121+ className = { `btn btn-ghost btn-xs rounded-full ${
122+ toggleSeparation === OR_SEPARATION
123+ ? "bg-white text-base-100 hover:bg-white hover:bg-opacity-50"
124+ : ""
125+ } `}
126+ >
127+ OR
128+ </ button >
129+ </ div >
130+ </ div >
131+
92132 < AsyncSelect
93133 noOptionsMessage = { ( ) => "Type to search" }
94134 loadingMessage = { ( ) => "Searching..." }
0 commit comments