Design research — coezzion_vendas_app
Data: 2026-07-09
US: 197021
Repo: /Users/alt-zz/projetos/arezzo/coezzion_vendas_app
Base spike: research-zzapp.md
Pesquisa file-level para implementação do PR2. Nenhum código alterado nesta fase.
1. Resumo executivo
| Área | Gap principal | Ação design |
|---|---|---|
ZZDateFormField | Sem ação de limpar data | allowClear + onClear |
| Minhas list | ShowCaseResponseItem non-null parse | Nullable + nullExpirationLabel |
| Minhas update | UpdateShowCaseRequest required + fallback +31 | Nullable; null explícito no clear |
| Minhas detail | Campo oculto se dateExpiration == null | Sempre exibir picker |
| Recommend create/edit | Toast obrigatório se null | Permitir submit com null |
| Recommend edit | Sem finalDateRange no picker | Alinhar max +31 com create |
2. Widget compartilhado
Arquivo: lib/theme_widgets/date_form_field/zz_date_form_field.dart
| Aspecto | AS-IS |
|---|---|
selectedDate | DateTime? |
onChanged | void Function(DateTime) — não aceita clear |
| Clear UX | Inexistente |
| Ícone sufixo | Apenas calendário |
Referência UX portal: coezzion-portal/src/routes/Vitrine/ListShowcase/EditShowcase/index.tsx — placeholder "Sem data de expiração".
TO-BE: parâmetros opcionais allowClear, onClear, clearedHint (default "Sem data de expiração"); ícone X ou ação no picker para limpar.
3. Minhas vitrines (Store)
Fluxo create
| Arquivo | Responsabilidade | Gap |
|---|---|---|
show_case/my_show_case_controller.dart L85 | setCreateMode() | OK |
search/show_case_search_products_controller.dart | expirationDate, create POST | Default +31 em create mode |
show_case_check_products/widgets/check_fast_catalog_product_list.dart L59–63 | ZZDateFormField | max +31; sem clear |
models/show_case/create_show_case_request.dart | DateTime? dateExpiration | Já serializa null — OK |
Fluxo edit
| Arquivo | Gap |
|---|---|
show_case_detail/show_case_detail_screen.dart L157–185 | if (dateExpiration != null) esconde campo |
show_case_detail/show_case_detail_controller.dart L243 | Fallback ?? DateTime.now().add(31d) no update — conflita com clear→never |
show_case_detail/show_case_detail_controller.dart L175–177 | Mesmo fallback no edit mode search |
Listagem
| Arquivo | Gap |
|---|---|
my_show_case_list/my_show_case_list_screen.dart L89 | Sem nullExpirationLabel |
models/show_case/show_case_response_item.dart L20 | asDateTimeOrThrow — quebra com null da API |
Models
| Model | Campo | AS-IS | TO-BE |
|---|---|---|---|
UpdateShowCaseRequest | dateExpiration | DateTime required | DateTime? |
ShowCaseResponseItem | expirationDate | required | DateTime? |
ShowCaseDetailResponse | dateExpiration | já nullable | OK |
4. Histórico de compras (Recommend)
Fluxo create
| Arquivo | Gap |
|---|---|
recommend_show_case_check_products_controller.dart L53–61 | Default +31 — manter |
recommend_show_case_check_products_controller.dart L85–87 | Toast se null — remover |
recommend_show_case_check_products_screen.dart L61–67 | Picker sem clear |
Fluxo edit
| Arquivo | Gap |
|---|---|
recommend_show_case_detail_controller.dart L148–151 | Toast se null — remover |
recommend_show_case_detail_screen.dart L180–184 | Sem finalDateRange; sem clear |
updateExpirationDate | Só aceita DateTime — precisa clearExpirationDate() |
Listagem
| Arquivo | AS-IS |
|---|---|
recommend_show_case_list_screen.dart L87–88 | Já passa nullExpirationLabel: 'Sem data de expiração' |
Models
| Model | Testes existentes |
|---|---|
recommend_show_case_list_item.dart | test/.../recommend_show_case_list_item_test.dart — null OK |
recommend_show_case_detail_response.dart | test/.../recommend_show_case_detail_response_test.dart — null OK |
create_recommend_show_case_request.dart | Teste só com data — adicionar null |
update_recommend_show_case_request.dart | Teste só com data — adicionar null |
5. Widget de lista (reutilizável)
Arquivo: lib/screens/show_case/widgets/listItem.dart
final showExpiration = expirationDate != null || nullExpirationLabel != null;
final expirationText = expirationDate != null
? 'Data de expiração: ${MaskUtils.formatDateBr(expirationDate!)}'
: nullExpirationLabel ?? '';
CA-4 já suportado — basta passar nullExpirationLabel na lista Minhas.
6. APIs consumidas (sem mudança de endpoint)
Prefixo api/show-case-new via ApiUtils:
| Fluxo | Método | Payload dateExpiration TO-BE |
|---|---|---|
| Minhas create | POST / | ISO ou null |
| Minhas update | PUT / | ISO ou null |
| Recommend create | POST /recommend | ISO ou null |
| Recommend update | PUT /recommend/{id} | ISO ou null |
Dependência: PR1 API publicado antes do merge do app (RF-11).
7. Fallbacks a remover (crítico)
Estes trechos reaplicam +31 no client quando null e conflitam com RF-02/RF-03:
| Arquivo | Linha | Trecho |
|---|---|---|
show_case_detail_controller.dart | L243 | expirationDate.value ?? detail... ?? now+31 |
show_case_detail_controller.dart | L175–177 | Idem no setEditMode |
TO-BE: se usuário limpou → enviar null; se não alterou campo em vitrine sem prazo → enviar null; se preencheu data → enviar ISO.
8. Arquivos fora do escopo
brand_show_case_*— read-only; já usanullExpirationLabelshow_case_screen.dart— hub; sem mudança de expiração- Share/copy flows — sem mudança (RF-05)
9. Decisões grill zzapp (2026-07-09)
Ver grill-decisions.md D-APP-8 a D-APP-15: detecção clear Minhas (dois PUT paths), ZZDateFormField parametrizável, JSON null explícito, picker Histórico min amanhã, deploy simultâneo, constantes centralizadas, testes models obrigatórios.
10. Smoke manual sugerido (pós-PR2)
- Create Minhas sem limpar → expira ~+31
- Create Minhas com clear → label "Sem data de expiração"
- Edit Minhas: remover / adicionar data
- Mesmos cenários Recommend
- Share link vitrine never-expire