Files
descrybe/docs/admin-roles-support/13-support-ratings.md
T
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

91 lines
2.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 13 — Support ticket ratings / CSAT
**Owner:** agent 13
**Depends on:** agent 12 schema (`030_support_desk.sql``support_csat_ratings`)
**Status:** implemented (API + tests)
## Goal
Customers rate **resolved** or **closed** tickets once (score 15 + optional comment). Platform admins see aggregate CSAT (no PII).
## Schema (agent 12)
Table `support_csat_ratings`:
| Column | Notes |
|--------|--------|
| `ticket_id` | `UNIQUE` — one rating per ticket |
| `company_id`, `user_id` | owner + tenant |
| `score` | `SMALLINT` 15 |
| `comment` | optional, max 2000 runes (truncated) |
| `created_at` | UTC |
Ticket columns `csat_token_hash` / `csat_invite_sent_at` exist for optional public token flow (not wired in this agent).
## Rules
1. Only `created_by_user_id` may rate (others → `404`).
2. Status must be `resolved` or `closed` else `400` (`ticket not eligible for rating`).
3. Duplicate insert → `409` (`already rated`).
4. Logs: `ticket_id` + `score` only — **never** comment, email, or names.
## APIs
| Method | Path | Auth | Response |
|--------|------|------|----------|
| `POST` | `/api/support/tickets/{id}/csat` | session + company | `201` `SupportCsat` |
| `GET` | `/api/support/tickets/{id}` | customer | additive `csat` / `csat_eligible` |
| `GET` | `/api/admin/support/tickets/{id}` | support desk | includes `csat` when present |
| `GET` | `/api/admin/support/csat?from=&to=` | platform admin | aggregate |
### Customer submit body
```json
{ "score": 4, "comment": "optional" }
```
### Admin aggregate
```json
{
"total": 12,
"average": 4.25,
"distribution": { "1": 0, "2": 1, "3": 2, "4": 4, "5": 5 },
"from": null,
"to": null
}
```
`from` / `to` are optional RFC3339 bounds on `created_at` (`to` exclusive).
## Code map
| Piece | Path |
|-------|------|
| Service | `apps/api/internal/support/ratings.go` |
| Types | `CSATInput`, `CSATRating`, `CSATAggregate` in `types.go` |
| Handlers | `apps/api/internal/httpapi/support_csat_handlers.go` |
| Routes | customer POST csat; admin GET `/api/admin/support/csat` |
| Unit tests | `ratings_test.go`, `support_csat_auth_test.go` |
| Integration | `ratings_integration_test.go` (needs `DATABASE_URL` + goose) |
## Verification
```bash
cd apps/api
go test ./internal/support/ -count=1 -run 'CSAT|NormalizeCSAT|ClientErrorCSAT'
go test ./internal/httpapi/ -count=1 -run 'SupportCSAT|SupportTicketCRUDAuth'
```
With DB migrated:
```bash
go test ./internal/support/ -count=1 -run TestSubmitCSATOwnershipAndOnce
```
## Out of scope (this agent)
- Public token CSAT (`POST /api/public/support/csat`)
- User/staff UI panels (agents 1415)
- Email CSAT invite stubs