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
6 changes: 3 additions & 3 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 17 additions & 5 deletions e2e/tests/assistants.e2e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,23 @@ test("Chat: mobile não transborda e o campo de pergunta é acessível por tecla
await page.goto(`${APP}/chat`);
await expect(page.getByLabel("Sua pergunta")).toBeVisible();
await assertNoHorizontalOverflow(page, "chat mobile");
await page.getByRole("button", { name: "Nova conversa" }).focus();
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
const focused = await page.evaluate(() => document.activeElement?.id || document.activeElement?.tagName);
assert.ok(focused);
const sidebarToggle = page.getByRole("button", { name: "Conversas e projeto", exact: true });
await expect(sidebarToggle).toHaveAttribute("aria-expanded", "false");
assert.equal(await page.getByRole("button", { name: "Nova conversa", exact: true }).isVisible(), false);
await sidebarToggle.focus();
await page.keyboard.press("Enter");
await expect(page.getByRole("button", { name: "Ocultar conversas e projeto", exact: true })).toHaveAttribute("aria-expanded", "true");
await expect(page.getByRole("button", { name: "Nova conversa", exact: true })).toBeVisible();
await page.keyboard.press("Enter");
await expect(sidebarToggle).toHaveAttribute("aria-expanded", "false");
for (let step = 0; step < 12; step += 1) {
await page.keyboard.press("Tab");
if (await page.evaluate(() => document.activeElement?.id === "chat-question")) break;
}
assert.equal(await page.evaluate(() => document.activeElement?.id), "chat-question");
await page.keyboard.type("Pergunta digitada pelo teclado");
assert.equal(await page.getByLabel("Sua pergunta").inputValue(), "Pergunta digitada pelo teclado");
await assertNoHorizontalOverflow(page, "chat mobile com painel recolhido");
} finally {
await context.close();
}
Expand Down
2 changes: 1 addition & 1 deletion e2e/tests/hierarchy-ui.e2e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ test("Épico → feature → PBI pela interface: validação, qualidade em tempo
await expect(page.getByText("Login válido")).toBeVisible();

await page.getByRole("button", { name: "Marcar como concluído" }).click();
await expect(page.getByText(/Concluído/i).first()).toBeVisible();
await expect(page.getByText("concluido", { exact: true }).first()).toBeVisible();
} finally {
await context.close();
}
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ export const App: React.FC = () => {
const { session, logout } = useAuth();
const [pathname, setPathname] = useState(window.location.pathname);

const [menuOpen, setMenuOpen] = useState(false);

const isAdmin = session.user?.role === "admin";
const activeTab = isProjectPath(pathname)
? "projects"
: (Object.keys(routes) as Array<keyof typeof routes>).find((key) => routes[key] === pathname) || "projects";

const setActiveTab = (tab: keyof typeof routes) => navigate(routes[tab]);
const setActiveTab = (tab: keyof typeof routes) => { setMenuOpen(false); navigate(routes[tab]); };

useEffect(() => {
const update = () => setPathname(window.location.pathname);
const update = () => { setPathname(window.location.pathname); setMenuOpen(false); };
window.addEventListener("popstate", update);
return () => window.removeEventListener("popstate", update);
}, []);
Expand All @@ -38,7 +40,8 @@ export const App: React.FC = () => {
S<b>•</b>NAPSE
</a>

<nav className="nav-links" aria-label="Navegação principal">
<Button className="mobile-menu-toggle" variant="secondary" aria-expanded={menuOpen} aria-controls="main-navigation" onClick={() => setMenuOpen(value => !value)}>Menu</Button>
<nav id="main-navigation" className={`nav-links ${menuOpen ? "is-open" : ""}`} aria-label="Navegação principal">
<button
onClick={() => setActiveTab("projects")}
className={activeTab === "projects" ? "active" : ""}
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/assets/styles/chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@
.chat-conversations { max-height: 200px; }
.chat-message { max-width: 100%; }
}

.chat-sidebar-toggle { display: none; }

@media (max-width: 900px) {
.chat-sidebar-toggle { display: inline-flex; margin: 16px 0 0; }
.chat-sidebar { display: none; }
.chat-sidebar.is-open { display: flex; }
.chat-thread { min-height: 400px; }
.chat-messages { min-height: 180px; max-height: 55dvh; }
}
238 changes: 235 additions & 3 deletions frontend/src/assets/styles/projects.css
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
gap: 20px;
}

