Skip to content

Commit 561a756

Browse files
author
Romulo Marques
committed
Hide Items page from delivery; document CRUD as reference
Remove sidebar link and redirect /items to dashboard. Keep implementation as a blueprint for new CRUDs. README section lists entry points. E2E asserts redirect only. Made-with: Cursor
1 parent cae79b8 commit 561a756

File tree

4 files changed

+28
-131
lines changed

4 files changed

+28
-131
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ The input variables, with their default values (some auto generated) are:
206206
- `postgres_password`: (default: `"changethis"`) The password for the PostgreSQL database, stored in .env, you can generate one with the method above.
207207
- `sentry_dsn`: (default: "") The DSN for Sentry, if you are using it, you can set it later in .env.
208208

209+
## Example Items CRUD (reference)
210+
211+
The **Items** feature is a full vertical slice (API, models, UI with table and dialogs, generated client, tests) kept as a **reference for new CRUDs**.
212+
213+
For product delivery, **the Items page is not shown in the app**: the sidebar has no Items entry, and `/items` **redirects to the dashboard**. The code stays in the tree for you to copy or read.
214+
215+
Main entry points:
216+
217+
- **Backend:** `backend/app/api/routes/items.py`, `Item` in `backend/app/models.py`, helpers in `backend/app/crud.py`, tests in `backend/tests/api/routes/test_items.py`.
218+
- **Frontend:** `frontend/src/routes/_layout/items.tsx`, components under `frontend/src/components/Items/`, `ItemsService` in `frontend/src/client/`.
219+
- **E2E:** `frontend/tests/items.spec.ts` covers the redirect for the hidden route.
220+
209221
## Backend Development
210222

211223
Backend docs: [backend/README.md](./backend/README.md).

frontend/src/components/Sidebar/AppSidebar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Briefcase, Building2, Home, Users } from "lucide-react"
1+
import { Building2, Home, Users } from "lucide-react"
22

33
import type { UserRole } from "@/client"
44
import { SidebarAppearance } from "@/components/Common/Appearance"
@@ -16,7 +16,6 @@ import { User } from "./User"
1616

1717
const baseItems: Item[] = [
1818
{ icon: Home, title: "Dashboard", path: "/" },
19-
{ icon: Briefcase, title: "Items", path: "/items" },
2019
{ icon: Building2, title: "Cadastro PJ", path: "/companies" },
2120
]
2221

frontend/src/routes/_layout/items.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
/**
2+
* Items CRUD — reference for new CRUDs (see README “Example Items CRUD”).
3+
* For delivery the UI is hidden: no sidebar link; `/items` redirects to the dashboard.
4+
*/
15
import { useSuspenseQuery } from "@tanstack/react-query"
2-
import { createFileRoute } from "@tanstack/react-router"
6+
import { createFileRoute, redirect } from "@tanstack/react-router"
37
import { Search } from "lucide-react"
48
import { Suspense } from "react"
59

@@ -17,6 +21,9 @@ function getItemsQueryOptions() {
1721
}
1822

