Skip to main content

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 de enableEditButton, 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, hasNext na listagem v2). Detalhes em context/*.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

PapelResponsabilidade
VendedorCria, 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 CasePersiste 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:

  1. 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.
  2. WHEN a API retorna a listagem paginada THEN o App SHALL parsear os campos data, total, pageNumber, pageSize, hasNext e hasSellerStock conforme o contrato em frontend-contract-diff.md.
  3. WHEN hasSellerStock é true THEN o App SHALL armazenar esse estado no controller da listagem para uso no bottom sheet de criação.
  4. WHEN hasSellerStock é false THEN o App SHALL permitir a criação de uma nova Vitrine de Estoque da Loja.
  5. 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:

  1. WHEN o vendedor toca no botão CRIAR VITRINE na tela Minhas Vitrines THEN o App SHALL exibir um bottom sheet com título Escolha uma opção e botão de fechar, conforme referência visual context/image-1.png.
  2. WHEN o bottom sheet é exibido THEN o App SHALL apresentar a opção Vitrine do estoque da loja com badge Nova e descrição Crie uma vitrine com todos os produtos do estoque da loja..
  3. WHEN o bottom sheet é exibido THEN o App SHALL apresentar a opção Criar nova vitrine com descrição Crie vitrines personalizadas escolhendo os produtos ideais para cada cliente..
  4. WHEN o vendedor seleciona Criar nova vitrine THEN o App SHALL iniciar o fluxo atual de criação via ShowCaseSearchProducts (showCaseType = Store).
  5. WHEN o vendedor seleciona Vitrine do estoque da loja e hasSellerStock é false THEN o App SHALL iniciar o fluxo de criação da Vitrine de Estoque da Loja (RF-03).
  6. WHEN hasSellerStock é true THEN o App SHALL manter a opção Vitrine do estoque da loja visível porém desabilitada para toque.
  7. WHEN hasSellerStock é true THEN o App SHALL exibir abaixo do título da opção a mensagem Você já possui uma Vitrine de Estoque ativa. Apenas uma vitrine desta modalidade é permitida por vez., conforme context/image-2.png.
  8. 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:

  1. WHEN o vendedor seleciona Vitrine do estoque da loja no bottom sheet (com hasSellerStock = 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.
  2. WHEN a tela de detalhe é aberta em modo de criação THEN o App SHALL enviar POST /api/show-case-new com showCaseType = SellerStock, name = "Estoque da loja", showCaseItems = [], dateExpiration = hoje + 30 dias (limite máximo desta modalidade, diferente dos 31 dias usados para showCaseType = Store), e os demais campos obrigatórios de CreateShowCaseRequest.
  3. WHEN o POST está em andamento THEN o App SHALL exibir o estado de carregamento (skeleton) já usado na tela de detalhe.
  4. 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 o id retornado para carregar o detalhe completo (GET /api/show-case-new/app/{id}), exibindo a tela normalmente (branching por showCaseType, ver RF-04). Não há distinção, do ponto de vista do App, entre "criada agora" e "já existente".
  5. 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, onRetry reenvia o POST).
  6. 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.
  7. WHEN o App cria uma vitrine SellerStock THEN o App SHALL NOT registrar reward event de criação (CreateShowcaseRewardEvent ou 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:

  1. WHEN o App carrega o detalhe via GET /api/show-case-new/app/{showCaseId} THEN o App SHALL parsear os campos showCaseType e totalItems em ShowCaseDetailResponse.
  2. 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ão ADICIONAR PRODUTOS), exceto pela correção de semântica do enableEditButton descrita em AC11 (aplicada a ambos os tipos).
  3. WHEN showCaseType = SellerStock (SellerStock) THEN o App SHALL exibir o título da AppBar como Estoque da loja.
  4. WHEN showCaseType = SellerStock THEN o App SHALL exibir o campo Nome com valor fixo Estoque da loja desabilitado para edição.
  5. WHEN showCaseType = SellerStock THEN o App SHALL exibir a seção de produtos com label Confira os produtos e contagem "{totalItems} produtos recomendados", usando totalItems e não showCaseItems.length.
  6. WHEN showCaseType = SellerStock THEN o App SHALL exibir no máximo 4 cards de produtos a partir de showCaseItems (prévia), SEM exibir o link Todos os produtos, mesmo quando totalItems for maior que 4 (não há endpoint de listagem completa por vitrine para esta modalidade).
  7. WHEN showCaseType = SellerStock THEN o App SHALL NOT exibir botão ADICIONAR PRODUTOS, ícones de remoção de produto nem fluxo de edição de listagem.
  8. WHEN showCaseType = SellerStock e o vendedor altera apenas a data de expiração THEN o App SHALL enviar PUT /api/show-case-new com showCaseType = SellerStock contendo somente dateExpiration efetivo (demais campos ignorados pela API).
  9. WHEN showCaseType = SellerStock THEN o App SHALL manter as ações Visualizar, Compartilhar e Copiar link na seção Escolha uma opção.
  10. WHEN showCaseType = SellerStock THEN o App SHALL manter os botões ATUALIZAR VITRINE (habilitado somente se a data de expiração mudou) e EXCLUIR VITRINE. Para showCaseType = SellerStock, o botão ATUALIZAR VITRINE SHALL NOT exigir showCaseItems não-vazio (guard removido para esta modalidade, já que uma loja com estoque zerado ainda deve poder atualizar a data de expiração).
  11. WHEN o vendedor seleciona uma data no campo Data de expiração (em qualquer showCaseType) THEN o App SHALL habilitar o botão ATUALIZAR VITRINE somente 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).
  12. WHEN showCaseType = SellerStock THEN 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.
  13. WHEN showCaseType = SellerStock THEN 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 para showCaseType = 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:

  1. WHEN o App serializa criação ou atualização de vitrine THEN CreateShowCaseRequest e UpdateShowCaseRequest SHALL incluir o campo showCaseType.
  2. WHEN o App desserializa o detalhe THEN ShowCaseDetailResponse SHALL incluir showCaseType (ShowCaseType, parseado da string da API) e totalItems (int).
  3. WHEN o App desserializa a listagem v2 THEN o model de resposta paginada SHALL expor hasSellerStock (bool) e hasNext (bool) além da lista de vitrines.
  4. WHEN showCaseType = SellerStock na criação THEN o App SHALL enviar showCaseItems como lista vazia independentemente de estado local.

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:

  1. WHEN o vendedor tenta sair da tela de detalhe com enableEditButton = true THEN o App SHALL exibir uma modal com título A vitrine possui alterações não salvas e a mensagem Deseja salvar as alterações antes de sair?, com as opções Salvar e Não salvar (comportamento já existente, sem mudança).
  2. WHEN o vendedor toca em Visualizar com enableEditButton = true THEN o App SHALL exibir a mesma modal com a mensagem Deseja salvar as alterações antes de visualizar?.
  3. WHEN o vendedor toca em Compartilhar com enableEditButton = true THEN o App SHALL exibir a mesma modal com a mensagem Deseja salvar as alterações antes de compartilhar?.
  4. WHEN o vendedor toca em Copiar link com enableEditButton = true THEN o App SHALL exibir a mesma modal com a mensagem Deseja salvar as alterações antes de copiar o link?.
  5. WHEN o vendedor escolhe Salvar em 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.
  6. WHEN o vendedor escolhe Não salvar em 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).
  7. WHEN enableEditButton = false (sem alterações pendentes) THEN o App SHALL executar Sair/Visualizar/Compartilhar/Copiar link normalmente, sem exibir a modal.
  8. 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.
  9. WHEN o vendedor escolhe Salvar e 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.
  10. Este comportamento vale para showCaseType = Store (Store) e showCaseType = SellerStock (SellerStock) — mesmo critério (enableEditButton) para os dois tipos.
  11. WHEN o vendedor escolhe Não salvar em Visualizar/Compartilhar/Copiar link THEN o campo editado SHALL continuar exibindo o valor pendente na tela (a alteração não é descartada; enableEditButton continua true e o aviso volta a aparecer se o vendedor tentar sair/visualizar/compartilhar/copiar novamente).
  12. 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; enableEditButton já retorna false quando detail.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ênciaDescriçãoStatus
197190Backend com endpoints v2, showCaseType, hasSellerStock, hasNext disponíveis, e comportamento get-or-create no POST (sem erro de duplicata)Pendente
frontend-contract-diff.mdContrato de API validado para DTOs e regras por tipoDisponível
AS-IS.mdMapeamento do código atual do AppDisponível
Referências visuais Figmacontext/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
197192Eventos Rewards específicos pós-criação/compartilhamento SellerStockPendente (não bloqueia layout)