FAQ
A tela de Ajuda do representante de vendas: uma lista de perguntas frequentes (FAQ) expansíveis, o telefone da central de atendimento, e um formulário curto para enviar uma mensagem de suporte. Um único RPC (getFaq) traz o FAQ, os assuntos de suporte e o contato; o envio da mensagem sai pelo Dispatcher.
The sales rep's Help screen: a list of expandable frequently-asked questions (FAQ), the contact-center phone number, and a short form to send a support message. A single RPC (getFaq) brings the FAQ, the support subjects and the contact; the message is sent through the Dispatcher.
La pantalla de Ayuda del representante de ventas: una lista de preguntas frecuentes (FAQ) expandibles, el teléfono del centro de atención, y un formulario corto para enviar un mensaje de soporte. Un único RPC (getFaq) trae el FAQ, los asuntos de soporte y el contacto; el envío del mensaje sale por el Dispatcher.
O que é e para que serveWhat it is and what it's forQué es y para qué sirve
A tela Ajuda / FAQ reúne, num só lugar, o autoatendimento do representante de vendas. Ela faz duas coisas: mostra as perguntas frequentes (que se abrem para revelar a resposta) e oferece um canal de suporte — o telefone da central e um formulário para enviar uma mensagem. Responde três perguntas do dia a dia: The Help / FAQ screen gathers the sales rep's self-service in one place. It does two things: it shows the frequently-asked questions (which expand to reveal the answer) and it offers a support channel — the contact-center phone and a form to send a message. It answers three everyday questions: La pantalla Ayuda / FAQ reúne el autoservicio del representante de ventas en un solo lugar. Hace dos cosas: muestra las preguntas frecuentes (que se abren para revelar la respuesta) y ofrece un canal de soporte — el teléfono del centro de atención y un formulario para enviar un mensaje. Responde tres preguntas del día a día:
Como uma coisa funciona?How does something work?¿Cómo funciona algo?
Uma lista de perguntas; tocar numa pergunta abre a resposta abaixo dela.A list of questions; tapping a question opens the answer below it.Una lista de preguntas; tocar una pregunta abre la respuesta debajo.
Como falar com o suporte?How to reach support?¿Cómo contactar soporte?
O telefone da central de atendimento fica em destaque, logo abaixo do FAQ.The contact-center phone is highlighted, right below the FAQ.El teléfono del centro de atención está destacado, justo debajo del FAQ.
Como pedir ajuda por escrito?How to ask for help in writing?¿Cómo pedir ayuda por escrito?
Escolha um assunto, descreva o problema e envie — a mensagem vai pelo Dispatcher.Pick a subject, describe the issue and submit — the message goes through the Dispatcher.Elija un asunto, describa el problema y envíe — el mensaje va por el Dispatcher.
Consulta + 1 envioRead + 1 submitConsulta + 1 envío
O FAQ e o contato são somente leitura. A única escrita da tela é o envio da mensagem de suporte, que sai pelo Dispatcher (transação SupportRequestAPI).
The FAQ and the contact are read-only. The screen's only write is the support message submission, which goes through the Dispatcher (transaction SupportRequestAPI).
El FAQ y el contacto son solo lectura. La única escritura de la pantalla es el envío del mensaje de soporte, que sale por el Dispatcher (transacción SupportRequestAPI).
Como acessarHow to openCómo acceder
- Abra o menu lateralOpen the side drawerAbra el menú lateralToque no ícone de menu em qualquer tela base (Home, Visitas, Pedidos).Tap the menu icon on any base screen (Home, Visits, Orders).Toque el icono de menú en cualquier pantalla base (Home, Visitas, Pedidos).
- Toque em "Ajuda"Tap "Help"Toque "Ayuda"O item Ajuda abre a tela FAQ como rota empurrada (com seta de voltar).The Help item opens the FAQ screen as a pushed route (with a back arrow).El ítem Ayuda abre la pantalla FAQ como ruta empujada (con flecha de volver).
- A tela abreThe screen opensLa pantalla abreMostra o FAQ, o contato e o formulário. Puxe para baixo para atualizar.It shows the FAQ, the contact and the form. Pull down to refresh.Muestra el FAQ, el contacto y el formulario. Deslice hacia abajo para actualizar.
Estrutura da telaScreen structureEstructura de la pantalla
- Última sincronizaçãoLast syncÚltima sincronización
- No topo, a data da última sincronização do FAQ (
DataLoadInfo).At the top, the FAQ's last sync date (DataLoadInfo).Arriba, la fecha de última sincronización del FAQ (DataLoadInfo). - Cabeçalho "Ajuda""Help" headerEncabezado "Ayuda"
- Ícone de ajuda + título da tela.Help icon + screen title.Icono de ayuda + título de la pantalla.
- Lista de FAQFAQ listLista de FAQ
- Um card por pergunta, com uma seta que aponta para baixo quando fechado e para cima quando aberto; o card fica branco quando expandido, revelando a resposta.One card per question, with a chevron pointing down when closed and up when open; the card turns white when expanded, revealing the answer.Una tarjeta por pregunta, con un chevron que apunta abajo cuando está cerrado y arriba cuando está abierto; la tarjeta se pone blanca al expandirse, revelando la respuesta.
- ContatoContactContacto
- Uma frase com o telefone da central de atendimento em destaque + intro do formulário.A sentence with the contact-center phone highlighted + the form intro.Una frase con el teléfono del centro de atención destacado + la introducción del formulario.
- Formulário de suporteSupport formFormulario de soporte
- Um seletor de assunto (abre um modal com a lista), um campo de descrição multilinha e o botão Enviar.A subject picker (opens a modal with the list), a multiline description field and the Submit button.Un selector de asunto (abre un modal con la lista), un campo de descripción multilínea y el botón Enviar.
EstadosStatesEstados
A tela tem dois grupos de estado: o carregamento do FAQ (a tela inteira) e o envio da mensagem de suporte (só o botão/formulário).The screen has two state groups: the FAQ load (whole screen) and the support message submission (just the button/form).La pantalla tiene dos grupos de estado: la carga del FAQ (toda la pantalla) y el envío del mensaje de soporte (solo el botón/formulario).
Carregamento do FAQFAQ loadCarga del FAQ
Envio da mensagemMessage submissionEnvío del mensaje
Botão EnviarSubmit buttonBotón Enviar O botão só fica ativo quando há um assunto escolhido e uma descrição não vazia. No sucesso, um aviso verde aparece e o formulário se limpa sozinho após ~1,2 s. The button is only enabled when a subject is picked and a non-empty description is typed. On success, a green notice shows and the form clears itself after ~1.2 s. El botón solo está activo cuando hay un asunto elegido y una descripción no vacía. Al tener éxito, aparece un aviso verde y el formulario se limpia solo tras ~1,2 s.
AçõesActionsAcciones
Abrir / fechar uma perguntaExpand / collapse a questionAbrir / cerrar una pregunta
Tocar num card de FAQ alterna entre aberto e fechado com animação. Vários cards podem ficar abertos ao mesmo tempo — o estado de expansão é lembrado enquanto a tela existe.Tapping a FAQ card toggles it open/closed with an animation. Multiple cards can stay open at once — the expanded state is remembered while the screen is alive.Tocar una tarjeta de FAQ alterna entre abierta y cerrada con animación. Varias tarjetas pueden quedar abiertas a la vez — el estado de expansión se recuerda mientras la pantalla exista.
Escolher um assuntoPick a subjectElegir un asunto
Tocar no campo de assunto abre um modal com a lista de assuntos de suporte; o assunto escolhido volta selecionado no campo.Tapping the subject field opens a modal with the support subjects; the chosen subject comes back selected in the field.Tocar el campo de asunto abre un modal con los asuntos de soporte; el asunto elegido vuelve seleccionado en el campo.
Enviar a mensagemSubmit the messageEnviar el mensaje
Com assunto + descrição preenchidos, o botão Enviar dispara a transação de suporte pelo Dispatcher. Em alguns mercados o nome de usuário é anexado à mensagem (ver Mercados).With subject + description filled, the Submit button fires the support transaction through the Dispatcher. In some markets the username is attached to the message (see Markets).Con asunto + descripción completados, el botón Enviar dispara la transacción de soporte por el Dispatcher. En algunos mercados el nombre de usuario se adjunta al mensaje (ver Mercados).
Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos
Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. A tela tem duas frentes distintas: uma leitura do FAQ (um único RPC getFaq, com cache write-through) e uma escrita (a mensagem de suporte, que sai pelo Dispatcher). Os dois grafos:Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. The screen has two distinct fronts: a read of the FAQ (a single getFaq RPC, with cache write-through) and a write (the support message, sent through the Dispatcher). Both graphs:Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. La pantalla tiene dos frentes distintos: una lectura del FAQ (un único RPC getFaq, con cache write-through) y una escritura (el mensaje de soporte, que sale por el Dispatcher). Los dos grafos:
Leitura — FAQ (cache write-through)Read — FAQ (cache write-through)Lectura — FAQ (cache write-through)
- FaqReplygRPC proto
- toFaqDTOFaqDTODTO · Freezed
- toDomainFaqEntitydomain
- toModelFaqModelObjectBox
- toDomainFaqEntitydomain · cache
- watchFaqNotifier + State
- → UIFaqPage
- watchFaqNotifier + State
- toDomainFaqEntitydomain · cache
- toModelFaqModelObjectBox
- toDomainFaqEntitydomain
- toFaqDTOFaqDTODTO · Freezed
Escrita — mensagem de suporte (Dispatcher)Write — support message (Dispatcher)Escritura — mensaje de soporte (Dispatcher)
- FaqPagesubmit
- submitSupportMessageFaqNotifier
- build(input)BuildHelpSupportDispatcherPayloadUseCase
- DispatcherEnvelopeSubmitHelpSupportUseCase
- dispatchDispatcherOrchestrator
- gRPCSupportRequestAPIsendTransaction
- dispatchDispatcherOrchestrator
- DispatcherEnvelopeSubmitHelpSupportUseCase
- build(input)BuildHelpSupportDispatcherPayloadUseCase
- submitSupportMessageFaqNotifier
Modelo de dadosData modelModelo de datos
O mesmo FAQ existe em quatro representações quase idênticas ao longo das camadas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (domínio) — e cada fronteira é atravessada por um mapper. Os nomes se mantêm em quase todas as camadas; muda pouquíssimo (o id dos itens é renomeado no Model, relações viram ToMany, e o lastSyncAt é gerado no mapper). O fetch é write-through: todo retorno é gravado no ObjectBox e a UI passa a ler do cache.The same FAQ exists in four near-identical representations across the layers — Proto (gRPC wire) → DTO (Freezed) → Model (ObjectBox) → Entity (domain) — and each boundary is crossed by a mapper. Names stay the same across almost all layers; very little changes (item id is renamed in the Model, relations become ToMany, and lastSyncAt is generated in the mapper). Fetch is write-through: every response is written to ObjectBox and the UI then reads from cache.El mismo FAQ existe en cuatro representaciones casi idénticas a lo largo de las capas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (dominio) — y cada frontera se cruza con un mapper. Los nombres se mantienen en casi todas las capas; cambia muy poco (el id de los ítems se renombra en el Model, las relaciones pasan a ToMany, y el lastSyncAt se genera en el mapper). El fetch es write-through: toda respuesta se graba en ObjectBox y la UI lee del caché.
Tudo chega num container Faq de 4 campos (contactCenter + faqs[] + supportSubjects[] + lastSyncAt), com duas sub-estruturas simples — FaqItem (pergunta/resposta) e SupportSubject (assunto do formulário). Diferente de Pedidos, não há nenhum enum de domínio: todos os campos são String em todas as camadas. A seguir, na ordem: o proto, as estruturas de dados campo-a-campo, e os mappers.Everything arrives in a 4-field Faq container (contactCenter + faqs[] + supportSubjects[] + lastSyncAt), with two simple sub-structures — FaqItem (question/answer) and SupportSubject (form subject). Unlike Orders, there is no domain enum at all: every field is a String across all layers. Next, in order: the proto, the field-by-field data structures, and the mappers.Todo llega en un container Faq de 4 campos (contactCenter + faqs[] + supportSubjects[] + lastSyncAt), con dos sub-estructuras simples — FaqItem (pregunta/respuesta) y SupportSubject (asunto del formulario). A diferencia de Pedidos, no hay ningún enum de dominio: todos los campos son String en todas las capas. A continuación, en orden: el proto, las estructuras de datos campo a campo, y los mappers.
Proto
FaqConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge. Um serviço (FaqConectaRepService), um método unário:One service (FaqConectaRepService), a single unary method:Un servicio (FaqConectaRepService), un método unario:
getFaqunaryrpc getFaq(FaqRequest) returns (FaqReply)
path /mn.bat.conectarep.streambridge.FaqConectaRepService/getFaq
FaqRequestlocationHierarchySfidstring· #1 · hierarquia do representante de vendas (resolvida no repository viacurrentResourceProvider)sales rep hierarchy (resolved in the repository viacurrentResourceProvider)jerarquía del representante de ventas (resuelta en el repository víacurrentResourceProvider)lastModifiedDatestring· #2 · optional (não usado hoje — o datasource só o envia se não vazio)optional (not used today — the datasource only sends it if non-empty)optional (no usado hoy — el datasource solo lo envía si no está vacío)
FaqReplyrepeated FaqItem faqs · repeated SupportSubject supportSubjects · string contactCenter — os campos estão detalhados nas Estruturas de dados abaixo.the fields are detailed in Data structures below.los campos están detallados en Estructuras de datos abajo.
Estruturas de dadosData structuresEstructuras de datos
Um dropdown por estrutura, aninhados pela hierarquia. Cada tabela tem uma coluna por camada — Proto · DTO · Model · Entity; o texto accent marca onde o tipo/nome primeiro muda (rename do id no Model, relação ToMany no Model, lastSyncAt gerado no mapper). ¹ = optional no proto.One dropdown per structure, nested by hierarchy. Each table has one column per layer — Proto · DTO · Model · Entity; the accent text marks where the type/name first changes (id rename in the Model, ToMany relation in the Model, lastSyncAt generated in the mapper). ¹ = optional in the proto.Un dropdown por estructura, anidados por jerarquía. Cada tabla tiene una columna por capa — Proto · DTO · Model · Entity; el texto accent marca dónde primero cambia el tipo/nombre (rename del id en el Model, relación ToMany en el Model, lastSyncAt generado en el mapper). ¹ = optional en el proto.
Faq raiz 4 campos
Campo Proto DTO Model Entity contactCenterstring String String String faqsrepeated FaqItem List<…DTO> ToMany<…Model>List<…Entity> supportSubjectsrepeated SupportSubject List<…DTO> ToMany<…Model>List<…Entity> lastSyncAt— DateTime?DateTime DateTime? O container é a própria
FaqReplyno proto (não há message "Faq" separada); viraFaqDTO/FaqModel/FaqEntity. No Model,lastSyncAté não-nulo (@Property(type: date)).The container isFaqReplyitself in the proto (there's no separate "Faq" message); it becomesFaqDTO/FaqModel/FaqEntity. In the Model,lastSyncAtis non-null (@Property(type: date)).El container es la propiaFaqReplyen el proto (no hay message "Faq" separada); pasa aFaqDTO/FaqModel/FaqEntity. En el Model,lastSyncAtes no-nulo (@Property(type: date)).FaqItem Faq.faqs[] 3 campos
Campo Proto DTO Model Entity idstring String faqItemIdString questionstring String String String answerstring String String String idé renomeado parafaqItemIdno Model porque o ObjectBox reservaidpara a chave@Id(int). O mapper reverte paraidna volta à Entity.idis renamed tofaqItemIdin the Model because ObjectBox reservesidfor the@Idkey (int). The mapper reverts toidback in the Entity.idse renombra afaqItemIden el Model porque ObjectBox reservaidpara la clave@Id(int). El mapper revierte aidal volver a la Entity.SupportSubject Faq.supportSubjects[] 2 campos
Campo Proto DTO Model Entity idstring String supportSubjectIdString subjectstring String String String Mesmo rename do
id→supportSubjectIdno Model, revertido no mapper de volta.Sameid→supportSubjectIdrename in the Model, reverted in the mapper on the way back.Mismo renameid→supportSubjectIden el Model, revertido en el mapper de vuelta.
Mappers
As conversões entre as camadas, todas como extension (5 direções por tipo):The conversions between layers, all as extensions (5 directions per type):Las conversiones entre capas, todas como extension (5 direcciones por tipo):
| DireçãoDirectionDirección | MétodoMethodMétodo |
|---|---|
| JSON → DTO | static fromMap(Map) (container + item + subject; stampa lastSyncAt = DateTimeUtils.now())(container + item + subject; stamps lastSyncAt = DateTimeUtils.now())(container + item + subject; sella lastSyncAt = DateTimeUtils.now()) |
| Proto → DTO | toFaqDTO() (container; itens/assuntos via toDTO(); stampa lastSyncAt)(container; items/subjects via toDTO(); stamps lastSyncAt)(container; ítems/asuntos vía toDTO(); sella lastSyncAt) |
| DTO → Entity | toDomain() |
| Entity → Model | toModel() (popula ToMany; lança StateError se lastSyncAt == null)(fills ToMany; throws StateError if lastSyncAt == null)(llena ToMany; lanza StateError si lastSyncAt == null) |
| Model → Entity | toDomain() |
Os únicos deltasThe only deltasLos únicos deltas
- nenhum enum de domínio — todo campo é
String/primitivo nas 4 camadasno domain enum — every field is aString/primitive across all 4 layersningún enum de dominio — todo campo esString/primitivo en las 4 capas id→faqItemId/supportSubjectIdrenomeado no Model (ObjectBox reservaidpara o@Idint)renamed in the Model (ObjectBox reservesidfor the@Idint)renombrado en el Model (ObjectBox reservaidpara el@Idint)- relações (
faqs,supportSubjects) viramToManyno Modelrelations (faqs,supportSubjects) becomeToManyin the Modelrelaciones (faqs,supportSubjects) pasan aToManyen el Model lastSyncAtgerado nos mappers JSON/Proto comDateTimeUtils.now()(ausente no proto); não-nulo no Model, nullable nas demaisgenerated in the JSON/Proto mappers withDateTimeUtils.now()(absent in the proto); non-null in the Model, nullable elsewheregenerado en los mappers JSON/Proto conDateTimeUtils.now()(ausente en el proto); no-nulo en el Model, nullable en las demástoModel()lançaStateErrorselastSyncAtfornull— protege a persistência (o repo só grava após um fetch bem-sucedido)throwsStateErroriflastSyncAtisnull— guards persistence (the repo only saves after a successful fetch)lanzaStateErrorsilastSyncAtesnull— protege la persistencia (el repo solo graba tras un fetch exitoso)
Repository
FaqRepositoryImpl implementaimplementsimplementa FaqRepositoryInterface e injeta os 3 datasources (mock/local/remote) + ConnectivityService + a flag useMockData + Ref. É só leitura — a escrita da mensagem de suporte não passa por aqui (vai pelo Dispatcher). Método a método:and injects the 3 datasources (mock/local/remote) + ConnectivityService + the useMockData flag + Ref. It is read-only — the support message write does not go through here (it goes through the Dispatcher). Method by method:e inyecta los 3 datasources (mock/local/remote) + ConnectivityService + la flag useMockData + Ref. Es solo lectura — la escritura del mensaje de soporte no pasa por aquí (va por el Dispatcher). Método a método:
getFaq({source = local}) mock / local / remote
RetornaReturnsDevuelve Result<FaqEntity, Failure>
Ponto de entrada do FAQ: decide a fonte pela source + flags, mapeia e grava no cache (write-through). Chamado pelo GetFaqUseCase.FAQ entry point: picks the source from source + flags, maps and writes to cache (write-through). Called by GetFaqUseCase.Punto de entrada del FAQ: elige la fuente por source + flags, mapea y graba en caché (write-through). Llamado por GetFaqUseCase.
Árvore de decisão de fonteSource decision treeÁrbol de decisión de fuente
useMockData== true ouorosource == mock→_fetchFromMock(): lê o mock por mercado, mapeia, grava no cache. A flag global tem precedência máxima.→_fetchFromMock(): reads the per-market mock, maps, writes to cache. The global flag has top precedence.→_fetchFromMock(): lee el mock por mercado, mapea, graba en caché. La flag global tiene máxima precedencia.source == localou offlineor offlineu offline→_fetchFromCacheOrFail(): só cache; se vazio →Error(NetworkFailure).→_fetchFromCacheOrFail(): cache only; if empty →Error(NetworkFailure).→_fetchFromCacheOrFail(): solo caché; si vacío →Error(NetworkFailure).- senão (remoto + conectado)otherwise (remote + connected)si no (remoto + conectado)→
_fetchFromRemoteWithFallback(): lêcurrentResourceProvider; senullcai pro cache; senão chama o remoto comlocationHierarchyId, mapeia, grava no cache; em erro, fallback pro cache.→_fetchFromRemoteWithFallback(): readscurrentResourceProvider; ifnullfalls back to cache; else calls remote withlocationHierarchyId, maps, writes to cache; on error, falls back to cache.→_fetchFromRemoteWithFallback(): leecurrentResourceProvider; sinullcae al caché; si no llama al remoto conlocationHierarchyId, mapea, graba en caché; en error, fallback al caché.
getCachedFaqLastSyncAt() local
RetornaReturnsDevuelve Future<DateTime?> (sem Result)(no Result)(sin Result)
Timestamp da última sincronização do FAQ, para o DataLoadInfo. Em erro, loga e retorna null.The FAQ's last-sync timestamp, for DataLoadInfo. On error, logs and returns null.Timestamp de última sincronización del FAQ, para DataLoadInfo. En error, loguea y devuelve null.
Datasources
Um card por datasource (dropdown). No corpo: método, envio, retorno, fluxo de uso e tratamento de erro.One card per datasource (dropdown). In the body: method, what it sends, return, usage flow and error handling.Un card por datasource (dropdown). En el cuerpo: método, envío, retorno, flujo de uso y manejo de errores.
Remote FaqRemoteDataSource gRPC
getFaq({locationHierarchySfid, lastModifiedDate?})
- EnvioSendsEnvío
- monta
FaqRequest(só anexalastModifiedDatese não vazio) e chama_client.getFaq(request)no clientFaqConectaRepServiceClient(viafaqServiceClientProvider).buildsFaqRequest(only attacheslastModifiedDateif non-empty) and calls_client.getFaq(request)onFaqConectaRepServiceClient(viafaqServiceClientProvider).armaFaqRequest(solo adjuntalastModifiedDatesi no está vacío) y llama_client.getFaq(request)enFaqConectaRepServiceClient(víafaqServiceClientProvider). - RetornoReturnRetorno
FaqEntity(viaresponse.toFaqDTO().toDomain())(viaresponse.toFaqDTO().toDomain())(víaresponse.toFaqDTO().toDomain())- Fluxo de usoUsage flowFlujo de uso
- chamado pelo caminho remoto do repository (
_fetchFromRemoteWithFallback), quando online e sem mock; o resultado é gravado no cache.called by the repository's remote path (_fetchFromRemoteWithFallback), when online and not mocking; the result is written to cache.llamado por el camino remoto del repository (_fetchFromRemoteWithFallback), online y sin mock; el resultado se graba en caché. - Tratamento de erroError handlingManejo de errores
GrpcError→GrpcExceptionHandler; outros →ServerException. Em erro, o repository faz fallback pro cache.GrpcError→GrpcExceptionHandler; others →ServerException. On error, the repository falls back to cache.GrpcError→GrpcExceptionHandler; otros →ServerException. En error, el repository hace fallback al caché.
Local FaqLocalDataSource ObjectBox
Envio / fluxo: persistência local via ObjectBox (ObjectBoxDatabase), boxes FaqModel, FaqItemModel e SupportSubjectModel — sem rede. Alimenta os caminhos cache do repository. Erro: falhas de persistência viram CacheException (não engolidas).Sends / flow: local persistence via ObjectBox (ObjectBoxDatabase), FaqModel, FaqItemModel and SupportSubjectModel boxes — no network. Feeds the repository's cache paths. Error: persistence failures become CacheException (not swallowed).Envío / flujo: persistencia local vía ObjectBox (ObjectBoxDatabase), boxes FaqModel, FaqItemModel y SupportSubjectModel — sin red. Alimenta los caminos caché del repository. Error: fallos de persistencia pasan a CacheException (no tragados).
getCachedFaq()
- RetornoReturnRetorno
FaqEntity?- ComportamentoBehaviorComportamiento
models.first.toDomain()— o agregado único, ounullse o cache está vazio.models.first.toDomain()— the single aggregate, ornullif the cache is empty.models.first.toDomain()— el agregado único, onullsi el caché está vacío.
getFaqLastSyncAt()
- RetornoReturnRetorno
DateTime?- ComportamentoBehaviorComportamiento
models.first.lastSyncAt— timestamp da última sync.— the last-sync timestamp.— timestamp de última sincronización.
saveFaq({entity})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- destrutivo:
clearFaq()+_box.put(entity.toModel())(grava boxes filhas em cascata). Cache-writer após cada fetch.destructive:clearFaq()+_box.put(entity.toModel())(writes child boxes in cascade). Cache-writer after each fetch.destructivo:clearFaq()+_box.put(entity.toModel())(graba boxes hijas en cascada). Cache-writer tras cada fetch.
clearFaq()
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- limpa as boxes de itens e assuntos e, por fim, a box do container.clears the item and subject boxes and finally the container box.limpia las boxes de ítems y asuntos y, por último, la box del container.
Mock FaqMockDataSource JSON
getFaq()
- EnvioSendsEnvío
- carrega o asset JSON
faq/faq(por mercado, real vs sintético viauseRealMockData) — sem rede.loads the JSON assetfaq/faq(per market, real vs synthetic viauseRealMockData) — no network.carga el asset JSONfaq/faq(por mercado, real vs sintético víauseRealMockData) — sin red. - RetornoReturnRetorno
FaqEntity(viaFaqDTOJsonMapper.fromMap→toDomain())(viaFaqDTOJsonMapper.fromMap→toDomain())(víaFaqDTOJsonMapper.fromMap→toDomain())- Fluxo de usoUsage flowFlujo de uso
- usado quando
useMockDataestá ligado ousource == mock; grava no cache como um fetch normal.used whenuseMockDatais on orsource == mock; writes to cache like a normal fetch.usado cuandouseMockDataestá activo osource == mock; graba en caché como un fetch normal. - Tratamento de erroError handlingManejo de errores
- asset ausente ou JSON inválido →
CacheException(sem rede envolvida).missing asset or invalid JSON →CacheException(no network involved).asset ausente o JSON inválido →CacheException(sin red involucrada).
Enums e labelsEnums & labelsEnums y labels
O modelo de dados do FAQ não tem nenhum enum de domínio — os campos são todos String/primitivo, sem status nem tipos tipados. O único enum da feature é de estado de UI, no State do Notifier (não trafega no wire):The FAQ data model has no domain enum at all — fields are all String/primitive, no typed status or types. The feature's only enum is a UI-state one, in the Notifier's State (it does not travel on the wire):El modelo de datos del FAQ no tiene ningún enum de dominio — los campos son todos String/primitivo, sin estado ni tipos tipados. El único enum de la feature es de estado de UI, en el State del Notifier (no viaja en el wire):
SubmissionStatus 4 · estado de UIUI stateestado de UI
| case | significadomeaningsignificado |
|---|---|
idle | ocioso (nada em andamento)idle (nothing in flight)ocioso (nada en curso) |
submitting | enviando (botão em loading)submitting (button loading)enviando (botón en loading) |
success | enviado com sucesso (aviso verde + reset)sent successfully (green notice + reset)enviado con éxito (aviso verde + reset) |
failure | falha no envio (aviso vermelho)submission failed (red notice)fallo en el envío (aviso rojo) |
UseCases
Um dropdown por UseCase. O de leitura delega ao repository; os dois de escrita compõem a transação de suporte (builder puro + submit pelo Dispatcher). Todos os providers são keepAlive.One dropdown per UseCase. The read one delegates to the repository; the two write ones compose the support transaction (pure builder + submit through the Dispatcher). All providers are keepAlive.Un dropdown por UseCase. El de lectura delega al repository; los dos de escritura componen la transacción de soporte (builder puro + submit por el Dispatcher). Todos los providers son keepAlive.
GetFaqUseCase 2 · leiturareadlectura
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({source}) | Result<FaqEntity, Failure> | Ponto de entrada do FAQ. Roteia por source (mock/local/remoto) → repository.getFaq. Chamado pelo FaqNotifier.FAQ entry point. Routes by source (mock/local/remote) → repository.getFaq. Called by FaqNotifier.Punto de entrada del FAQ. Rutea por source (mock/local/remoto) → repository.getFaq. Llamado por FaqNotifier. |
getCachedLastSyncAt() | Future<DateTime?> | Timestamp da última sincronização (sem Result). Alimenta o DataLoadInfo.Last-sync timestamp (no Result). Feeds DataLoadInfo.Timestamp de última sincronización (sin Result). Alimenta DataLoadInfo. |
BuildHelpSupportDispatcherPayloadUseCase 1 · builderbuilderbuilder
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
build({input}) | DispatcherEnvelope | implements DispatcherPayloadBuilder<HelpSupportDispatcherPayloadInput>. Puro — monta o JSON {support: {...}} (data ISO, subject, description trimmed, resourceSfid primary/secondary, userName só se sendsUserName) e resolve o serviceName de DispatcherType.helpSupport.implements DispatcherPayloadBuilder<HelpSupportDispatcherPayloadInput>. Pure — builds the {support: {...}} JSON (ISO date, subject, trimmed description, primary/secondary resourceSfid, userName only if sendsUserName) and resolves the serviceName from DispatcherType.helpSupport.implements DispatcherPayloadBuilder<HelpSupportDispatcherPayloadInput>. Puro — arma el JSON {support: {...}} (fecha ISO, subject, description trimmed, resourceSfid primary/secondary, userName solo si sendsUserName) y resuelve el serviceName de DispatcherType.helpSupport. |
SubmitHelpSupportUseCase 1 · enviosubmitenvío
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
submit({envelope}) | Result<DispatcherAck, Failure> | Entrega o DispatcherEnvelope ao DispatcherOrchestrator.dispatch (store + tracking + reenvio idempotente). É a única escrita da feature.Hands the DispatcherEnvelope to DispatcherOrchestrator.dispatch (store + tracking + idempotent resend). It's the feature's only write.Entrega el DispatcherEnvelope al DispatcherOrchestrator.dispatch (store + tracking + reenvío idempotente). Es la única escritura de la feature. |
Notifier & State
O FaqNotifier (@riverpod, with AsyncGuard<FaqState>) é o cérebro da tela. O build() observa os 3 UseCases (leitura + builder + submit) e retorna _load(). O State (FaqState, Freezed) é a fonte única de verdade da page: guarda o FAQ (AsyncValue<FaqEntity>) e o estado de cliente (cards abertos, assunto escolhido, texto da descrição, estado do envio). O Notifier concentra toda a lógica de escrita — a page só chama métodos dele.The FaqNotifier (@riverpod, with AsyncGuard<FaqState>) is the screen's brain. build() watches the 3 UseCases (read + builder + submit) and returns _load(). The State (FaqState, Freezed) is the page's single source of truth: it holds the FAQ (AsyncValue<FaqEntity>) and the client state (open cards, chosen subject, description text, submission status). The Notifier owns all write logic — the page only calls its methods.El FaqNotifier (@riverpod, with AsyncGuard<FaqState>) es el cerebro de la pantalla. build() observa los 3 UseCases (lectura + builder + submit) y retorna _load(). El State (FaqState, Freezed) es la fuente única de verdad de la page: guarda el FAQ (AsyncValue<FaqEntity>) y el estado de cliente (tarjetas abiertas, asunto elegido, texto de la descripción, estado del envío). El Notifier concentra toda la lógica de escritura — la page solo llama a sus métodos.
MétodosMethodsMétodos
_load({source = local}) private
RetornoReturnRetorno Future<FaqState>
Dono único da montagem do State: busca o FAQ via execute(source) (getOrThrow()) e retorna um FaqState com faq = AsyncValue.data(...). Chamado pelo build() (via guardedBuild) e pelo refresh().Sole owner of building the State: fetches the FAQ via execute(source) (getOrThrow()) and returns a FaqState with faq = AsyncValue.data(...). Called by build() (via guardedBuild) and refresh().Dueño único del armado del State: busca el FAQ vía execute(source) (getOrThrow()) y retorna un FaqState con faq = AsyncValue.data(...). Llamado por build() (vía guardedBuild) y refresh().
refresh() pull-to-refresh
RetornoReturnRetorno Future<void>
Recarrega via _load(source: remote) (dentro de runGuarded) e preserva o estado de cliente: expandedFaqIds, selectedSubjectId e supportDescription. Não seta AsyncValue.loading (o pull-to-refresh tem indicador próprio).Reloads via _load(source: remote) (inside runGuarded) and preserves client state: expandedFaqIds, selectedSubjectId and supportDescription. Doesn't set AsyncValue.loading (pull-to-refresh has its own indicator).Recarga vía _load(source: remote) (dentro de runGuarded) y preserva el estado de cliente: expandedFaqIds, selectedSubjectId y supportDescription. No setea AsyncValue.loading (pull-to-refresh tiene su propio indicador).
toggleExpand({faqId})
RetornoReturnRetorno void
Adiciona/remove o faqId do Set expandedFaqIds — abre/fecha aquele card. Vários podem ficar abertos.Adds/removes the faqId from the expandedFaqIds Set — opens/closes that card. Multiple can stay open.Agrega/quita el faqId del Set expandedFaqIds — abre/cierra esa tarjeta. Varias pueden quedar abiertas.
selectSubject({subjectId})
RetornoReturnRetorno void
Fixa o assunto escolhido (selectedSubjectId) e limpa qualquer submissionFailure pendente.Sets the chosen subject (selectedSubjectId) and clears any pending submissionFailure.Fija el asunto elegido (selectedSubjectId) y limpia cualquier submissionFailure pendiente.
updateDescription({description})
RetornoReturnRetorno void
Atualiza o texto da descrição e limpa a falha pendente. A validação (isSubmissionFormValid) é um getter do State.Updates the description text and clears the pending failure. Validation (isSubmissionFormValid) is a State getter.Actualiza el texto de la descripción y limpia el fallo pendiente. La validación (isSubmissionFormValid) es un getter del State.
submitSupportMessage() → Dispatcher
RetornoReturnRetorno Future<void>
Guarda de reentrada (form inválido / já enviando) → seta submitting → lê currentResourceProvider + marketConfigurationProvider → monta o HelpSupportDispatcherPayloadInput (resource + subject cruos, submittedAt = DateTimeUtils.now(), transactionReference) → build() → submit(). Em sucesso: success, espera ~1,2 s e limpa assunto + descrição. Em falha: failure com o Failure.Reentrancy guard (invalid form / already submitting) → sets submitting → reads currentResourceProvider + marketConfigurationProvider → builds the HelpSupportDispatcherPayloadInput (raw resource + subject, submittedAt = DateTimeUtils.now(), transactionReference) → build() → submit(). On success: success, waits ~1.2 s and clears subject + description. On failure: failure with the Failure.Guarda de reentrada (form inválido / ya enviando) → setea submitting → lee currentResourceProvider + marketConfigurationProvider → arma el HelpSupportDispatcherPayloadInput (resource + subject crudos, submittedAt = DateTimeUtils.now(), transactionReference) → build() → submit(). En éxito: success, espera ~1,2 s y limpia asunto + descripción. En fallo: failure con el Failure.
resetSubmissionStatus()
RetornoReturnRetorno void
Volta o submissionStatus para idle e limpa a falha. Chamado pela page após exibir o aviso (sucesso/erro).Resets submissionStatus to idle and clears the failure. Called by the page after showing the notice (success/error).Vuelve submissionStatus a idle y limpia el fallo. Llamado por la page tras mostrar el aviso (éxito/error).
State disponível para a PageState available to the PageState disponible para la Page
FaqState campos + gettersfields + getterscampos + getters
| campo | tipo | default |
|---|---|---|
faq | AsyncValue<FaqEntity> | loading() |
expandedFaqIds | Set<String> | {} |
selectedSubjectId | String? | null |
supportDescription | String | "" |
submissionStatus | SubmissionStatus | idle |
submissionFailure | Failure? | null |
Getters: lastSyncAt, contactCenter, faqs, supportSubjects, selectedSubject, isExpanded({faqId}), isSubmissionFormValid (assunto ≠ null & descrição não vazia), isSubmitting. Toda leitura da page passa por eles.Getters: lastSyncAt, contactCenter, faqs, supportSubjects, selectedSubject, isExpanded({faqId}), isSubmissionFormValid (subject ≠ null & non-empty description), isSubmitting. Every page read goes through them.Getters: lastSyncAt, contactCenter, faqs, supportSubjects, selectedSubject, isExpanded({faqId}), isSubmissionFormValid (asunto ≠ null & descripción no vacía), isSubmitting. Toda lectura de la page pasa por ellos.
Page e widgetsPage & widgetsPage y widgets
A FaqPage (ConsumerWidget) observa o faqProvider, monta os widgets filhos e escuta a transição do envio (ref.listen) para disparar o ConectaNotice (verde no sucesso, vermelho na falha) e resetar o status. Loading e erro são globais (faqAsync.when). Árvore de composição:FaqPage (ConsumerWidget) watches faqProvider, composes the child widgets and listens to the submission transition (ref.listen) to fire the ConectaNotice (green on success, red on failure) and reset the status. Loading and error are global (faqAsync.when). Composition tree:FaqPage (ConsumerWidget) observa faqProvider, compone los widgets hijos y escucha la transición del envío (ref.listen) para disparar el ConectaNotice (verde en éxito, rojo en fallo) y resetear el status. Loading y error son globales (faqAsync.when). Árbol de composición:
- FaqPage
- AppPageShell displayBackButton
- CustomLoadingIndicator loading
- FailureStateView error → invalidate(faqProvider)
- CustomPullToRefresh data → refresh()
- DataLoadInfo lastSyncAt
- FaqHelpHeaderWidget ícone + título "Ajuda"
- FaqSectionTitleWidget título "FAQ"
- FaqListWidget
- CustomEmptyState lista vazia
- FaqCardWidget expansível · chevron ↓/↑ · branco quando aberto → toggleExpand
- FaqSectionTitleWidget título "Suporte"
- SupportIntroWidget contactCenter + intro
- SupportSubjectFieldWidget → selectSubject
- SupportSubjectPickerModalContent modal · ConectaModal → backWithResult(subject.id)
- SupportDescriptionFieldWidget CustomInput multilinha → updateDescription
- SupportSubmitButtonWidget CustomButton.filled · enable/loading → submitSupportMessage
- AppPageShell displayBackButton
O FaqCardWidget é o componente expansível canônico: chevron para baixo fechado / cima aberto e fundo branco quando expandido, com animação de tamanho + fade. O modal de assunto retorna o id via AppRouter.backWithResult.The FaqCardWidget is the canonical expandable component: chevron down closed / up open and a white background when expanded, with a size + fade animation. The subject modal returns the id via AppRouter.backWithResult.El FaqCardWidget es el componente expandible canónico: chevron abajo cerrado / arriba abierto y fondo blanco al expandirse, con animación de tamaño + fade. El modal de asunto devuelve el id vía AppRouter.backWithResult.
Notas por mercadoMarket notesNotas por mercado
O FAQ é dirigido por configuração de mercado (End Market Configuration): a chave faqConfig habilita a feature e a transação de suporte (DispatcherType.helpSupport) tem enabledMarkets: [BR, CL, ZA]. Está disponível em três mercados:FAQ is driven by market configuration (End Market Configuration): the faqConfig key enables the feature and the support transaction (DispatcherType.helpSupport) has enabledMarkets: [BR, CL, ZA]. It's available in three markets:El FAQ se rige por configuración de mercado (End Market Configuration): la clave faqConfig habilita la feature y la transacción de soporte (DispatcherType.helpSupport) tiene enabledMarkets: [BR, CL, ZA]. Está disponible en tres mercados:
O único comportamento que varia por mercado é faqConfig.sendsUserName — se o nome de usuário do representante é anexado à mensagem de suporte enviada:The only per-market behavior is faqConfig.sendsUserName — whether the sales rep's username is attached to the submitted support message:El único comportamiento que varía por mercado es faqConfig.sendsUserName — si el nombre de usuario del representante se adjunta al mensaje de soporte enviado:
| ChaveKeyClave | BR | CL | ZA | AR | PY | PE |
|---|---|---|---|---|---|---|
faqConfig (feature)(feature)(feature) |
x | x | x | — | — | — |
faqConfig.sendsUserName |
false | false | true | — | — | — |
África do SulSouth AfricaSudáfrica
Único mercado com sendsUserName: true — o campo userName (o resource.username) é incluído no payload {support: {...}}. Em BR/CL o campo é omitido.
The only market with sendsUserName: true — the userName field (resource.username) is included in the {support: {...}} payload. In BR/CL the field is omitted.
Único mercado con sendsUserName: true — el campo userName (resource.username) se incluye en el payload {support: {...}}. En BR/CL el campo se omite.
Pendências / roadmapPending / roadmapPendientes / roadmap
- AR · PY · PE — existem como mercados do app e têm mock de FAQ preparado (
ar_faq.json,py_faq.json,pe_faq.json), mas não têmfaqConfigno EMC — o item "Ajuda" não aparece no menu e a feature fica indisponível até o mercado ativar a config.AR · PY · PE — exist as app markets and have FAQ mock staged (ar_faq.json,py_faq.json,pe_faq.json), but have nofaqConfigin the EMC — the "Help" item doesn't show in the menu and the feature stays unavailable until the market turns the config on.AR · PY · PE — existen como mercados de la app y tienen mock de FAQ preparado (ar_faq.json,py_faq.json,pe_faq.json), pero no tienenfaqConfigen el EMC — el ítem "Ayuda" no aparece en el menú y la feature queda indisponible hasta que el mercado active la config. - A transação de escrita
SupportRequestAPI(DispatcherType.helpSupport) ainda não tem doc de transação dedicada emdocs/dispatcher/— quando existir, esta feature e a transação serão cruzadas.TheSupportRequestAPIwrite transaction (DispatcherType.helpSupport) does not yet have a dedicated transaction doc indocs/dispatcher/— once it does, this feature and the transaction will be cross-linked.La transacción de escrituraSupportRequestAPI(DispatcherType.helpSupport) aún no tiene doc de transacción dedicada endocs/dispatcher/— cuando exista, esta feature y la transacción se cruzarán.