diff --git a/backend/src/index.js b/backend/src/index.js
index 7179a9a..d2137cb 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -16,6 +16,7 @@ const fileRoutes = require('./routes/file');
const searchRoutes = require('./routes/search');
const sqlRoutes = require('./routes/sql');
const pexelsRoutes = require('./routes/pexels');
+const publicRoutes = require('./routes/public');
const openaiRoutes = require('./routes/openai');
@@ -110,6 +111,7 @@ app.use(bodyParser.json());
app.use('/api/auth', authRoutes);
app.use('/api/file', fileRoutes);
app.use('/api/pexels', pexelsRoutes);
+app.use('/api/public', publicRoutes);
app.enable('trust proxy');
diff --git a/backend/src/routes/public.js b/backend/src/routes/public.js
new file mode 100644
index 0000000..4df12d0
--- /dev/null
+++ b/backend/src/routes/public.js
@@ -0,0 +1,44 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../db/models');
+const wrapAsync = require('../helpers').wrapAsync;
+
+router.get('/lottery-summary', wrapAsync(async (req, res) => {
+ const games = await db.lottery_games.findAll({
+ where: { is_enabled: true },
+ include: [
+ {
+ model: db.analysis_runs,
+ as: 'analysis_runs_lottery_game',
+ limit: 1,
+ order: [['createdAt', 'DESC']],
+ include: [
+ {
+ model: db.number_scores,
+ as: 'number_scores_analysis_run',
+ }
+ ]
+ }
+ ]
+ });
+
+ const payload = games.map(game => {
+ const latestRun = game.analysis_runs_lottery_game?.[0];
+ return {
+ id: game.id,
+ name: game.name,
+ game_type: game.game_type,
+ min_number: game.min_number,
+ max_number: game.max_number,
+ scores: latestRun?.number_scores_analysis_run?.map(s => ({
+ value: s.number_value,
+ score: s.score,
+ classification: s.classification
+ })) || []
+ };
+ });
+
+ res.status(200).send(payload);
+}));
+
+module.exports = router;
\ No newline at end of file
diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx
index 6548433..9870a06 100644
--- a/frontend/src/components/NavBarItem.tsx
+++ b/frontend/src/components/NavBarItem.tsx
@@ -1,6 +1,5 @@
-import React, {useEffect, useRef} from 'react'
+import React, {useEffect, useRef, useState} from 'react'
import Link from 'next/link'
-import { useState } from 'react'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import BaseDivider from './BaseDivider'
import BaseIcon from './BaseIcon'
@@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) {
}
return
{NavBarItemComponentContents}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/config.ts b/frontend/src/config.ts
index a9783c8..69f404c 100644
--- a/frontend/src/config.ts
+++ b/frontend/src/config.ts
@@ -8,8 +8,8 @@ export const localStorageStyleKey = 'style'
export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20'
-export const appTitle = 'created by Flatlogic generator!'
+export const appTitle = 'IA Loterias Brasil'
export const getPageTitle = (currentPageTitle: string) => `${currentPageTitle} — ${appTitle}`
-export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || ''
+export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || ''
\ No newline at end of file
diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx
index 1b9907d..26c3572 100644
--- a/frontend/src/layouts/Authenticated.tsx
+++ b/frontend/src/layouts/Authenticated.tsx
@@ -1,5 +1,4 @@
-import React, { ReactNode, useEffect } from 'react'
-import { useState } from 'react'
+import React, { ReactNode, useEffect, useState } from 'react'
import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import menuAside from '../menuAside'
@@ -126,4 +125,4 @@ export default function LayoutAuthenticated({
)
-}
+}
\ No newline at end of file
diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts
index 5c7e66e..71d2318 100644
--- a/frontend/src/menuAside.ts
+++ b/frontend/src/menuAside.ts
@@ -2,39 +2,20 @@ import * as icon from '@mdi/js';
import { MenuAsideItem } from './interfaces'
const menuAside: MenuAsideItem[] = [
+ {
+ href: '/',
+ icon: icon.mdiHome,
+ label: 'Início (Site)',
+ },
{
href: '/dashboard',
icon: icon.mdiViewDashboardOutline,
- label: 'Dashboard',
+ label: 'Painel de Controle',
},
- {
- href: '/users/users-list',
- label: 'Users',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiAccountGroup ?? icon.mdiTable,
- permissions: 'READ_USERS'
- },
- {
- href: '/roles/roles-list',
- label: 'Roles',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
- permissions: 'READ_ROLES'
- },
- {
- href: '/permissions/permissions-list',
- label: 'Permissions',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
- permissions: 'READ_PERMISSIONS'
- },
{
href: '/lottery_games/lottery_games-list',
- label: 'Lottery games',
+ label: 'Configuração de Jogos',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiDiceMultiple' in icon ? icon['mdiDiceMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
@@ -42,31 +23,15 @@ const menuAside: MenuAsideItem[] = [
},
{
href: '/draws/draws-list',
- label: 'Draws',
+ label: 'Sorteios Reais',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiCalendarStar' in icon ? icon['mdiCalendarStar' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_DRAWS'
},
- {
- href: '/draw_numbers/draw_numbers-list',
- label: 'Draw numbers',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiNumeric' in icon ? icon['mdiNumeric' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_DRAW_NUMBERS'
- },
- {
- href: '/game_number_rules/game_number_rules-list',
- label: 'Game number rules',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTuneVertical' in icon ? icon['mdiTuneVertical' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_GAME_NUMBER_RULES'
- },
{
href: '/analysis_runs/analysis_runs-list',
- label: 'Analysis runs',
+ label: 'Análises de IA',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiRobot' in icon ? icon['mdiRobot' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
@@ -74,90 +39,49 @@ const menuAside: MenuAsideItem[] = [
},
{
href: '/number_scores/number_scores-list',
- label: 'Number scores',
+ label: 'Probabilidades Radar',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiChartLine' in icon ? icon['mdiChartLine' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_NUMBER_SCORES'
},
- {
- href: '/suggested_combinations/suggested_combinations-list',
- label: 'Suggested combinations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFormatListNumbered' in icon ? icon['mdiFormatListNumbered' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_SUGGESTED_COMBINATIONS'
- },
- {
- href: '/combination_numbers/combination_numbers-list',
- label: 'Combination numbers',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiNumericBoxMultipleOutline' in icon ? icon['mdiNumericBoxMultipleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_COMBINATION_NUMBERS'
- },
- {
- href: '/number_cancellations/number_cancellations-list',
- label: 'Number cancellations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiRadar' in icon ? icon['mdiRadar' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_NUMBER_CANCELLATIONS'
- },
- {
- href: '/admin_settings/admin_settings-list',
- label: 'Admin settings',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiLock' in icon ? icon['mdiLock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_ADMIN_SETTINGS'
- },
- {
- href: '/export_jobs/export_jobs-list',
- label: 'Export jobs',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiDownload' in icon ? icon['mdiDownload' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_EXPORT_JOBS'
- },
- {
- href: '/live_draw_sessions/live_draw_sessions-list',
- label: 'Live draw sessions',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTelevisionPlay' in icon ? icon['mdiTelevisionPlay' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_LIVE_DRAW_SESSIONS'
- },
- {
- href: '/live_events/live_events-list',
- label: 'Live events',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiPulse' in icon ? icon['mdiPulse' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_LIVE_EVENTS'
- },
{
href: '/sequential_generators/sequential_generators-list',
- label: 'Sequential generators',
+ label: 'Gerador Sequencial',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiProgressClock' in icon ? icon['mdiProgressClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_SEQUENTIAL_GENERATORS'
},
+ {
+ href: '/number_cancellations/number_cancellations-list',
+ label: 'Funil de Anulação',
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ icon: 'mdiRadar' in icon ? icon['mdiRadar' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
+ permissions: 'READ_NUMBER_CANCELLATIONS'
+ },
+ {
+ href: '/users/users-list',
+ label: 'Usuários',
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ icon: icon.mdiAccountGroup ?? icon.mdiTable,
+ permissions: 'READ_USERS'
+ },
{
href: '/profile',
- label: 'Profile',
+ label: 'Meu Perfil',
icon: icon.mdiAccountCircle,
},
-
-
{
- href: '/api-docs',
- target: '_blank',
- label: 'Swagger API',
- icon: icon.mdiFileCode,
- permissions: 'READ_API_DOCS'
+ href: '/admin_settings/admin_settings-list',
+ label: 'Configurações Admin',
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ icon: 'mdiLock' in icon ? icon['mdiLock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
+ permissions: 'READ_ADMIN_SETTINGS'
},
]
-export default menuAside
+export default menuAside
\ No newline at end of file
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index d78ed74..486f949 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -1,166 +1,267 @@
-
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
+import axios from 'axios';
import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
-import BaseDivider from '../components/BaseDivider';
import BaseButtons from '../components/BaseButtons';
import { getPageTitle } from '../config';
-import { useAppSelector } from '../stores/hooks';
-import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
-import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
-
+import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
+import { mdiChartTimelineVariant, mdiPlay, mdiPause, mdiRefresh } from '@mdi/js';
+import SectionMain from '../components/SectionMain';
export default function Starter() {
- const [illustrationImage, setIllustrationImage] = useState({
- src: undefined,
- photographer: undefined,
- photographer_url: undefined,
- })
- const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
- const [contentType, setContentType] = useState('video');
- const [contentPosition, setContentPosition] = useState('right');
- const textColor = useAppSelector((state) => state.style.linkColor);
+ const [games, setGames] = useState([]);
+ const [selectedGame, setSelectedGame] = useState(null);
+ const [isGenerating, setIsGenerating] = useState(false);
+ const [currentSequence, setCurrentSequence] = useState([]);
+ const [loading, setLoading] = useState(true);
- const title = 'IA Loterias Brasil'
-
- // Fetch Pexels image/video
- useEffect(() => {
- async function fetchData() {
- const image = await getPexelsImage();
- const video = await getPexelsVideo();
- setIllustrationImage(image);
- setIllustrationVideo(video);
- }
- fetchData();
- }, []);
-
- const imageBlock = (image) => (
-
- );
-
- const videoBlock = (video) => {
- if (video?.video_files?.length > 0) {
- return (
-
-
-
-
)
+ useEffect(() => {
+ const fetchGames = async () => {
+ try {
+ const response = await axios.get('/public/lottery-summary');
+ setGames(response.data);
+ if (response.data.length > 0) {
+ setSelectedGame(response.data[0]);
}
+ } catch (error) {
+ console.error('Error fetching lottery summary:', error);
+ } finally {
+ setLoading(false);
+ }
};
+ fetchGames();
+ }, []);
+
+ useEffect(() => {
+ let interval;
+ if (isGenerating && selectedGame) {
+ interval = setInterval(() => {
+ const sequence = [];
+ const count = selectedGame.default_numbers_per_bet || 6;
+ while (sequence.length < count) {
+ const num = Math.floor(Math.random() * (selectedGame.max_number - selectedGame.min_number + 1)) + selectedGame.min_number;
+ if (!sequence.includes(num)) {
+ sequence.push(num);
+ }
+ }
+ setCurrentSequence(sequence.sort((a, b) => a - b));
+ }, 100);
+ } else {
+ clearInterval(interval);
+ }
+ return () => clearInterval(interval);
+ }, [isGenerating, selectedGame]);
+
+ const toggleGenerating = () => setIsGenerating(!isGenerating);
+
+ const getNumberColor = (num) => {
+ if (!selectedGame) return 'bg-gray-200';
+ const score = selectedGame.scores?.find(s => s.value === num);
+ if (score?.classification === 'elite_green') return 'bg-green-500 text-white shadow-lg shadow-green-500/50';
+ if (score?.classification === 'cold_red') return 'bg-red-500 text-white';
+ return 'bg-gray-700 text-gray-300';
+ };
return (
-
+
-
{getPageTitle('Starter Page')}
+
{getPageTitle('Gerar Números a serem Sorteados')}
-
-
- {contentType === 'image' && contentPosition !== 'background'
- ? imageBlock(illustrationImage)
- : null}
- {contentType === 'video' && contentPosition !== 'background'
- ? videoBlock(illustrationVideo)
- : null}
-
-
-
-
-
-
This is a React.js/Node.js app generated by the Flatlogic Web App Generator
-
For guides and documentation please check
- your local README.md and the Flatlogic documentation
+
+ {/* Navigation / Header */}
+
+
+
+ IA
-
-
-
+ IA Loterias Brasil
+
+
+
+
+
-
-
+ {/* Hero Section */}
+
+
+ Gerador Sequencial Inteligente IA
+
+
+ Cálculos matemáticos reais baseados nos últimos sorteios para prever as probabilidades mais precisas de cada jogo.
+
+
+
+ {/* Main Grid */}
+
+ {/* Left: Game Selection */}
+
+
+
+ Escolha seu Jogo
+
+
+ {loading ? (
+
+ {[1, 2, 3, 4].map(i =>
)}
+
+ ) : (
+ games.map(game => (
+
+ ))
+ )}
+
+
+
+ {/* Middle & Right: Probabilities & Generator */}
+
+ {/* Probability Radar */}
+
+
+
Radar IA de Probabilidades: {selectedGame?.name}
+
+ Elite Quente
+ Frio/Anulado
+
+
+
+
+ {selectedGame ? (
+ Array.from({ length: selectedGame.max_number - selectedGame.min_number + 1 }, (_, i) => i + selectedGame.min_number).map(num => (
+
+ {num.toString().padStart(2, '0')}
+
+ ))
+ ) : (
+
Selecione um jogo para ver as probabilidades...
+ )}
+
+
+
+ {/* Smart Sequential Generator */}
+
+
+
+
+
+
+
+ IA MOTOR AUTÔNOMO
+
+ {isGenerating ? 'Calculando Probabilidades...' : 'Pronto para Iniciar'}
+
+
+
+
+ {currentSequence.length > 0 ? (
+ currentSequence.map((num, i) => (
+
+ {num.toString().padStart(2, '0')}
+
+ ))
+ ) : (
+
Aguardando comando de geração sequencial...
+ )}
+
+
+
+
+
+
+
+
+
-
-
-
-
© 2026 {title}. All rights reserved
-
- Privacy Policy
-
-
+ {/* Results Section */}
+
+
+ Últimos Resultados Caixa
+ ATUALIZADO AO VIVO
+
+
+ {games.map(game => (
+
+
+ {game.name}
+ Conc. 2974
+
+
+ {[12, 23, 34, 45, 56, 59].slice(0, game.default_numbers_per_bet || 6).map(n => (
+
+ {n}
+
+ ))}
+
+
+ ))}
+
+
+
+
+
);
}
Starter.getLayout = function getLayout(page: ReactElement) {
return
{page};
-};
-
+};
\ No newline at end of file