.project-card {
.ds-card.project-card {
padding: 24px;
display: grid;
gap: 12px;
Expand Down Expand Up @@ -140,15 +140,15 @@
overflow: hidden;
}

.projects-state {
.ds-card.projects-state {
padding: 40px 24px;
display: grid;
justify-items: center;
gap: 16px;
text-align: center;
}

.project-form {
.ds-card.project-form {
max-width: 720px;
padding: 28px;
display: grid;
Expand Down Expand Up @@ -681,3 +681,235 @@
rgba(36, 190, 219, 0);
}
}

.app-content > .projects-page {
max-width: 1320px;
margin-inline: auto;
padding: 40px;
}

.project-card > .ds-badge {
justify-self: start;
}

@media (max-width: 640px) {
.app-content > .projects-page {
padding: 24px 16px;
}
}
.app-content > .projects-page {
max-width: 1200px;
}

.ds-card.quality-panel {
padding: 24px;
border-left-color: var(--brand-primary);
}

.quality-checklist {
grid-template-columns: repeat(2, minmax(0, 1fr));
}

.quality-check-item {
grid-template-columns: 22px minmax(0, 1fr);
padding: 16px;
}

.quality-check-action {
grid-column: 2;
}

.quality-badge-tag.satisfied {
color: #86efac;
background: rgba(34, 197, 94, .1);
border: 1px solid rgba(34, 197, 94, .25);
}

.pbi-story-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin: 8px 0;
}

.pbi-story-field {
min-width: 0;
padding: 18px;
border: 1px solid var(--border-subtle);
border-radius: 10px;
background: rgba(255, 255, 255, .025);
}

.project-card .pbi-story-field dt {
margin: 0 0 10px;
font-size: .75rem;
letter-spacing: .04em;
font-weight: 600;
}

.pbi-story-field dd {
margin: 0;
line-height: 1.6;
white-space: pre-wrap;
}

.projects-page > .projects-page > .projects-grid {
grid-template-columns: minmax(0, 1fr);
list-style: none;
margin: 0;
padding: 0;
}

.project-card dl dd {
margin-inline-start: 0;
line-height: 1.6;
}

@media (max-width: 760px) {
.quality-checklist,
.pbi-story-grid {
grid-template-columns: minmax(0, 1fr);
}

.ds-card.quality-panel,
.ds-card.project-card {
padding: 20px;
}

.projects-heading {
flex-wrap: wrap;
align-items: flex-start;
}
}

.ds-card.item-history-panel {
padding: 28px;
display: grid;
gap: 18px;
}

.history-list {
list-style: none;
margin: 8px 0 0;
padding: 0 0 0 24px;
border-left: 1px solid var(--border-subtle);
display: grid;
gap: 20px;
}

.history-item-card {
position: relative;
min-width: 0;
padding: 22px;
border: 1px solid var(--border-subtle);
border-radius: 12px;
background: rgba(10, 13, 20, .45);
}

.history-item-card::before {
content: "";
position: absolute;
top: 27px;
left: -30px;
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--brand-primary);
box-shadow: 0 0 0 4px var(--bg-card);
}

.history-entry-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 8px 16px;
align-items: center;
}

.history-entry-header time {
color: var(--text-secondary);
font-size: .8rem;
}

.history-entry-action {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin: 10px 0 18px;
}

.history-action-label {
font-size: 1rem;
font-weight: 600;
color: var(--brand-primary);
}

.history-change-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin: 0 0 18px;
}

.history-change-list > div {
padding: 12px 14px;
border-radius: 8px;
background: rgba(255, 255, 255, .025);
border: 1px solid var(--border-subtle);
min-width: 0;
}

.history-change-list dt {
font-size: .75rem;
color: var(--text-secondary);
margin-bottom: 6px;
}

.history-change-list dd {
margin: 0;
white-space: pre-wrap;
overflow-wrap: anywhere;
line-height: 1.6;
}

.history-justification {
border-left: 3px solid var(--brand-primary);
padding: 12px 16px;
background: rgba(249, 115, 22, .06);
border-radius: 6px;
line-height: 1.6;
overflow-wrap: anywhere;
}

.history-no-justification {
color: var(--text-secondary);
font-size: .8rem;
line-height: 1.5;
}

@media (max-width: 640px) {
.ds-card.item-history-panel { padding: 20px 16px; }
.history-list { padding-left: 18px; }
.history-item-card { padding: 16px; }
.history-item-card::before { left: -24px; }
.history-change-list { grid-template-columns: minmax(0, 1fr); }
}

.project-filter { margin-block: 8px; }
.project-field textarea { min-height: 88px; }
.project-form .project-actions { padding-top: 8px; border-top: 1px solid var(--border-subtle); }
.project-filter select { min-height: 44px; }
.project-filters { margin-bottom: 24px; }

.projects-page select {
min-width: 0;
min-height: 44px;
max-width: 100%;
padding: 10px 12px;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
background: var(--bg-secondary);
color: var(--text-primary);
font: inherit;
}
10 changes: 9 additions & 1 deletion frontend/src/styles/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,18 @@
}

.ds-card--glass {
padding: 0;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: var(--shadow-md);
transition: border-color var(--transition-normal);
}
.ds-card--glass:hover { border-color: var(--border-active); }

:where(button, a, input, select, textarea, summary):focus-visible {
outline: 2px solid var(--accent-secondary);
outline-offset: 3px;
}

@media (max-width: 640px) {
.ds-button { min-height: 44px; }
}
Loading
Loading