AS-IS — CRUD de Vitrines
Data: 2026-07-09
Spike: 198299
Glossário: CONTEXT.md
Pesquisas: research-showcase-api.md · research-zzapp.md · research-portal.md · research-showcase-web.md · research-jobs.md
1. Mapa de tipos e atores
| Tipo domínio | Enum API | Quem cria | Quem edita | UI principal | Índice ES |
|---|---|---|---|---|---|
| Vitrine de loja | Store | Vendedor | Vendedor | zzapp — Minhas vitrines | arezzo-showcase |
| Vitrine por histórico | Recommendation | Vendedor | Vendedor | zzapp — Histórico de compras | arezzo-showcase-recommend |
| Vitrine da marca | Brand | Ops/merchan | Ops | zzportal — Vitrines | arezzo-showcase-brand |
| Vitrine marca–vendedor | BrandSeller | Vendedor (share) | — | zzapp — share da marca | arezzo-showcase-brand-seller |
Campo de prazo: DateExpiration em todos (herdado de ElasticBaseIndex).
Exclusão: soft-delete via DateDeleted (ortogonal à expiração).
2. Diagrama de contexto
┌─────────────────────┐
│ coezzion-portal │ cria/edita Brand
│ (zzportal) │
└──────────┬──────────┘
│ HTTP
┌──────────────┐ ┌──────────▼──────────┐ ┌────────────────────┐
│ zzapp │───►│ service-showcase │◄───│ service-jobs │
│ Minhas │ │ Elasticsearch │ │ brand status/temp │
│ Histórico │ │ Store/Rec/Brand/* │ │ product sync SQS │
│ Marca (read) │ └──────────┬──────────┘ └────────────────────┘
└──────────────┘ │
│ GET public
┌──────────▼──────────┐
│ showcase-web │ cliente final
│ (sem UX de expira) │
└─────────────────────┘
3. CRUD — Vitrine de loja (Store / Minhas)
3.1 Create
| Camada | Comportamento |
|---|---|
| UI zzapp | Default expirationDate = now/start + 31d; picker min amanhã, max now+31 |
| Request | POST api/show-case-new — dateExpiration opcional (DateTime?) |
| Handler | SetDateExpiration(date) → se null, DateCreated + 31d |
| Validação API | Sem regra FluentValidation de data |
| Resultado | { id, url } |
3.2 Read
| Lista app | GET .../store/{storeId} — só ativas (sem delete, DateExpiration >= now/d) |
| Detalhe app | GET .../app/{id} |
| Público | GET .../{id} → GetActiveObject (null se expirada/deletada) |
| showcase-web | Empty “Vitrine não encontrada” se null |
3.3 Update
| UI | Nome, produtos, data (campo só se detail já tem data) |
| Request | PUT api/show-case-new — app envia dateExpiration sempre (tipo non-null no Dart update) |
| Handler | Reconstrói modelo + SetDateExpiration |
3.4 Delete
| Request | PUT api/show-case-new/delete |
| Efeito | DateDeleted = now (soft). Documento permanece no ES |
3.5 Expiração Store (AS-IS)
- Não há job de bulk-expire.
- Após
DateExpiration, some de lista/get ativo e do link público. - Default/máximo prático: ~31 dias (UI + fallback server).
4. CRUD — Vitrine por histórico (Recommendation)
4.1 Create
| Camada | Comportamento |
|---|---|
| UI | Customer → produtos → check; data obrigatória na UI; default +31; max +31 |
| Request | POST .../recommend — dateExpiration required no command |
| Validator | NotEmpty + GreaterThan(now) — sem max |
| Handler | SetDates() depois sobrescreve com command; checa unicidade ativa user+cpf+store |
| Nome | Derivado do cliente (não free-text como Minhas) |
4.2 Read / Update / Delete
| Op | Endpoint |
|---|---|
| List | GET .../recommend/{storeId}/{page}/{size} (ativas) |
| Detail app | GET .../recommend/app/{id} |
| Public | GET .../recommend/{id} |
| Update | PUT .../recommend/{id} — products + dateExpiration (required validator) |
| Delete | DELETE .../recommend/{id} soft-delete |
4.3 Lista UI
Já suporta nullExpirationLabel: 'Sem data de expiração' (defensivo); create/edit ainda forçam data.
5. CRUD — Vitrine da marca (Brand) — referência
| Op | Comportamento resumido |
|---|---|
| Create | CSV SKUs → temp + SetDates (+30) |
| Update header | dateExpiration opcional → null vira MaxValue; Status por janela Start/Expiration |
| List admin | Filtros de status; portal formata data ou vazio |
| Job diário | UpdateStatusShowCasesBrand recalcula Status |
| App vendedor | Somente leitura + share → BrandSeller |
Precedente de “sem data de expiração”: UI portal + MaxValue no ES + null no DTO.
6. CRUD — BrandSeller
- Create no share: copia
DateExpirationda marca. - Sem fluxo de edição de prazo pelo vendedor.
- Fora do CA da Spike.
7. Lifecycle unificado
[Create] ──DateExpiration set──► [Ativa]
│
┌───────────────┼────────────────┐
▼ ▼ ▼
[Expirada] [Soft-deleted] [Sem prazo*]
(read filter) (DateDeleted) *só Brand hoje (MaxValue)
│ │
└────── docs permanecem no ES ──┘
* Store/Recommend não têm caminho de produto “sem prazo” no AS-IS (Store null vira +31; Recommend exige data).
8. Regras de “vitrine ativa”
| Tipo | Critério principal |
|---|---|
| Store / Recommend | DateDeleted == null e !IsExpired() / ES DateExpiration >= now |
| Brand (app) | Status == true e DateExpiration >= now (+ StartDate na lógica de status) |
| Brand (job) | StartDate.Date <= today && DateExpiration?.Date >= today → Status true |
9. Defaults e validação (comparativo)
| Store | Recommend | Brand | |
|---|---|---|---|
| Default | +31 (server se null; UI +31) | UI +31; server sobrescreve com body | Temp +30; header null → MaxValue |
| Required | Não (mas null vira +31) | Sim (API + UI) | Não |
| Futura | Não enforced API | Sim | Se preenchida, UI portal valida |
| Max dias | UI +31 | UI create +31; edit sem max; API sem max | Sem max no portal |
| Sem expiração | Não suportado de ponta a ponta | Não (apesar de label na lista) | Sim (MaxValue) |
10. Jobs e efeitos colaterais
| Job | Efeito em vitrine |
|---|---|
| UpdateStatusShowCasesBrand | Só Brand.Status |
| RemoveShowCasesBrandTemp | Só temps Brand |
| ShowCaseProduct* | Atualiza produtos no índice (estoque/preço/disponibilidade) para todas as vitrines que referenciam produtos — não altera DateExpiration |
11. O que a SP pede vs AS-IS (gap)
| CA | AS-IS | Gap |
|---|---|---|
| CA-1 Data opcional | Recommend obriga; Store null vira +31 | Tornar opcional de verdade |
| CA-2 Default + remoção | Default existe; remoção não vira “sem prazo” | Null semântico + UI clearable |
| CA-3 Edit add/change/remove | Change sim; remove “sem prazo” não | Clear + persist MaxValue |
| CA-4 Label “Sem data…” | Recommend/Brand list ok; Minhas não | Minhas list/detail + API null |
| CA-5 Outras regras | Jobs product / share / estoque independentes | Não regredir |
12. Inconsistências relevantes
- 30 vs 31 dias (SP/SetDates vs SetDateExpiration/app).
- Brand: “sem data” = MaxValue; Store: null = +31.
- Cap de dias só no app (e incompleto no recommend edit).
- Expectativa de “job marca expiração de vendedor” não existe no código.