Skip to content

App do Responsável (mobile) ​

O que é este documento — o mapa funcional e técnico do app do responsável do ionCLASS: o cliente mobile (guardião/responsável) que aciona o "Estou Chegando", acompanha a retirada ao vivo, gerencia responsáveis e permissões por aluno e recebe notificações de entrada/saída. É uma das quatro superfícies do produto — veja as irmãs em Monitor de Portaria, Painel Público e Painel Administrativo.


1. O que é ​

O app do responsável é um cliente REST puro do backend Laravel (/api/v1/*) — sem código compartilhado com as outras superfícies, apenas o contrato de API espelhado à mão (endpoints + schemas Zod). O público é o responsável: pai, mãe ou outro responsável vinculado a um ou mais alunos. Ele:

  • sinaliza que está chegando para retirar o(s) filho(s) ("Estou Chegando");
  • acompanha o estado de retirada ao vivo de cada aluno (o mesmo fluxo que a portaria opera);
  • gerencia outros responsáveis e permissões temporárias por aluno (gated por capabilities);
  • recebe alertas de entrada/saída e notificações push.

Autenticação é Sanctum bearer token, guardado em MMKV; um 401 limpa token + usuário e volta ao login. Onboarding é CPF + email → código de 6 dígitos (sem código de convite, sem fila de aprovação) — ver Autenticação e Autorização.

2. Stack técnica ​

CamadaTecnologia
RuntimeExpo SDK 55 + Expo Router (roteamento por arquivos), React Native 0.83 com New Architecture
UI / temaNativeWind 4 (Tailwind para RN), primitivos estilo shadcn, Reanimated 4 (worklets)
Estado local / storageMMKV — token, escola ativa, flags
HTTPaxios + interceptors (Bearer, X-School-Id, X-Pin-Confirmation, 401) · zod como fonte de verdade dos tipos
Tempo reallaravel-echo + pusher-js/react-native (Reverb, protocolo pusher)
PushFirebase FCM + Notifee (apresentação/canal local das notificações)
Localizaçãoexpo-location — posição lida no toque do "Estou Chegando"; a geocerca em segundo plano que dispara o aviso de chegada é opt-in atrás da tela de divulgação (§4.4)

⚠️ O app é buildado direto do nativo (Xcode/Android Studio), não só por configuração declarativa. Não há test runner, lint ou typecheck configurados por padrão — a checagem de tipos é feita à parte.

3. Estrutura de telas ​

O app se organiza em três grandes áreas: as telas fora da sessão (autenticação), as abas (a casa do app) e as telas de detalhe empilhadas sobre as abas.

3.1 Fora da sessão ​

TelaConteúdo
Boas-vindasSplash / entrada
LoginLogin (CPF/email + senha)
Primeiro acessoOnboarding: informa CPF + email para o match
Verificar códigoConfirma o código de 6 dígitos enviado por email (Fortify)
FotoCaptura da foto do responsável (usada na verificação da portaria)
Esqueci a senha / Redefinir senhaFluxo de recuperação de senha

3.2 As 5 abas ​

Ordem da barra (a central em destaque): Início · Alunos · ESTOU CHEGANDO · Alertas · Menu.

AbaConteúdo
InícioHome: resumo dos alunos / atalhos
AlunosLista de alunos e detalhe do aluno com estado de retirada ao vivo, responsáveis e permissões
ESTOU CHEGANDOAba central: monta o sinal de chegada; leva à tela do sinal em andamento
AlertasFeed de entradas/saídas
MenuPonto de entrada para perfil, notificações, segurança e veículos

3.3 Telas de detalhe empilhadas ​

  • Menu → Perfil, Notificações, Segurança.
  • Veículos → lista, cadastro e edição — CRUD de veículos.
  • Raiz → definição do PIN e permissão/preview de localização.

4. Funcionalidades ​

4.1 Onboarding (CPF + email → código) ​

Sem código de convite e sem fila de aprovação de responsável: o app coleta CPF + email, o backend faz o match com o responsável pré-cadastrado e dispara um código de 6 dígitos por email; a confirmação passa pelo Fortify. CPF desconhecido → 422 silencioso (não abre fila administrativa). Detalhes em Autenticação e Autorização.

LoginCadastro (primeiro acesso)Verificação por código

4.2 Alunos e detalhe ao vivo ​

A aba Alunos e o detalhe do aluno trazem o estado de retirada de cada aluno e reconciliam ao vivo por WebSocket (§6). O detalhe é também onde vivem responsáveis e permissões do aluno.

Início / DashboardListagem de criançasPerfil da criança

4.3 Gestão de responsáveis e permissões temporárias (capabilities) ​

Cada vínculo aluno↔responsável tem dois eixos. O parentesco (Pai/Mãe, Avó, Tio, Motorista…) é apenas um rótulo e não concede nada. Quem carrega as regras é o tipo de responsabilidade:

Tipo de responsabilidadePapel
Responsável PedagógicoO "master" do aluno — por padrão tem todas as capacidades
Responsável FinanceiroPor padrão: saídas antecipadas + buscar aluno
ResponsávelPor padrão: apenas buscar aluno

O que cada tipo permite é configurado por escola, no painel (ver Painel Administrativo §4.5.1). O backend resolve o conjunto contra essa matriz e devolve capabilities já pronto no detalhe do aluno; o app libera telas/ações conforme essas capabilities:

CapabilityHabilita
manage_responsiblesIncluir, editar e remover outros responsáveis do aluno
view_responsiblesVer quem está autorizado a retirar o aluno
view_temporary_permissionsVisualizar as permissões temporárias do aluno
create_temporary_permissionsCriar, editar e cancelar permissões temporárias
view_activitiesVer o histórico de entradas e saídas do aluno
pick_up_studentBuscar o aluno — sinalizar chegada ("Estou Chegando") e retirá-lo

pick_up_student todo responsável tem, sempre: é o uso principal do app, então a escola vê essa caixa marcada e travada na matriz e não consegue desmarcá-la. O "Estou Chegando" não é gateado por capacidade — o que ele exige é a confirmação por PIN/biometria do aparelho.

O responsável não escolhe permissões no app: toda pessoa que ele cadastra entra como "Responsável". Pedagógico e Financeiro são atribuídos pela escola (painel, importação CSV ou integração do controle de acesso).

Endpoints do lado do app: me/students/{student}/responsibles (vincular/editar/remover), além dos endpoints de permissões temporárias e de pessoas autorizadas.

4.4 "Estou Chegando" ​

O fluxo central do produto. Na aba ESTOU CHEGANDO o responsável:

  1. seleciona quais alunos vai retirar (irmãos podem ir juntos no mesmo sinal). Vêm pré-marcados os filhos na escola; os que estão fora da escola podem ser incluídos à mão e entram no sinal normalmente, mas continuam aparecendo como "Fora da escola" até a entrada ser registrada (ver presença real);
  2. escolhe o modo de transporte (a pé / veículo) e, se dirigindo, o veículo;
  3. escolhe o ponto de retirada (portão);
  4. envia a geolocalização junto ao sinal — a permissão de localização é pedida neste toque (o app não mantém watcher ligado nem pede permissão no login).

Aviso de chegada (opt-in). Além do aviso manual, o responsável pode ativar uma geocerca que, com o app fechado, percebe a entrada no raio da escola no horário de retirada e reporta o ENTER ao serviço de tracking; o backend então manda ao responsável o push arrival_incoming ("toque para avisar a portaria") — não há notificação local, para o aviso passar pelo opt-out e pelo horário silencioso do servidor. A geocerca não avisa a escola sozinha — o sinal continua manual; se o responsável já avisou de longe, o backend apenas promove o sinal para "chegou". Como isso usa localização em segundo plano, o Google Play exige Prominent Disclosure: o pedido de permissão só sai de uma tela in-app que descreve a coleta, com "Permitir" e "Agora não" — nunca no bootstrap do app. Um só controle: a linha "Proximidade da escola" em Menu › Notificações (a preferência arrival_incoming do servidor, que também arma/desarma a geocerca do aparelho), mais um card-convite na aba chegando. Negar não trava nada: o aviso manual segue funcionando. Código: lib/legal/location-disclosure.ts, components/tracking/location-disclosure-modal.tsx, lib/tracking/use-arrival-alert.ts.

POST v1/arrival-signals cria o sinal; GET v1/arrival-signals/active alimenta a tela de sinal ativo, e POST v1/arrival-signals/{id}/cancel cancela. Avisar a escola é uma solicitação de saída: o POST passa pelo gate do PIN (§4.7) e exige o header X-Pin-Confirmation — na prática, a biometria do aparelho. O gate é declarado na rota mas fica desligado por configuração até o app estar publicado, porque ligá-lo com clientes antigos em campo devolveria 403 no fluxo mais usado. active e cancel seguem sem gate. O sinal cai na fila da portaria (Monitor de Portaria) e no painel público (Painel Público). Ver o fluxo ponta a ponta em Ciclo de Retirada.

Estou Chegando — seleção de escolaEstou Chegando — sinal em andamentoAlertas / Notificações

4.5 Alertas ​

Feed de entradas/saídas do(s) aluno(s) — o histórico de eventos de acesso, também alimentado por push e reconciliado ao vivo.

4.6 Veículos (CRUD) ​

Cadastro/edição/remoção de veículos do responsável, com marcação de favorito. Usados no "Estou Chegando" e exibidos no cartão da portaria para identificação visual.

4.7 Segurança / PIN / biometria do aparelho ​

Um PIN de 4 dígitos protege ações sensíveis. Ao verificar/definir/redefinir o PIN, o backend emite uma confirmação de curta duração (token + validade, 15 minutos), mantida só em memória (nunca em MMKV, some ao relançar o app). O interceptor do axios injeta esse token no header X-Pin-Confirmation; ele expira de forma "dura", sem renovação automática. Fica na tela de Segurança (+ fluxo de definição do PIN).

Biometria do aparelho. A confirmação padrão é a biometria do próprio celular (Face ID / digital): o PIN fica guardado no keychain/keystore do aparelho, exigindo autenticação para ser lido, e a leitura biométrica é o que o devolve; o app então troca esse PIN pela confirmação de sempre. É um único prompt do sistema operacional. Como esse degrau vive dentro do gate, todas as ações protegidas ganham a biometria de uma vez — avisar chegada, saída antecipada, autorizar pessoa, editar responsável, excluir conta.

O PIN em claro nunca vai para o MMKV; lá ficam só os flags de opt-in. Qualquer falha (recusa, leitura ruim, chave invalidada pelo sistema, PIN alterado em outro aparelho) cai no teclado de PIN, e um aparelho sem biometria cadastrada nunca vê a tela de biometria. A ativação é oferecida uma única vez após uma confirmação de PIN bem-sucedida, e pode ser ligada/desligada em Segurança. Sair da conta apaga a confirmação e o PIN guardado.

Não confundir com o consentimento biométrico do controle de acesso

O consentimento biométrico (LGPD, art. 11) autoriza enviar a foto do responsável ao leitor facial do controle de acesso e fica no Perfil (§4.9). A biometria do aparelho é resolvida pelo sistema operacional, não sai do celular, não trafega para a Iongrade e não coleta dado biométrico novo. São coisas distintas.

4.8 Notificações ​

Tela de Notificações: toggle master + preferências por tipo de notificação + quiet hours (horário silencioso). O registro do device de push é feito por POST v1/me/devices (e DELETE v1/me/devices/{device} no logout).

4.9 Perfil ​

Tela de Perfil: editar dados, foto, consentimento biométrico + face-quality e exclusão de conta.

5. Multi-escola ​

O responsável tem vínculo N:N com escolas. A escola ativa é mantida pelo app e persistida no MMKV; toda chamada REST injeta o header X-School-Id com ela. Quando há mais de uma escola, o usuário troca a ativa pelo avatar da escola no header, e trocar de escola reseta a navegação e reabre a conexão de tempo real.

⚠️ Este seletor de escola é considerado temporário ("remover quando o rollout terminar"). A escola escopa o que o app vê/edita/cria.

6. Tempo real ​

O app assina o canal privado responsible.{userId}.school.{schoolId} (autorizado por identidade do usuário e responsável ativo naquela escola — um responsável bloqueado não assina). As credenciais do Reverb vêm de GET v1/me/monitor/credentials (mesmo shape do operador) e o handshake do canal passa por /api/broadcasting/auth (Sanctum, com Bearer + X-School-Id).

  • Conecta só em foreground. A camada de tempo real recria o Echo quando token ou escola mudam.
  • Eventos: StudentStateChanged (estado de retirada), StudentRosterChanged (rebusca a lista) e ResponsibleNotificationCreated (inbox).
  • Reconciliação por versão do estado de retirada (regra da portaria): aplica o novo estado sem nunca regredir abaixo da versão local; evento mais antigo é ignorado (identidade do objeto preservada para evitar re-render).
  • É best-effort: se o WS cair, a verdade do servidor reconcilia no próximo refetch ao focar a tela.

Ver Tempo Real e o runbook de operação em Runbook de Tempo Real.

7. Push (FCM + Notifee) ​

WebSocket cobre o app aberto; FCM cobre o app fechado — os dois convivem. O device é registrado em POST v1/me/devices; o Notifee apresenta a notificação local (canal/quiet hours). A deduplicação usa a versão do estado de retirada (estado nunca regride) e o identificador real da notificação (inbox), então push + WS nunca divergem.

8. Tema ​

Fonte única de tema (tokens de cor + adapter do React Navigation). Paleta de marca: navy #043471 (primary no claro), azul #0277F4 (accent), light #5AB5FC, turquoise #21D7CA. O CSS de estilos é gerado a partir dessa fonte de tema — nunca editar à mão (é sobrescrito). Dark mode pelo esquema de cores do sistema via NativeWind.

9. Ver também ​

Documentação do ionCLASS