Skip to content

Commit e53add6

Browse files
authored
Develop (#107)
2 parents 5df1eee + 617873f commit e53add6

34 files changed

Lines changed: 18588 additions & 471 deletions

frontend/components.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "radix-nova",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.js",
8+
"css": "src/styles/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"iconLibrary": "lucide",
14+
"rtl": false,
15+
"aliases": {
16+
"components": "@/components",
17+
"utils": "@/lib/utils",
18+
"ui": "@/components/ui",
19+
"lib": "@/lib",
20+
"hooks": "@/hooks"
21+
},
22+
"menuColor": "default",
23+
"menuAccent": "subtle",
24+
"registries": {
25+
"@animate-ui": "https://animate-ui.com/r/{name}.json"
26+
}
27+
}

frontend/index.html

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,37 @@
55
<!--C:\Users\benev\projetos\Node\vagas-full\frontend\src\assets\logo-painel-vagas.svg-->
66
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
77
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
89
<script>
910
(function () {
10-
const storageKey = "jobs-theme-preference";
1111
const root = document.documentElement;
12+
13+
14+
const storedPreference =
15+
localStorage.getItem("jobs-theme-preference") ||
16+
localStorage.getItem("theme") ||
17+
localStorage.getItem("vite-ui-theme");
18+
1219
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
13-
const storedPreference = localStorage.getItem(storageKey);
20+
1421
const themePreference = storedPreference === "light" || storedPreference === "dark" || storedPreference === "system"
1522
? storedPreference
1623
: "system";
17-
const resolvedTheme = themePreference === "system" ? (prefersDark ? "dark" : "light") : themePreference;
1824

19-
root.classList.toggle("dark", resolvedTheme === "dark");
25+
const resolvedTheme = themePreference === "system"
26+
? (prefersDark ? "dark" : "light")
27+
: themePreference;
28+
29+
if (resolvedTheme === "dark") {
30+
root.classList.add("dark");
31+
} else {
32+
root.classList.remove("dark");
33+
}
34+
2035
root.setAttribute("data-theme", resolvedTheme);
2136
})();
2237
</script>
38+
2339
<title>Painel de Vagas</title>
2440
</head>
2541
<body>

