Skip to content

Feature/add pagination#59

Merged
Benevanio merged 3 commits into
developfrom
feature/add-pagination
Apr 5, 2026
Merged

Feature/add pagination#59
Benevanio merged 3 commits into
developfrom
feature/add-pagination

Conversation

@Benevanio

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings April 5, 2026 13:38
@vercel

vercel Bot commented Apr 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
painel-vagas Ready Ready Preview, Comment Apr 5, 2026 1:38pm

@Benevanio
Benevanio merged commit 0eddcf1 into develop Apr 5, 2026
8 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in JobAtlas – Kanban Apr 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the jobs UI pagination experience in the frontend (numeric pagination controls + bounded page size) and adjusts backend Swagger initialization to be optional via dynamic import, alongside related test updates.

Changes:

  • Frontend: clamp/limit page size in useJobsPagination and update the jobs table footer to show a results summary + numeric pagination with arrows.
  • Frontend: update unit tests to reflect the new pagination UI and page-size constraints.
  • Backend: register Swagger docs via dynamic import and update server-entry tests accordingly.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Vagas Full Setup 1.0.0.exe Packaged installer artifact included in the PR.
package.json Adds Swagger dependencies to root dependencies list and formatting change in electron-builder config.
package-lock.json Locks Swagger dependency additions.
frontend/src/hooks/useJobsPagination.ts Introduces clampPageSize and changes default page size to 4 with bounds (1..10).
frontend/src/components/JobsTableCard.tsx Replaces old pagination controls with results summary + numeric pagination + page-size input.
frontend/src/App.tsx Removes unused jobs prop wiring to JobsTableCard.
frontend/tests/unit/utils/useJobsPagination.test.ts Adds act() usage and tests for page-size clamping.
frontend/tests/unit/components/JobsTableCard.test.tsx Updates tests for new pagination UI and results summary.
backend/src/server.js Makes Swagger docs registration optional via dynamic import; wraps startup in async flow.
backend/tests/unit/services/server.test.js Updates mocks/import flow to account for dynamic imports and startup behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/server.js
// Router (if used in future) works correctly.
app.use((req, res) => {
if (req.path.startsWith("/api")) {
return res.status(404).json({ error: "Rota não encontrada." });

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No fallback do SPA, a resposta 404 para rotas /api usa o campo error, mas o frontend lê mensagens de erro do campo message (ver frontend/src/services/jobsService.ts). Para manter consistência com o restante da API (jobsApiApp), retorne { message: "..." } (ou inclua ambos).

Suggested change
return res.status(404).json({ error: "Rota não encontrada." });
return res
.status(404)
.json({ message: "Rota não encontrada.", error: "Rota não encontrada." });

Copilot uses AI. Check for mistakes.
Comment thread backend/src/server.js
Comment on lines +19 to +24
const [{ default: swaggerUi }, { default: swaggerSpec }] = await Promise.all([
import("swagger-ui-express"),
import("./swagger.js"),
]);

app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swaggerSpec vem de ./swagger.js, que hoje resolve apis: [path.resolve("src/**/*.js")] (dependente de process.cwd()). No Electron o cwd costuma ser o app root, então o glob pode não encontrar backend/src/**/*.js e a documentação fica incompleta. Sugestão: ajustar o swagger.js para resolver o diretório baseado em import.meta.url (ou passar um caminho absoluto daqui do server).

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +277
<nav className="flex items-center gap-1 self-end md:self-auto" aria-label="Paginação">
<button
type="button"
aria-label="Pagina anterior"
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
className="flex h-9 min-w-9 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40"
>
<FiChevronLeft className="h-4 w-4" />
</button>

{paginationItems.map((item, index) => {
if (typeof item !== "number") {
return (
<span key={`${item}-${index}`} className="px-2 text-sm text-muted-foreground" aria-hidden="true">
</span>
);
}

const isCurrent = item === currentPage;

return (
<button
key={item}
type="button"
aria-current={isCurrent ? "page" : undefined}
onClick={() => onPageChange(item)}
className={cn(
"flex h-9 min-w-9 items-center justify-center rounded-full px-3 text-sm font-medium transition-colors",
isCurrent
? "bg-[#0c6b35] text-white shadow-sm"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
{item}
</button>
);
})}

<button
type="button"
aria-label="Pagina seguinte"
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
disabled={currentPage === totalPages}
className="flex h-9 min-w-9 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40"

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Os aria-labels da paginação estão sem acento ("Pagina"). Como o restante do UI usa PT-BR com acentuação (ex.: "Título"), ajuste para "Página anterior" / "Página seguinte" para consistência e melhor leitura por leitores de tela.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +14
function clampPageSize(value: number) {
if (!Number.isFinite(value)) {
return 1;
}

return Math.min(10, Math.max(1, Math.trunc(value)));

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O limite de page size (1..10) está “hardcoded” tanto no hook (clampPageSize) quanto no input (max={10}). Para evitar divergência futura, considere extrair esses limites para constantes compartilhadas (ex.: MIN_PAGE_SIZE/MAX_PAGE_SIZE) e reutilizá-las no hook, UI e testes.

Suggested change
function clampPageSize(value: number) {
if (!Number.isFinite(value)) {
return 1;
}
return Math.min(10, Math.max(1, Math.trunc(value)));
const MIN_PAGE_SIZE = 1;
const MAX_PAGE_SIZE = 10;
function clampPageSize(value: number) {
if (!Number.isFinite(value)) {
return MIN_PAGE_SIZE;
}
return Math.min(MAX_PAGE_SIZE, Math.max(MIN_PAGE_SIZE, Math.trunc(value)));

Copilot uses AI. Check for mistakes.
Comment on lines 31 to 65
currentPage: 1,
totalPages: 2,
pageSize: 25,
totalPages: 5,
pageSize: 4,
onPageChange: vi.fn(),
onPageSizeChange: vi.fn(),
};

describe("JobsTableCard", () => {
it("renderiza tabela com palavras-chave em tags e colunas de spam e lido", () => {
it("renderiza tabela com resumo, palavras-chave e colunas de spam e lido", () => {
render(<JobsTableCard {...baseProps} />);

expect(screen.getByText("Vagas Encontradas")).toBeInTheDocument();
expect(screen.getByText(/Resultados: 1 vaga encontrada/i)).toBeInTheDocument();
expect(screen.getByText("Abrir vaga")).toBeInTheDocument();
expect(screen.getByText("JavaScript")).toBeInTheDocument();
expect(screen.getByText("UX-UI")).toBeInTheDocument();
expect(screen.getByText(/spam/i)).toBeInTheDocument();
expect(screen.getByText(/lido/i)).toBeInTheDocument();
});

it("aciona paginacao", () => {
it("aciona paginacao numerica", () => {
render(<JobsTableCard {...baseProps} />);
fireEvent.click(screen.getByRole("button", { name: "Proxima" }));
expect(baseProps.onPageChange).toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "2" }));
expect(baseProps.onPageChange).toHaveBeenCalledWith(2);
});

it("exibe setas para voltar e avancar entre paginas", () => {
render(<JobsTableCard {...baseProps} currentPage={3} />);

fireEvent.click(screen.getByRole("button", { name: /pagina anterior/i }));
fireEvent.click(screen.getByRole("button", { name: /pagina seguinte/i }));

expect(baseProps.onPageChange).toHaveBeenCalledWith(2);
expect(baseProps.onPageChange).toHaveBeenCalledWith(4);
});

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Os testes reutilizam o mesmo mock baseProps.onPageChange entre casos sem limpar as chamadas. Isso pode mascarar falhas (um teste passar por causa de chamadas de um teste anterior). Sugestão: criar baseProps via factory por teste ou usar beforeEach(() => { baseProps.onPageChange.mockClear(); baseProps.onPageSizeChange.mockClear(); })/vi.clearAllMocks().

Copilot uses AI. Check for mistakes.
Comment thread backend/src/server.js
Comment on lines +51 to +55
app.listen(PORT, () => {
// eslint-disable-next-line no-console
console.log(`API de vagas rodando em http://localhost:${PORT}`);
console.log(`Documentação da API em http://localhost:${PORT}/docs`);
});

Copilot AI Apr 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O callback de listen sempre loga a URL de /docs, mesmo quando o Swagger falha ao registrar (o registerSwaggerDocs() apenas faz warn). Isso pode gerar logs enganosos e, com ELECTRON_STATIC_DIR, /docs pode cair no catch-all do SPA. Sugestão: fazer registerSwaggerDocs() retornar boolean e logar/registrar a rota apenas quando habilitada.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants