diff --git a/backend/src/index.js b/backend/src/index.js index 2f8b1a4..239b3b1 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -1,4 +1,3 @@ - const express = require('express'); const cors = require('cors'); const app = express(); @@ -117,7 +116,7 @@ app.use('/api/markets', passport.authenticate('jwt', {session: false}), marketsR app.use('/api/selections', passport.authenticate('jwt', {session: false}), selectionsRoutes); -app.use('/api/tickets', passport.authenticate('jwt', {session: false}), ticketsRoutes); +app.use('/api/tickets', ticketsRoutes); app.use('/api/ticket_selections', passport.authenticate('jwt', {session: false}), ticket_selectionsRoutes); @@ -167,4 +166,4 @@ db.sequelize.sync().then(function () { }); }); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/backend/src/routes/tickets.js b/backend/src/routes/tickets.js index 2882320..ddf1337 100644 --- a/backend/src/routes/tickets.js +++ b/backend/src/routes/tickets.js @@ -1,5 +1,6 @@ -const express = require('express'); +const express = require('express'); +const passport = require('passport'); const TicketsService = require('../services/tickets'); const TicketsDBApi = require('../db/api/tickets'); const wrapAsync = require('../helpers').wrapAsync; @@ -14,8 +15,6 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); -router.use(checkCrudPermissions('tickets')); - /** * @swagger @@ -53,11 +52,16 @@ router.use(checkCrudPermissions('tickets')); * description: The Tickets managing API */ +// Public route for auto ticket generation router.post('/generate', wrapAsync(async (req, res) => { const payload = await TicketsService.generateAutoTicket(req.body.data, req.currentUser); res.status(200).send(payload); })); +// Authenticate the rest of the routes +router.use(passport.authenticate('jwt', { session: false })); +router.use(checkCrudPermissions('tickets')); + /** * @swagger * /api/tickets: @@ -443,4 +447,4 @@ router.get('/:id', wrapAsync(async (req, res) => { router.use('/', require('../helpers').commonErrorHandler); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 4362b9e..34edf78 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,166 +1,242 @@ - -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; +import { mdiFlash, mdiDiceMultiple, mdiTrophy, mdiRefresh, mdiCheckDecagram } from '@mdi/js'; 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 BaseIcon from '../components/BaseIcon'; +import SectionMain from '../components/SectionMain'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; +import { useAppDispatch } from '../stores/hooks'; +import { generateAutoTicket } from '../stores/tickets/ticketsSlice'; +export default function Home() { + const dispatch = useAppDispatch(); + const [targetOdds, setTargetOdds] = useState('2.50'); + const [gameCount, setGameCount] = useState('5'); + const [generatedTicket, setGeneratedTicket] = useState(null); + const [loading, setLoading] = useState(false); -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 title = 'App Draft' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - -
-
- ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- -
- - Video by {video.user.name} on Pexels - -
-
) - } - }; + const handleGenerate = async () => { + setLoading(true); + try { + const resultAction = await dispatch(generateAutoTicket({ desired_odds: targetOdds, game_count: gameCount })); + if (generateAutoTicket.fulfilled.match(resultAction)) { + setGeneratedTicket(resultAction.payload); + } + } catch (error) { + console.error('Failed to generate ticket:', error); + } finally { + setLoading(false); + } + }; return ( -
+
- {getPageTitle('Starter Page')} + {getPageTitle('BetMagic - Auto Ticket Generator')} - -
- {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

-
- - - - - -
+ {/* Header */} +
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
+
+ + Login + + +
+ + {/* Hero Section */} +
+
+
+
+
+ +
+
+

+ Build Your Dream Ticket in Seconds. +

+

+ Stop wasting hours searching for games. Our smart algorithm finds the best odds based on your target and builds your winning ticket instantly. +

+
+ +
+ + 10,000+ Tickets Generated Today +
+
+
+ + {/* Generator Widget */} +
+ +
+
+
+ +
+

Quick Generator

+
+ +
+
+ +
+ setTargetOdds(e.target.value)} + className="w-full bg-slate-900/50 border border-slate-700 rounded-xl px-4 py-3 text-2xl font-bold text-green-400 focus:outline-none focus:border-green-500 transition-colors" + /> + x +
+
+
+ +
+ setGameCount(e.target.value)} + className="w-full bg-slate-900/50 border border-slate-700 rounded-xl px-4 py-3 text-2xl font-bold focus:outline-none focus:border-green-500 transition-colors" + /> +
+
+
+ + + + {/* Result Area */} + {generatedTicket && ( +
+
+ Generated Selections + Total Odds: {generatedTicket.totalOdds.toFixed(2)}x +
+
+ {generatedTicket.selections.map((sel: any, idx: number) => ( +
+
+

{sel.market?.game?.title || 'Unknown Match'}

+

{sel.name}

+

{sel.market?.name}

+
+
+ + {parseFloat(sel.odds).toFixed(2)} + +
+
+ ))} +
+ +
+ )} +
+
+
+
+
+ + {/* Feature Section */} +
+
+
+

Why BetMagic?

+

Our platform is designed for professional and casual bettors alike.

+
+ +
+ {[ + { icon: mdiFlash, title: 'Lightning Fast', desc: 'Generate tickets in under 1 second using our optimized selection engine.' }, + { icon: mdiTrophy, title: 'Highest Success', desc: 'We only pick selections from verified markets with live data integration.' }, + { icon: mdiRefresh, title: 'Always Fresh', desc: 'Game data and odds are updated in real-time to ensure your ticket is valid.' } + ].map((feature, i) => ( +
+
+ +
+

{feature.title}

+

{feature.desc}

+
+ ))} +
+
+
+ + {/* Footer */} +
+
+
+ + BETMAGIC +
+

© 2026 BetMagic AI. Responsible betting encouraged.

+
+ Privacy + Terms +
+
+
+ +
); } -Starter.getLayout = function getLayout(page: ReactElement) { +Home.getLayout = function getLayout(page: ReactElement) { return {page}; -}; - +}; \ No newline at end of file