Compare commits

1 Commits

Author SHA1 Message Date
Carlos 9a7024b6b2 Initial commit 2026-06-25 18:52:19 +02:00
91 changed files with 0 additions and 3931 deletions
-13
View File
@@ -1,13 +0,0 @@
# Environment variables for 1000apps
# Copy this to .env and fill in the values
# Database
DATABASE_URL=postgresql://user:password@1000apps-db-1:5432/1000apps
# NextAuth
NEXTAUTH_SECRET=CHANGE_THIS_TO_RANDOM_32_CHARS
NEXTAUTH_URL=https://1000apps.fluxtechnologies.uk
# Google OAuth (optional - for Google login)
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
-5
View File
@@ -1,5 +0,0 @@
node_modules/
.next/
.env
.env.local
*.log
-51
View File
@@ -1,51 +0,0 @@
# Multi-stage build for Next.js
FROM node:20-alpine AS base
WORKDIR /app
# Install dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat openssl
COPY package.json package-lock.json* ./
COPY prisma ./prisma/
RUN npm install --frozen-lockfile
# Generate Prisma Client with correct binary targets
RUN npx prisma generate
# Build stage
FROM base AS builder
WORKDIR /app
ENV DATABASE_URL="postgresql://user:***@localhost:5432/1000apps"
# Install openssl for Prisma binary target detection
RUN apk add --no-cache openssl
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# Production stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Install runtime dependencies for Prisma
RUN apk add --no-cache libc6-compat openssl
# Create non-root user and set permissions
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001 -G nodejs
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/pages ./pages
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
COPY --from=builder --chown=nextjs:nodejs /app/components ./components
COPY --from=builder --chown=nextjs:nodejs /app/styles ./styles
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 3000
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
-45
View File
@@ -1,45 +0,0 @@
# ✅ PostgreSQL Persistence - CONFIRMED
## Test Date: 2026-06-27 14:10 UTC
### User Created via API
```json
POST /api/auth/signup
{
"username": "persistence-test",
"password": "test123"
}
Response:
{
"message": "Usuário criado com sucesso",
"user": {
"id": "4df05930-f96b-4e0c-bccd-2254b2341c61",
"username": "persistence-test"
}
}
```
### PostgreSQL Record
```sql
SELECT username, email, "createdAt" FROM "User" WHERE username='persistence-test';
username | email | createdAt
-------------------+------------------------------------+---------------
persistence-test | persistence-test@1000apps.local | 2026-06-27 14:10:43.235
```
### Services Running
- ✅ 1000apps-app (Next.js) - porta 3000
- ✅ 1000apps-postgres (PostgreSQL 15) - porta 5432
- ✅ coolify-proxy (Traefik) - roteamento HTTPS
### URLs
- **Production**: https://1000apps.fluxtechnologies.uk
- **Signup**: /api/auth/signup (POST)
- **Login**: /signin (web form com CSRF)
### Environment Variables Active
- DATABASE_URL → postgresql://1000apps:***@1000apps-postgres:5432/1000apps
- NEXTAUTH_SECRET → configurado (base64 32 bytes)
- NEXTAUTH_URL → https://1000apps.fluxtechnologies.uk
-5
View File
@@ -1,6 +1 @@
# 1000Apps Platform # 1000Apps Platform
# Build trigger Thu Jul 2 05:42:43 CEST 2026
# Teste webhook Fri Jul 3 20:24:47 CEST 2026
# Teste webhook Fri Jul 3 20:24:56 CEST 2026
# Teste webhook 2 Fri Jul 3 20:37:45 CEST 2026
# Teste deploy flag Fri Jul 3 20:56:19 CEST 2026
-22
View File
@@ -1,22 +0,0 @@
services:
app:
build: .
restart: unless-stopped
environment:
DATABASE_URL: file:/app/data.db
NEXTAUTH_SECRET: test-secret-2x4=
NEXTAUTH_URL: https://1000apps.fluxtechnologies.uk
volumes:
- ./data.db:/app/data.db
networks:
- coolify
labels:
- "traefik.enable=true"
- "traefik.http.routers.1000apps.rule=Host(`1000apps.fluxtechnologies.uk`)"
- "traefik.http.routers.1000apps.entrypoints=https"
- "traefik.http.routers.1000apps.tls.certresolver=letsencrypt"
- "traefik.http.services.100apps.loadbalancer.server.port=3000"
networks:
coolify:
external: true
-44
View File
@@ -1,44 +0,0 @@
services:
db:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: change_me_password
POSTGRES_DB: 1000apps
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- internal
expose:
- "5432"
app:
build: .
restart: unless-stopped
depends_on:
- db
environment:
DATABASE_URL: postgresql://user:change_me_password@db:5432/1000apps
NEXTAUTH_SECRET: test-secret-2x4=
NEXTAUTH_URL: https://1000apps.fluxtechnologies.uk
GOOGLE_CLIENT_ID: ""
GOOGLE_CLIENT_SECRET: ""
networks:
- internal
- coolify-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.1000apps.rule=Host(`1000apps.fluxtechnologies.uk`)"
- "traefik.http.routers.1000apps.entrypoints=https"
- "traefik.http.routers.1000apps.tls=true"
- "traefik.http.routers.1000apps.tls.certresolver=letsencrypt"
- "traefik.http.services.1000apps.loadbalancer.server.port=3000"
volumes:
postgres_data:
networks:
internal:
coolify-proxy:
external: true
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
set -e
echo "Running Prisma migrations..."
npx prisma migrate deploy
echo "Starting Next.js..."
npm run start
-9
View File
@@ -1,9 +0,0 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
-8
View File
@@ -1,8 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
domains: ['lh3.googleusercontent.com', 'avatars.githubusercontent.com'],
},
}
-1662
View File
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
{
"name": "1000apps",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.x",
"react": "^18",
"react-dom": "^18",
"next-auth": "^4.24.0",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.0.0",
"bcryptjs": "^2.4.3"
},
"devDependencies": {
"prisma": "^5.0.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"typescript": "^5",
"tailwindcss": "^3.4.0",
"autoprefixer": "^10.0.0",
"postcss": "^8"
}
}
-9
View File
@@ -1,9 +0,0 @@
// _app.jsx — SessionProvider de autenticação REMOVIDO (login desativado temporariamente).
// App agora abre direto no dashboard, sem verificação de sessão.
import "@/styles/globals.css"
export default function App({ Component, pageProps }) {
return (
<Component {...pageProps} />
)
}
-11
View File
@@ -1,11 +0,0 @@
export default function Aurea() {
return (
<div style={{minHeight:'100vh',background:'linear-gradient(135deg,#1a0a00,#3d2000)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',color:'#FFD700',fontFamily:'serif',textAlign:'center',padding:'2rem'}}>
<div style={{fontSize:'4rem',marginBottom:'1rem'}}>👑</div>
<h1 style={{fontSize:'3rem',marginBottom:'0.5rem'}}>Aurea</h1>
<p style={{fontSize:'1.2rem',color:'#c8a400',marginBottom:'2rem'}}>O Império Começa Aqui</p>
<p style={{color:'#a0826d',maxWidth:'500px',marginBottom:'2rem'}}>Simulador de vida onde cada escolha te leva mais perto da riqueza. Construa impérios, domine o mercado.</p>
<span style={{background:'#FFD700',color:'#1a0a00',padding:'0.5rem 2rem',borderRadius:'20px',fontWeight:'bold'}}>Em Breve</span>
</div>
)
}
-11
View File
@@ -1,11 +0,0 @@
export default function ExpedicaoGold() {
return (
<div style={{minHeight:'100vh',background:'linear-gradient(135deg,#0a1a00,#1a3a00)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',color:'#90EE90',fontFamily:'sans-serif',textAlign:'center',padding:'2rem'}}>
<div style={{fontSize:'4rem',marginBottom:'1rem'}}>🗺</div>
<h1 style={{fontSize:'3rem',marginBottom:'0.5rem'}}>Expedição Gold</h1>
<p style={{fontSize:'1.2rem',color:'#50aa50',marginBottom:'2rem'}}>Conquiste o Ouro do Mundo</p>
<p style={{color:'#668866',maxWidth:'500px',marginBottom:'2rem'}}>Expedições terrestres, marítimas, espaciais e espirituais. Cada jornada é única. Cada conquista, sua.</p>
<span style={{background:'#90EE90',color:'#0a1a00',padding:'0.5rem 2rem',borderRadius:'20px',fontWeight:'bold'}}>Em Breve no 1000Apps</span>
</div>
)
}
-11
View File
@@ -1,11 +0,0 @@
export default function HumanCity() {
return (
<div style={{minHeight:'100vh',background:'linear-gradient(135deg,#050520,#0a0a40)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',color:'#a0a0ff',fontFamily:'sans-serif',textAlign:'center',padding:'2rem'}}>
<div style={{fontSize:'4rem',marginBottom:'1rem'}}>🏙</div>
<h1 style={{fontSize:'3rem',marginBottom:'0.5rem'}}>Human City</h1>
<p style={{fontSize:'1.2rem',color:'#7070cc',marginBottom:'2rem'}}>Simule. Construa. Viva.</p>
<p style={{color:'#5555aa',maxWidth:'500px',marginBottom:'2rem'}}>Simulação de vida estilo The Sims. Crie personagens, construa cidades, escreva sua história.</p>
<span style={{background:'#a0a0ff',color:'#050520',padding:'0.5rem 2rem',borderRadius:'20px',fontWeight:'bold'}}>Em Desenvolvimento</span>
</div>
)
}
-11
View File
@@ -1,11 +0,0 @@
export default function TerraVerse() {
return (
<div style={{minHeight:'100vh',background:'linear-gradient(135deg,#000510,#001a3a)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',color:'#00d4ff',fontFamily:'sans-serif',textAlign:'center',padding:'2rem'}}>
<div style={{fontSize:'4rem',marginBottom:'1rem'}}>🌍</div>
<h1 style={{fontSize:'3rem',marginBottom:'0.5rem'}}>TerraVerse</h1>
<p style={{fontSize:'1.2rem',color:'#0099bb',marginBottom:'2rem'}}>O Novo Mundo te Espera</p>
<p style={{color:'#6699aa',maxWidth:'500px',marginBottom:'2rem'}}>Game de realidade aumentada que vai revolucionar como você explora, conquista e monetiza o mundo real.</p>
<span style={{background:'#00d4ff',color:'#000510',padding:'0.5rem 2rem',borderRadius:'20px',fontWeight:'bold'}}>Em Breve</span>
</div>
)
}
-61
View File
@@ -1,61 +0,0 @@
import { useState } from 'react';
export default function TicTacToe() {
const [board, setBoard] = useState(Array(9).fill(null));
const [xIsNext, setXIsNext] = useState(true);
const winner = calculateWinner(board);
const status = winner
? `Winner: ${winner}`
: `Next player: ${xIsNext ? 'X' : 'O'}`;
function handleClick(i) {
const boardCopy = [...board];
if (winner || boardCopy[i]) return;
boardCopy[i] = xIsNext ? 'X' : 'O';
setBoard(boardCopy);
setXIsNext(!xIsNext);
}
function calculateWinner(squares) {
const lines = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6],
];
for (let [a, b, c] of lines) {
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
const renderSquare = (i) => (
<button className="square" onClick={() => handleClick(i)}>
{board[i]}
</button>
);
const statusStyle = { marginBottom: '1rem', fontSize: '1.2rem' };
const boardStyle = { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '4px', width: '300px', margin: '0 auto' };
const squareStyle = { background: '#fff', border: '1px solid #999', fontSize: '2rem', width: '100%', height: '80px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' };
return (
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
<h1>Tic Tac Toe</h1>
<div style={statusStyle}>{status}</div>
<div style={boardStyle}>
{[0, 1, 2, 3, 4, 5, 6, 7, 8].map(i => (
<div key={i} style={squareStyle}>
{renderSquare(i)}
</div>
))}
</div>
{winner && (
<button onClick={() => { setBoard(Array(9).fill(null)); setXIsNext(true); }}>
Play Again
</button>
)}
</div>
);
}
-222
View File
@@ -1,222 +0,0 @@
import { useState, useEffect } from "react"
import { useRouter } from "next/router"
// Top 100 Notícias - Trends Brasil e Mundo (Junho 2026)
const weeklyTrends = [
// ==== TECNOLOGIA ====
{ id: 1, category: "TECNOLOGIA", title: "IA Generativa Revoluciona Produtividade Home Office", summary: "Novas ferramentas de IA estão aumentando a produtividade em 40% nas tarefas administrativas remotas. Profissionais relatam economia de 8 horas semanais.", date: "2026-06-22", views: "125K", trend: "+87%" },
{ id: 2, category: "TECNOLOGIA", title: "GPT-7 Lançado com Suporte Multimodal Avançado", summary: "A nova geração da IA consegue processar texto, imagem, áudio e vídeo simultaneamente com 98% de precisão em tarefas complexas.", date: "2026-06-20", views: "210K", trend: "+145%" },
{ id: 3, category: "TECNOLOGIA", title: "Quantum Computing Chega a Servidores Cloud", summary: "Google Cloud e AWS anunciam ofertas de quantum computing comercial. Empresas começam a testar otimizações em cadeia de suprimentos.", date: "2026-06-18", views: "89K", trend: "+67%" },
{ id: 4, category: "TECNOLOGIA", title: "Chips Neuromórficos Consomem 90% Menos Energia", summary: "Processadores inspirados no cérebro humano prometem revolucionar IoT e dispositivos vestíveis com bateria de anos.", date: "2026-06-15", views: "156K", trend: "+92%" },
{ id: 5, category: "TECNOLOGIA", title: "Apple Visão Pro 2 Vende 5 Milhões em 1º Mês", summary: "A realidade aumentada da Apple supera expectativas com foco em produtividade e desenvolvimento de apps.", date: "2026-06-24", views: "320K", trend: "+210%" },
{ id: 6, category: "TECNOLOGIA", title: "5G mmWave Agora Disponível em São Paulo", summary: "Velocidades de 10Gbps já são realidade para empresas da capital. Aplicações em VR/AR e telemedicina ganham impulso.", date: "2026-06-19", views: "78K", trend: "+45%" },
{ id: 7, category: "TECNOLOGIA", title: "Windows 12 com IA Integrada está em Beta", summary: "Microsoft lança preview com Copilot nativo em todos os menus. Usuários podem ajustar configurações via voz natural.", date: "2026-06-21", views: "189K", trend: "+112%" },
{ id: 8, category: "TECNOLOGIA", title: "Robôs Autônomos Começam a Entregar em Curitiba", summary: "Startup brasileira inicia operação com robôs de entrega usando IA para navegação a pé. Primeira cidade do Brasil com tecnologia.", date: "2026-06-23", views: "145K", trend: "+78%" },
{ id: 9, category: "TECNOLOGIA", title: "Edge AI Processa Dados SEM Internet", summary: "Novos chips permitem inferência local com latência de 5ms. Revolução para privacidade e aplicações offline.", date: "2026-06-17", views: "98K", trend: "+83%" },
{ id: 10, category: "TECNOLOGIA", title: "Brasil Lidera Inovação em Fintech com IA", summary: "Pix com reconhecimento facial e detecção de fraudes em tempo real utilizam tecnologia brasileira adotada por 15 países.", date: "2026-06-25", views: "167K", trend: "+123%" },
{ id: 11, category: "TECNOLOGIA", title: "Protocolo Web5 Transforma Web3 no Brasil", summary: "Novo padrão de internet descentralizada permite identidade soberana sem blockchain. 50 apps já migraram.", date: "2026-06-23", views: "145K", trend: "+89%" },
{ id: 12, category: "TECNOLOGIA", title: "Smart Glasses da Xiaomi Disparam com 500H Bateria", summary: "Óculos inteligentes com tradução simultânea em 50 idiomas. Preço acessível faz disparar vendas no Brasil.", date: "2026-06-26", views: "189K", trend: "+134%" },
// ==== SAÚDE ====
{ id: 13, category: "SAÚDE", title: "Apps de Monitoramento Cardíaco Atingem 80% de Precisão", summary: "Estudo revela que wearables detectam arritmias cardíacas com 80% de precisão, alertando usuários para consultas médicas preventivas.", date: "2026-06-21", views: "98K", trend: "+65%" },
{ id: 14, category: "SAÚDE", title: "Vacina mRNA Contra Dengue Mostra 95% de Eficácia", summary: "Fase 3 concluída com sucesso no Brasil. Ministério da Saúde prevê imunização nacional até 2027.", date: "2026-06-18", views: "201K", trend: "+187%" },
{ id: 15, category: "SAÚDE", title: "Micro-Doses de Café Reduzem Ansiedade", summary: "Pesquisa clínica mostra 30% redução em nível de ansiedade com doses precisas de cafeína. Protocolo inclui 45 minutos de intervalo.", date: "2026-06-23", views: "195K", trend: "+103%" },
{ id: 16, category: "SAÚDE", title: "Terapia Genética Cura Tipo 1 Diabetes no Brasil", summary: "Primeiro paciente recebe tratamento com modificação genética. Estudo piloto da USP mostra redução de 90% em insulina necessária.", date: "2026-06-19", views: "289K", trend: "+234%" },
{ id: 17, category: "SAÚDE", title: "Longevidade: Peptídeos da Serpente Estendem Vida em 15%", summary: "Pesquisa da Butantan identifica compostos que atrasam envelhecimento. Testes em humanos começam em julho.", date: "2026-06-20", views: "145K", trend: "+156%" },
{ id: 18, category: "SAÚDE", title: "Meta da OMS: Zero Malária no Brasil é Alcançada", summary: "Ministério da Saúde anuncia eliminação do transmissor na Amazônia. Brasil é o 5º país a atingir a meta.", date: "2026-06-22", views: "178K", trend: "+98%" },
{ id: 19, category: "SAÚDE", title: "Neurofeedback Treina Alunos para Concentração Máxima", summary: "Estudo da UNICAMP com 500 estudantes mostra melhora de 45% em provas de matemática após 8 semanas.", date: "2026-06-16", views: "87K", trend: "+52%" },
{ id: 20, category: "SAÚDE", title: "Câncer de Mama Detectado 2 Anos Antecipado com IA", summary: "Algoritmo analisa mamografia com 97% de precisão. Sistema implantado em 200 clínicas pelo SUS.", date: "2026-06-24", views: "234K", trend: "+167%" },
{ id: 21, category: "SAÚDE", title: "Fitas do Timo Contra Idade Avançam para Fase Humana", summary: "Immunotherapy regenerativa mostra 70% de eficácia em pacientes com 70+ anos. Espera-se revolução no envelhecimento.", date: "2026-06-17", views: "112K", trend: "+134%" },
{ id: 22, category: "SAÚDE", title: "Telemedicina Brasileira Vence Prêmio Internacional", summary: "Plataforma de atendimento remoto ganha categoria inovação social. 50 mil médicos atendem 2 milhões de pacientes via app.", date: "2026-06-25", views: "156K", trend: "+89%" },
{ id: 23, category: "SAÚDE", title: "Therapy Dogs Aliviam Burnout em 70% dos Médicos", summary: "Clínicas brasileiras adotam cães terapeutas em planta. Redução de estresse e aumento de satisfação no trabalho.", date: "2026-06-26", views: "201K", trend: "+145%" },
{ id: 24, category: "SAÚDE", title: "Exoesqueletos Normais Vendem 100 Mil Unidades no Brasil", summary: "Roupa robótica ajuda reabilitação de idosos pós-cirurgia. Idosos caminham 40% mais rápido com apoio robótico.", date: "2026-06-24", views: "189K", trend: "+198%" },
// ==== FINANÇAS ====
{ id: 25, category: "FINANÇAS", title: "Criptomoedas Voláteis Mas Recuperação Silenciosa", summary: "Após correção de 60%, principais criptos mostram recuperação estável. Especialistas apontam novo ciclo de acumulação entre traders.", date: "2026-06-23", views: "210K", trend: "+42%" },
{ id: 26, category: "FINANÇAS", title: "Real Digital Atinge 10 Milhões de Contas Ativas", summary: "CBDC brasileira supera expectativas com adoção acelerada. 30% da população adulta já tem acesso à conta Física.", date: "2026-06-20", views: "312K", trend: "+245%" },
{ id: 27, category: "FINANÇAS", title: "Bitcoin ETF Aprovação Eleita Melhor Notícia do Ano", summary: "BlackRock e Fidelity lançam fundos que movimentaram $50 bilhões em 30 dias. Mercado entra na era institucional.", date: "2026-06-22", views: "278K", trend: "+198%" },
{ id: 28, category: "FINANÇAS", title: "Pix Internacional Conecta Brasil a 40 Países", summary: "Transferências instantâneas entre Brasil e LATAM usando stablecoins. Remessas caem 60% em custo médio.", date: "2026-06-19", views: "167K", trend: "+156%" },
{ id: 29, category: "FINANÇAS", title: "OpenFinance Brasileiro Integra 500 Instituições", summary: "Sistema de dados financeiros aberto permite comparação de investimentos em tempo real. Usuários economizam em média R$ 2.300/ano.", date: "2026-06-21", views: "145K", trend: "+112%" },
{ id: 30, category: "FINANÇAS", title: "DeFi Brasileiro Surge com Protocolos Locais", summary: "Novos protocolos DeFi permissionless atraem $2 bilhões. Brasileiros lideram inovação em pré-fixado tokenizado.", date: "2026-06-18", views: "189K", trend: "+178%" },
{ id: 31, category: "FINANÇAS", title: "Inflação Chega a 2,1% - Menor em 12 Anos", summary: "IPCA de junho encerra ano de estabilidade. Mercado aguarda decisão do Copom para juros ao menor em 2026.", date: "2026-06-15", views: "234K", trend: "+87%" },
{ id: 32, category: "FINANÇAS", title: "Bolsonaro Lança Fundo de Investimento Privado", summary: "Fundo de R$ 500 milhões foca em startups de segurança e agronegócio. Investimento recorde em venture do Ex-Presidente.", date: "2026-06-24", views: "456K", trend: "+312%" },
{ id: 33, category: "FINANÇAS", title: "Suzano S/A Split 3:1 Aprovação Massiva", summary: "Ação sobe 45% após anúncio de divisão de ações. Investidores antigos do Vale veem nova oportunidade.", date: "2026-06-22", views: "178K", trend: "+78%" },
{ id: 34, category: "FINANÇAS", title: "Nubank Lobby EUA para Bancar Brasil no Outside", summary: "Fintech solicita operadora de crédito nos EUA para oferecer conta internacional aos clientes. Expansão recorde para fintech.", date: "2026-06-25", views: "198K", trend: "+123%" },
{ id: 35, category: "FINANÇAS", title: "Banco Central Testa CBDC para Pagamentos Internacionais", summary: "Real Digital 2.0 permite conversão automática para 15 moedas. Mercadorias podem ser pagas com QR global.", date: "2026-06-26", views: "278K", trend: "+178%" },
{ id: 36, category: "FINANÇAS", title: "Startup Brasileira Vence Hackathon do FMI", summary: "Solução de stablecoins para pequenos agricultores atrai investimento de $50 milhões. Projeto já piloto em 5 estados.", date: "2026-06-23", views: "145K", trend: "+145%" },
// ==== EDUCAÇÃO ====
{ id: 37, category: "EDUCAÇÃO", title: "Cursos Online de Programação Aumentam 200%", summary: "Demanda por carreiras tech dispara após chegar GPT-6. Plataformas reportam matrículas acima do esperado para desenvolvimento mobile.", date: "2026-06-20", views: "156K", trend: "+185%" },
{ id: 38, category: "EDUCAÇÃO", title: "USP Oferece Diploma em IA com Certificado On-chain", summary: "Primeira universidade do Brasil a tokenizar diplomas. NFTs verificam autenticidade em blockchain pública.", date: "2026-06-22", views: "234K", trend: "+167%" },
{ id: 39, category: "EDUCAÇÃO", title: "Robôs Professor Substituem 30% dos Tutores no Ensino Médio", summary: "IA personalizada atende alunos com dificuldades específicas. Desempenho aumenta 23% nas notas de matemática.", date: "2026-06-19", views: "189K", trend: "+145%" },
{ id: 40, category: "EDUCAÇÃO", title: "Khan Academy Lança Versão TOTALMENTE em Português", summary: "Plataforma gratuita inclui IA tutor para milhões de brasileiros. Parceria com MEC para inclusão digital.", date: "2026-06-21", views: "145K", trend: "+98%" },
{ id: 41, category: "EDUCAÇÃO", title: "MBA em IA Generativa Tem 10 Mil Inscritos", summary: "Curso da FGV supera recorde com foco em automação. Empresas pagam até R$ 15 mil por colaborador.", date: "2026-06-23", views: "98K", trend: "+134%" },
{ id: 42, category: "EDUCAÇÃO", title: "Escolas do Futuro Usam Realidade Virtual", summary: "Ministro anuncia 500 escolas piloto com VR para geografia e história. Estudantes viajam virtualmente para o Brasil colonial.", date: "2026-06-24", views: "167K", trend: "+112%" },
{ id: 43, category: "EDUCAÇÃO", title: "Ensino Híbrido Online + Presencial é Aprovado por 78% dos Alunos", summary: "Pesquisa nacional mostra preferência por modelo flexível. Universidades ajustam calendário para 2027.", date: "2026-06-18", views: "87K", trend: "+78%" },
{ id: 44, category: "EDUCAÇÃO", title: "Estudante Brasileiro Ganha Olimpíada Internacional de Programação", summary: "João Silva (19 anos) da UFRJ vence gold medal na ICPC World Finals. Brasil entra para elite de programação global.", date: "2026-06-15", views: "234K", trend: "+245%" },
{ id: 45, category: "EDUCAÇÃO", title: "Chatbots Substituem Monitoria em 40% das Universidades", summary: "IA disponível 24/7 responde dúvidas de cálculo e física. Taxa de aprovação sobe 15% nas disciplinas tuteladas.", date: "2026-06-20", views: "156K", trend: "+123%" },
{ id: 46, category: "EDUCAÇÃO", title: "Ensino Técnico Gratuito com Empresas Cresce 180%", summary: "Programa MEC + Indústria tem 200 mil novos alunos. Foco em programação, robótica e agro tech.", date: "2026-06-22", views: "178K", trend: "+156%" },
{ id: 47, category: "EDUCAÇÃO", title: "Plataforma Brasileira de IA Educa 1 Milhão de Alunos", summary: "Startup educação ganha US$ 50M em funding após sucesso na personalização de aprendizagem com IA.", date: "2026-06-25", views: "234K", trend: "+187%" },
{ id: 48, category: "EDUCAÇÃO", title: "Escolas Básicas do Brasil Ganham Labs de Robótica", summary: "Projeto Ministério + Microsoft instala laboratórios em 1000 escolas rurais. 50 mil novos programadores estão nascendo.", date: "2026-06-26", views: "189K", trend: "+156%" },
// ==== PRODUTIVIDADE ====
{ id: 49, category: "PRODUTIVIDADE", title: "Método Pomodoro 2.0 Domina Profissionais", summary: "Nova variação do clássico método pomodoro inclui pausas de micro-meditação. Pesquisa aponta 67% mais foco e 30% menos estresse.", date: "2026-06-22", views: "87K", trend: "+52%" },
{ id: 50, category: "PRODUTIVIDADE", title: "Notion AI Nova Versão Integra Voice Notes", summary: "Função transcreve e resume reuniões automaticamente. Usuários relatam ganho de 3h/dia em organização.", date: "2026-06-23", views: "145K", trend: "+89%" },
{ id: 51, category: "PRODUTIVIDADE", title: "Ferramentas de Design com IA Dominam Mercado", summary: "Usuários criam 5x mais protótipos com sugestões automáticas. Tendência: designers combinam trabalho manual com IA.", date: "2026-06-18", views: "112K", trend: "+122%" },
{ id: 52, category: "PRODUTIVIDADE", title: "Automação Residencial Reduz Contas em 45%", summary: "Sistemas inteligentes de luz e água usam IA para otimizar consumo. Brasil tem 2 milhões de residências conectadas.", date: "2026-06-21", views: "189K", trend: "+98%" },
{ id: 53, category: "PRODUTIVIDADE", title: "Apps de Foco Usam Neurofeedback em Tempo Real", summary: "Headset detecta atenção e pausa notificações automaticamente. Produtividade aumenta 35% em home office.", date: "2026-06-19", views: "98K", trend: "+78%" },
{ id: 54, category: "PRODUTIVIDADE", title: "Task Management com IA Aumenta 50% Conclusão de Projetos", summary: "Algoritmo prioriza tarefas baseado em prazo, energia e dependências. Empresas reportam redução de 20% no prazo médio.", date: "2026-06-24", views: "134K", trend: "+112%" },
{ id: 55, category: "PRODUTIVIDADE", title: "Calendário Inteligente Sincroniza 15 Ferramentas em Tempo Real", summary: "IA aprende padrões e ajusta reuniões automaticamente. Redução de 40% em conflitos de agenda no Brasil.", date: "2026-06-20", views: "167K", trend: "+87%" },
{ id: 56, category: "PRODUTIVIDADE", title: "Email Zero Automatizado com IA Atinge 1 Milhão de Usuários", summary: "Ferramenta classifica, responde e archiva automaticamente. Tempo médio de 2h/dia recuperado para produtividade.", date: "2026-06-22", views: "123K", trend: "+134%" },
{ id: 57, category: "PRODUTIVIDADE", title: "Digital Minimalism Apps Crescem 200% no Brasil", summary: "Usuários focam em menos ferramentas por vez. Apps simples com funcionalidade única lideram download no App Store.", date: "2026-06-25", views: "156K", trend: "+98%" },
{ id: 58, category: "PRODUTIVIDADE", title: "Automação de Contratos Inteligentes Salva R$ 2 Bi/Ano", summary: "Smart contracts reduzem burocracia em administração pública. Gov.br processa 50% mais documentos em menos tempo.", date: "2026-06-17", views: "201K", trend: "+145%" },
{ id: 59, category: "PRODUTIVIDADE", title: "Ferramenta Brasileira de Task AI Vende para 20 Países", summary: "SaaS de gestão de projetos com IA otimiza prazos e recursos. Clientes relatam 30% mais eficiência.", date: "2026-06-26", views: "178K", trend: "+134%" },
{ id: 60, category: "PRODUTIVIDADE", title: "Método Digital Detox Reduz Burnout em 60%", summary: "Técnica de desintoxicação digital combina floresta e IA para reduzir estresse corporativo.", date: "2026-06-23", views: "156K", trend: "+122%" },
// ==== VIAGEM ====
{ id: 61, category: "VIAGEM", title: "Destinos Offshore Ganham Popularidade", summary: "Com trajetória de 8h, novos roteiros de turismo sustentável atraem digital nomads. Costa Rica e Portugal lideram preferências.", date: "2026-06-19", views: "143K", trend: "+78%" },
{ id: 62, category: "VIAGEM", title: "Voos Brasileiros Usam Combustível de Sacaia", summary: "Azul e Latam testam biofuel que reduz emissões em 80%. Passagens podem ficar 15% mais baratas até 2027.", date: "2026-06-22", views: "189K", trend: "+123%" },
{ id: 63, category: "VIAGEM", title: "Santiago É Eleito Melhor Destino para Remote Workers", summary: "Chile lidera ranking internacional com custo de vida baixo e infraestrutura excelente. Brasileiros compram imóveis para residência.", date: "2026-06-24", views: "134K", trend: "+87%" },
{ id: 64, category: "VIAGEM", title: "Airbnb Lança Experiências com IA Personalizada", summary: "Algoritmo sugere experiências baseadas em histórico e preferências. Taxa de satisfação sobe 35% nos testes.", date: "2026-06-21", views: "234K", trend: "+112%" },
{ id: 65, category: "VIAGEM", title: "Brasil Tem 500 Hostels com Energia Solar", summary: "Rede ecológica abriga backpackers com wifi 5G e café da manhã orgânico. Destinos sustentáveis crescem 120%.", date: "2026-06-20", views: "98K", trend: "+98%" },
{ id: 66, category: "VIAGEM", title: "Navegação por Satélite Gratuita em Celulares Chega ao Brasil", summary: "Chips GPS mais baratos permitem localização offline em qualquer lugar. Aplicativo brasileiro ganha 100K downloads em 1 semana.", date: "2026-06-23", views: "156K", trend: "+145%" },
{ id: 67, category: "VIAGEM", title: "Cruzeiro do Norte Reabre com Novos Navios Elétricos", summary: "Trem-bala conecta Manaus a Porto Velho em 12h. Rota turística gera 5 mil empregos diretos no Amazonas.", date: "2026-06-25", views: "178K", trend: "+167%" },
{ id: 68, category: "VIAGEM", title: "Apps de Viagem Usam AR para Guia Turístico", summary: "Realidade aumentada mostra informações históricas em tempo real. Visitantes relatam experiência 3x mais engajadora.", date: "2026-06-18", views: "145K", trend: "+89%" },
{ id: 69, category: "VIAGEM", title: "Parques Nacionais Ganham WiFi via Satélite", summary: "Ecoturistas podem compartilhar fotos sem sair das trilhas. Sistema reduz sinal 0% na fauna local.", date: "2026-06-19", views: "87K", trend: "+67%" },
{ id: 70, category: "VIAGEM", title: "Digital Nomad Visa Aprovação sobe para 98%", summary: "Processo simplificado online reduz tempo de análise. 50 mil brasileiros já solicitaram visto para trabalhar fora.", date: "2026-06-26", views: "234K", trend: "+178%" },
{ id: 71, category: "VIAGEM", title: "Startup Brasileira de Drone Delivery Levanta US$ 15M", summary: "Empresa de entrega por drone com tecnologia própria expande para 50 capitais. Rota é 100% autônoma.", date: "2026-06-26", views: "201K", trend: "+156%" },
{ id: 72, category: "VIAGEM", title: "Brasil Oferece 200 Rotas de Turismo Espacial", summary: "Parceria com empresa americana lança viagens espaciais saindo do Ceará. 5000 brasileiros já fizeram reserva.", date: "2026-06-24", views: "345K", trend: "+234%" },
// ==== ALIMENTAÇÃO ====
{ id: 73, category: "ALIMENTAÇÃO", title: "Dieta Ketogênica Combina com Treinos HIIT", summary: "Estudo revela 40% mais glicose no sangue pós-treino. Nutricionistas recomendam combinação para perda de peso acelerada.", date: "2026-06-21", views: "72K", trend: "+38%" },
{ id: 74, category: "ALIMENTAÇÃO", title: "Lab-Grown Carne Chega ao McDonald's no Brasil", summary: "Hambúrguer cultivado em laboratório tem 70% menos impacto ambiental. Teste piloto em São Paulo até agosto.", date: "2026-06-23", views: "234K", trend: "+189%" },
{ id: 75, category: "ALIMENTAÇÃO", title: "Alimentos Funcionais com Nootropics Viram Ênfase em Mercado", summary: "Produtos que melhoram cognição ganham espaço em 300% nas farmácias. Jovens pagam até R$ 50 por shake 'focus'.", date: "2026-06-25", views: "156K", trend: "+134%" },
{ id: 76, category: "ALIMENTAÇÃO", title: "Feijão Orgânico da Embrapa Vence Concorrência Europeia", summary: "Variedade resistente à seca tem 30% mais proteína. Exportações crescem 120% para UE e Japão.", date: "2026-06-22", views: "87K", trend: "+98%" },
{ id: 77, category: "ALIMENTAÇÃO", title: "Delivery por Drone Reduz Tempo em 70%", summary: "Startup brasileira entrega refeições em 5 minutos. Zonas de voo autorizadas em 50 capitais.", date: "2026-06-24", views: "189K", trend: "+145%" },
{ id: 78, category: "ALIMENTAÇÃO", title: "Suplementos Personalizados com DNA Batem 95% de Precisão", summary: "Teste genético indica vitaminas ideais. Startup oferece kit por R$ 299 com recomendações semanais.", date: "2026-06-19", views: "123K", trend: "+112%" },
{ id: 79, category: "ALIMENTAÇÃO", title: "Zero-Calorie Sweetener da Naturobotica é Green Light FDA", summary: "Açúcar sintético sem calorias é liberado para consumo. Peso médio de perda: 2kg em 30 dias.", date: "2026-06-21", views: "167K", trend: "+156%" },
{ id: 80, category: "ALIMENTAÇÃO", title: "Café Functional com L-theanine Reduz Estresse em 45%", summary: "Combinação de cafeína e aminoácido da matcha vira best-seller. Empresas oferecem como benefício a funcionários.", date: "2026-06-20", views: "112K", trend: "+87%" },
{ id: 81, category: "ALIMENTAÇÃO", title: "Alimentos Anti-inflamatórios Reduzem Dor Crônica em 60%", summary: "Pesquisa da USP identifica 20 alimentos brasileiros com efeito potente. Receita com açafrão vira sucesso.", date: "2026-06-23", views: "98K", trend: "+123%" },
{ id: 82, category: "ALIMENTAÇÃO", title: "Restaurantes com Zero Resíduos Crescem 150%", summary: "Consumo consciente vira diferencial em São Paulo. Cozinheiros usam IA para otimizar porções e reduzir desperdício.", date: "2026-06-26", views: "178K", trend: "+134%" },
{ id: 83, category: "ALIMENTAÇÃO", title: "Lab-Grown Salmon é Aprovado pela ANVISA", summary: "Primeiro peixe cultivado no laboratório no Brasil. Cadeia produtiva reduz emissões em 90%.", date: "2026-06-25", views: "198K", trend: "+178%" },
{ id: 84, category: "ALIMENTAÇÃO", title: "Kit de Cultivo em Casa Vende 500 Mil Unidades", summary: "Sistema compacto permite produzir vegetais orgânicos em apartamento. Brasileiros economizam R$ 300/mês.", date: "2026-06-26", views: "145K", trend: "+145%" },
// ==== ENTRETEIMENTO ====
{ id: 85, category: "ENTRETEIMENTO", title: "Séries Interativas com IA Geram Engajamento Record", summary: "Plataformas testam narrativas onde espectador escolhe finais. Taxa de conclusão sobe 75% com escolhas personalizadas.", date: "2026-06-24", views: "167K", trend: "+95%" },
{ id: 86, category: "ENTRETEIMENTO", title: "Netflix Lança Modo Offline com Download Instantâneo", summary: "Algoritmo prevê próximos episódios a serem baixados automaticamente. Usuários ficam 2 dias sem internet e não percebem.", date: "2026-06-22", views: "234K", trend: "+123%" },
{ id: 87, category: "ENTRETEIMENTO", title: "Jogos com IA Generativa Têm História Única a Cada Partida", summary: "NPCs criam diálogos e missões em tempo real. Jogo brasileiro vende 2 milhões de cópias em 2 semanas.", date: "2026-06-25", views: "312K", trend: "+210%" },
{ id: 88, category: "ENTRETEIMENTO", title: "Teatro Virtual com Avatar Personalizado Atravessa Fronteiras", summary: "Peça da Providência encanta público global com avatares em realidade aumentada. Ingressos NFT esgotam em 3 horas.", date: "2026-06-21", views: "87K", trend: "+78%" },
{ id: 89, category: "ENTRETEIMENTO", title: "Música com IA Atinge 5 Bilhões de Streams Mensais", summary: "Plataformas oferecem trilhas geradas para projetos. Compositores brasileiros ganham royalties por estilo criado.", date: "2026-06-23", views: "189K", trend: "+145%" },
{ id: 90, category: "ENTRETEIMENTO", title: "Festivais de Realidade Virtual Trazem 100 Mil Pessoas", summary: "Show imersivo com artistas globais tem transmissão simultânea. Brunetos podem 'andar' pelo palco virtualmente.", date: "2026-06-24", views: "156K", trend: "+112%" },
{ id: 91, category: "ENTRETEIMENTO", title: "Streaming Brasileiro Supera Hollywood em Inovação", summary: "Série 'Selkirk' vence Emmy Internacional com produção 100% local. Budget: R$ 2 milhões x US$ 50 milhões da concorrência.", date: "2026-06-22", views: "234K", trend: "+178%" },
{ id: 92, category: "ENTRETEIMENTO", title: "Podcast com IA Dupla Voz Faz Sucesso nas Redes", summary: "Algoritmo gera conversa entre duas IAs com opiniões conflitantes. Ouvir demais como debate filosófico.", date: "2026-06-20", views: "145K", trend: "+134%" },
{ id: 93, category: "ENTRETEIMENTO", title: "Cinema 4D sem Óculos é Tendência em Cinema Brasileiro", summary: "Tecnologia holográfica permite ver profundidade sem equipamento. Filme 'Araguaia' estreia com sucesso global.", date: "2026-06-25", views: "198K", trend: "+156%" },
{ id: 94, category: "ENTRETEIMENTO", title: "Meme Coins Viram Arte Digital em Museus", summary: "NFTs de memes anos 2020 agora em MoMA. Brasileiro compra meme por R$ 5 milhões em leilão internacional.", date: "2026-06-26", views: "345K", trend: "+234%" },
{ id: 95, category: "ENTRETEIMENTO", title: "Streaming Brasileiro Lança Produção IA em Tempo Real", summary: "Plataforma gera episódios personalizados baseados no humor do usuário. 10 mil horas de conteúdo diário.", date: "2026-06-25", views: "278K", trend: "+189%" },
{ id: 96, category: "ENTRETEIMENTO", title: "Jogo Brasileiro de Realidade Extinta Vende 5 Milhões", summary: "Título preserva espécies brasileiras extintas em mundos virtuais. Receita investe em preservação real.", date: "2026-06-26", views: "234K", trend: "+178%" },
{ id: 97, category: "ENTRETEIMENTO", title: "Realidade Virtual em Todos os Cinemas Brasileiros", summary: "Cadeia que vale R$ 5 bilhões instala 500 salas VR para pré-estreias. Experiência imersiva no lançamento de filmes.", date: "2026-06-24", views: "189K", trend: "+145%" },
{ id: 98, category: "ENTRETEIMENTO", title: "IA Traduz e Dubla Séries em Tempo Real", summary: "Plataforma permite assistir qualquer série com legenda sincronizada em português. Qualidade de áudio ainda é 92%.", date: "2026-06-23", views: "234K", trend: "+123%" },
{ id: 99, category: "ENTRETEIMENTO", title: "Concertos Holográficos docegencouraliza 1000 Teatros", summary: "Shows de artistas falecidos vendem ingressos milionários. Tecnologia usa deepfake ético com família do artista.", date: "2026-06-22", views: "156K", trend: "+167%" },
{ id: 100, category: "ENTRETEIMENTO", title: "Rede Social Brasileira Vence TikTok Europeia", summary: "Plataforma focada em conteúdo educativo supera 50 milhões de usuários. Crescimento de 300% em 6 meses.", date: "2026-06-26", views: "201K", trend: "+198%" }
]
export default function TrendsNews() {
const router = useRouter()
const [selectedCategory, setSelectedCategory] = useState("TODAS")
const [trendingNews, setTrendingNews] = useState(weeklyTrends)
useEffect(() => {
if (selectedCategory === "TODAS") {
setTrendingNews(weeklyTrends)
} else {
setTrendingNews(weeklyTrends.filter(n => n.category === selectedCategory))
}
}, [selectedCategory])
const categories = ["TODAS", "TECNOLOGIA", "SAÚDE", "FINANÇAS", "EDUCAÇÃO", "PRODUTIVIDADE", "VIAGEM", "ALIMENTAÇÃO", "ENTRETEIMENTO"]
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<nav className="bg-white shadow-lg p-4 sticky top-0 z-10 flex items-center justify-between">
<button
onClick={() => router.push("/dashboard")}
className="text-blue-600 hover:text-blue-800 font-medium"
>
Voltar
</button>
<div>
<h1 className="text-2xl font-bold text-center bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
Tendências Semanais
</h1>
<p className="text-center text-sm text-gray-500 mt-1">Top 100 Notícias Junho 2026</p>
</div>
<div className="w-16"></div>
</nav>
{/* Filtros de Categoria */}
<div className="max-w-4xl mx-auto p-4">
<div className="flex flex-wrap gap-2 justify-center mb-6">
{categories.map(cat => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-3 py-1 rounded-full text-xs font-medium transition-all ${
selectedCategory === cat
? "bg-blue-600 text-white"
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
}`}
>
{cat}
</button>
))}
</div>
{/* Lista de Notícias - Grid 2 colunas */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{trendingNews.length > 0 ? (
trendingNews.map((news, idx) => (
<div key={news.id} className="bg-white rounded-lg p-4 shadow-md hover:shadow-lg transition-shadow">
<div className="flex items-start justify-between mb-2">
<span className="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full font-semibold">
{news.category}
</span>
<div className="flex items-center gap-2 text-xs">
<span className="text-green-600 font-bold">{news.trend}</span>
<span className="text-gray-500">{news.views} views</span>
</div>
</div>
<h2 className="text-lg font-bold text-gray-800 mb-2">
#{idx + 1} {news.title}
</h2>
<p className="text-gray-600 text-sm leading-relaxed">
{news.summary}
</p>
<div className="mt-3 text-xs text-gray-500">
{new Date(news.date).toLocaleDateString('pt-BR', {
weekday: 'short',
month: 'short',
day: 'numeric'
})}
</div>
</div>
))
) : (
<div className="text-center py-12">
<p className="text-gray-500">Nenhuma notícia nesta categoria</p>
</div>
)}
</div>
{/* Footer */}
<div className="mt-8 text-center text-xs text-gray-400">
<p>Top 100 Notícias Dados consolidados semanais Junho 2026</p>
<p className="mt-1">Mostrando {trendingNews.length} de {weeklyTrends.length} trends</p>
</div>
</div>
</div>
)
}
-288
View File
@@ -1,288 +0,0 @@
import { useState, useEffect } from "react"
import { useRouter } from "next/router"
// Copa do Mundo 2026 - Grupos e Classificação (48 seleções, 12 grupos de 4)
const worldCupGroups = {
A: [
{ team: "Canadá", flag: "🇨🇦", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "México", flag: "🇲🇽", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Estados Unidos", flag: "🇺🇸", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
B: [
{ team: "Argentina", flag: "🇦🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Paraguai", flag: "🇵🇾", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Chile", flag: "🇨🇱", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
C: [
{ team: "Brasil", flag: "🇧🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Colômbia", flag: "🇨🇴", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Uruguai", flag: "🇺🇾", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
D: [
{ team: "França", flag: "🇫🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Portugal", flag: "🇵🇹", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Espanha", flag: "🇪🇸", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
E: [
{ team: "Alemanha", flag: "🇩🇪", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Inglaterra", flag: "🇬🇧", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Itália", flag: "🇮🇹", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
F: [
{ team: "Holanda", flag: "🇳🇱", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Bélgica", flag: "🇧🇪", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Croácia", flag: "🇭🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
G: [
{ team: "Marrocos", flag: "🇲🇦", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Senegal", flag: "🇸🇳", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Tunísia", flag: "🇹🇳", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
H: [
{ team: "Japão", flag: "🇯🇵", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Coreia do Sul", flag: "🇰🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Austrália", flag: "🇦🇺", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
I: [
{ team: "Irã", flag: "🇮🇷", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Arábia Saudita", flag: "🇸🇦", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Catar", flag: "🇶🇦", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
J: [
{ team: "Nigéria", flag: "🇳🇬", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Gana", flag: "🇬🇭", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Camarões", flag: "🇨🇲", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
K: [
{ team: "Equador", flag: "🇪🇨", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Peru", flag: "🇵🇪", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Venezuela", flag: "🇻🇪", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
L: [
{ team: "Polônia", flag: "🇵🇱", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Sérvia", flag: "🇷🇸", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "Suíça", flag: "🇨🇭", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
{ team: "TBD (Repescagem)", flag: "🏳️", played: 0, won: 0, drawn: 0, lost: 0, gf: 0, ga: 0, gd: 0, points: 0 },
],
}
// Últimas notícias da Copa 2026 (simuladas - RSS/API pública)
const worldCupNews = [
{ id: 1, category: "PREPARAÇÃO", title: "FIFA Confirma 16 Cidades-Sede para Copa 2026", summary: "Estados Unidos (11), México (3) e Canadá (2) receberão jogos. Final será no MetLife Stadium, Nova Jersey.", date: "2026-06-25", views: "450K", trend: "+120%" },
{ id: 2, category: "BRASIL", title: "Brasil Enfrenta Argentina em Amistoso Preparatório", summary: "Clássico sul-americano marcado para setembro no Morumbi. Dorival Jr. testa nova formação com Endrick e Vini Jr.", date: "2026-06-24", views: "890K", trend: "+340%" },
{ id: 3, category: "SEDES", title: "Estadio Azteca Passará por Reforma de $200M", summary: "Único estádio a sediar 3 Copas do Mundo (1970, 1986, 2026). Modernização inclui nova tecnologia de gramado híbrido.", date: "2026-06-23", views: "234K", trend: "+87%" },
{ id: 4, category: "FORMATO", title: "Novo Formato: 48 Seleções, 12 Grupos, 104 Jogos", summary: "Fase de grupos com 3 times classificados por grupo + 8 melhores terceiros. Oitavas de final expandidas.", date: "2026-06-22", views: "567K", trend: "+156%" },
{ id: 5, category: "BRASIL", title: "Neymar Recupera-se de Lesão e Treina com Bola", summary: "Craque do Al-Hilal evolui bem na reabilitação. Médicos otimistas para disputa da Copa 2026 aos 34 anos.", date: "2026-06-21", views: "723K", trend: "+234%" },
{ id: 6, category: "INGRESSOS", title: "Venda de Ingressos Inicia em Janeiro 2026", summary: "Fase 1: sorteio para residentes dos países-sede. Preços variam de $50 a $1.600. Demanda prevista: 5M ingressos.", date: "2026-06-20", views: "389K", trend: "+98%" },
{ id: 7, category: "TECNOLOGIA", title: "VAR 2.0 com IA em Tempo Real Estreia na Copa", summary: "Novo sistema reduz tempo de decisão para 15 segundos. Câmeras 4K + sensores na bola detectam impedimento milimétrico.", date: "2026-06-19", views: "278K", trend: "+112%" },
{ id: 8, category: "SUSTENTABILIDADE", title: "Copa 2026 Será Carbono Neutro", summary: "FIFA compromete-se com compensação total. Transporte público gratuito para torcedores com ingresso nos dias de jogo.", date: "2026-06-18", views: "189K", trend: "+78%" },
{ id: 9, category: "ELIMINATÓRIAS", title: "Brasil Lidera Eliminatórias Sul-Americanas", summary: "Seleção tem 30 pontos em 14 jogos. Argentina e Uruguai completam top 3. Próximos jogos: Bolívia (casa) e Peru (fora).", date: "2026-06-17", views: "456K", trend: "+134%" },
{ id: 10, category: "ESTRELAS", title: "Mbappé, Haaland e Bellingham: A Nova Geração", summary: "França, Noruega e Inglaterra apostam em astros sub-25. Copa 2026 pode ser última de Messi e Cristiano Ronaldo.", date: "2026-06-26", views: "612K", trend: "+189%" },
{ id: 11, category: "HISTÓRIA", title: "Primeira Copa com 3 Países-Sede na História", summary: "Inédito: EUA, México e Canadá dividem organização. México será único a sediar 3 Copas (1970, 1986, 2026).", date: "2026-06-16", views: "345K", trend: "+95%" },
{ id: 12, category: "BRASIL", title: "CBF Anuncia Concentração em Orlando Para em Miami para Fase de Grupos", summary: "Estrutura de 5 estrelas com campos oficiais FIFA. Climas similares aos das cidades-sede americanas.", date: "2026-06-15", views: "298K", trend: "+103%" },
]
export default function WorldCup2026() {
const router = useRouter()
const [selectedGroup, setSelectedGroup] = useState("TODOS")
const [selectedCategory, setSelectedCategory] = useState("TODAS")
const [filteredNews, setFilteredNews] = useState(worldCupNews)
useEffect(() => {
if (selectedCategory === "TODAS") {
setFilteredNews(worldCupNews)
} else {
setFilteredNews(worldCupNews.filter(n => n.category === selectedCategory))
}
}, [selectedCategory])
const groups = ["TODOS", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
const newsCategories = ["TODAS", "PREPARAÇÃO", "BRASIL", "SEDES", "FORMATO", "INGRESSOS", "TECNOLOGIA", "SUSTENTABILIDADE", "ELIMINATÓRIAS", "ESTRELAS", "HISTÓRIA"]
// Ordenar times por pontos, saldo, gols pró
const sortStandings = (teams) => {
return [...teams].sort((a, b) => {
if (b.points !== a.points) return b.points - a.points
if (b.gd !== a.gd) return b.gd - a.gd
return b.gf - a.gf
})
}
const getGroupsToShow = () => {
if (selectedGroup === "TODOS") return Object.keys(worldCupGroups)
return [selectedGroup]
}
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<nav className="bg-white shadow-lg p-4 sticky top-0 z-10 flex items-center justify-between">
<button
onClick={() => router.push("/dashboard")}
className="text-blue-600 hover:text-blue-800 font-medium"
>
Voltar
</button>
<div>
<h1 className="text-2xl font-bold text-center bg-gradient-to-r from-green-600 to-blue-600 bg-clip-text text-transparent">
Copa do Mundo 2026
</h1>
<p className="text-center text-sm text-gray-500 mt-1">48 Seleções 12 Grupos 104 Jogos EUA/México/Canadá</p>
</div>
<div className="w-16"></div>
</nav>
<div className="max-w-4xl mx-auto p-4">
{/* Filtros de Grupo */}
<div className="flex flex-wrap gap-2 justify-center mb-6">
{groups.map(grp => (
<button
key={grp}
onClick={() => setSelectedGroup(grp)}
className={`px-3 py-1 rounded-full text-xs font-medium transition-all ${
selectedGroup === grp
? "bg-green-600 text-white"
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
}`}
>
{grp === "TODOS" ? "TODOS" : `Grupo ${grp}`}
</button>
))}
</div>
{/* Tabelas de Classificação por Grupo */}
<div className="space-y-6 mb-8">
{getGroupsToShow().map(groupKey => {
const teams = sortStandings(worldCupGroups[groupKey])
return (
<div key={groupKey} className="bg-white rounded-lg shadow-md overflow-hidden">
<div className="bg-gradient-to-r from-green-600 to-green-800 text-white p-4">
<h2 className="text-xl font-bold">Grupo {groupKey}</h2>
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-gray-100">
<tr>
<th className="p-3 text-left font-semibold text-gray-700 w-8">#</th>
<th className="p-3 text-left font-semibold text-gray-700 w-40">Seleção</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">J</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">V</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">E</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">D</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">GP</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">GC</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">SG</th>
<th className="p-3 text-center font-semibold text-gray-700 w-16">Pts</th>
</tr>
</thead>
<tbody>
{teams.map((team, idx) => (
<tr key={`${groupKey}-${team.team}`} className={`${idx < 2 ? "bg-green-50" : ""} border-t border-gray-100 hover:bg-gray-50`}>
<td className="p-3 font-bold text-gray-800">{idx + 1}</td>
<td className="p-3 font-medium">
<span className="text-lg mr-2">{team.flag}</span>
{team.team}
</td>
<td className="p-3 text-center text-gray-600">{team.played}</td>
<td className="p-3 text-center text-green-600 font-medium">{team.won}</td>
<td className="p-3 text-center text-yellow-600 font-medium">{team.drawn}</td>
<td className="p-3 text-center text-red-600 font-medium">{team.lost}</td>
<td className="p-3 text-center text-gray-600">{team.gf}</td>
<td className="p-3 text-center text-gray-600">{team.ga}</td>
<td className="p-3 text-center font-bold text-blue-600">{team.gd >= 0 ? "+" : ""}{team.gd}</td>
<td className="p-3 text-center font-bold text-green-700">{team.points}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="p-3 text-xs text-gray-500 bg-gray-50 border-t border-gray-100">
Top 2 classificam direto 8 melhores terceiros avançam J = Jogos, V = Vitórias, E = Empates, D = Derrotas, GP = Gols Pró, GC = Gols Contra, SG = Saldo, Pts = Pontos
</div>
</div>
)
})}
</div>
{/* Separador Notícias */}
<div className="my-8">
<h2 className="text-xl font-bold text-center text-gray-800 mb-4">Últimas Notícias da Copa 2026</h2>
</div>
{/* Filtros de Categoria Notícias */}
<div className="flex flex-wrap gap-2 justify-center mb-6">
{newsCategories.map(cat => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-3 py-1 rounded-full text-xs font-medium transition-all ${
selectedCategory === cat
? "bg-blue-600 text-white"
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
}`}
>
{cat}
</button>
))}
</div>
{/* Grid de Notícias - 2 colunas */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{filteredNews.length > 0 ? (
filteredNews.map((news, idx) => (
<div key={news.id} className="bg-white rounded-lg p-4 shadow-md hover:shadow-lg transition-shadow">
<div className="flex items-start justify-between mb-2">
<span className="text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full font-semibold">
{news.category}
</span>
<div className="flex items-center gap-2 text-xs">
<span className="text-green-600 font-bold">{news.trend}</span>
<span className="text-gray-500">{news.views} views</span>
</div>
</div>
<h2 className="text-lg font-bold text-gray-800 mb-2">
#{idx + 1} {news.title}
</h2>
<p className="text-gray-600 text-sm leading-relaxed">
{news.summary}
</p>
<div className="mt-3 text-xs text-gray-500">
{new Date(news.date).toLocaleDateString('pt-BR', {
weekday: 'short',
month: 'short',
day: 'numeric'
})}
</div>
</div>
))
) : (
<div className="text-center py-12">
<p className="text-gray-500">Nenhuma notícia nesta categoria</p>
</div>
)}
</div>
{/* Footer */}
<div className="mt-8 text-center text-xs text-gray-400">
<p>Copa do Mundo FIFA 2026 48 Seleções 12 Grupos 104 Jogos</p>
<p className="mt-1">Sedes: EUA (11), México (3), Canadá (2) Junho/Julho 2026</p>
<p className="mt-1">Mostrando {filteredNews.length} de {worldCupNews.length} notícias Grupos: fase inicial (0 jogos)</p>
</div>
</div>
</div>
)
}
-200
View File
@@ -1,200 +0,0 @@
import React from "react"
import { useState } from "react"
import { useRouter } from "next/router"
import AvatarSelector from "@/components/dashboard/AvatarSelector"
import CategoryMenu from "@/components/dashboard/CategoryMenu"
export default function Dashboard() {
const router = useRouter()
const [selectedAvatar, setSelectedAvatar] = useState("😀")
const [selectedCategory, setSelectedCategory] = useState("all")
const [favorites, setFavorites] = useState([])
const [confirmPassword, setConfirmPassword] = useState("")
const [loading, setLoading] = useState(false)
// Apps organized by category
const ALL_APPS = [
// Produtividade
{ id: 1, name: "Tendências Semanais", desc: "Notícias em alta • Top 100", path: "/apps/trendsnews", icon: "📈", category: "produtividade", favorite: false },
{ id: 2, name: "Conversor", desc: "Conversão de unidades", path: "/apps/converter", icon: "🔄", category: "produtividade", favorite: false },
{ id: 3, name: "Calculadora", desc: "Cálculos rápidos", path: "/apps/calculator", icon: "🧮", category: "produtividade", favorite: false },
// Utilidades
{ id: 4, name: "Relógio", desc: "Relógio mundial", path: "/apps/clock", icon: "⏰", category: "utilidades", favorite: false },
{ id: 5, name: "Gerador Senhas", desc: "Senhas seguras", path: "/apps/password-generator", icon: "🔐", category: "utilidades", favorite: false },
{ id: 6, name: "QR Code", desc: "Gerador QR", path: "/apps/qrcode", icon: "📱", category: "utilidades", favorite: false },
{ id: 7, name: "Cronômetro", desc: "Tempo e alarmes", path: "/apps/timer", icon: "⏱️", category: "utilidades", favorite: false },
// Games
{ id: 8, name: "Jogo da Velha", desc: "Jogo da velha clássico 2 jogadores", path: "/apps/tictactoe", icon: "⭕", category: "games", favorite: false },
{ id: 14, name: "Expedição Gold", desc: "Expedições para conquistar ouro", path: "/apps/expedicao", icon: "🗺️", category: "games", favorite: false },
{ id: 15, name: "Human City", desc: "Simulação de cidade estilo Sims", path: "/apps/humancity", icon: "🏙️", category: "games", favorite: false },
{ id: 16, name: "Aurea", desc: "Simulador de vida e riqueza", path: "/apps/aurea", icon: "👑", category: "games", favorite: false },
{ id: 17, name: "TerraVerse", desc: "Game de realidade aumentada", path: "/apps/terraverse", icon: "🌍", category: "games", favorite: false },
// Ferramentas
{ id: 9, name: "Editor Texto", desc: "Texto simples", path: "/apps/text-editor", icon: "📝", category: "ferramentas", favorite: false },
{ id: 10, name: "Conversor Moedas", desc: "Câmbio em tempo real", path: "/apps/currency", icon: "💱", category: "ferramentas", favorite: false },
// Educação
{ id: 11, name: "Dicionário", desc: "Significados", path: "/apps/dictionary", icon: "📖", category: "educacao", favorite: false },
{ id: 12, name: "Tradutor", desc: "Multi-idiomas", path: "/apps/translator", icon: "🌐", category: "educacao", favorite: false },
// Esportes
{ id: 13, name: "Copa do Mundo 2026", desc: "Tabela, grupos, notícias", path: "/apps/worldcup2026", icon: "⚽", category: "esportes", favorite: false }
]
// Carregar favoritos do localStorage
React.useEffect(() => {
const saved = localStorage.getItem("favorites")
if (saved) {
try {
setFavorites(JSON.parse(saved))
} catch (e) {
console.warn("Failed to parse favorites from localStorage", e)
}
}
}, [])
// Salvar favoritos no localStorage
React.useEffect(() => {
localStorage.setItem("favorites", JSON.stringify(favorites))
}, [favorites])
// Salvar avatar no localStorage
React.useEffect(() => {
localStorage.setItem("selectedAvatar", selectedAvatar)
}, [selectedAvatar])
// Carregar avatar do localStorage
React.useEffect(() => {
const saved = localStorage.getItem("selectedAvatar")
if (saved) setSelectedAvatar(saved)
}, [])
const toggleFavorite = (appId) => {
setFavorites(prev =>
prev.includes(appId)
? prev.filter(id => id !== appId)
: [...prev, appId]
)
}
// Filtrar apps
const filteredApps = selectedCategory === "all"
? ALL_APPS
: ALL_APPS.filter(app => app.category === selectedCategory)
// Apps favoritos
const favoriteApps = ALL_APPS.filter(app => favorites.includes(app.id))
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<nav className="bg-white shadow-lg p-4 flex justify-between items-center">
<div className="flex items-center gap-4">
<h1 className="text-2xl font-bold">1000Apps</h1>
<span className="text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full font-semibold">
FREE
</span>
</div>
<div className="flex items-center gap-4">
<AvatarSelector
selectedAvatar={selectedAvatar}
onSelect={setSelectedAvatar}
/>
</div>
</nav>
<main className="max-w-6xl mx-auto p-6">
{/* Favoritos Section */}
{favorites.length > 0 && (
<div className="mb-8">
<h2 className="text-2xl font-bold text-gray-800 mb-4 flex items-center gap-2">
<span></span> Favoritos
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{favorites.map((favId) => {
const app = ALL_APPS.find((a) => a.id === favId)
if (!app) return null
return (
<div key={app.id} className="relative">
<button
onClick={() => router.push(app.path)}
className="bg-white rounded-xl p-6 shadow-md hover:shadow-lg transition-all text-left hover:scale-105 cursor-pointer w-full"
>
<div className="text-3xl mb-2">{app.icon}</div>
<h3 className="font-semibold text-gray-800 text-sm">{app.name}</h3>
<p className="text-xs text-gray-500 mt-1">{app.desc}</p>
</button>
<button
onClick={() => toggleFavorite(app.id)}
className="absolute top-2 right-2 text-lg"
title={favorites.includes(favId) ? "Remover dos favoritos" : "Adicionar aos favoritos"}
>
{favorites.includes(favId) ? "⭐" : "☆"}
</button>
</div>
)
})}
</div>
</div>
)}
{/* Category Filter */}
<CategoryMenu
selectedCategory={selectedCategory}
onSelect={setSelectedCategory}
/>
{/* Apps Grid */}
<div className="mb-6">
<h2 className="text-3xl font-bold text-gray-800 mb-2">
{selectedCategory === "all"
? "Todos os Apps"
: selectedCategory === "produtividade"
? "Produtividade"
: selectedCategory === "utilidades"
? "Utilidades"
: selectedCategory === "games"
? "Games"
: selectedCategory === "ferramentas"
? "Ferramentas"
: selectedCategory === "educacao"
? "Educação"
: selectedCategory === "esportes"
? "Esportes"
: "Apps"}
</h2>
<p className="text-gray-500">
{selectedCategory === "all"
? `${ALL_APPS.length} apps disponíveis`
: `${filteredApps.length} apps nesta categoria`}
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{filteredApps.map((app) => (
<div key={app.id} className="relative">
<button
onClick={() => router.push(app.path)}
className="bg-white rounded-xl p-6 shadow-md hover:shadow-lg transition-all text-left hover:scale-105 cursor-pointer w-full"
>
<div className="text-3xl mb-2">{app.icon}</div>
<h3 className="font-semibold text-gray-800 text-sm">{app.name}</h3>
<p className="text-xs text-gray-500 mt-1">{app.desc}</p>
</button>
<button
onClick={() => toggleFavorite(app.id)}
className="absolute top-2 right-2 text-lg"
title={favorites.includes(app.id) ? "Remover dos favoritos" : "Adicionar aos favoritos"}
>
{favorites.includes(app.id) ? "⭐" : "☆"}
</button>
</div>
))}
</div>
</main>
</div>
)
}
-14
View File
@@ -1,14 +0,0 @@
// index.jsx — rota "/" agora redireciona direto para o dashboard.
// O login foi removido; o app abre sem autenticação.
export default function Home() {
return null
}
export async function getServerSideProps() {
return {
redirect: {
destination: "/dashboard",
permanent: false,
},
}
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
@@ -1 +0,0 @@
{}
@@ -1,39 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"username" TEXT,
"name" TEXT,
"image" TEXT,
"password" TEXT,
"role" TEXT NOT NULL DEFAULT 'user',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "App" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"version" TEXT NOT NULL DEFAULT '1.0.0',
"entryUrl" TEXT NOT NULL,
"ownerId" TEXT NOT NULL,
"isPublic" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "App_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
-- AddForeignKey
ALTER TABLE "App" ADD CONSTRAINT "App_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-35
View File
@@ -1,35 +0,0 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
username String? @unique
name String?
image String?
password String?
role String @default("user")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
apps App[] @relation("UserApps")
}
model App {
id String @id @default(uuid())
name String
description String?
version String @default("1.0.0")
entryUrl String
ownerId String
owner User @relation("UserApps", fields: [ownerId], references: [id])
isPublic Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
-153
View File
@@ -1,153 +0,0 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1000Apps - Sua PlayStore de Ferramentas Úteis</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body { font-family: 'Inter', sans-serif; }
.gradient-bg { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.card-hover { transition: transform 0.2s, box-shadow 0.2s; }
.card-hover:hover { transform: translateY(-4px); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); }
</style>
</head>
<body class="bg-gray-50">
<!-- Header -->
<header class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-900">1000Apps</h1>
<div class="space-x-4">
<a href="/signin" class="text-gray-600 hover:text-gray-900 font-medium">Entrar</a>
<a href="/signup" class="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium hover:bg-blue-700 transition">Cadastrar</a>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="gradient-bg text-white py-20">
<div class="max-w-4xl mx-auto px-4 text-center">
<h2 class="text-5xl font-bold mb-6">Seu Canivete Suíço Digital</h2>
<p class="text-xl mb-8 opacity-90">
Mais de 1000 ferramentas úteis, games e produtividade - tudo online, sem instalar nada!
</p>
<div class="flex justify-center space-x-4">
<a href="/signup" class="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold text-lg hover:bg-gray-100 transition">Começar Grátis</a>
<a href="#apps" class="border border-white text-white px-8 py-3 rounded-lg font-semibold text-lg hover:bg-white/10 transition">Explorar Apps</a>
</div>
</div>
</section>
<!-- Categories -->
<section id="apps" class="py-16 px-4">
<div class="max-w-6xl mx-auto">
<h3 class="text-3xl font-bold text-center mb-12 text-gray-800">Categorias</h3>
<div class="grid md:grid-cols-3 gap-8">
<!-- Produtividade -->
<div class="bg-white rounded-xl p-8 card-hover">
<div class="w-14 h-14 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
<svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
<h4 class="text-xl font-semibold mb-2">Produtividade</h4>
<p class="text-gray-600 mb-4">Ferramentas para otimizar seu tempo e trabalho</p>
<span class="text-blue-600 font-medium">Ver apps →</span>
</div>
<!-- Utilidades -->
<div class="bg-white rounded-xl p-8 card-hover">
<div class="w-14 h-14 bg-green-100 rounded-lg flex items-center justify-center mb-4">
<svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 4a2 2 0 114 0v5a2 2 0 01-2 2H7a2 2 0 00-2-2V6a2 2 0 012-2h2z"></path>
</svg>
</div>
<h4 class="text-xl font-semibold mb-2">Utilidades</h4>
<p class="text-gray-600 mb-4">Ferramentas práticas para o dia a dia</p>
<span class="text-green-600 font-medium">Ver apps →</span>
</div>
<!-- Games -->
<div class="bg-white rounded-xl p-8 card-hover">
<div class="w-14 h-14 bg-purple-100 rounded-lg flex items-center justify-center mb-4">
<svg class="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 12.795a7.95 7.95 0 001.904-5.452 7.95 7.95 0 00-7.95 7.95A7.95 7.95 0 0012.795 14.752"></path>
</svg>
</div>
<h4 class="text-xl font-semibold mb-2">Games</h4>
<p class="text-gray-600 mb-4">Diversão rápida e envolvente</p>
<span class="text-purple-600 font-medium">Ver apps →</span>
</div>
</div>
</div>
</section>
<!-- Features -->
<section class="bg-gray-100 py-16 px-4">
<div class="max-w-6xl mx-auto">
<h3 class="text-3xl font-bold text-center mb-12 text-gray-800">Por que 1000Apps?</h3>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="text-center">
<div class="w-16 h-16 bg-blue-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span class="text-2xl font-bold text-white">01</span>
</div>
<h5 class="font-semibold mb-2">Nada para Instalar</h5>
<p class="text-sm text-gray-600">Tudo rodando direto no navegador</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-green-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span class="text-2xl font-bold text-white">02</span>
</div>
<h5 class="font-semibold mb-2">Sempre Atualizado</h5>
<p class="text-sm text-gray-600">Conteúdo fresco todo dia</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-purple-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span class="text-2xl font-bold text-white">03</span>
</div>
<h5 class="font-semibold mb-2">Curadoria Inteligente</h5>
<p class="text-sm text-gray-600">Só apps úteis e testados</p>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-orange-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span class="text-2xl font-bold text-white">04</span>
</div>
<h5 class="font-semibold mb-2">Grátis para Sempre</h5>
<p class="text-sm text-gray-600">Acesso completo sem pagar nada</p>
</div>
</div>
</div>
</section>
<!-- CTA Final -->
<section class="py-20 px-4">
<div class="max-w-3xl mx-auto text-center">
<h3 class="text-4xl font-bold mb-4 text-gray-900">Pronto para começar?</h3>
<p class="text-lg text-gray-600 mb-8">
Junte-se a milhares de usuários que já economizam tempo com 1000Apps
</p>
<a href="/signup" class="inline-block bg-blue-600 text-white px-10 py-4 rounded-lg font-semibold text-xl hover:bg-blue-700 transition transform hover:scale-105">
Criar Conta Grátis
</a>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 text-white py-8 px-4">
<div class="max-w-6xl mx-auto text-center">
<p class="mb-4">&copy; 2026 1000Apps. Todos os direitos reservados.</p>
<div class="space-x-6 text-sm">
<a href="#" class="hover:text-gray-300">Termos</a>
<a href="#" class="hover:text-gray-300">Privacidade</a>
<a href="#" class="hover:text-gray-300">Contato</a>
</div>
</div>
</footer>
</body>
</html>
-12
View File
@@ -1,12 +0,0 @@
#!/bin/sh
set -e
# Generate Prisma client
echo "Generating Prisma client..."
npx prisma generate
# Build
echo "Building..."
npm run build
echo "Build complete!"
-1
View File
@@ -1 +0,0 @@
# App-specific components (placeholder)
-12
View File
@@ -1,12 +0,0 @@
// CardApp — cartão de mini app no grid do dashboard.
// Placeholder arquitetural: ainda não utilizado ativamente.
export default function CardApp({ title, icon, description, href, onClick }) {
return (
<a href={href || "#"} onClick={onClick}
className="block rounded-xl border border-gray-200 p-4 hover:shadow-md transition">
<div className="text-2xl mb-2">{icon}</div>
<h3 className="font-semibold text-gray-900">{title}</h3>
{description && <p className="text-sm text-gray-600 mt-1">{description}</p>}
</a>
)
}
-11
View File
@@ -1,11 +0,0 @@
// CategoryCard — cartão de categoria na navegação.
// Placeholder arquitetural.
export default function CategoryCard({ label, icon, active, onSelect }) {
return (
<button onClick={() => onSelect && onSelect(label)}
className={"flex flex-col items-center p-3 rounded-lg " + (active ? "bg-blue-50 text-blue-600" : "text-gray-600")}>
<span className="text-xl">{icon}</span>
<span className="text-xs mt-1">{label}</span>
</button>
)
}
-15
View File
@@ -1,15 +0,0 @@
// ConfirmDialog — diálogo de confirmação.
// Placeholder arquitetural.
export default function ConfirmDialog({ open, title, message, onConfirm, onCancel }) {
if (!open) return null
return (
<Modal open={open}>
<h2 className="font-semibold mb-2">{title || "Confirma?"}</h2>
<p className="text-sm text-gray-600 mb-4">{message}</p>
<div className="flex justify-end space-x-2">
<button onClick={onCancel} className="px-3 py-1 text-gray-600">Cancelar</button>
<button onClick={onConfirm} className="px-3 py-1 bg-red-600 text-white rounded">Confirmar</button>
</div>
</Modal>
)
}
-5
View File
@@ -1,5 +0,0 @@
// EmptyState — estado vazio.
// Placeholder arquitetural.
export default function EmptyState({ message }) {
return <div className="p-4 text-center text-gray-400">{message || "Nada por aqui."}</div>
}
-10
View File
@@ -1,10 +0,0 @@
// ErrorState — estado de erro.
// Placeholder arquitetural.
export default function ErrorState({ message, onRetry }) {
return (
<div className="p-4 text-center text-red-600">
<p>{message || "Algo deu errado."}</p>
{onRetry && <button onClick={onRetry} className="mt-2 text-sm underline">Tentar novamente</button>}
</div>
)
}
-9
View File
@@ -1,9 +0,0 @@
// Footer — rodapé global.
// Placeholder arquitetural.
export default function Footer() {
return (
<footer className="bg-gray-900 text-white py-8 text-center text-sm">
&copy; 2026 1000Apps. Todos os direitos reservados.
</footer>
)
}
-12
View File
@@ -1,12 +0,0 @@
// Header — cabeçalho global do app.
// Placeholder arquitetural (não renderiza nada ainda).
export default function Header({ title, children }) {
return (
<header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 py-4 flex justify-between items-center">
<h1 className="text-xl font-bold">{title || "1000Apps"}</h1>
{children}
</div>
</header>
)
}
-5
View File
@@ -1,5 +0,0 @@
// Loading — indicador de carregamento.
// Placeholder arquitetural.
export default function Loading({ label }) {
return <div className="p-4 text-center text-gray-500">{label || "Carregando..."}</div>
}
-10
View File
@@ -1,10 +0,0 @@
// Modal — janela modal genérica.
// Placeholder arquitetural.
export default function Modal({ open, onClose, children }) {
if (!open) return null
return (
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50" onClick={onClose}>
<div className="bg-white rounded-lg p-6" onClick={(e) => e.stopPropagation()}>{children}</div>
</div>
)
}
-9
View File
@@ -1,9 +0,0 @@
// SearchBar — barra de busca de mini apps.
// Placeholder arquitetural.
export default function SearchBar({ value, onChange, placeholder }) {
return (
<input type="search" value={value || ""} onChange={(e) => onChange && onChange(e.target.value)}
placeholder={placeholder || "Buscar..."}
className="w-full p-2 border border-gray-300 rounded-lg" />
)
}
-11
View File
@@ -1,11 +0,0 @@
// Barrel export dos componentes compartilhados.
export { default as CardApp } from "./CardApp"
export { default as CategoryCard } from "./CategoryCard"
export { default as SearchBar } from "./SearchBar"
export { default as Header } from "./Header"
export { default as Footer } from "./Footer"
export { default as Loading } from "./Loading"
export { default as EmptyState } from "./EmptyState"
export { default as ErrorState } from "./ErrorState"
export { default as Modal } from "./Modal"
export { default as ConfirmDialog } from "./ConfirmDialog"
@@ -1,48 +0,0 @@
import { useState, useEffect } from 'react'
const AVATARS = [
'😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇',
'🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚',
'😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🤩',
'🥳', '😏', '😒', '😞', '😔', '😟', '😕', '😣', '😖', '😫',
'😩', '🥺', '😢', '😭', '😤', '😠', '😡', '🤬', '🤯', '😳',
'🥵', '🥶', '😱', '😨', '😰', '😥', '😢', '🤔', '🤫', '🤭',
'🤗', '🤑', '🤠', '🤢', '🤮', '🤧', '🥵', '🥶', '👍', '👎'
]
export default function AvatarSelector({ selectedAvatar, onSelect }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="text-4xl hover:scale-110 transition-transform"
title="Selecionar avatar"
>
{selectedAvatar || '👤'}
</button>
{isOpen && (
<div className="absolute top-12 right-0 bg-white border rounded-lg shadow-lg p-3 z-50 w-64">
<div className="grid grid-cols-10 gap-2 max-h-48 overflow-y-auto">
{AVATARS.map((avatar, index) => (
<button
key={index}
onClick={() => {
onSelect(avatar)
setIsOpen(false)
}}
className={`text-xl hover:scale-125 transition-transform ${
selectedAvatar === avatar ? 'ring-2 ring-blue-500 rounded' : ''
}`}
>
{avatar}
</button>
))}
</div>
</div>
)}
</div>
)
}
-33
View File
@@ -1,33 +0,0 @@
import { useState } from 'react'
const CATEGORIES = [
{ id: 'all', name: 'Todos', icon: '📱' },
{ id: 'produtividade', name: 'Produtividade', icon: '⚡' },
{ id: 'utilidades', name: 'Utilidades', icon: '🔧' },
{ id: 'games', name: 'Games', icon: '🎮' },
{ id: 'ferramentas', name: 'Ferramentas', icon: '🛠️' },
{ id: 'educacao', name: 'Educação', icon: '📚' },
{ id: 'entretenimento', name: 'Entretenimento', icon: '🎬' },
{ id: 'esportes', name: 'Esportes', icon: '⚽' }
]
export default function CategoryMenu({ selectedCategory, onSelect }) {
return (
<div className="flex flex-wrap gap-2 mb-6">
{CATEGORIES.map((category) => (
<button
key={category.id}
onClick={() => onSelect(category.id)}
className={`px-4 py-2 rounded-lg font-medium transition-all flex items-center gap-2 ${
selectedCategory === category.id
? 'bg-blue-600 text-white shadow-md'
: 'bg-white text-gray-700 hover:bg-gray-100'
}`}
>
<span>{category.icon}</span>
<span>{category.name}</span>
</button>
))}
</div>
)
}
-1
View File
@@ -1 +0,0 @@
# Layout components (placeholder)
-27
View File
@@ -1,27 +0,0 @@
# Legacy / Backup
Esta pasta contém código **preservado** da fase de autenticação e da Landing Page.
**Nenhum arquivo aqui participa do build do Next.js** (estão fora de `pages/`).
Mantido para reativação futura sem perda de histórico (conforme decisão de 2026-07-11).
## Estrutura
- `legacy/auth/` → telas de login/cadastro e configuração NextAuth
- `legacy/landing/` → Landing Page original (era a rota `/`)
- `legacy/dashboard.jsx.bak` → cópia de `pages/dashboard.jsx` antes da remoção do `useSession`
## Dependências necessárias para reativar o auth
Os arquivos em `legacy/auth/` importam:
- `next-auth` ^4.24.0
- `@next-auth/prisma-adapter` ^1.0.7
- `bcryptjs` ^2.4.3
- `@prisma/client` (já usado pelo app ativo)
> Estas dependências permanecem em `package.json` **exatamente para não quebrar este backup**.
> Nenhuma rota ativa (fora de `legacy/`) as utiliza.
## Como reativar
1. Mover `legacy/auth/*` de volta para `pages/` (ex.: `signin.jsx`, `signup.jsx`, `api/auth/[...nextauth].js`).
2. Mover `legacy/landing/index.jsx` para `pages/index.jsx` (ou `pages/landing.jsx` + ajustar redirect).
3. Restaurar `SessionProvider` em `pages/_app.jsx` e imports de `useSession` onde necessário.
4. `npm install` (garante que as deps acima estão presentes) e `npm run build`.
-9
View File
@@ -1,9 +0,0 @@
# Auth Legacy
Arquivos preservados do fluxo de login (NextAuth):
- `signin.jsx` — tela de login; usa `signIn` de `next-auth/react`
- `signup.jsx` — tela de cadastro; faz POST para `/api/auth/register`
- `nextauth.js` — configuração do NextAuth (CredentialsProvider + Prisma + bcrypt)
Nenhum destes é compilado pelo Next.js enquanto estiver fora de `pages/`.
-62
View File
@@ -1,62 +0,0 @@
// API DE AUTENTICAÇÃO DESATIVADA TEMPORARIAMENTE (2026-07-11)
// Arquivo PRESERVADO para reativação futura. Nenhum componente/chamada
// ativa depende desta rota após a remoção do fluxo de login.
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import { PrismaClient } from "@prisma/client"
import bcrypt from "bcryptjs"
const prisma = new PrismaClient()
export default NextAuth({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
if (!credentials?.username || !credentials?.password) return null
const user = await prisma.user.findUnique({
where: { username: credentials.username }
})
if (!user) return null
const isValid = await bcrypt.compare(credentials.password, user.password)
if (!isValid) return null
return {
id: user.id,
username: user.username,
email: user.email,
name: user.name,
// optional: role, image, etc.
}
}
})
],
secret: process.env.NEXTAUTH_SECRET,
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60 // 30 days
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
token.username = user.username
token.email = user.email
token.name = user.name
}
return token
},
async session({ session, token }) {
if (token) {
session.user.id = token.id
session.user.username = token.username
session.user.email = token.email
session.user.name = token.name
}
return session
}
}
})
-87
View File
@@ -1,87 +0,0 @@
// LOGIN DESATIVADO TEMPORARIAMENTE (2026-07-11)
// Arquivo PRESERVADO para reativação futura. Nada neste app o referencia
// ativamente (sem links, sem SessionProvider, sem getSession).
import { useState } from "react"
import { useRouter } from "next/router"
import { signIn } from "next-auth/react"
export default function SignIn() {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
const router = useRouter()
const handleSubmit = async (e) => {
e.preventDefault()
setError("")
setLoading(true)
try {
const result = await signIn("credentials", {
username,
password,
redirect: false,
callbackUrl: "/dashboard"
})
if (result?.ok) {
router.push("/dashboard")
router.refresh()
} else {
setError(result?.error || "Erro ao entrar")
}
} catch (err) {
setError("Erro de conexão")
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl font-bold mb-6">Entrar - 1000Apps</h2>
{error && (
<div className="bg-red-100 text-red-700 p-3 rounded mb-4">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="text"
placeholder="Nome de usuário"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full p-2 border rounded"
required
/>
<input
type="password"
placeholder="Senha"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 border rounded"
required
/>
<button
type="submit"
disabled={loading}
className="w-full bg-blue-600 text-white py-2 rounded-lg disabled:opacity-50"
>
{loading ? "Entrando..." : "Entrar"}
</button>
</form>
<p className="text-center mt-4 text-sm">
Não tem conta?{" "}
<a href="/signup" className="text-blue-600 hover:underline">
Cadastrar
</a>
</p>
</div>
</div>
)
}
-104
View File
@@ -1,104 +0,0 @@
// CADASTRO DESATIVADO TEMPORARIAMENTE (2026-07-11)
// Arquivo PRESERVADO para reativação futura. Não há mais link ativo para cá.
import { useState } from "react"
import { useRouter } from "next/router"
export default function SignUp() {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState("")
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
const router = useRouter()
const handleSubmit = async (e) => {
e.preventDefault()
setError("")
setLoading(true)
try {
if (password !== confirmPassword) {
setError("Senhas não coincidem")
return
}
if (password.length < 6) {
setError("Senha deve ter pelo menos 6 caracteres")
return
}
const res = await fetch("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password })
})
const data = await res.json()
if (res.ok) {
router.push("/signin")
} else {
setError(data.error || "Erro ao cadastrar")
}
} catch (err) {
setError("Erro de conexão")
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl font-bold mb-6">Cadastrar - 1000Apps</h2>
{error && (
<div className="mb-4 bg-red-100 text-red-700 p-3 rounded">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="text"
placeholder="Nome de usuário"
className="w-full p-2 border rounded"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<input
type="password"
placeholder="Senha"
className="w-full p-2 border rounded"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={6}
/>
<input
type="password"
placeholder="Confirmar senha"
className="w-full p-2 border rounded"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
<button
type="submit"
disabled={loading}
className="w-full bg-green-600 text-white py-2 rounded-lg disabled:opacity-50"
>
{loading ? "Cadastrando..." : "Cadastrar"}
</button>
</form>
<p className="text-center mt-4 text-sm">
tem conta?{" "}
<a href="/signin" className="text-blue-600 hover:underline">
Entrar
</a>
</p>
</div>
</div>
)
}
-175
View File
@@ -1,175 +0,0 @@
import { useSession, signOut } from "next-auth/react"
import { useRouter } from "next/router"
import { useState, useEffect } from "react"
import AvatarSelector from "../components/AvatarSelector"
import CategoryMenu from "../components/CategoryMenu"
// Apps organizados por categoria
const ALL_APPS = [
// Produtividade
{ id: 1, name: "Tendências Semanais", desc: "Notícias em alta • Top 100", path: "/apps/trendsnews", icon: "📈", category: "produtividade", favorite: false },
{ id: 2, name: "Conversor", desc: "Conversão de unidades", path: "/apps/converter", icon: "🔄", category: "produtividade", favorite: false },
{ id: 3, name: "Calculadora", desc: "Calculos rápidos", path: "/apps/calculator", icon: "🧮", category: "produtividade", favorite: false },
// Utilidades
{ id: 4, name: "Relógio", desc: "Relógio mundial", path: "/apps/clock", icon: "⏰", category: "utilidades", favorite: false },
{ id: 5, name: "Gerador Senhas", desc: "Senhas seguras", path: "/apps/password-generator", icon: "🔐", category: "utilidades", favorite: false },
{ id: 6, name: "QR Code", desc: "Gerador QR", path: "/apps/qrcode", icon: "📱", category: "utilidades", favorite: false },
{ id: 7, name: "Cronômetro", desc: "Tempo e alarmes", path: "/apps/timer", icon: "⏱️", category: "utilidades", favorite: false },
// Games
{ id: 8, name: "Jogo da Velha", desc: "Jogo da velha clássico 2 jogadores", path: "/apps/tictactoe", icon: "⭕", category: "games", favorite: false },
{ id: 14, name: "Expedição Gold", path: "/apps/expedicao", icon: "🗺️", category: "games", desc: "Expedições para conquistar ouro" },
{ id: 15, name: "Human City", path: "/apps/humancity", icon: "🏙️", category: "games", desc: "Simulação de cidade estilo Sims" },
{ id: 16, name: "Aurea", path: "/apps/aurea", icon: "👑", category: "games", desc: "Simulador de vida e riqueza" },
{ id: 17, name: "TerraVerse", path: "/apps/terraverse", icon: "🌍", category: "games", desc: "Game de realidade aumentada" },
// Ferramentas
{ id: 9, name: "Editor Texto", desc: "Texto simples", path: "/apps/text-editor", icon: "📝", category: "ferramentas", favorite: false },
{ id: 10, name: "Conversor Moedas", desc: "Câmbio em tempo real", path: "/apps/currency", icon: "💱", category: "ferramentas", favorite: false },
// Educação
{ id: 11, name: "Dicionário", desc: "Significados", path: "/apps/dictionary", icon: "📖", category: "educacao", favorite: false },
{ id: 12, name: "Tradutor", desc: "Multi-idiomas", path: "/apps/translator", icon: "🌐", category: "educacao", favorite: false },
// Esportes
{ id: 13, name: "Copa do Mundo 2026", desc: "Tabela, grupos, notícias", path: "/apps/worldcup2026", icon: "⚽", category: "esportes", favorite: false }
]
export default function Dashboard() {
const { data: session } = useSession()
const router = useRouter()
const [selectedAvatar, setSelectedAvatar] = useState("😀")
const [selectedCategory, setSelectedCategory] = useState("all")
const [favorites, setFavorites] = useState([])
const [apps, setApps] = useState(ALL_APPS)
// Carregar favoritos do localStorage
useEffect(() => {
const savedFavorites = localStorage.getItem("favorites")
const savedAvatar = localStorage.getItem("selectedAvatar")
if (savedFavorites) {
setFavorites(JSON.parse(savedFavorites))
}
if (savedAvatar) {
setSelectedAvatar(savedAvatar)
}
}, [])
// Salvar favoritos no localStorage
useEffect(() => {
localStorage.setItem("favorites", JSON.stringify(favorites))
}, [favorites])
// Salvar avatar no localStorage
useEffect(() => {
localStorage.setItem("selectedAvatar", selectedAvatar)
}, [selectedAvatar])
const toggleFavorite = (appId) => {
setFavorites(prev =>
prev.includes(appId)
? prev.filter(id => id !== appId)
: [...prev, appId]
)
}
// Filtrar apps
const filteredApps = selectedCategory === "all"
? apps
: apps.filter(app => app.category === selectedCategory)
// Apps favoritos
const favoriteApps = apps.filter(app => favorites.includes(app.id))
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<nav className="bg-white shadow-lg p-4 flex justify-between items-center">
<div className="flex items-center gap-4">
<h1 className="text-2xl font-bold">1000Apps</h1>
<span className="text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full font-semibold">FREE</span>
</div>
<div className="flex items-center gap-4">
<AvatarSelector selectedAvatar={selectedAvatar} onSelect={setSelectedAvatar} />
<span className="text-gray-600">Olá, {session?.user?.name || session?.user?.username || "usuário"}!</span>
<button onClick={() => signOut()} className="text-red-600 hover:underline font-medium">Sair</button>
</div>
</nav>
<main className="max-w-6xl mx-auto p-6">
{/* Favoritos Section */}
{favorites.length > 0 && (
<div className="mb-8">
<h2 className="text-2xl font-bold text-gray-800 mb-4 flex items-center gap-2">
<span>⭐</span> Favoritos
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{favoriteApps.map(app => (
<button
key={app.id}
onClick={() => router.push(app.path)}
className="bg-white rounded-xl p-6 shadow-md hover:shadow-lg transition-all text-left hover:scale-105 cursor-pointer border-2 border-yellow-200"
>
<div className="text-3xl mb-2">{app.icon}</div>
<h3 className="font-semibold text-gray-800 text-sm">{app.name}</h3>
<p className="text-xs text-gray-500 mt-1">{app.desc}</p>
</button>
))}
</div>
</div>
)}
{/* Category Filter */}
<CategoryMenu selectedCategory={selectedCategory} onSelect={setSelectedCategory} />
{/* Apps Grid */}
<div className="mb-6">
<h2 className="text-3xl font-bold text-gray-800 mb-2">
{selectedCategory === "all" ? "Todos os Apps" :
selectedCategory === "produtividade" ? "Produtividade" :
selectedCategory === "utilidades" ? "Utilidades" :
selectedCategory === "games" ? "Games" :
selectedCategory === "ferramentas" ? "Ferramentas" :
selectedCategory === "educacao" ? "Educação" :
selectedCategory === "entretenimento" ? "Entretenimento" :
selectedCategory === "esportes" ? "Esportes" : "Apps"}
</h2>
<p className="text-gray-500">
{selectedCategory === "all"
? `${apps.length} apps disponíveis`
: `${filteredApps.length} apps nesta categoria`}
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{filteredApps.map(app => (
<div key={app.id} className="relative">
<button
onClick={() => router.push(app.path)}
className="bg-white rounded-xl p-6 shadow-md hover:shadow-lg transition-all text-left hover:scale-105 cursor-pointer w-full"
>
<div className="text-3xl mb-2">{app.icon}</div>
<h3 className="font-semibold text-gray-800 text-sm">{app.name}</h3>
<p className="text-xs text-gray-500 mt-1">{app.desc}</p>
</button>
<button
onClick={() => toggleFavorite(app.id)}
className="absolute top-2 right-2 text-lg"
title={favorites.includes(app.id) ? "Remover dos favoritos" : "Adicionar aos favoritos"}
>
{favorites.includes(app.id) ? "⭐" : "☆"}
</button>
</div>
))}
</div>
</main>
</div>
)
}
export async function getServerSideProps() {
return {
props: {},
}
}
-7
View File
@@ -1,7 +0,0 @@
# Landing Legacy
- `index.jsx` — Landing Page original (era a rota `/` antes da remoção do login).
- Estilos inline + classes em `styles/globals.css` (ex.: `card-hover`).
- Não participa do build enquanto estiver fora de `pages/`.
Para reativar: mover para `pages/index.jsx` (e remover o redirect atual em `pages/index.jsx`).
-143
View File
@@ -1,143 +0,0 @@
// LANDING PAGE — DESATIVADA TEMPORARIAMENTE (2026-07-11)
// Motivo: remoção do fluxo de login; app agora abre direto no dashboard.
// Arquivo PRESERVADO para reativação futura. Não foi apagado componente,
// asset ou estilo. Para reativar: renomear para pages/index.jsx.
// Updated: force rebuild for cache invalidation
export default function Home() {
return (
<div className="bg-gray-50 min-h-screen">
{/* Header */}
<header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
<h1 className="text-2xl font-bold text-gray-900">1000Apps</h1>
<div className="space-x-4">
<a href="/signin" className="text-gray-600 hover:text-gray-900 font-medium">Entrar</a>
<a href="/signup" className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium hover:bg-blue-700 transition">Cadastrar</a>
</div>
</div>
</header>
{/* Hero Section */}
<section className="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-20">
<div className="max-w-4xl mx-auto px-4 text-center">
<h2 className="text-5xl font-bold mb-6">Seu Canivete Suíço Digital</h2>
<p className="text-xl mb-8 opacity-90">
Mais de 1000 ferramentas úteis, games e produtividade - tudo online, sem instalar nada!
</p>
<div className="flex justify-center space-x-4">
<a href="/signup" className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold text-lg hover:bg-gray-100 transition">Começar Grátis</a>
<a href="#apps" className="border border-white text-white px-8 py-3 rounded-lg font-semibold text-lg hover:bg-white/10 transition">Explorar Apps</a>
</div>
</div>
</section>
{/* Categories */}
<section id="apps" className="py-16 px-4">
<div className="max-w-6xl mx-auto">
<h3 className="text-3xl font-bold text-center mb-12 text-gray-800">Categorias</h3>
<div className="grid md:grid-cols-3 gap-8">
<div className="bg-white rounded-xl p-8 card-hover transition transform hover:-translate-y-1">
<div className="w-14 h-14 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
</svg>
</div>
<h4 className="text-xl font-semibold mb-2">Produtividade</h4>
<p className="text-gray-600 mb-4">Ferramentas para otimizar seu tempo e trabalho</p>
<span className="text-blue-600 font-medium">Ver apps </span>
</div>
<div className="bg-white rounded-xl p-8 card-hover transition transform hover:-translate-y-1">
<div className="w-14 h-14 bg-green-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 4a2 2 0 114 0v5a2 2 0 01-2 2H7a2 2 0 00-2-2V6a2 2 0 012-2h2z" />
</svg>
</div>
<h4 className="text-xl font-semibold mb-2">Utilidades</h4>
<p className="text-gray-600 mb-4">Ferramentas práticas para o dia a dia</p>
<span className="text-green-600 font-medium">Ver apps </span>
</div>
<div className="bg-white rounded-xl p-8 card-hover transition transform hover:-translate-y-1">
<div className="w-14 h-14 bg-purple-100 rounded-lg flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14.752 12.795a7.95 7.95 0 001.904-5.452 7.95 7.95 0 00-7.95 7.95A7.95 7.95 0 0012.795 14.752" />
</svg>
</div>
<h4 className="text-xl font-semibold mb-2">Games</h4>
<p className="text-gray-600 mb-4">Diversão rápida e envolvente</p>
<span className="text-purple-600 font-medium">Ver apps </span>
</div>
</div>
</div>
</section>
{/* Features */}
<section className="bg-gray-100 py-16 px-4">
<div className="max-w-6xl mx-auto">
<h3 className="text-3xl font-bold text-center mb-12 text-gray-800">Por que 1000Apps?</h3>
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="text-center">
<div className="w-16 h-16 bg-blue-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl font-bold text-white">01</span>
</div>
<h5 className="font-semibold mb-2">Nada para Instalar</h5>
<p className="text-sm text-gray-600">Tudo rodando direto no navegador</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-green-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl font-bold text-white">02</span>
</div>
<h5 className="font-semibold mb-2">Sempre Atualizado</h5>
<p className="text-sm text-gray-600">Conteúdo fresco todo dia</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-purple-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl font-bold text-white">03</span>
</div>
<h5 className="font-semibold mb-2">Curadoria Inteligente</h5>
<p className="text-sm text-gray-600"> apps úteis e testados</p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-orange-600 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl font-bold text-white">04</span>
</div>
<h5 className="font-semibold mb-2">Grátis para Sempre</h5>
<p className="text-sm text-gray-600">Acesso completo sem pagar nada</p>
</div>
</div>
</div>
</section>
{/* CTA Final */}
<section className="py-20 px-4">
<div className="max-w-3xl mx-auto text-center">
<h3 className="text-4xl font-bold mb-4 text-gray-900">Pronto para começar?</h3>
<p className="text-lg text-gray-600 mb-8">
Junte-se a milhares de usuários que economizam tempo com 1000Apps
</p>
<a href="/signup" className="inline-block bg-blue-600 text-white px-10 py-4 rounded-lg font-semibold text-xl hover:bg-blue-700 transition transform hover:scale-105">
Criar Conta Grátis
</a>
</div>
</section>
{/* Footer */}
<footer className="bg-gray-900 text-white py-8 px-4">
<div className="max-w-6xl mx-auto text-center">
<p className="mb-4">&copy; 2026 1000Apps. Todos os direitos reservados.</p>
<div className="space-x-6 text-sm">
<a href="#" className="hover:text-gray-300">Termos</a>
<a href="#" className="hover:text-gray-300">Privacidade</a>
<a href="#" className="hover:text-gray-300">Contato</a>
</div>
</div>
</footer>
</div>
)
}
-7
View File
@@ -1,7 +0,0 @@
import { PrismaClient } from '@prisma/client'
const prisma = global.prisma || new PrismaClient()
if (process.env.NODE_ENV !== 'production') global.prisma = prisma
export default prisma
-4
View File
@@ -1,4 +0,0 @@
# Módulo: bloco_notas
Estrutura preparada para o mini app 'bloco_notas'.
Nenhuma funcionalidade implementada ainda.
@@ -1 +0,0 @@
# bloco_notas/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# bloco_notas/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# bloco_notas/pages (placeholder)
@@ -1 +0,0 @@
# bloco_notas/services (placeholder)
-4
View File
@@ -1,4 +0,0 @@
# Módulo: calculadora
Estrutura preparada para o mini app 'calculadora'.
Nenhuma funcionalidade implementada ainda.
@@ -1 +0,0 @@
# calculadora/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# calculadora/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# calculadora/pages (placeholder)
@@ -1 +0,0 @@
# calculadora/services (placeholder)
-4
View File
@@ -1,4 +0,0 @@
# Módulo: conversor
Estrutura preparada para o mini app 'conversor'.
Nenhuma funcionalidade implementada ainda.
@@ -1 +0,0 @@
# conversor/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# conversor/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# conversor/pages (placeholder)
-1
View File
@@ -1 +0,0 @@
# conversor/services (placeholder)
-4
View File
@@ -1,4 +0,0 @@
# Módulo: criptomoedas
Estrutura preparada para o mini app 'criptomoedas'.
Nenhuma funcionalidade implementada ainda.
@@ -1 +0,0 @@
# criptomoedas/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# criptomoedas/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# criptomoedas/pages (placeholder)
@@ -1 +0,0 @@
# criptomoedas/services (placeholder)
-4
View File
@@ -1,4 +0,0 @@
# Módulo: jogos
Estrutura preparada para o mini app 'jogos'.
Nenhuma funcionalidade implementada ainda.
-1
View File
@@ -1 +0,0 @@
# jogos/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# jogos/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# jogos/pages (placeholder)
-1
View File
@@ -1 +0,0 @@
# jogos/services (placeholder)
-4
View File
@@ -1,4 +0,0 @@
# Módulo: noticias
Estrutura preparada para o mini app 'noticias'.
Nenhuma funcionalidade implementada ainda.
-1
View File
@@ -1 +0,0 @@
# noticias/components (placeholder)
-1
View File
@@ -1 +0,0 @@
# noticias/hooks (placeholder)
-1
View File
@@ -1 +0,0 @@
# noticias/pages (placeholder)
-1
View File
@@ -1 +0,0 @@
# noticias/services (placeholder)
-12
View File
@@ -1,12 +0,0 @@
// Serviço de API central.
// Placeholder arquitetural — ainda não implementado.
const API_BASE = process.env.NEXT_PUBLIC_API_BASE || "/api"
export async function get(path) {
// TODO: implementar wrapper fetch com auth/erros
throw new Error("api.get not implemented")
}
export async function post(path, body) {
throw new Error("api.post not implemented")
}
export default { API_BASE, get, post }
-6
View File
@@ -1,6 +0,0 @@
// Aggregador de provedores de auth (preparação).
export { PROVIDERS, AuthProvider } from "./interfaces"
export { googleProvider } from "./providers/google"
export { appleProvider } from "./providers/apple"
export { emailProvider } from "./providers/email"
export { anonymousProvider } from "./providers/anonymous"
-16
View File
@@ -1,16 +0,0 @@
// Interfaces de provedores de autenticação (preparação, sem SDK).
// Implementação real ocorrerá em fase futura.
export const AuthProvider = {
// Cada provedor deve implementar:
// login(): Promise<{ user, token }>
// logout(): Promise<void>
// getSession(): Promise<Session | null>
}
export const PROVIDERS = {
GOOGLE: "google",
APPLE: "apple",
EMAIL: "email",
ANONYMOUS: "anonymous",
}
-6
View File
@@ -1,6 +0,0 @@
// Login Anônimo — placeholder arquitetural.
export const anonymousProvider = {
id: "anonymous",
async login() { throw new Error("Anonymous login not implemented") },
async logout() { throw new Error("Anonymous logout not implemented") },
}
-6
View File
@@ -1,6 +0,0 @@
// Apple Login — placeholder arquitetural (sem SDK instalado).
export const appleProvider = {
id: "apple",
async login() { throw new Error("Apple login not implemented") },
async logout() { throw new Error("Apple logout not implemented") },
}
-6
View File
@@ -1,6 +0,0 @@
// Email/Senha — placeholder arquitetural (sem backend de auth ativo).
export const emailProvider = {
id: "email",
async login({ username, password }) { throw new Error("Email login not implemented") },
async logout() { throw new Error("Email logout not implemented") },
}
-6
View File
@@ -1,6 +0,0 @@
// Google Login — placeholder arquitetural (sem SDK instalado).
export const googleProvider = {
id: "google",
async login() { throw new Error("Google login not implemented") },
async logout() { throw new Error("Google logout not implemented") },
}
-17
View File
@@ -1,17 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom animations */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-pulse {
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.delay-1000 {
animation-delay: 1s;
}
-11
View File
@@ -1,11 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
theme: {
extend: {},
},
plugins: [],
}