Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions frontend/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
# Arquitetura do Frontend

O frontend é organizado por domínios de negócio, não por tipos técnicos de arquivo.
O frontend usa Feature-Based Architecture. Cada funcionalidade mantém seus componentes, contratos e integrações; camadas internas existem somente quando necessárias.

## Camadas
## Organização

- `src/app`: composição da aplicação, providers, rotas e páginas de nível global.
- `src/domains/<domain>/domain`: tipos de negócio e regras puras, sem React ou HTTP.
- `src/domains/<domain>/application`: hooks React e casos de uso que orquestram regras de domínio.
- `src/domains/<domain>/infrastructure`: gateways de API e código específico de transporte.
- `src/domains/<domain>/presentation`: páginas e componentes pertencentes ao domínio.
- `src/shared`: primitivas de UI, assets, hooks e utilitários técnicos reutilizáveis.
- `src/app/routes`: definição das rotas e página de rota não encontrada.
- `src/app/providers`: composição dos providers globais.
- `src/app/layouts`: layout autenticado e seus componentes de navegação.
- `src/app/dashboard`: composição, navegação, providers e tema do painel atual.
- `src/features`: applications, auth, dashboard, help, home, jobs, marketing, mentoring, notifications e profile.
- `src/shared/api`: cliente HTTP compartilhado e tratamento de erros de API.
- `src/shared/utils`, `hooks`, `ui`, `assets` e `feedback`: utilitários, hooks, componentes, recursos e contexto de feedback reutilizáveis.
- `src/styles`: estilos globais.

> Exceção: o domínio `new_dashboard` ainda não segue integralmente essas 4 camadas. Hoje ele usa uma organização própria (`components/`, `context/`, `hooks/`, `infrastructure/`, `types/`, `utils/`, `layout.tsx`, `page.tsx`), sem pastas `domain/`, `application/` ou `presentation/` separadas. `auth`, `jobs` e `marketing` seguem o padrão de 4 camadas descrito acima.
## Convenções das funcionalidades

## Domínios
- `api/`: integrações HTTP; clientes e contratos existentes são preservados.
- `hooks/`: hooks de carregamento, persistência e estado.
- `components/` e `pages/`: componentes e páginas específicos da funcionalidade.
- `domain/` e `types/`: regras puras, schemas e contratos, quando necessários.
- `application/`: contextos e providers com responsabilidade de aplicação; não são hooks.
- Arquivos pequenos de contratos, eventos ou dados podem permanecer na raiz da funcionalidade. Não criar diretórios vazios.

- `auth`: estado de sessão, acesso à API de credenciais/OAuth e telas de login, registro e callback.
- `jobs`: entidades de vagas, filtros, deduplicação, paginação, acesso à API de vagas/scraper e UI de vagas.
- `marketing`: seções da landing page pública.
- `new_dashboard`: nova estrutura de painel do frontend. Contribuidores que procuram a experiência atual de dashboard devem começar por aqui.
O catálogo anterior mantém seu contrato em `features/jobs/types/job.types.ts`, distinto do contrato do painel em `features/jobs/domain/job.ts`. O contexto e os dados demonstrativos legados permanecem em `features/jobs/legacy`. Candidaturas, notas e Kanban pertencem a `features/applications`.

## Política de imports
## Imports

O código deve importar a partir de `@/app`, `@/domains` ou `@/shared`. Pastas técnicas antigas como `src/components`, `src/pages`, `src/hooks`, `src/services`, `src/context`, `src/lib` e `src/types` foram removidas para manter uma estrutura única orientada por domínio.
Preferir imports canônicos com `@/app`, `@/features` e `@/shared`. Preservar imports relativos locais quando claros. Código específico permanece na funcionalidade; somente recursos reutilizáveis pertencem a shared. Evitar dependências circulares. Rotas, exports, providers, APIs e modelos distintos são preservados pela organização.
10 changes: 5 additions & 5 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ Variáveis usadas:

## Organização

O frontend segue uma estrutura orientada por domínio:
O frontend segue uma arquitetura Feature-Based, com domínio separado quando necessário:

- `src/app`: composição da aplicação, providers, rotas e layouts autenticados.
- `src/domains/auth`: login, registro, callback OAuth e estado de sessão.
- `src/domains/jobs`: busca, filtros, paginação e interface de vagas.
- `src/domains/marketing`: landing page pública.
- `src/domains/new_dashboard`: nova estrutura de painel do frontend. É aqui que contribuidores devem procurar a experiência nova de dashboard, com abas de home, vagas, mentoring, perfil e ajuda.
- `src/features/auth`: login, registro, callback OAuth e estado de sessão.
- `src/features/jobs`: busca, filtros, paginação e interface de vagas, preservando os contratos distintos dos catálogos.
- `src/features/marketing`: landing page pública.
- `src/app/dashboard`: composição, providers, tema e navegação do painel. As funcionalidades de home, vagas, candidaturas, mentoria, perfil, ajuda e notificações estão em `src/features/`.
- `src/shared`: componentes de UI, assets, hooks e utilitários reutilizáveis.

