Header menu via header.js

This commit is contained in:
karim hassan
2025-08-24 23:47:39 +00:00
parent f1103d67a0
commit eabd0aa50f
25 changed files with 869 additions and 185 deletions

21
backend/sql/seed.sql Normal file
View File

@@ -0,0 +1,21 @@
-- Super Sunday Seed (tournoi + joueurs)
BEGIN;
INSERT INTO tournaments (name, location, start_date, end_date)
VALUES ('Super Sunday Demo', 'Padel Club', CURRENT_DATE, CURRENT_DATE)
ON CONFLICT DO NOTHING;
-- Récupère l'id du tournoi inséré / existant
WITH t AS (
SELECT id FROM tournaments WHERE name='Super Sunday Demo' ORDER BY id DESC LIMIT 1
)
INSERT INTO participants (tournament_id, full_name)
SELECT t.id, p.full_name
FROM t
JOIN (VALUES
('Alex Dupont'),('Samira El Idrissi'),('Marco Rossi'),('Lina Gomez'),
('Yuki Tanaka'),('Nina Kowalski'),('Oliver Smith'),('Fatou Ndiaye')
) AS p(full_name) ON TRUE
ON CONFLICT DO NOTHING;
COMMIT;

View File

@@ -1,11 +1,15 @@
import pg from 'pg';
const { Pool } = pg;
/**
* Minimal PG pool using env vars:
* PGHOST, PGUSER, PGPASSWORD, PGDATABASE, PGPORT
*/
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.PGHOST || 'db',
port: Number(process.env.PGPORT || 5432),
host: process.env.PGHOST || 'localhost',
user: process.env.PGUSER || 'postgres',
password: process.env.PGPASSWORD || 'postgres',
database: process.env.PGDATABASE || 'supersunday',
port: Number(process.env.PGPORT || 5432),
});
export default pool;
module.exports = { pool };

View File

@@ -1,24 +1,28 @@
import 'dotenv/config';
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import authRoutes from './routes/auth.js';
import tournamentsRoutes from './routes/tournaments.js';
import matchesRoutes from './routes/matches.js';
import standingsRoutes from './routes/standings.js';
const express = require('express');
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
app.get('/api/health', (req,res)=>res.json({ ok:true, ts: Date.now() }));
// Health
app.get('/api/health', (req, res) => res.json({ ok: true, ts: Date.now() }));
app.use('/api/auth', authRoutes);
app.use('/api/tournaments', tournamentsRoutes);
app.use('/api/matches', matchesRoutes);
app.use('/api/tournaments', standingsRoutes);
// Routes
app.use('/api/tournaments', require('./routes/tournaments'));
app.use('/api/participants', require('./routes/participants'));
const PORT = process.env.PORT || 4000;
// "by tournament" nicer path
app.get('/api/tournaments/:id/participants', (req, res, next) => {
req.url = '/by-tournament/' + req.params.id;
return require('./routes/participants')(req, res, next);
});
// 404 for unknown API paths
app.use('/api', (req, res) => res.status(404).json({ error: 'not_found' }));
const PORT = Number(process.env.PORT || 4000);
app.listen(PORT, () => console.log(`API listening on :${PORT}`));
app.use('/api/participants', require('./routes/participants'));
app.get('/api/tournaments/:id/participants', (req,res,next)=>{
req.url = '/by-tournament/' + req.params.id;
return require('./routes/participants')(req,res,next);
});

View File

@@ -0,0 +1,40 @@
const express = require('express');
const router = express.Router();
const { pool } = require('../db');
// GET /api/tournaments/:id/participants
router.get('/by-tournament/:id', async (req, res) => {
const id = Number(req.params.id);
if (!id) return res.status(400).json({ error: 'bad_tournament_id' });
try {
const { rows } = await pool.query(
'SELECT id, tournament_id, full_name FROM participants WHERE tournament_id = $1 ORDER BY id DESC',
[id]
);
res.json(rows);
} catch (e) {
console.error('GET participants by tournament', e);
res.status(500).json({ error: 'server_error' });
}
});
// POST /api/participants { tournament_id, full_name }
router.post('/', async (req, res) => {
const { tournament_id, full_name } = req.body || {};
const tid = Number(tournament_id);
if (!tid || !full_name || !full_name.trim()) {
return res.status(400).json({ error: 'missing_fields' });
}
try {
const { rows } = await pool.query(
'INSERT INTO participants (tournament_id, full_name) VALUES ($1, $2) RETURNING id, tournament_id, full_name',
[tid, full_name.trim()]
);
res.status(201).json(rows[0]);
} catch (e) {
console.error('POST /participants', e);
res.status(500).json({ error: 'server_error' });
}
});
module.exports = router;

