Aplicação web mobile-first para rastreamento de hábitos e rotinas semanais.
Produção: routineflow-sepia.vercel.app
- Visão Geral
- Funcionalidades
- Arquitetura
- Stack Tecnológica
- Decisões Técnicas
- Modelo de Dados
- Regras de Negócio
- Rotas da Aplicação
- Responsividade e Design System
- Como Executar Localmente
- Variáveis de Ambiente
- Deploy
- Créditos
RoutineFlow é uma SPA (Single Page Application) para gerenciamento de rotinas e hábitos pessoais. O usuário configura tarefas recorrentes por dia da semana e tarefas avulsas para datas específicas, marcando conclusões diariamente e visualizando sua consistência ao longo do tempo em um mapa de calor.
Toda a persistência é feita via Supabase (PostgreSQL + Auth), sem nenhum servidor próprio. O frontend é servido pelo Vercel.
- Cadastro com nome, e-mail e senha
- Login com persistência de sessão via Supabase Auth (JWT)
- Logout com limpeza de estado local e redirecionamento automático
- Dados completamente isolados por usuário (Row Level Security no Supabase)
- Criar tarefas associadas a um dia fixo da semana (0 = Domingo … 6 = Sábado)
- Adicionar, editar e remover subtarefas por tarefa
- Reordenar tarefas via drag-and-drop (posição persistida no banco)
- Editar título e subtarefas de tarefas existentes
- Excluir tarefas
- Seletor de data independente (dia, mês e ano via dropdowns)
- Exibe tarefas recorrentes do dia da semana correspondente à data selecionada
- Exibe tarefas avulsas criadas para aquela data exata
- Criar, editar e excluir tarefas avulsas diretamente no dashboard
- Checkboxes com lógica hierárquica:
- Marcar a tarefa pai → todas as subtarefas são marcadas
- Completar todas as subtarefas → pai é marcado automaticamente
- Desmarcar qualquer subtarefa → pai é desmarcado
- Barra de progresso por tarefa (% de subtarefas concluídas)
- Percentual de sucesso do dia em tempo real
- Mensagem motivacional por dia da semana
- Animações de entrada e saída com Framer Motion
- Mobile: 49 dias (7 semanas)
- Desktop: 364 dias (1 ano)
- Escala de cores pela % de conclusão do dia (cinza → azul claro → azul médio → azul intenso)
- Tooltip com data e percentual ao passar o cursor
- Legenda de dias da semana
main.tsx
└── App.tsx
└── QueryClientProvider
└── AppProvider (React Context — estado global)
└── TooltipProvider
└── Router (Wouter)
├── / → landing.tsx
├── /login → auth.tsx
├── /register → auth.tsx
├── /dashboard → dashboard.tsx [protegida]
├── /setup → setup.tsx [protegida]
└── /analytics → analytics.tsx [protegida]
Rotas protegidas são envolvidas por ProtectedRoute, que redireciona para /login quando não há sessão ativa.
routineflow/
├── client/
│ ├── index.html
│ └── src/
│ ├── App.tsx # Roteamento e proteção de rotas
│ ├── main.tsx # Entry point — ReactDOM.createRoot
│ ├── index.css # Variáveis CSS + tema Tailwind v4
│ ├── components/
│ │ ├── ui/ # 12 componentes shadcn/ui (apenas os utilizados)
│ │ │ ├── button.tsx
│ │ │ ├── calendar.tsx
│ │ │ ├── card.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── select.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ └── tooltip.tsx
│ │ ├── layout.tsx # Shell da aplicação: sidebar (desktop) + menu hambúrguer (mobile)
│ │ └── heatmap.tsx # Mapa de calor interativo com tooltip
│ ├── pages/
│ │ ├── landing.tsx # Página pública com hero + features
│ │ ├── auth.tsx # Login e cadastro (componente único, controlado pela prop `type`)
│ │ ├── dashboard.tsx # Rastreamento diário — tarefas recorrentes e avulsas
│ │ ├── setup.tsx # Configuração da rotina semanal com drag-and-drop
│ │ ├── analytics.tsx # Histórico e mapa de calor
│ │ └── not-found.tsx # Página 404
│ ├── lib/
│ │ ├── store.tsx # AppContext — todo o estado e lógica de negócio
│ │ ├── supabase.ts # Inicialização do cliente Supabase
│ │ ├── queryClient.ts # Configuração do TanStack Query (instância global)
│ │ └── utils.ts # Helper `cn` (clsx + tailwind-merge)
│ └── hooks/
│ └── use-toast.ts # Sistema de notificações toast (estado externo)
├── attached_assets/
│ └── generated_images/
│ └── minimalist_blue_abstract_habit_tracker_logo.png
├── package.json
├── tsconfig.json
├── vite.config.ts
├── postcss.config.js
└── vercel.json
Todo o estado da aplicação vive no AppProvider (client/src/lib/store.tsx), exposto via useApp(). As páginas consomem apenas o que precisam — nenhuma lógica de negócio fica fora do store.
O contexto sincroniza com o Supabase Auth via onAuthStateChange e carrega os dados do banco quando o userId muda.
type AppContextType = {
// Estado
user: User | null
tasks: Task[] // tarefas recorrentes (por dia da semana)
dailyTasks: DailyTask[] // tarefas avulsas (por data específica)
logs: Record<string, Record<string, boolean>> // date → { itemId: done }
// Auth
logout(): void
// Tarefas recorrentes
addTask(title, dayOfWeek, subtasks?): Promise<void>
updateTask(taskId, title, subtasks): Promise<void>
deleteTask(taskId): Promise<void>
reorderTasks(dayOfWeek, orderedIds): Promise<void>
// Tarefas avulsas
addDailyTask(title, date, subtasks?): Promise<void>
updateDailyTask(taskId, title, subtasks): Promise<void>
deleteDailyTask(taskId): Promise<void>
// Rastreamento
toggleTask(date, itemId): Promise<void>
getDailyProgress(date): number
getTaskStatus(date, itemId): boolean
getDailyTasksForDate(date): DailyTask[]
}Sem servidor próprio. Toda a persistência usa o SDK do Supabase direto do cliente.
| Tabela | Descrição |
|---|---|
users |
Gerenciada pelo Supabase Auth |
tasks |
Tarefas recorrentes — user_id, title, day_of_week, position |
task_subtasks |
Subtarefas de tarefas recorrentes — task_id, title |
daily_tasks |
Tarefas avulsas — user_id, title, date |
daily_subtasks |
Subtarefas de tarefas avulsas — daily_task_id, title |
checks |
Log de conclusões — user_id, date, item_id, done (unique: user_id + date + item_id) |
| Tecnologia | Versão | Propósito |
|---|---|---|
| React | 19.2.0 | Biblioteca de UI |
| TypeScript | 5.6.3 | Tipagem estática |
| Vite | 7.1.9 | Build tool e dev server |
| Tecnologia | Versão | Propósito |
|---|---|---|
| Wouter | 3.3.5 | Roteamento leve no cliente (~1.5 KB) |
| React Context API | — | Estado global da aplicação |
| TanStack React Query | 5.60.5 | Instância de QueryClient (disponível para uso futuro) |
| Tecnologia | Versão | Propósito |
|---|---|---|
| Supabase JS | 2.90.1 | Auth (JWT) + banco PostgreSQL + RLS |
| Tecnologia | Versão | Propósito |
|---|---|---|
| Tailwind CSS | 4.1.14 | Framework CSS utilitário via plugin Vite |
| shadcn/ui + Radix UI | — | 12 componentes acessíveis e sem estilo próprio |
| Framer Motion | 12.23.24 | Animações, transições e drag-and-drop (Reorder) |
| Lucide React | 0.545.0 | Ícones |
| react-day-picker | 9.11.1 | Componente de calendário |
| Tecnologia | Versão | Propósito |
|---|---|---|
| React Hook Form | 7.66.0 | Gerenciamento de formulários |
| Zod | 3.25.76 | Validação de schemas |
| @hookform/resolvers | 3.10.0 | Integração RHF + Zod |
| Tecnologia | Versão | Propósito |
|---|---|---|
| date-fns | 3.6.0 | Formatação e manipulação de datas |
| clsx + tailwind-merge | — | Composição condicional de classes CSS |
- Outfit — Headings (
font-heading) - Inter — Body text
O escopo de estado da aplicação é limitado: um usuário, suas tarefas e seus logs. O Context API é suficiente para essa carga e evita dependência extra. O useMemo no value do provider previne re-renders desnecessários.
- PostgreSQL gerenciado sem configuração de servidor
- Auth com JWT integrado — sem precisar construir autenticação
- SDK JavaScript com tipagem completa
- Row Level Security para isolamento de dados por usuário
- Elimina a necessidade de uma API REST própria
Bundle ~7× menor (1.5 KB vs ~10 KB). A API cobre todas as necessidades da aplicação: rotas simples, redirecionamento e guards via componente.
A v4 integra via plugin Vite, sem tailwind.config.js externo. Todo o design system (cores, fontes, raios) é definido como variáveis CSS no index.css, tornando o tema facilmente customizável sem tocar em JavaScript.
Task— recorrente, atrelada a umdayOfWeek(0–6). Aparece toda vez que aquele dia da semana chegar.DailyTask— avulsa, atrelada a uma data exata (YYYY-MM-DD). Aparece apenas naquele dia.
Ambos contribuem igualmente para o cálculo de progresso, permitindo flexibilidade sem poluir a rotina semanal com exceções.
A reordenação de tarefas usa Reorder.Group e Reorder.Item do Framer Motion. A ordem é persistida no banco na coluna position da tabela tasks, sendo carregada ordenada pelo servidor.
A função toggleTask aplica a mudança localmente de forma imediata (applyLocal) e persiste no banco em seguida. O usuário vê o feedback visual instantâneo, sem esperar o round-trip ao Supabase.
O histórico (checks) não é carregado infinitamente — apenas os últimos 120 dias. Isso mantém o payload inicial pequeno e é suficiente para o mapa de calor (máximo 364 dias configurável).
O vercel.json redireciona todas as rotas para index.html, permitindo que o Wouter gerencie a navegação no cliente sem erros 404 em refresh ou acesso direto por URL.
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}type SubTask = {
id: string
title: string
}
type Task = {
id: string
title: string
dayOfWeek: number // 0 = Domingo … 6 = Sábado
subtasks: SubTask[]
}
type DailyTask = {
id: string
title: string
date: string // YYYY-MM-DD
subtasks: SubTask[]
}
type User = {
id: string // UUID do Supabase Auth
name: string
email: string
}
// Espelhado na tabela `checks`
type Logs = Record<
string, // date (YYYY-MM-DD)
Record<
string, // itemId (UUID de task ou subtask)
boolean // concluído?
>
>| ID | Regra |
|---|---|
| RN01 | E-mail deve ser único no sistema |
| RN02 | Senha com mínimo de 6 caracteres |
| RN03 | Nome obrigatório no cadastro (mínimo 3 caracteres) |
| RN04 | Sessão mantida até logout explícito |
| ID | Regra |
|---|---|
| RN05 | Cada tarefa recorrente pertence a exatamente um dia da semana |
| RN06 | Tarefas avulsas pertencem a uma data específica (YYYY-MM-DD) |
| RN07 | Tarefas podem ter zero ou mais subtarefas |
| RN08 | Título da tarefa é obrigatório |
| RN09 | A posição das tarefas recorrentes é persistida no banco (coluna position) |
| ID | Regra |
|---|---|
| RN10 | Marcar tarefa pai → todas as subtarefas são marcadas |
| RN11 | Completar todas as subtarefas → tarefa pai marcada automaticamente |
| RN12 | Desmarcar qualquer subtarefa → tarefa pai desmarcada |
| RN13 | Progresso = (itens concluídos ÷ total de itens) × 100 |
| RN14 | Tarefas recorrentes e avulsas contribuem igualmente para o progresso do dia |
| ID | Regra |
|---|---|
| RN15 | Dias sem tarefas configuradas exibem 0% (cinza) |
| RN16 | A janela exibida parte da data atual para o passado |
| RN17 | Checks carregados cobrem os últimos 120 dias |
| Rota | Componente | Acesso |
|---|---|---|
/ |
landing.tsx |
Público |
/login |
auth.tsx (type="login") |
Público — redireciona para /dashboard se logado |
/register |
auth.tsx (type="register") |
Público — redireciona para /dashboard se logado |
/dashboard |
dashboard.tsx |
Autenticado |
/setup |
setup.tsx |
Autenticado |
/analytics |
analytics.tsx |
Autenticado |
* |
not-found.tsx |
Público |
| Faixa | Prefixo Tailwind | Comportamento |
|---|---|---|
| < 768 px | (padrão) | Mobile |
| ≥ 768 px | md: |
Desktop |
| Elemento | Mobile | Desktop |
|---|---|---|
| Navegação | Menu hambúrguer com overlay animado | Sidebar fixa de 256 px |
| Mapa de calor | 49 dias (7 semanas) | 364 dias (1 ano) |
| Seletor de data | Dropdowns em linha (flex-1) | Dropdowns com larguras fixas |
| Padding do conteúdo | p-4, pt-16 (compensa header fixo) |
p-8, ml-64 (compensa sidebar) |
O tema é definido inteiramente por variáveis CSS em client/src/index.css e consumido pelo Tailwind v4.
| Variável CSS | Uso |
|---|---|
--primary |
Azul elétrico — CTAs, destaques, checkboxes |
--background |
Fundo escuro da aplicação |
--card |
Fundo de cards e painéis |
--muted-foreground |
Texto secundário e placeholders |
--sidebar / --sidebar-border |
Cores exclusivas da sidebar |
| Conclusão | Classe Tailwind |
|---|---|
| 0% | bg-muted |
| 1–29% | bg-primary/30 |
| 30–59% | bg-primary/60 |
| 60–100% | bg-primary |
- Node.js 18 LTS ou superior
- npm 9+
- Projeto no Supabase com as tabelas criadas
Execute no SQL Editor do Supabase:
create table tasks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
title text not null,
day_of_week int not null,
position int default 0,
created_at timestamptz default now()
);
create table task_subtasks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
task_id uuid references tasks on delete cascade not null,
title text not null,
created_at timestamptz default now()
);
create table daily_tasks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
title text not null,
date date not null,
created_at timestamptz default now()
);
create table daily_subtasks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
daily_task_id uuid references daily_tasks on delete cascade not null,
title text not null,
created_at timestamptz default now()
);
create table checks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
date date not null,
item_id uuid not null,
done boolean default false,
unique(user_id, date, item_id)
);
-- RLS
alter table tasks enable row level security;
alter table task_subtasks enable row level security;
alter table daily_tasks enable row level security;
alter table daily_subtasks enable row level security;
alter table checks enable row level security;
create policy "own tasks" on tasks for all using (auth.uid() = user_id);
create policy "own task_subtasks" on task_subtasks for all using (auth.uid() = user_id);
create policy "own daily_tasks" on daily_tasks for all using (auth.uid() = user_id);
create policy "own daily_subtasks" on daily_subtasks for all using (auth.uid() = user_id);
create policy "own checks" on checks for all using (auth.uid() = user_id);# 1. Clone o repositório
git clone <url-do-repositório>
cd routineflow
# 2. Instale as dependências
npm install
# 3. Configure as variáveis de ambiente
cp .env.example .env
# Edite .env com suas credenciais do Supabase
# 4. Inicie o servidor de desenvolvimento
npm run dev
# Disponível em http://localhost:5173| Comando | Descrição |
|---|---|
npm run dev |
Servidor de desenvolvimento com HMR |
npm run build |
Build de produção em dist/ |
npm run preview |
Serve o build de produção localmente |
npm run check |
Checagem de tipos TypeScript sem emissão |
Crie um arquivo .env na raiz do projeto:
VITE_SUPABASE_URL=https://<seu-projeto>.supabase.co
VITE_SUPABASE_ANON_KEY=<sua-anon-key>Ambas encontradas no painel do Supabase em Project Settings → API.
O prefixo
VITE_é obrigatório para que o Vite exponha as variáveis ao bundle do cliente.
A aplicação é publicada no Vercel com deploy contínuo a partir da branch main.
- Importe o repositório no painel do Vercel
- Adicione as variáveis de ambiente:
VITE_SUPABASE_URLeVITE_SUPABASE_ANON_KEY - O Vercel detecta automaticamente o Vite:
- Build command:
npm run build - Output directory:
dist - Root directory:
.(raiz do repositório)
- Build command:
- O vercel.json garante que todas as rotas sejam redirecionadas para
index.html
| Alias | Resolve para |
|---|---|
@ |
client/src/ |
@assets |
attached_assets/ |
Desenvolvido por Rodrigo Barros
linkedin.com/in/rodrigocavalcantedebarros
Última atualização: Março de 2026