Veja também [ARCHITECTURE.md](ARCHITECTURE.md).
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import { AppRoutes } from "@/app/AppRoutes";
import { AppRoutes } from "@/app/routes/AppRoutes";
import Loading from "@/shared/ui/Loading";

function App() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
import { useAuth } from "@/domains/auth/application/AuthContext";
import { useAuth } from "@/features/auth/application/AuthContext";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
import { DashboardTab } from "./components/dashboard/DashboardTab";
import { HelpTab } from "./components/help/HelpTab";
import { HomeTab } from "./components/home/HomeTab";
import { AddJobModal } from "./components/jobs/AddJobModal";
import { JobDetailModal } from "./components/jobs/JobDetailModal";
import { JobTab } from "./components/jobs/JobTab";
import { Header } from "./components/layout/Header";
import { MobileTabBar } from "./components/layout/MobileTabBar";
import { Sidebar } from "./components/layout/Sidebar";
import { MentoringTab } from "./components/mentoring/MentoringTab";
import { ProfileTab } from "./components/profile/ProfileTab";
import { Toast } from "./components/shared/Toast";
import { jobStatuses } from "./constants";
import { useDashboardJobs } from "./hooks/useDashboardJobs";
import { useUserDashboardData } from "./hooks/useUserDashboardData";
import { DashboardTab } from "@/features/dashboard/components/DashboardTab";
import { HelpTab } from "@/features/help/components/HelpTab";
import { HomeTab } from "@/features/home/components/HomeTab";
import { AddJobModal } from "@/features/applications/components/AddJobModal";
import { JobDetailModal } from "@/features/jobs/components/JobDetailModal";
import { JobTab } from "@/features/jobs/components/JobTab";
import { Header } from "@/app/dashboard/components/Header";
import { MobileTabBar } from "@/app/dashboard/components/MobileTabBar";
import { Sidebar } from "@/app/dashboard/components/Sidebar";
import { MentoringTab } from "@/features/mentoring/components/MentoringTab";
import { ProfileTab } from "@/features/profile/components/ProfileTab";
import { Toast } from "@/shared/ui/Toast";
import { jobStatuses } from "@/features/jobs/constants";
import { useDashboardJobs } from "@/features/jobs/hooks/useDashboardJobs";
import { useUserDashboardData } from "@/features/profile/hooks/useUserDashboardData";
import {
getJobFilterOptions,
FALLBACK_JOB_FAMILIES,
type JobFamilyOption,
} from "./infrastructure/dashboardJobsApi";
import type {
CareerChecklist,
Job,
JobModelFilter,
JobStatus,
MatchSort,
NewJob,
SearchPreferences,
TechnologyExperience,
UserProfile,
} from "./types";
} from "@/features/jobs/api/dashboardJobsApi";
import type { CareerChecklist, SearchPreferences, TechnologyExperience, UserProfile } from "@/features/profile/domain/user";
import type { Job, JobModelFilter, JobStatus, MatchSort, NewJob } from "@/features/jobs/domain/job";
import {
type ContinentFilter,
type CountryFilter,
} from "./utils/locationFilters";
} from "@/features/jobs/domain/locationFilters";
import {
getModelFilterFromJobTypes,
modelFilterToApiFilter,
} from "./utils/jobModelFilters";
import { parseSearchKeywords } from "./utils/searchKeywords";
} from "@/features/jobs/domain/jobModelFilters";
import { parseSearchKeywords } from "@/features/jobs/domain/searchKeywords";

function getSection(pathname: string) {
if (pathname.startsWith("/dashboard")) return "dashboard";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ReactNode } from "react";
import { JobsProvider } from "./context/JobsContext";
import { ThemeProvider } from "./context/ThemeContext";
import { ToastProvider } from "./context/ToastContext";
import { JobsProvider } from "@/features/jobs/legacy/application/JobsContext";
import { ThemeProvider } from "@/app/dashboard/theme/ThemeContext";
import { ToastProvider } from "@/shared/feedback/ToastContext";

export function NewDashboardLayout({ children }: { children: ReactNode }) {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useAuth } from "@/domains/auth/application/AuthContext";
import { useAuth } from "@/features/auth/application/AuthContext";
import { Bell, ChevronDown, Mail } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
Expand All @@ -7,14 +7,15 @@ import {
getDashboardNotificationFeed,
markDashboardNotificationsRead,
markSingleNotificationRead,
} from "../../infrastructure/notificationsApi";
import type { Message, Notification, UserProfile } from "../../types";
} from "@/features/notifications/api";
import type { Message, Notification } from "@/features/notifications/types";
import type { UserProfile } from "@/features/profile/domain/user";
import {
DASHBOARD_NOTIFICATIONS_REFRESH_EVENT,
type DashboardNotificationsRefreshDetail,
} from "../../utils/notificationEvents";
import { MessageDetailModal } from "./MessageDetailModal";
import { ThemeToggle } from "./ThemeToggle";
} from "@/features/notifications/events";
import { MessageDetailModal } from "@/features/notifications/components/MessageDetailModal";
import { ThemeToggle } from "@/app/dashboard/components/ThemeToggle";