View File

@@ -1,59 +1,68 @@
import { Router } from 'express';
import pool from '../db.js';
import { requireAuth } from '../middleware/auth.js';
const express = require('express');
const router = express.Router();
const { pool } = require('../db');
const router = Router();
// List
router.get('/', async (req,res)=>{
const { rows } = await pool.query('select * from tournaments order by start_date nulls last, id desc limit 200');
res.json(rows);
// GET /api/tournaments -> liste
router.get('/', async (req, res) => {
try {
const { rows } = await pool.query(
'SELECT id, name, location, start_date, end_date FROM tournaments ORDER BY id DESC'
);
res.json(rows);
} catch (e) {
console.error('GET /tournaments', e);
res.status(500).json({ error: 'server_error' });
}
});
// One
router.get('/:id', async (req,res)=>{
const { rows } = await pool.query('select * from tournaments where id=$1', [req.params.id]);
if (!rows[0]) return res.status(404).json({ error:'not_found' });
res.json(rows[0]);
});
// Participants
router.get('/:id/participants', async (req,res)=>{
const { rows } = await pool.query('select * from participants where tournament_id=$1 order by id', [req.params.id]);
res.json(rows);
});
// Matches
router.get('/:id/matches', async (req,res)=>{
const { rows } = await pool.query('select * from matches where tournament_id=$1 order by starts_at nulls last, id', [req.params.id]);
res.json(rows);
});
// Create tournament (admin)
router.post('/', requireAuth, async (req,res)=>{
// POST /api/tournaments -> créer
router.post('/', async (req, res) => {
const { name, location, start_date, end_date } = req.body || {};
if (!name) return res.status(400).json({ error:'name_required' });
const { rows } = await pool.query(
'insert into tournaments(name,location,start_date,end_date) values ($1,$2,$3,$4) returning *',
[name, location||null, start_date||null, end_date||null]
);
res.status(201).json(rows[0]);
if (!name || !start_date || !end_date) {
return res.status(400).json({ error: 'missing_fields' });
}
try {
const { rows } = await pool.query(
`INSERT INTO tournaments (name, location, start_date, end_date)
VALUES ($1, $2, $3, $4)
RETURNING id, name, location, start_date, end_date`,
[name, location || null, start_date, end_date]
);
res.status(201).json(rows[0]);
} catch (e) {
console.error('POST /tournaments', e);
res.status(500).json({ error: 'server_error' });
}
});
// Add participant (admin)
router.post('/:id/participants', requireAuth, async (req,res)=>{
const { full_name } = req.body || {};
const tid = Number(req.params.id);
if (!tid) return res.status(400).json({ error:'bad_tournament_id' });
if (!full_name) return res.status(400).json({ error:'full_name_required' });
// ensure tournament exists
const t = await pool.query('select id from tournaments where id=$1', [tid]);
if (!t.rows[0]) return res.status(404).json({ error:'tournament_not_found' });
const { rows } = await pool.query(
'insert into participants(tournament_id, full_name) values ($1,$2) returning *',
[tid, full_name]
);
res.status(201).json(rows[0]);
// DELETE /api/tournaments/:id -> supprimer (avec nettoyage dépendances si pas de CASCADE)
router.delete('/:id', async (req, res) => {
const id = Number(req.params.id);
if (!id) return res.status(400).json({ error: 'bad_id' });
const client = await pool.connect();
try {
await client.query('BEGIN');
// Si votre schéma n'a PAS de ON DELETE CASCADE, on nettoie à la main :
// matches → participants(tournament row)
try { await client.query('DELETE FROM matches WHERE tournament_id = $1', [id]); } catch {}
try { await client.query('DELETE FROM participants WHERE tournament_id = $1', [id]); } catch {}
const result = await client.query('DELETE FROM tournaments WHERE id = $1', [id]);
await client.query('COMMIT');
if (result.rowCount === 0) {
return res.status(404).json({ error: 'not_found' });
}
res.json({ ok: true, id });
} catch (e) {
await client.query('ROLLBACK');
console.error('DELETE /tournaments/:id', e);
res.status(500).json({ error: 'server_error' });
} finally {
client.release();
}
});
export default router;
module.exports = router;