frontend/package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@
1313
"test:watch": "vitest --watch"
1414
},
1515
"dependencies": {
16+
"@fontsource-variable/geist": "^5.2.9",
17+
"@lottiefiles/dotlottie-react": "^0.19.4",
1618
"class-variance-authority": "^0.7.1",
1719
"clsx": "^2.1.1",
20+
"framer-motion": "^12.40.0",
21+
"lottie-react": "^2.4.1",
1822
"lucide-react": "^0.577.0",
23+
"motion": "^12.40.0",
24+
"radix-ui": "^1.4.3",
1925
"react": "^19.2.4",
2026
"react-dom": "^19.2.4",
2127
"react-icons": "^5.6.0",
28+
"react-router-dom": "^7.15.1",
2229
"rolldown": "^1.0.0-rc.10",
23-
"tailwind-merge": "^3.5.0"
30+
"shadcn": "^4.8.1",
31+
"tailwind-merge": "^3.6.0",
32+
"tw-animate-css": "^1.4.0"
2433
},
2534
"devDependencies": {
2635
"@eslint/js": "^9.39.4",

frontend/src/App.tsx

Lines changed: 21 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,31 @@
1-
import { JobsFiltersCard } from "@/components/JobsFiltersCard";
2-
import { JobsHeaderCard } from "@/components/JobsHeaderCard";
3-
import { JobsTableCard } from "@/components/JobsTableCard";
4-
import { Button } from "@/components/ui/button";
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";
9-
import { useCallback, type SetStateAction } from "react";
10-
import { FiRefreshCw } from "react-icons/fi";
11-
12-
function formatDate(timestamp: JobsMeta["modifiedAt"]): string {
13-
if (!timestamp) {
14-
return "-";
15-
}
16-
return new Date(timestamp).toLocaleString("pt-BR");
17-
}
1+
import { useState, useEffect } from "react";
2+
import { Route, Routes } from "react-router-dom";
3+
import LandingPage from "./pages/LandingPage";
4+
import Dashboard from "./pages/Dashboard";
5+
import NotFound from "./not_found";
6+
import Loading from "./Loading";
187

198
function App() {
20-
const { files, selectedFile, setSelectedFile, jobs, meta, loading, scraping, error, triggerScraper } =
21-
useJobsData();
22-
23-
const { search, setSearch, keywordFilter, setKeywordFilter, keywords, filteredJobs } = useJobsFiltering(jobs);
9+
const [appCarregando, setAppCarregando] = useState(true);
2410

25-
const { currentPage, setCurrentPage, pageSize, setPageSize, resetPagination, totalPages, paginatedJobs } =
26-
useJobsPagination({
27-
filteredJobs,
28-
});
11+
useEffect(() => {
12+
const timer = setTimeout(() => {
13+
setAppCarregando(false);
14+
}, 2000);
2915

30-
const handleSearchChange = useCallback(
31-
(value: SetStateAction<string>) => {
32-
setSearch((previous) => (typeof value === "function" ? value(previous) : value));
33-
resetPagination();
34-
},
35-
[setSearch, resetPagination],
36-
);
37-
38-
const handleKeywordFilterChange = useCallback(
39-
(value: SetStateAction<string[]>) => {
40-
setKeywordFilter((previous) => (typeof value === "function" ? value(previous) : value));
41-
resetPagination();
42-
},
43-
[setKeywordFilter, resetPagination],
44-
);
16+
return () => clearTimeout(timer);
17+
}, []);
4518

46-
const handleSelectedFileChange = useCallback(
47-
(value: SetStateAction<string>) => {
48-
setSelectedFile((previous) => (typeof value === "function" ? value(previous) : value));
49-
resetPagination();
50-
},
51-
[setSelectedFile, resetPagination],
52-
);
53-
54-
const handlePageSizeChange = useCallback(
55-
(value: number) => {
56-
setPageSize(value);
57-
resetPagination();
58-
},
59-
[setPageSize, resetPagination],
60-
);
61-
62-
const handleScraper = useCallback(() => {
63-
void triggerScraper();
64-
}, [triggerScraper]);
19+
if (appCarregando) {
20+
return <Loading />;
21+
}
6522

6623
return (
67-
<main className="relative min-h-screen overflow-hidden bg-background px-4 transition-colors duration-300 md:px-8">
68-
<div className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(circle_at_20%_15%,rgba(236,195,117,0.35),transparent_30%),radial-gradient(circle_at_80%_10%,rgba(92,151,191,0.28),transparent_35%),radial-gradient(circle_at_50%_95%,rgba(201,120,99,0.22),transparent_40%)] dark:bg-[radial-gradient(circle_at_20%_15%,rgba(240,180,95,0.18),transparent_30%),radial-gradient(circle_at_80%_10%,rgba(92,151,191,0.18),transparent_35%),radial-gradient(circle_at_50%_95%,rgba(201,120,99,0.15),transparent_40%)]" />
69-
70-
<section className="mx-auto flex w-full flex-col gap-6">
71-
<JobsHeaderCard />
72-
73-
<JobsFiltersCard
74-
search={search}
75-
setSearch={handleSearchChange}
76-
keywordFilter={keywordFilter}
77-
setKeywordFilter={handleKeywordFilterChange}
78-
keywords={keywords}
79-
selectedFile={selectedFile}
80-
setSelectedFile={handleSelectedFileChange}
81-
files={files}
82-
meta={meta}
83-
actions={
84-
<>
85-
<Button
86-
onClick={handleScraper}
87-
disabled={scraping}
88-
className="h-12 md:h-14 w-full sm:w-auto rounded-xl md:rounded-2xl bg-[#0c6b35] px-6 text-base text-white shadow-sm hover:bg-[#0a5b2d] whitespace-nowrap flex items-center gap-2" >
89-
<FiRefreshCw className={`h-4 w-4 ${scraping ? "animate-spin" : ""}`} />
90-
{scraping ? "Buscando vagas..." : "Buscar vagas"}
91-
</Button>
92-
</>
93-
}
94-
/>
95-
96-
<JobsTableCard
97-
meta={meta}
98-
filteredJobs={filteredJobs}
99-
paginatedJobs={paginatedJobs}
100-
loading={loading || scraping}
101-
error={error}
102-
formatDate={formatDate}
103-
currentPage={currentPage}
104-
totalPages={totalPages}
105-
pageSize={pageSize}
106-
onPageChange={setCurrentPage}
107-
onPageSizeChange={handlePageSizeChange}
108-
/>
109-
</section>
110-
</main>
24+
<Routes>
25+
<Route path="/" element={<LandingPage />} />
26+
<Route path="/app" element={<Dashboard />} />
27+
<Route path="*" element={<NotFound />} />
28+
</Routes>
11129
);
11230
}
11331

frontend/src/Loading.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
2+
import loadingAnimation from "./assets/teste.json";
3+
4+
export default function Loading() {
5+
return (
6+
<div className="fixed inset-0 z-50 flex flex-col items-center justify-center bg-background px-6">
7+
<div className="w-64 h-64 flex items-center justify-center select-none pointer-events-none">
8+
<DotLottieReact
9+
data={loadingAnimation}
10+
loop
11+
autoplay
12+
className="w-full h-full object-contain"
13+
/>
14+
</div>
15+
<p className="mt-4 text-lg font-medium text-muted-foreground animate-pulse">
16+
Carregando...
17+
</p>
18+
</div>
19+
);
20+
}

frontend/src/assets/amazon.png

91.6 KB
Loading

0 commit comments

Comments
 (0)