📚 Pré-requisitos Teóricos: este projeto consome os serviços da Parte 01 — Spring Boot API e a infraestrutura conteinerizada da Parte 05 — Docker + PostgreSQL.

📝 Projeto Lista de Tarefas 06 — Spring Boot + Vue.js ➔ Quasar Framework

Seja muito bem-vindo à Parte 06 do ecossistema de aplicações Lista de Tarefas!

Nesta etapa prática, você aprenderá a dominar a primeira das três grandes trilhas de equivalência entre desenvolvimento Web e Mobile Híbrido / Cross-platform:

\[\text{Web SPA (Vue.js 3)} \longrightarrow \text{Mobile Cross-Platform (Quasar Framework + Capacitor)}\]

Enquanto nas Partes 03 e 04 construímos clientes nativos clássicos para desktop e mobile, a Parte 06 ensina a filosofia do Single Codebase: como reaproveitar a reatividade, os componentes e a lógica de uma aplicação Vue 3 para gerar simultaneamente uma Web SPA de alta performance e um aplicativo móvel nativo para Android e iOS utilizando o poderoso Quasar Framework.


🗺️ 1. Visão Geral da Arquitetura & Papel no Ecossistema

Tanto a aplicação Web (Vue 3) quanto o cliente Mobile (Quasar) operam como consumidores da nossa Fonte Única da Verdade (SSOT): a API REST desenvolvida em Spring Boot na porta 8080, conectada ao banco de dados relacional (H2 ou PostgreSQL conteinerizado).

Ecossistema Vue 3 para Quasar Single Codebase

Por que a combinação Vue 3 + Quasar Framework?


⚡ 2. Fluxo Reativo e Resolução de Plataforma

Um dos desafios fundamentais do desenvolvimento cross-platform é o roteamento dinâmico de rede:

Fluxo de Requisições Quasar e Reatividade

  1. No Navegador Web: A requisição HTTP via Axios conecta-se em http://localhost:8080/api/tarefas.
  2. No Emulador Android (via Capacitor): O plugin de boot do Quasar detecta a plataforma através de Platform.is.capacitor && Platform.is.android e redireciona automaticamente para o endereço virtual do host: \(\text{URL Mobile} = \mathbf{http://10.0.2.2:8080/api/tarefas}\)
  3. No Dispositivo Físico: Conectado à rede Wi-Fi, o aplicativo utiliza o IP local da máquina de desenvolvimento (ex: http://192.168.1.50:8080/api).

📑 3. Engenharia de Software & Especificação de Requisitos

3.1. Histórias de Usuário (Web & Mobile Quasar)

3.2. Requisitos Funcionais (RF)

ID Descrição Endpoint REST
RF-V01 Consultar e exibir todas as tarefas cadastradas ao carregar a página ou acionar o botão de recarga. GET /api/tarefas
RF-V02 Cadastrar nova tarefa com descrição preenchida e status inicial não concluído. POST /api/tarefas
RF-V03 Atualizar o estado de conclusão (concluida = true/false) de forma assíncrona. PUT /api/tarefas/{id}
RF-V04 Modificar a descrição textual de uma tarefa através de modal interativo. PUT /api/tarefas/{id}
RF-V05 Deletar permanentemente uma tarefa após confirmação do usuário. DELETE /api/tarefas/{id}

3.3. Requisitos Não-Funcionais (RNF)

ID Categoria Descrição
RNF-V01 Reatividade O estado da UI deve ser gerenciado por reatividade nativa do Vue 3 (ref e Composition API).
RNF-V02 Cross-Platform O código deve compilar tanto para Web SPA quanto para aplicativo móvel Android através do Quasar CLI e Capacitor.
RNF-V03 Resolução de Rede O cliente deve detectar em tempo de execução se está rodando em ambiente Capacitor e chavear a base URL para 10.0.2.2:8080.
RNF-V04 Feedback Visual Todas as ações do usuário (sucesso, erro, carregamento) devem acionar componentes nativos do Quasar (Notify, Dialog, QInnerLoading).
RNF-V05 CORS As requisições originadas do Vite (:5173), Quasar (:9000) e Capacitor (http://localhost) devem ser aceitas pela API via @CrossOrigin.

3.4. Casos de Uso do Sistema

flowchart LR
    User(("👤 Usuário Web / Mobile"))

    subgraph "Aplicação Vue 3 / Quasar"
        UC01["UC01: Listar Tarefas com Ordenação"]
        UC02["UC02: Inserir Nova Tarefa"]
        UC03["UC03: Alternar Conclusão da Tarefa"]
        UC04["UC04: Editar Tarefa em Diálogo Modal"]
        UC05["UC05: Excluir Tarefa com Alerta"]
    end

    User --> UC01
    User --> UC02
    User --> UC03
    User --> UC04
    User --> UC05

UC01: Listar Tarefas com Ordenação

UC02: Inserir Nova Tarefa

UC03: Alternar Conclusão da Tarefa


3.5. Matriz de Rastreabilidade (RTM)

Caso de Uso História de Usuário Requisito Funcional Requisito Não-Funcional Arquivo Responsável
UC01 US-V01 RF-V01 RNF-V01, RNF-V04 TarefasPage.vue, tarefaService.js
UC02 US-V02 RF-V02 RNF-V01, RNF-V04 TarefasPage.vue, QInput
UC03 US-V03 RF-V03 RNF-V01, RNF-V04 TarefasPage.vue, QCheckbox
UC04 US-V04 RF-V04 RNF-V01, RNF-V04 TarefasPage.vue, QDialog
UC05 US-V05 RF-V05 RNF-V01, RNF-V04 TarefasPage.vue, $q.dialog()

🛠️ 4. Estrutura de Pastas da Parte 06

A Parte 06 organiza-se em dois ambientes complementares compartilhando a mesma API central:

fullstack_tarefas_06_spring_vue/
├── assets/
│   ├── arquitetura_vue_quasar_ecosystem.svg  ← Diagrama conceitual Single Codebase
│   └── fluxo_requisicao_quasar_api.svg       ← Diagrama do ciclo reativo Quasar/Axios
├── web-vue/                                 ← Aplicação Web SPA pura (Vue 3 + Vite)
│   ├── src/
│   │   ├── services/tarefaService.js         ← Comunicação HTTP com Axios (Porta 8080)
│   │   ├── App.vue                           ← Componente principal da Web SPA
│   │   └── main.js                           ← Inicialização do Vue 3
│   ├── package.json                          ← Dependências do Vite e Vue 3
│   └── vite.config.js                        ← Configuração do bundler Vite
├── mobile-quasar/                            ← Aplicação Cross-Platform com Quasar Framework
│   ├── src/
│   │   ├── boot/axios.js                     ← Plugin de boot com resolução inteligente de IP
│   │   ├── services/tarefaService.js         ← Camada de serviço desacoplada
│   │   ├── layouts/MainLayout.vue            ← Layout mobile responsivo (Header e Toolbar)
│   │   ├── pages/TarefasPage.vue             ← Tela completa com componentes Quasar
│   │   ├── router/routes.js                  ← Definição de rotas do Vue Router
│   │   └── css/app.scss                      ← Paleta de cores corporativa
│   ├── quasar.config.js                      ← Configuração de compilação Quasar
│   ├── capacitor.config.json                 ← Configuração do empacotamento móvel
│   └── package.json                          ← Dependências do Quasar e Capacitor
└── index.md                                  ← Este Guia Padrão Ouro Educacional

💻 5. Passo a Passo de Implementação

Passo 1: O Cliente Web SPA com Vue 3 (web-vue/)

No arquivo web-vue/src/services/tarefaService.js, configuramos o Axios apontando para a porta 8080:

import axios from 'axios'

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api'

const apiClient = axios.create({
  baseURL: API_URL,
  headers: {
    'Content-Type': 'application/json',
  },
})

export default {
  getTarefas() {
    return apiClient.get('/tarefas')
  },
  addTarefa(tarefa) {
    return apiClient.post('/tarefas', tarefa)
  },
  updateTarefa(id, tarefa) {
    return apiClient.put(`/tarefas/${id}`, tarefa)
  },
  deleteTarefa(id) {
    return apiClient.delete(`/tarefas/${id}`)
  },
}

Passo 2: O Cliente Cross-Platform com Quasar (mobile-quasar/)

1. Resolução Dinâmica de IP (src/boot/axios.js):

No Quasar, criamos um arquivo de boot que injeta a instância do Axios e adapta o IP automaticamente:

import { boot } from 'quasar/wrappers'
import axios from 'axios'
import { Platform } from 'quasar'

const getBaseUrl = () => {
  // Se estiver rodando dentro do Capacitor no Android, usa o alias do host:
  if (Platform.is.capacitor && Platform.is.android) {
    return 'http://10.0.2.2:8080/api'
  }
  // No navegador web ou desktop:
  return process.env.API_URL || 'http://localhost:8080/api'
}

const api = axios.create({
  baseURL: getBaseUrl(),
  headers: { 'Content-Type': 'application/json' },
})

export default boot(({ app }) => {
  app.config.globalProperties.$axios = axios
  app.config.globalProperties.$api = api
})

export { api }

2. Layout Mobile Responsivo (src/layouts/MainLayout.vue):

Utiliza o sistema de grid e barras de ferramentas do Quasar:

<template>
  <q-layout view="lHh Lpr lFf">
    <q-header elevated class="bg-primary text-white">
      <q-toolbar>
        <q-avatar icon="check_circle" color="secondary" text-color="white" />
        <q-toolbar-title>
          <div class="text-weight-bold">Minhas Tarefas</div>
          <div class="text-caption text-indigo-2">Quasar Framework · Mobile & Web</div>
        </q-toolbar-title>
        <q-btn flat round dense icon="refresh" @click="eventoRecarregar" title="Recarregar" />
      </q-toolbar>
    </q-header>

    <q-page-container>
      <router-view @recarregar-disparado="registrarHandler" />
    </q-page-container>
  </q-layout>
</template>

3. Página de Tarefas com Componentes Quasar (src/pages/TarefasPage.vue):

Implementa listagem com QList, caixas de seleção QCheckbox, modal de edição QDialog, feedback sonoro/visual via $q.notify() e diálogo de exclusão via $q.dialog():

<template>
  <q-page class="q-pa-md max-container">
    <!-- Formulário em Card -->
    <q-card flat bordered class="q-mb-md">
      <q-card-section class="q-pa-sm">
        <q-input
          v-model="novaTarefaDescricao"
          placeholder="O que precisa ser feito?"
          outlined
          dense
          @keyup.enter="adicionarTarefa"
        >
          <template v-slot:after>
            <q-btn color="primary" icon="add" label="Adicionar" unelevated @click="adicionarTarefa" />
          </template>
        </q-input>
      </q-card-section>
    </q-card>

    <!-- Lista com QList e QItem -->
    <q-card flat bordered>
      <q-list separator v-if="tarefas.length > 0">
        <q-item v-for="tarefa in tarefas" :key="tarefa.id" clickable v-ripple>
          <q-item-section side>
            <q-checkbox
              :model-value="tarefa.concluida"
              @update:model-value="(val) => alternarStatus(tarefa, val)"
              color="secondary"
            />
          </q-item-section>
          <q-item-section>
            <q-item-label :class="{ 'text-strike text-grey-6': tarefa.concluida }">
              {{ tarefa.descricao }}
            </q-item-label>
          </q-item-section>
          <q-item-section side>
            <div class="row q-gutter-xs">
              <q-btn flat round dense color="info" icon="edit" @click.stop="abrirModalEdicao(tarefa)" />
              <q-btn flat round dense color="negative" icon="delete" @click.stop="confirmarExclusao(tarefa)" />
            </div>
          </q-item-section>
        </q-item>
      </q-list>
    </q-card>
  </q-page>
</template>

🚀 6. Como Executar no Laboratório

1. Iniciar a API Spring Boot (SSOT)

A API central deve estar ativa na porta 8080:

# Na pasta da Parte 01:
cd catalogo_projetos/fullstack_tarefas_01_spring_api/api-spring
./mvnw spring-boot:run

# Ou via Docker Compose na Parte 05:
cd ../../fullstack_tarefas_05_arquitetura
docker compose up -d

2. Executar a Web SPA Pura (Vue 3 + Vite)

cd catalogo_projetos/fullstack_tarefas_06_spring_vue/frontend
npm install
npm run dev

Acesse no navegador: http://localhost:5173.

3. Executar o Quasar no Modo Web (Desenvolvimento Rápido)

cd catalogo_projetos/fullstack_tarefas_06_spring_vue/mobile-quasar
npm install
npx quasar dev

Acesse no navegador: http://localhost:9000.

4. Executar o Quasar no Modo Mobile (Android via Capacitor)

Para abrir o projeto diretamente no Android Studio com suporte ao emulador oficial:

npx quasar dev -m capacitor -T android

O Quasar compilará os assets web, sincronizará a pasta dist/spa com o projeto Android nativo do Capacitor e iniciará a aplicação conectando-se automaticamente em http://10.0.2.2:8080.


🧪 7. Matriz de Homologação & Testes de Aceitação

# Ação de Teste Comportamento Esperado Resultado
01 Abrir a Web SPA em localhost:5173. Lista de tarefas é carregada da API na porta 8080; exibe badge verde de conexão. [x] PASS
02 Cadastrar tarefa via Web SPA. Tarefa é salva no backend; aparece imediatamente na listagem. [x] PASS
03 Abrir o Quasar em localhost:9000 ou no emulador. A mesma tarefa cadastrada na Web SPA é exibida de forma sincronizada no Quasar. [x] PASS
04 Marcar tarefa como concluída no Quasar. Texto ganha efeito tachado; ao atualizar a Web SPA, o status reflete a conclusão. [x] PASS
05 Editar descrição via modal QDialog. Diálogo abre com texto preenchido; ao salvar, a notificação Notify confirma sucesso. [x] PASS
06 Excluir tarefa confirmando no $q.dialog. Item é removido do banco; mensagem de feedback surge no topo da tela. [x] PASS

🏆 8. Conclusão & Próximos Passos

Parabéns! Você dominou a transição da Web para o Mobile com o ecossistema Vue:

👉 Próximo Passo: Parte 07 — Spring Boot + React ➔ React Native (Expo)

Na próxima etapa da esteira (Parte 07), você verá a correspondência equivalente no ecossistema React: React 19 ➔ React Native + Expo (“Construa interfaces com componentes e deixe o ecossistema complementar”), renderizando componentes nativos reais com o motor Yoga Flexbox!