interface HeaderProps {
title?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cn } from "@/shared/lib/utils";
import { cn } from "@/shared/utils/utils";
import { Link, useLocation } from "react-router-dom";
import { Icons } from "../shared/Icons";
import { Icons } from "@/app/dashboard/components/Icons";

const tabs = [
{ label: "Início", to: "/home", icon: Icons.Home },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { cn } from "@/shared/lib/utils";
import { cn } from "@/shared/utils/utils";
import { LogOut } from "lucide-react";
import { Link, useLocation } from "react-router-dom";
import { useAuth } from "@/domains/auth/application/AuthContext";
import { CandidateLogo } from "../shared/CandidateLogo";
import { Icons } from "../shared/Icons";
import { useAuth } from "@/features/auth/application/AuthContext";
import { CandidateLogo } from "@/app/dashboard/components/CandidateLogo";
import { Icons } from "@/app/dashboard/components/Icons";

const items = [
{ label: "Início", to: "/home", icon: Icons.Home },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useContext, type ReactNode } from "react";
import { useTheme as useSharedTheme } from "@/shared/hooks/useTheme";
import { darkColors, lightColors } from "../constants";
import { darkColors, lightColors } from "@/app/dashboard/theme/colors";

type ThemeContextValue = ReturnType<typeof useSharedTheme> & {
colors: typeof lightColors;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/dashboard/theme/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useDashboardTheme as useTheme } from "@/app/dashboard/theme/ThemeContext";
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useAuth } from "@/domains/auth/application/AuthContext";
import { useAuth } from "@/features/auth/application/AuthContext";
import { useTheme } from "@/shared/hooks/useTheme";
import { ThemeToggle } from "@/shared/ui/theme-toggle";
import { Bell, Mail } from "lucide-react";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AuthenticatedHeader } from "@/app/AuthenticatedHeader";
import { AuthenticatedSidebar } from "@/app/AuthenticatedSidebar";
import { AuthenticatedHeader } from "@/app/layouts/AuthenticatedHeader";
import { AuthenticatedSidebar } from "@/app/layouts/AuthenticatedSidebar";
import { Outlet } from "react-router-dom";

export function AuthenticatedLayout() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAuth } from "@/domains/auth/application/AuthContext";
import { cn } from "@/shared/lib/utils";
import { useAuth } from "@/features/auth/application/AuthContext";
import { cn } from "@/shared/utils/utils";
import {
BriefcaseBusiness,
ChartNoAxesColumn,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthProvider } from "@/domains/auth/application/AuthContext";
import { AuthProvider } from "@/features/auth/application/AuthContext";
import type { ReactNode } from "react";
import { BrowserRouter } from "react-router-dom";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import NotFound from "@/app/NotFound";
import { useAuth } from "@/domains/auth/application/AuthContext";
import AuthCallbackPage from "@/domains/auth/presentation/pages/AuthCallbackPage";
import LoginPage from "@/domains/auth/presentation/pages/LoginPage";
import RegisterPage from "@/domains/auth/presentation/pages/RegisterPage";
import LandingPage from "@/domains/marketing/presentation/pages/LandingPage";
import PrivacyPolicyPage from "@/domains/marketing/presentation/pages/PrivacyPolicyPage";
import TermsOfUsePage from "@/domains/marketing/presentation/pages/TermsOfUsePage";
import NewDashboardPage from "@/domains/new_dashboard/NewDashboardPage";
import NewDashboardLayout from "@/domains/new_dashboard/layout";
import NotFound from "@/app/routes/NotFound";
import { useAuth } from "@/features/auth/application/AuthContext";
import AuthCallbackPage from "@/features/auth/pages/AuthCallbackPage";
import LoginPage from "@/features/auth/pages/LoginPage";
import RegisterPage from "@/features/auth/pages/RegisterPage";
import LandingPage from "@/features/marketing/pages/LandingPage";
import PrivacyPolicyPage from "@/features/marketing/pages/PrivacyPolicyPage";
import TermsOfUsePage from "@/features/marketing/pages/TermsOfUsePage";
import NewDashboardPage from "@/app/dashboard/DashboardPage";
import NewDashboardLayout from "@/app/dashboard/DashboardProviders";
import Loading from "@/shared/ui/Loading";
import { Navigate, Route, Routes } from "react-router-dom";

Expand Down
File renamed without changes.

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/domains/new_dashboard/constants.ts

This file was deleted.

4 changes: 0 additions & 4 deletions frontend/src/domains/new_dashboard/constants/index.ts

This file was deleted.

Loading
Loading