1- import { useCallback , useEffect , useMemo , useState } from "react" ;
1+ import { useCallback , type SetStateAction } from "react" ;
22import { JobsFiltersCard } from "@/components/JobsFiltersCard" ;
33import { JobsHeaderCard } from "@/components/JobsHeaderCard" ;
44import { JobsTableCard } from "@/components/JobsTableCard" ;
5- import { fetchJobFiles , fetchJobsByFile } from "@/services/jobsService" ;
6- import type { Job , JobFile , JobsMeta } from "@/types/jobs" ;
5+ import { useJobsData } from "@/hooks/useJobsData" ;
6+ import { useJobsFiltering } from "@/hooks/useJobsFiltering" ;
7+ import { useJobsPagination } from "@/hooks/useJobsPagination" ;
8+ import type { JobsMeta } from "@/types/jobs" ;
79
810function formatDate ( timestamp : JobsMeta [ "modifiedAt" ] ) : string {
911 if ( ! timestamp ) {
@@ -13,100 +15,46 @@ function formatDate(timestamp: JobsMeta["modifiedAt"]): string {
1315}
1416
1517function App ( ) {
16- const [ files , setFiles ] = useState < JobFile [ ] > ( [ ] ) ;
17- const [ selectedFile , setSelectedFile ] = useState ( "" ) ;
18- const [ search , setSearch ] = useState ( "" ) ;
19- const [ keywordFilter , setKeywordFilter ] = useState ( "all" ) ;
20- const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
21- const [ pageSize , setPageSize ] = useState ( 25 ) ;
22- const [ jobs , setJobs ] = useState < Job [ ] > ( [ ] ) ;
23- const [ meta , setMeta ] = useState < JobsMeta > ( { file : "" , modifiedAt : null , total : 0 } ) ;
24- const [ loading , setLoading ] = useState ( false ) ;
25- const [ error , setError ] = useState ( "" ) ;
18+ const { files, selectedFile, setSelectedFile, jobs, meta, loading, error, loadJobs } = useJobsData ( ) ;
2619
27- const keywords = useMemo ( ( ) => {
28- const values = Array . from ( new Set ( jobs . map ( ( job ) => String ( job . palavra || "" ) . trim ( ) ) . filter ( Boolean ) ) ) ;
29- return values . sort ( ( a , b ) => a . localeCompare ( b ) ) ;
30- } , [ jobs ] ) ;
20+ const { search, setSearch, keywordFilter, setKeywordFilter, keywords, filteredJobs } = useJobsFiltering ( jobs ) ;
3121
32- const filteredJobs = useMemo ( ( ) => {
33- const term = search . trim ( ) . toLowerCase ( ) ;
34-
35- return jobs . filter ( ( job ) => {
36- const byKeyword = keywordFilter === "all" || String ( job . palavra || "" ) === keywordFilter ;
37- if ( ! byKeyword ) {
38- return false ;
39- }
40-
41- if ( ! term ) {
42- return true ;
43- }
44-
45- const text = [ job . titulo , job . empresa , job . local , job . link , job . palavra ]
46- . map ( ( value ) => String ( value || "" ) . toLowerCase ( ) )
47- . join ( " " ) ;
48-
49- return text . includes ( term ) ;
22+ const { currentPage, setCurrentPage, pageSize, setPageSize, resetPagination, totalPages, paginatedJobs } =
23+ useJobsPagination ( {
24+ filteredJobs,
5025 } ) ;
51- } , [ jobs , search , keywordFilter ] ) ;
52-
53- const totalPages = useMemo ( ( ) => {
54- return Math . max ( 1 , Math . ceil ( filteredJobs . length / pageSize ) ) ;
55- } , [ filteredJobs . length , pageSize ] ) ;
56-
57- const paginatedJobs = useMemo ( ( ) => {
58- const start = ( currentPage - 1 ) * pageSize ;
59- return filteredJobs . slice ( start , start + pageSize ) ;
60- } , [ filteredJobs , currentPage , pageSize ] ) ;
61-
62- const loadJobs = useCallback ( async ( fileName : string ) => {
63- setLoading ( true ) ;
64- setError ( "" ) ;
65- try {
66- const data = await fetchJobsByFile ( fileName ) ;
67- setJobs ( data . jobs ) ;
68- setMeta ( {
69- file : data . file ,
70- modifiedAt : data . modifiedAt ,
71- total : data . total ,
72- } ) ;
73- setSelectedFile ( data . file || fileName || "" ) ;
74- } catch ( err : unknown ) {
75- setJobs ( [ ] ) ;
76- setMeta ( { file : "" , modifiedAt : null , total : 0 } ) ;
77- setError ( err instanceof Error ? err . message : "Erro inesperado ao carregar vagas." ) ;
78- } finally {
79- setLoading ( false ) ;
80- }
81- } , [ ] ) ;
8226
83- useEffect ( ( ) => {
84- async function initializeFiles ( ) {
85- const foundFiles = await fetchJobFiles ( ) ;
86- setFiles ( foundFiles ) ;
87- if ( foundFiles [ 0 ] ?. file ) {
88- setSelectedFile ( foundFiles [ 0 ] . file ) ;
89- }
90- }
91-
92- initializeFiles ( ) . catch ( ( ) => {
93- setError ( "Nao foi possivel listar arquivos .xlsx da pasta output." ) ;
94- } ) ;
95- } , [ ] ) ;
27+ const handleSearchChange = useCallback (
28+ ( value : SetStateAction < string > ) => {
29+ setSearch ( ( previous ) => ( typeof value === "function" ? value ( previous ) : value ) ) ;
30+ resetPagination ( ) ;
31+ } ,
32+ [ setSearch , resetPagination ] ,
33+ ) ;
9634
97- useEffect ( ( ) => {
98- if ( selectedFile ) {
99- loadJobs ( selectedFile ) ;
100- }
101- } , [ selectedFile , loadJobs ] ) ;
35+ const handleKeywordFilterChange = useCallback (
36+ ( value : SetStateAction < string > ) => {
37+ setKeywordFilter ( ( previous ) => ( typeof value === "function" ? value ( previous ) : value ) ) ;
38+ resetPagination ( ) ;
39+ } ,
40+ [ setKeywordFilter , resetPagination ] ,
41+ ) ;
10242
103- useEffect ( ( ) => {
104- setCurrentPage ( 1 ) ;
105- } , [ search , keywordFilter , selectedFile , pageSize ] ) ;
43+ const handleSelectedFileChange = useCallback (
44+ ( value : SetStateAction < string > ) => {
45+ setSelectedFile ( ( previous ) => ( typeof value === "function" ? value ( previous ) : value ) ) ;
46+ resetPagination ( ) ;
47+ } ,
48+ [ setSelectedFile , resetPagination ] ,
49+ ) ;
10650
107- useEffect ( ( ) => {
108- setCurrentPage ( ( previous ) => Math . min ( previous , totalPages ) ) ;
109- } , [ totalPages ] ) ;
51+ const handlePageSizeChange = useCallback (
52+ ( value : number ) => {
53+ setPageSize ( value ) ;
54+ resetPagination ( ) ;
55+ } ,
56+ [ setPageSize , resetPagination ] ,
57+ ) ;
11058
11159 return (
11260 < main className = "relative min-h-screen overflow-hidden bg-background px-4 py-8 md:px-8" >
@@ -117,12 +65,12 @@ function App() {
11765
11866 < JobsFiltersCard
11967 search = { search }
120- setSearch = { setSearch }
68+ setSearch = { handleSearchChange }
12169 keywordFilter = { keywordFilter }
122- setKeywordFilter = { setKeywordFilter }
70+ setKeywordFilter = { handleKeywordFilterChange }
12371 keywords = { keywords }
12472 selectedFile = { selectedFile }
125- setSelectedFile = { setSelectedFile }
73+ setSelectedFile = { handleSelectedFileChange }
12674 files = { files }
12775 loading = { loading }
12876 onRefresh = { ( ) => loadJobs ( selectedFile ) }
@@ -140,7 +88,7 @@ function App() {
14088 totalPages = { totalPages }
14189 pageSize = { pageSize }
14290 onPageChange = { setCurrentPage }
143- onPageSizeChange = { setPageSize }
91+ onPageSizeChange = { handlePageSizeChange }
14492 />
14593 </ section >
14694 </ main >
0 commit comments