1923
export const Route = createFileRoute("/_layout/items")({
24+
beforeLoad: () => {
25+
throw redirect({ to: "/" })
26+
},
2027
component: Items,
2128
head: () => ({
2229
meta: [

frontend/tests/items.spec.ts

Lines changed: 7 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,11 @@
11
import { expect, test } from "@playwright/test"
2-
import { createUser } from "./utils/privateApi"
3-
import {
4-
randomEmail,
5-
randomItemDescription,
6-
randomItemTitle,
7-
randomPassword,
8-
} from "./utils/random"
9-
import { logInUser } from "./utils/user"
102

11-
test("Items page is accessible and shows correct title", async ({ page }) => {
3+
test("Items route redirects to dashboard (page hidden for delivery)", async ({
4+
page,
5+
}) => {
126
await page.goto("/items")
13-
await expect(page.getByRole("heading", { name: "Items" })).toBeVisible()
14-
await expect(page.getByText("Create and manage your items")).toBeVisible()
15-
})
16-
17-
test("Add Item button is visible", async ({ page }) => {
18-
await page.goto("/items")
19-
await expect(page.getByRole("button", { name: "Add Item" })).toBeVisible()
20-
})
21-
22-
test.describe("Items management", () => {
23-
test.use({ storageState: { cookies: [], origins: [] } })
24-
let email: string
25-
const password = randomPassword()
26-
27-
test.beforeAll(async () => {
28-
email = randomEmail()
29-
await createUser({ email, password })
30-
})
31-
32-
test.beforeEach(async ({ page }) => {
33-
await logInUser(page, email, password)
34-
await page.goto("/items")
35-
})
36-
37-
test("Create a new item successfully", async ({ page }) => {
38-
const title = randomItemTitle()
39-
const description = randomItemDescription()
40-
41-
await page.getByRole("button", { name: "Add Item" }).click()
42-
await page.getByLabel("Title").fill(title)
43-
await page.getByLabel("Description").fill(description)
44-
await page.getByRole("button", { name: "Save" }).click()
45-
46-
await expect(page.getByText("Item created successfully")).toBeVisible()
47-
await expect(page.getByText(title)).toBeVisible()
48-
})
49-
50-
test("Create item with only required fields", async ({ page }) => {
51-
const title = randomItemTitle()
52-
53-
await page.getByRole("button", { name: "Add Item" }).click()
54-
await page.getByLabel("Title").fill(title)
55-
await page.getByRole("button", { name: "Save" }).click()
56-
57-
await expect(page.getByText("Item created successfully")).toBeVisible()
58-
await expect(page.getByText(title)).toBeVisible()
59-
})
60-
61-
test("Cancel item creation", async ({ page }) => {
62-
await page.getByRole("button", { name: "Add Item" }).click()
63-
await page.getByLabel("Title").fill("Test Item")
64-
await page.getByRole("button", { name: "Cancel" }).click()
65-
66-
await expect(page.getByRole("dialog")).not.toBeVisible()
67-
})
68-
69-
test("Title is required", async ({ page }) => {
70-
await page.getByRole("button", { name: "Add Item" }).click()
71-
await page.getByLabel("Title").fill("")
72-
await page.getByLabel("Title").blur()
73-
74-
await expect(page.getByText("Title is required")).toBeVisible()
75-
})
76-
77-
test.describe("Edit and Delete", () => {
78-
let itemTitle: string
79-
80-
test.beforeEach(async ({ page }) => {
81-
itemTitle = randomItemTitle()
82-
83-
await page.getByRole("button", { name: "Add Item" }).click()
84-
await page.getByLabel("Title").fill(itemTitle)
85-
await page.getByRole("button", { name: "Save" }).click()
86-
await expect(page.getByText("Item created successfully")).toBeVisible()
87-
await expect(page.getByRole("dialog")).not.toBeVisible()
88-
})
89-
90-
test("Edit an item successfully", async ({ page }) => {
91-
const itemRow = page.getByRole("row").filter({ hasText: itemTitle })
92-
await itemRow.getByRole("button").last().click()
93-
await page.getByRole("menuitem", { name: "Edit Item" }).click()
94-
95-
const updatedTitle = randomItemTitle()
96-
await page.getByLabel("Title").fill(updatedTitle)
97-
await page.getByRole("button", { name: "Save" }).click()
98-
99-
await expect(page.getByText("Item updated successfully")).toBeVisible()
100-
await expect(page.getByText(updatedTitle)).toBeVisible()
101-
})
102-
103-
test("Delete an item successfully", async ({ page }) => {
104-
const itemRow = page.getByRole("row").filter({ hasText: itemTitle })
105-
await itemRow.getByRole("button").last().click()
106-
await page.getByRole("menuitem", { name: "Delete Item" }).click()
107-
108-
await page.getByRole("button", { name: "Delete" }).click()
109-
110-
await expect(
111-
page.getByText("The item was deleted successfully"),
112-
).toBeVisible()
113-
await expect(page.getByText(itemTitle)).not.toBeVisible()
114-
})
115-
})
116-
})
117-
118-
test.describe("Items empty state", () => {
119-
test.use({ storageState: { cookies: [], origins: [] } })
120-
121-
test("Shows empty state message when no items exist", async ({ page }) => {
122-
const email = randomEmail()
123-
const password = randomPassword()
124-
await createUser({ email, password })
125-
await logInUser(page, email, password)
126-
127-
await page.goto("/items")
128-
129-
await expect(page.getByText("You don't have any items yet")).toBeVisible()
130-
await expect(page.getByText("Add a new item to get started")).toBeVisible()
131-
})
7+
await expect(page).toHaveURL("/")
8+
await expect(
9+
page.getByText("Welcome back, nice to see you again!"),
10+
).toBeVisible()
13211
})

0 commit comments

Comments
 (0)