Requisitos — [Front][App] Ajuste de layout nova vitrine
Task #197313
Task pai: US 196053 ADRs relacionados: — AS-IS validado: context/AS-IS.md
Status: refinado Sessão de grilling: 25/06/2026 — refinamento com base na US 196053, contrato de API (
frontend-contract-diff.md), referências visuais e código do App Sessão de grilling (revisão): 03/07/2026 — RF-03 reescrito (fluxo de criação sem tela intermediária), RF-04 ganhou AC11/AC12/AC13 (correção deenableEditButton, supressão de reward events, limite de data de 30 dias para SellerStock), RF-06 novo (aviso de alterações não salvas), contrato de API atualizado (get-or-create no POST,hasNextna listagem v2). Detalhes emcontext/*.md.
Visão geral
Adaptar o fluxo de Minhas Vitrines no App Flutter (coezzion_vendas_app) para suportar a modalidade Vitrine de Estoque da Loja (showCaseType = "SellerStock"), coexistindo com a vitrine personalizada atual (showCaseType = "Store").
A task cobre exclusivamente alterações de layout e DTOs no App: bottom sheet de escolha de modalidade, integração do endpoint v2 da listagem com hasSellerStock, fluxo de criação sem seleção de produtos e tela de detalhe condicionada ao tipo de vitrine.
Papéis
| Papel | Responsabilidade |
|---|---|
| Vendedor | Cria, visualiza, compartilha e edita data de expiração das vitrines no App |
| App (Flutter) | Renderiza layouts, consome APIs show-case-new e aplica regras por showCaseType |
| API Show Case | Persiste vitrines, retorna hasSellerStock, showCaseType e totalItems |
Requisitos
RF-01 — Listagem com endpoint v2 e flag hasSellerStock
User Story: Como vendedor, eu quero que a listagem de Minhas Vitrines reflita se já possuo uma Vitrine de Estoque da Loja ativa, para que o App saiba quando bloquear uma nova criação dessa modalidade.
Acceptance Criteria:
- WHEN o App carrega a listagem de Minhas Vitrines THEN o App SHALL consumir
GET /api/show-case-new/v2/store/{storeId}no lugar do endpoint v1. - WHEN a API retorna a listagem paginada THEN o App SHALL parsear os campos
data,total,pageNumber,pageSize,hasNextehasSellerStockconforme o contrato emfrontend-contract-diff.md. - WHEN
hasSellerStockétrueTHEN o App SHALL armazenar esse estado no controller da listagem para uso no bottom sheet de criação. - WHEN
hasSellerStockéfalseTHEN o App SHALL permitir a criação de uma nova Vitrine de Estoque da Loja. - WHEN a listagem é exibida THEN o App SHALL manter o comportamento atual de busca, paginação infinita e navegação para o detalhe ao tocar em um item.
RF-02 — Bottom sheet de escolha de modalidade ao criar vitrine
User Story: Como vendedor, eu quero escolher o tipo de vitrine ao clicar em "CRIAR VITRINE", para decidir entre compartilhar o estoque completo da loja ou criar uma vitrine personalizada.
Acceptance Criteria:
- WHEN o vendedor toca no botão
CRIAR VITRINEna tela Minhas Vitrines THEN o App SHALL exibir um bottom sheet com títuloEscolha uma opçãoe botão de fechar, conforme referência visualcontext/image-1.png. - WHEN o bottom sheet é exibido THEN o App SHALL apresentar a opção
Vitrine do estoque da lojacom badgeNovae descriçãoCrie uma vitrine com todos os produtos do estoque da loja.. - WHEN o bottom sheet é exibido THEN o App SHALL apresentar a opção
Criar nova vitrinecom descriçãoCrie vitrines personalizadas escolhendo os produtos ideais para cada cliente.. - WHEN o vendedor seleciona
Criar nova vitrineTHEN o App SHALL iniciar o fluxo atual de criação viaShowCaseSearchProducts(showCaseType = Store). - WHEN o vendedor seleciona
Vitrine do estoque da lojaehasSellerStockéfalseTHEN o App SHALL iniciar o fluxo de criação da Vitrine de Estoque da Loja (RF-03). - WHEN
hasSellerStockétrueTHEN o App SHALL manter a opçãoVitrine do estoque da lojavisível porém desabilitada para toque. - WHEN
hasSellerStockétrueTHEN o App SHALL exibir abaixo do título da opção a mensagemVocê já possui uma Vitrine de Estoque ativa. Apenas uma vitrine desta modalidade é permitida por vez., conformecontext/image-2.png. - WHEN o vendedor fecha o bottom sheet sem selecionar THEN o App SHALL retornar à listagem sem alterar o estado.
RF-03 — Criação da Vitrine de Estoque da Loja
User Story: Como vendedor, eu quero criar uma vitrine com todo o estoque da loja sem selecionar produtos manualmente, para compartilhar o catálogo completo com menos esforço operacional.
Acceptance Criteria:
- WHEN o vendedor seleciona
Vitrine do estoque da lojano bottom sheet (comhasSellerStock = false) THEN o App SHALL navegar diretamente para a tela de detalhe (ShowCaseDetailScreen) em modo de criação, sem exibir nenhuma tela intermediária de formulário. - WHEN a tela de detalhe é aberta em modo de criação THEN o App SHALL enviar
POST /api/show-case-newcomshowCaseType = SellerStock,name = "Estoque da loja",showCaseItems = [],dateExpiration= hoje + 30 dias (limite máximo desta modalidade, diferente dos 31 dias usados parashowCaseType = Store), e os demais campos obrigatórios deCreateShowCaseRequest. - WHEN o POST está em andamento THEN o App SHALL exibir o estado de carregamento (skeleton) já usado na tela de detalhe.
- WHEN o POST retorna sucesso (inclusive quando a API retorna os dados de uma vitrine SellerStock já existente, conforme
context/frontend-contract-diff.md) THEN o App SHALL usar oidretornado para carregar o detalhe completo (GET /api/show-case-new/app/{id}), exibindo a tela normalmente (branching porshowCaseType, ver RF-04). Não há distinção, do ponto de vista do App, entre "criada agora" e "já existente". - WHEN o POST retorna um erro inesperado (rede, 5xx, etc.) THEN o App SHALL exibir um estado de erro genérico com botão de tentar novamente (mesmo padrão do
UpsellRecommendationScreen: título/descrição fixos,onRetryreenvia o POST). - WHEN o fluxo de criação SellerStock está ativo THEN o App SHALL NOT exibir etapa de busca ou seleção de produtos (
ShowCaseSearchProducts) nem nenhuma tela intermediária com campos de nome/data. - WHEN o App cria uma vitrine SellerStock THEN o App SHALL NOT registrar reward event de criação (
CreateShowcaseRewardEventou equivalente) até que a task 197192 defina e implemente eventos específicos para esta modalidade.
RF-04 — Detalhe da vitrine condicionado ao showCaseType
User Story: Como vendedor, eu quero visualizar e gerenciar vitrines de estoque da loja com layout e regras distintas das vitrines personalizadas, para não tentar editar campos que o negócio restringe.
Acceptance Criteria:
- WHEN o App carrega o detalhe via
GET /api/show-case-new/app/{showCaseId}THEN o App SHALL parsear os camposshowCaseTypeetotalItemsemShowCaseDetailResponse. - WHEN
showCaseType = Store(Store) THEN o App SHALL manter o layout e comportamento atuais da tela de detalhe (nome editável, seleção/remoção de produtos, botãoADICIONAR PRODUTOS), exceto pela correção de semântica doenableEditButtondescrita em AC11 (aplicada a ambos os tipos). - WHEN
showCaseType = SellerStock(SellerStock) THEN o App SHALL exibir o título da AppBar comoEstoque da loja. - WHEN
showCaseType = SellerStockTHEN o App SHALL exibir o campo Nome com valor fixoEstoque da lojadesabilitado para edição. - WHEN
showCaseType = SellerStockTHEN o App SHALL exibir a seção de produtos com labelConfira os produtose contagem"{totalItems} produtos recomendados", usandototalItemse nãoshowCaseItems.length. - WHEN
showCaseType = SellerStockTHEN o App SHALL exibir no máximo 4 cards de produtos a partir deshowCaseItems(prévia), SEM exibir o linkTodos os produtos, mesmo quandototalItemsfor maior que 4 (não há endpoint de listagem completa por vitrine para esta modalidade). - WHEN
showCaseType = SellerStockTHEN o App SHALL NOT exibir botãoADICIONAR PRODUTOS, ícones de remoção de produto nem fluxo de edição de listagem. - WHEN
showCaseType = SellerStocke o vendedor altera apenas a data de expiração THEN o App SHALL enviarPUT /api/show-case-newcomshowCaseType = SellerStockcontendo somentedateExpirationefetivo (demais campos ignorados pela API). - WHEN
showCaseType = SellerStockTHEN o App SHALL manter as açõesVisualizar,CompartilhareCopiar linkna seçãoEscolha uma opção. - WHEN
showCaseType = SellerStockTHEN o App SHALL manter os botõesATUALIZAR VITRINE(habilitado somente se a data de expiração mudou) eEXCLUIR VITRINE. ParashowCaseType = SellerStock, o botãoATUALIZAR VITRINESHALL NOT exigirshowCaseItemsnão-vazio (guard removido para esta modalidade, já que uma loja com estoque zerado ainda deve poder atualizar a data de expiração). - WHEN o vendedor seleciona uma data no campo Data de expiração (em qualquer
showCaseType) THEN o App SHALL habilitar o botãoATUALIZAR VITRINEsomente se a nova data for diferente da data originalmente carregada (comparação real contra o valor original — correção de comportamento aplicada tanto a Store quanto a SellerStock; hoje qualquer toque no campo já habilitava o botão, mesmo sem mudança real de valor). - WHEN
showCaseType = SellerStockTHEN o App SHALL NOT registrar reward events nativos do detalhe (Visualizar,Compartilhar,Copiar link) até que a task 197192 defina e implemente eventos específicos para esta modalidade. - WHEN
showCaseType = SellerStockTHEN o campo Data de expiração SHALL permitir seleção até 30 dias a partir de hoje (finalDateRange), diferente do limite de 31 dias usado parashowCaseType = Store— o limite passa a ser condicionado ao tipo, em vez de um valor fixo único.
RF-05 — DTOs e requests atualizados
User Story: Como desenvolvedor do App, eu quero que os models reflitam o contrato da API, para serializar e desserializar corretamente os novos campos da modalidade SellerStock.
Acceptance Criteria:
- WHEN o App serializa criação ou atualização de vitrine THEN
CreateShowCaseRequesteUpdateShowCaseRequestSHALL incluir o camposhowCaseType. - WHEN o App desserializa o detalhe THEN
ShowCaseDetailResponseSHALL incluirshowCaseType(ShowCaseType, parseado da string da API) etotalItems(int). - WHEN o App desserializa a listagem v2 THEN o model de resposta paginada SHALL expor
hasSellerStock(bool) ehasNext(bool) além da lista de vitrines. - WHEN
showCaseType = SellerStockna criação THEN o App SHALL enviarshowCaseItemscomo lista vazia independentemente de estado local.
RF-06 — Aviso de alterações não salvas ao sair, visualizar, compartilhar ou copiar link
User Story: Como vendedor, eu quero ser avisado quando tento sair da tela, visualizar, compartilhar ou copiar o link de uma vitrine com alterações não salvas, para não perder essas alterações nem compartilhar dados desatualizados com a cliente.
Acceptance Criteria:
- WHEN o vendedor tenta sair da tela de detalhe com
enableEditButton = trueTHEN o App SHALL exibir uma modal com títuloA vitrine possui alterações não salvase a mensagemDeseja salvar as alterações antes de sair?, com as opçõesSalvareNão salvar(comportamento já existente, sem mudança). - WHEN o vendedor toca em
VisualizarcomenableEditButton = trueTHEN o App SHALL exibir a mesma modal com a mensagemDeseja salvar as alterações antes de visualizar?. - WHEN o vendedor toca em
CompartilharcomenableEditButton = trueTHEN o App SHALL exibir a mesma modal com a mensagemDeseja salvar as alterações antes de compartilhar?. - WHEN o vendedor toca em
Copiar linkcomenableEditButton = trueTHEN o App SHALL exibir a mesma modal com a mensagemDeseja salvar as alterações antes de copiar o link?. - WHEN o vendedor escolhe
Salvarem qualquer uma das modais acima THEN o App SHALL salvar as alterações (updateShowCase()) e, em caso de sucesso, prosseguir com a ação original (sair / visualizar / compartilhar / copiar link) usando os dados atualizados. - WHEN o vendedor escolhe
Não salvarem qualquer uma das modais acima THEN o App SHALL prosseguir com a ação original usando os dados já salvos anteriormente (sem persistir a alteração pendente). - WHEN
enableEditButton = false(sem alterações pendentes) THEN o App SHALL executar Sair/Visualizar/Compartilhar/Copiar link normalmente, sem exibir a modal. - WHEN o vendedor fecha a modal sem escolher uma opção THEN o App SHALL permanecer na tela de detalhe, sem executar a ação original.
- WHEN o vendedor escolhe
Salvare a operação falha (validação do formulário ou erro da API) THEN o App SHALL manter o vendedor na tela de detalhe, sem prosseguir com a ação original, exibindo o erro já tratado hoje pelo fluxo de atualização. - Este comportamento vale para
showCaseType = Store(Store) eshowCaseType = SellerStock(SellerStock) — mesmo critério (enableEditButton) para os dois tipos. - WHEN o vendedor escolhe
Não salvarem Visualizar/Compartilhar/Copiar link THEN o campo editado SHALL continuar exibindo o valor pendente na tela (a alteração não é descartada;enableEditButtoncontinuatruee o aviso volta a aparecer se o vendedor tentar sair/visualizar/compartilhar/copiar novamente). - WHEN a tela de detalhe está em modo de criação (
isCreating) durante o carregamento ou em estado de erro THEN o App SHALL permitir sair sem exibir o aviso de alterações não salvas (não existe vitrine criada ainda para ter alterações pendentes;enableEditButtonjá retornafalsequandodetail.valueé nulo).
Fora de escopo deste RF: o botão de voltar visível da AppBar hoje chama Navigator.pop() diretamente e não consulta onWillPop (só o botão/gesto físico do Android consulta) — bug pré-existente, documentado separadamente em context/bug-appbar-back-button-ignora-onwillpop.md, não tratado como AC aqui. O botão EXCLUIR VITRINE também fica fora — usa somente a confirmação de exclusão já existente (ConfirmBottomSheet: "Deseja excluir a vitrine nome?"), sem checagem de alterações pendentes antes, já que excluir descarta tudo de qualquer forma.
Fora de Escopo
- Experiência da cliente na vitrine web (abas Recomendados / Mais produtos) — coberto pela task 197194
- Nova URL de vitrine SellerStock — coberto pela task 197189
- Integração de eventos Rewards específicos da modalidade SellerStock — coberto pela task 197192
- Alterações de backend e contratos de API — coberto pelas tasks 197190 e 197191
- Vitrines recomendadas por cliente (
recommend) e vitrines de marca (brand) - Testes automatizados e documentação de QA
Dependências
| Dependência | Descrição | Status |
|---|---|---|
| 197190 | Backend com endpoints v2, showCaseType, hasSellerStock, hasNext disponíveis, e comportamento get-or-create no POST (sem erro de duplicata) | Pendente |
| frontend-contract-diff.md | Contrato de API validado para DTOs e regras por tipo | Disponível |
| AS-IS.md | Mapeamento do código atual do App | Disponível |
| Referências visuais Figma | context/image.png a image-3.png (image-3.png não se aplica mais — não há tela nova de criação, ver RF-03) | Disponível |
| 197192 | Eventos Rewards específicos pós-criação/compartilhamento SellerStock | Pendente (não bloqueia layout) |