Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Escolha o momento e veja os lugares de Campo Grande que mais combinam com você.
Translation pending
LifestyleTypeScript · PLpgSQLGitHub @FrancoxkrsOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Lugares que combinam com você. PWA de recomendação de restaurantes por ocasião em Campo Grande (MS).
A pessoa escolhe a ocasião ("A dois, pra conversar", "Pagode e porção"...) e o tipo de comida, recebe os lugares que mais combinam, com o índice de compatibilidade e o "Por que combina com você?", e manda as 3 melhores opções para o grupo votar pelo WhatsApp, arrastando cards.
Precisa de Node 20+ e Docker (o Supabase local roda em containers).
npm install
npx supabase start # sobe Postgres, Auth, Storage e um caixa de e-mail local
cp .env.example .env.local # cole as chaves que o comando acima imprimiu
npm run dev
NEXT_PUBLIC_SUPABASE_URL = API URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY = Publishable key, SUPABASE_SECRET_KEY = Secret key.ADMIN_EMAILS para virar admin.supabase/seed.sql, fotos de teste do Unsplash) para testar. Em produção ele começa vazio e se enche pelo botão de importação do /admin.npx supabase db reset apaga tudo e recria o banco com o seed.| Quem | Onde | O que faz |
|---|---|---|
| Qualquer pessoa, sem conta | /, /o/..., /l/..., /v/... | Escolhe ocasião e comida, vê lugares, vota em link de amigo, avalia em 3 toques |
| Cliente com conta | /perfil | Listas "Quero ir" e "Já fui", selo Explorador (5 avaliações), nome |
| Dono de restaurante | /painel | Pede acesso ao lugar; depois de aprovado vê aparições por ocasião, cliques no WhatsApp e rotas, quem salvou; edita WhatsApp, Instagram, cardápio, tipo de comida e foto de capa |
| Time Kombin (admin) | /admin | Importa restaurantes do Google com um botão, revisa lugares, marca destaques, cadastra lugar à mão, aprova donos, vê cliques |
Login: e-mail e senha (com "Criar conta" e "Esqueci a senha") e, opcionalmente, Google (Supabase Auth). Admin é quem tem o e-mail em ADMIN_EMAILS ou role = 'admin' na tabela profiles.
https://kombin-seven.vercel.app) e, em Redirect URLs, adicione https://kombin-seven.vercel.app/auth/callback. Sem isso, o link de "esqueci a senha" volta para o lugar errado.https://<seu-projeto>.supabase.co/auth/v1/callback.AUTH_GOOGLE=1 e faça um Redeploy. O botão "Continuar com Google" só aparece com ela, para nunca mostrar um botão que dá erro.| Parte | Onde está |
|---|---|
| Descobrir: saudação do momento, ocasiões em carrossel, sua lista, "Escolhas para o seu momento" | src/app/page.tsx, src/components/OccasionCard.tsx, src/components/RestaurantCard.tsx |
| Filtros (momento, com quem, orçamento por pessoa, sem couvert, dá pra conversar, espaço kids, bairro) e contagem ao vivo | src/app/buscar |
Ranking por ocasião ou por comida (/o/tudo?c=pizza) + chips rápidos | src/app/o/[slug]/page.tsx, src/components/QuickPrefs.tsx |
| Tipos de comida, etiquetas de ambiente e mapeamento dos tipos do Google | src/lib/cuisines.ts, src/lib/tags.ts |
| Índice de compatibilidade (regras e pesos, sem IA em tempo real) | src/lib/match.ts, pesos em src/lib/occasions.ts |
| "Decidir com o mozão/galera": link de votação sem login, cards para arrastar, resultado ao vivo | src/app/v/[code], src/components/VoteBoard.tsx |
| Página do lugar: galeria, "Por que combina com você?", informações, sinais da galera, salvar | src/app/l/[id]/page.tsx |
| Botão "Reservar via WhatsApp" com mensagem pronta e clique rastreado | src/app/api/go/[kind]/route.ts |
| Avaliação relâmpago em 3 toques (gasto, couvert, barulho) | src/components/MicroReview.tsx, src/app/api/review/route.ts |
| Login com senha, cadastro, nova senha, perfil, painel do restaurante, admin | src/app/entrar, redefinir-senha, perfil, painel, admin |
Importação do Google pelo /admin (ou terminal), notas por regra e vibe-check opcional por IA de visão | src/app/admin/ImportPanel.tsx, src/app/admin/import-actions.ts, src/lib/ingest/ |
| Animações: transição entre telas e foto que "voa" para a página do lugar (View Transitions), abas, coração, confete | src/app/globals.css, src/components/ui/ |
| Banco | supabase/migrations/ |
Cada ocasião dá pesos aos critérios: vibe (nota por regra, da IA ou do admin para aquela ocasião), barulho, couvert, orçamento, distância, etiquetas que a ocasião pede (ex.: aniversário quer "bom pra grupos" e "aceita reserva") e tamanho do grupo. Cada critério tem um encaixe de 0 a 1 e uma confiança, que cresce com as micro-reviews:
compat = 100 × (Σ peso·confiança·encaixe + 0,5) / (Σ peso·confiança + 1)
Lugar sem dados fica em 50%. Filtros duros (tipo de comida, bairro, etiquetas marcadas, "sem couvert", orçamento muito abaixo) tiram o lugar da lista. O resultado nunca passa de 99% e sempre vem com até 3 motivos e 1 alerta. Para mudar o comportamento, mexa nos pesos em src/lib/occasions.ts. Os testes estão em tests/match.test.ts (npm test).
Três coisas deixam o índice mais esperto, todas de graça (testes em tests/context.test.ts):
src/lib/weather.ts, src/lib/context.ts): a cada 15 minutos o servidor consulta a Open-Meteo (sem chave, sem cadastro) para o centro de Campo Grande. Chovendo ou abaixo de 18 °C, lugar com a etiqueta "Coberto / climatizado" sobe e lugar só com "Área externa" desce, e a lista mostra "Chovendo agora em CG · Priorizando lugares cobertos". Em noite quente (24 °C ou mais, sem chuva) a área externa ganha um empurrão. Lugar sem nenhuma das duas etiquetas não muda. A importação marca "Coberto" quando o Google diz que o lugar não tem mesa ao ar livre; o resto se ajusta no /admin.src/components/MicroReview.tsx, src/app/api/review/route.ts): avaliação vale em dobro se a pessoa fez check-in (o navegador calcula a distância até o lugar com Haversine e manda só "sim"; a localização nunca sai do celular) ou clicou em "Como chegar" entre 30 minutos e 3 dias antes de avaliar. A view place_signals faz as médias ponderadas.src/lib/taste.ts): cada busca fica na tabela search_log (sem localização) e, junto com os lugares que a pessoa escolhe (Como chegar, WhatsApp, salvos, avaliados), vira um gosto: etiquetas, comidas, gasto típico e se prefere lugar calmo. O que foi feito há um mês vale metade. Depois de umas 3 buscas, o gosto entra como mais um critério e a lista avisa "No seu estilo".supabase/migrations/0001_init.sql, 0002_contas_paineis_culinaria.sql, 0003_tags_destaque.sql, 0004_seguranca.sql, 0005_cardapio_dono.sql e 0006_indice_contexto.sql. O editor de consultas da Vercel não serve: ele não roda vários comandos de uma vez.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY e SUPABASE_SECRET_KEY. Nos dois casos, cadastre ADMIN_EMAILS na Vercel.GOOGLE_MAPS_API_KEY na Vercel (Settings > Environment Variables), depois faça um Redeploy. Antes, crie um alerta de orçamento de US$ 1 e limite a cota diária da API./admin > Importar do Google > Começar importação. Primeiro entram, pelo nome, as casas essenciais de Campo Grande (lista ESSENTIALS em src/lib/ingest/google.ts: Enoteca Decanter, Turista Sushi, Mr. VIP...). Depois vêm 16 buscas (pizzarias, japoneses, bares com música...); entram só lugares abertos, com nota 4,2+ e 150+ avaliações, já aprovados e com notas por ocasião calculadas a partir dos atributos do Google (música ao vivo, bom pra grupos, cardápio infantil...). Dá para rodar de novo: só entram lugares novos. Lugar específico que faltou: campo "Adicionar um lugar específico" na mesma tela.GEMINI_API_KEY (ou ANTHROPIC_API_KEY). Em /admin > Importar aparece "Refinar com IA de visão": a IA olha 3 fotos de cada lugar e ajusta as notas pelo ambiente; ambiente feio volta para "Para revisar"./admin > Aprovados, confira o tipo de comida, marque os destaques (aparecem primeiro no Descobrir) e cadastre o WhatsApp de cada lugar.npm run refresh (renova as coordenadas, regra dos 30 dias do Google).Pelo terminal, a mesma importação: npm run ingest -- --dry-run (não grava, salva scripts/out/), --limit 40, --vision, --min-rating 4.3, --min-reviews 200, --provider anthropic. Precisa das chaves no .env.local (npx vercel env pull .env.local baixa as da Vercel).
| Etapa | Cota grátis | Depois |
|---|---|---|
| Importação: Text Search com atributos (Enterprise + Atmosphere) | 1.000 chamadas | US$ 40 por mil |
| Fotos exibidas no app (Place Photos) | 1.000 fotos | US$ 7 por mil |
| Vibe-check no Gemini Flash (opcional, 3 fotos por lugar) | plano gratuito (com limite por minuto) | ver preços do Google AI |
| Página do lugar: nota e horário ao vivo (Enterprise) | 1.000 | US$ 20 por mil |
Uma importação completa gasta 16 buscas. As fotos passam pelo nosso servidor (/api/photo/...) e ficam no cache da Vercel por 1 dia; a nota e o horário ao vivo, por 30 minutos. Assim cada foto é cobrada no máximo uma vez por dia, e não a cada visita. No plano gratuito do Gemini, o Google pode usar as imagens enviadas para melhorar os modelos.
place_id (permitido para sempre) e as coordenadas por até 30 dias.src/lib/validation.ts, zod) e conferir o limite de requisições (src/lib/rate-limit.ts). A chave pública vai para o navegador só para o login; a migração 0004_seguranca.sql liga RLS em todas as tabelas, libera apenas leitura do que é público ou da própria pessoa e revoga qualquer escrita pela chave pública.GOOGLE_DAILY_LIMIT, padrão 500 chamadas). Mesmo assim, coloque cota diária na Places API e alerta de orçamento no Google Cloud: é o único freio que vale fora do código.ADMIN_EMAILS e ainda não tem conta. Em produção prefira update profiles set role = 'admin' where id = (select id from auth.users where email = '[email protected]'); e deixe ADMIN_EMAILS vazio, ou mantenha a confirmação de e-mail ligada.Suba o repositório no GitHub e importe na Vercel (plano Hobby). Cadastre as mesmas variáveis do .env.local no painel da Vercel. O plano Hobby é para uso não comercial: quando começarem a cobrar restaurantes, migrem para o Pro ou para outro provedor.
Direção "Tela cheia": a foto do lugar ocupa a tela e o resto é noite.
#2A1B12, superfícies #36251A e #423024, texto #F6F0E6, secundário #B9B0A6. Um destaque só, o amarelo do ipê #FFC81E: botão principal, "% combina" e o sublinhado da aba ativa. Tokens em src/app/globals.css.title (semibold, letra apertada); o tamanho do nome do lugar vem de posterNameSize em src/lib/screen.ts.btn() e BTN_* em src/components/ui/styles.ts), foto não tem canto arredondado, botão por cima de foto é um círculo claro (ROUND_LIGHT). Sem vidro, sem sombra, sem gradiente de enfeite; o véu escuro só existe embaixo de texto em cima de foto (scrim-bottom, scrim-top).src/components/Discover.tsx), passa pra cima para o próximo; a última tela da ocasião manda os 3 primeiros pra votar ou abre a lista com filtros. O servidor já manda o ranking das 6 ocasiões, então trocar de aba é instantâneo.HIDDEN em src/components/ui/TabBar.tsx).kb_did). Conta serve para listas, selo e painéis.src/lib/catalog.ts) e é renovado na hora quando o admin ou um dono muda algo. O login só é verificado quando há cookie de sessão. Dados do Google ao vivo carregam depois da página. Cada tela tem esqueleto de carregamento.<ViewTransition>); gestos (arrastar card, folha de avaliação) usam motion. Tudo respeita "reduzir movimento" do sistema.| Comando | O que faz |
|---|---|
npm run dev | servidor de desenvolvimento |
npm test | testes do índice e da importação |
npx supabase start / npx supabase db reset | banco local / recriar com dados fictícios |
npx tsx scripts/gen-seed.ts | regenera supabase/seed.sql a partir de tests/fixtures.ts |
npm run lint / npm run typecheck | qualidade |
npm run ingest -- [--dry-run] [--limit N] [--vision] [--provider gemini|anthropic] | importação pelo terminal |
npm run refresh | renova coordenadas do Google |