feat: implement customizable funnel stages per tenant

- Modified attendances.funnel_stage in DB from ENUM to VARCHAR.

- Created tenant_funnels table and backend API routes to manage custom stages.

- Added /admin/funnels page for Admins/Managers to create, edit, order, and color-code their funnel stages.

- Updated Dashboard, UserDetail, and AttendanceDetail to fetch and render dynamic funnel stages instead of hardcoded enums.

- Added defensive checks and logging to GET /users/:idOrSlug to fix sporadic 500 errors during impersonation handoffs.
This commit is contained in:
Cauê Faleiros
2026-03-13 10:25:23 -03:00
parent 1d49161a05
commit 7ab54053db
18 changed files with 588 additions and 33 deletions

20
debug3.txt Normal file
View File

@@ -0,0 +1,20 @@
Wait, let's look at `index.js` line 354 BEFORE my fix:
```javascript
const [rows] = await pool.query('SELECT * FROM users WHERE id = ? OR slug = ?', [req.params.idOrSlug, req.params.idOrSlug]);
if (rows.length === 0) return res.status(404).json({ error: 'Not found' });
if (req.user.role !== 'super_admin' && rows[0].tenant_id !== req.user.tenant_id) {
```
The ONLY way this throws 500 is if:
1. `pool.query` fails (e.g. database disconnected, which isn't the case).
2. `req.user` is somehow null or undefined (but `authenticateToken` guarantees it exists).
3. `rows[0]` is undefined (but `rows.length === 0` handles that).
Wait, what if `req.user.tenant_id` is null? `null !== 'tenant_xyz'` is true, so it returns 403, not 500.
What if the 500 is coming from `GET /api/users/u_71657ec7` but it's not actually hitting `/users/:idOrSlug`?
Is there a middleware or something? No.
Ah! What if the user you are impersonating was deleted from the database? `rows.length === 0` -> returns 404, not 500.
Let's check the local logs AGAIN after my recent rebuild. I added `console.error('Error in GET /users/:idOrSlug:', error);`
Let's deliberately trigger the error locally to see it. But I don't have the browser.