Per la tasca de fer un prompt per generar una aplicació vam fer un formulari de registre que hem pogut aprofitar per a aquesta tasca.

Codi del formulari:

from flask import Flask, request, render_template_string, redirect, url_for, flash, session
import sqlite3
import re
from functools import wraps

app = Flask(__name__)
app.secret_key = 'clau_secreta_lan_party_2026' 

# --- CONFIGURACIÓ ---
USUARI_ADMIN = "admin"
PASSWORD_ADMIN = "1234" 
DB_NAME = 'lan_party_v2.db'
URL_DESTI_SITES = "https://sites.google.com/view/lanpartyhub/home"

# --- Base de Dades ---
def init_db():
    with sqlite3.connect(DB_NAME) as conn:
        conn.execute('''
            CREATE TABLE IF NOT EXISTS assistents (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                nom TEXT NOT NULL,
                cognoms TEXT NOT NULL,
                nickname TEXT NOT NULL UNIQUE,
                email TEXT NOT NULL,
                password TEXT NOT NULL,
                telefon TEXT NOT NULL,
                dni TEXT NOT NULL,
                dies TEXT NOT NULL,
                intolerancies TEXT
            )
        ''')

# --- Decorador Admin ---
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('admin_logged_in'):
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

# --- CSS i JS ---
# He afegit el JavaScript per validar els dies
PLANTILLA_BASE_CAP = '''
<style>
    :root { --neon-blue: #00f2ff; --neon-purple: #bc13fe; --bg: #0d0d0d; }
    body { font-family: 'Segoe UI', sans-serif; background: var(--bg); color: white; display: flex; justify-content: center; padding: 40px 20px; margin: 0; }
    .container { background: #1a1a1a; padding: 30px; border-radius: 15px; box-shadow: 0 0 20px var(--neon-blue); width: 100%; max-width: 500px; }
    h2 { text-align: center; color: var(--neon-blue); text-transform: uppercase; letter-spacing: 2px; }
    input, textarea { width: 100%; padding: 12px; margin: 8px 0; background: #2a2a2a; border: 1px solid var(--neon-purple); color: white; border-radius: 5px; box-sizing: border-box; }
    input:focus { outline: none; border-color: var(--neon-blue); box-shadow: 0 0 8px var(--neon-blue); }
    .days-group { background: #2a2a2a; padding: 12px; border-radius: 5px; border: 1px solid var(--neon-purple); margin: 10px 0; }
    .days-options { display: flex; justify-content: space-around; margin-top: 5px; }
    .btn-submit { width: 100%; padding: 15px; background: var(--neon-purple); border: none; color: white; font-weight: bold; cursor: pointer; border-radius: 5px; margin-top: 20px; transition: 0.3s; text-transform: uppercase; }
    .btn-submit:hover { background: var(--neon-blue); color: black; box-shadow: 0 0 15px var(--neon-blue); }
    .btn-secondary { display: block; width: fit-content; margin: 15px auto 0; padding: 8px 15px; background: transparent; border: 1px solid #555; color: #888; text-decoration: none; font-size: 0.85rem; border-radius: 5px; text-align: center; }
    .btn-secondary:hover { border-color: var(--neon-blue); color: var(--neon-blue); }
    .error-flash { color: #ff4444; text-align: center; font-weight: bold; list-style: none; padding: 0; margin-bottom: 10px; }
    table { width: 100%; border-collapse: collapse; background: #1a1a1a; margin-top: 20px; color: white; }
    th, td { padding: 10px; border: 1px solid #333; text-align: left; font-size: 0.8rem; }
    th { background: #222; color: var(--neon-purple); }
</style>

<script>
    function validarFormulari() {
        // Busquem tots els checkboxes amb el nom 'dies'
        const checkboxes = document.querySelectorAll('input[name="dies"]:checked');
        if (checkboxes.length === 0) {
            alert("Has de seleccionar almenys un dia d'assistència.");
            return false; // Atura l'enviament del formulari
        }
        return true; // Tot correcte
    }
</script>
'''

HTML_REGISTRE = PLANTILLA_BASE_CAP + '''
<div class="container">
    <h2>🎮 REGISTRE</h2>
    {% with messages = get_flashed_messages() %}
      {% if messages %}<ul class="error-flash">{% for msg in messages %}<li>{{ msg }}</li>{% endfor %}</ul>{% endif %}
    {% endwith %}
    
    <form method="POST" onsubmit="return validarFormulari()">
        <input type="text" name="nom" placeholder="Nom" required>
        <input type="text" name="cognoms" placeholder="Cognoms" required>
        <input type="text" name="nickname" placeholder="Nickname (Usuari)" required>
        <input type="password" name="password" placeholder="Contrasenya" required>
        <input type="email" name="email" placeholder="Email" required>
        <input type="tel" name="telefon" placeholder="Telèfon" pattern="[6789][0-9]{8}" required title="9 dígits (6xxxxxxxx)">
        <input type="text" name="dni" placeholder="DNI / NIE" pattern="([0-9]{8}[A-Z])|([XYZ][0-9]{7}[A-Z])" style="text-transform: uppercase;" required>
        
        <div class="days-group">
            <span style="font-size: 0.85rem; color: var(--neon-blue);">Dies d'assistència (mínim 1):</span>
            <div class="days-options">
                <label><input type="checkbox" name="dies" value="10"> 10</label>
                <label><input type="checkbox" name="dies" value="11"> 11</label>
                <label><input type="checkbox" name="dies" value="12"> 12</label>
            </div>
        </div>
        
        <textarea name="intolerancies" rows="2" placeholder="Intoleràncies o al·lèrgies..."></textarea>
        <button type="submit" class="btn-submit">Finalitzar Registre</button>
    </form>
    <a href="/login" class="btn-secondary">Ja tinc compte / Iniciar sessió</a>
</div>
'''

HTML_LOGIN = PLANTILLA_BASE_CAP + '''
<div class="container">
    <h2>🔑 ACCÉS</h2>
    {% with messages = get_flashed_messages() %}
      {% if messages %}<ul class="error-flash">{% for msg in messages %}<li>{{ msg }}</li>{% endfor %}</ul>{% endif %}
    {% endwith %}
    <form method="POST">
        <input type="text" name="user" placeholder="Nickname o Admin" required>
        <input type="password" name="pass" placeholder="Contrasenya" required>
        <button type="submit" class="btn-submit">Entrar</button>
    </form>
    <a href="/" class="btn-secondary">Tornar al registre</a>
</div>
'''

# --- Lògica de Flask ---

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        nickname = request.form['nickname'].strip()
        dies_llista = request.form.getlist('dies')
        
        # Validació de seguretat extra al servidor
        if not dies_llista:
            flash("Has de triar almenys un dia.")
            return redirect(url_for('index'))
            
        try:
            with sqlite3.connect(DB_NAME) as conn:
                conn.execute('''
                    INSERT INTO assistents (nom, cognoms, nickname, email, password, telefon, dni, dies, intolerancies)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', (request.form['nom'], request.form['cognoms'], nickname, 
                     request.form['email'], request.form['password'], request.form['telefon'], 
                     request.form['dni'].upper(), ", ".join(dies_llista), request.form['intolerancies']))
            return redirect(URL_DESTI_SITES)
        except sqlite3.IntegrityError:
            flash("Aquest Nickname ja existeix.")
            return redirect(url_for('index'))
    return render_template_string(HTML_REGISTRE)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user = request.form['user'].strip()
        password = request.form['pass']
        
        if user == USUARI_ADMIN and password == PASSWORD_ADMIN:
            session['admin_logged_in'] = True
            return redirect(url_for('admin'))
        
        with sqlite3.connect(DB_NAME) as conn:
            cursor = conn.execute('SELECT password FROM assistents WHERE nickname = ?', (user,))
            result = cursor.fetchone()
            if result and result[0] == password:
                return redirect(URL_DESTI_SITES)
            
        flash("Credencials incorrectes.")
    return render_template_string(HTML_LOGIN)

@app.route('/admin')
@login_required
def admin():
    with sqlite3.connect(DB_NAME) as conn:
        cursor = conn.execute('SELECT * FROM assistents')
        assistents = cursor.fetchall()
    files_html = "".join([f"<tr>{''.join([f'<td>{dada}</td>' for dada in a])}</tr>" for a in assistents])
    return render_template_string(PLANTILLA_BASE_CAP + f'''
        <div style="width: 95%; max-width: 1200px;">
            <div style="display:flex; justify-content:space-between; align-items:center;">
                <h2 style="color:var(--neon-blue);">Admin - Llista Assistents</h2>
                <a href="/logout" style="color:#ff4444; text-decoration:none;">Tancar Sessió</a>
            </div>
            <div style="overflow-x:auto;">
                <table>
                    <tr><th>ID</th><th>Nom</th><th>Cognoms</th><th>Nick</th><th>Email</th><th>Pass</th><th>Tel</th><th>DNI</th><th>Dies</th><th>Intol.</th></tr>
                    {files_html}
                </table>
            </div>
            <br><a href="/" style="color:var(--neon-blue); text-decoration:none;">← Tornar al registre</a>
        </div>
    ''')

@app.route('/logout')
def logout():
    session.pop('admin_logged_in', None)
    return redirect(url_for('login'))

if __name__ == '__main__':
    init_db()
    app.run(debug=True)

Com es veu a un navegador:

Hem posat que siguin obligatoris tots els camps i cada camp amb el seu format específic si és necessari, per exemple al número de telèfon s’ha de posar només números, que comencin del 6 al 9 i que sigui 9 de llarg en total, al DNI amb el format de DNI i el correu electrònic amb format de correu.

això es defineix amb les següents línies:

<form method="POST" onsubmit="return validarFormulari()">
        <input type="text" name="nom" placeholder="Nom" required>
        <input type="text" name="cognoms" placeholder="Cognoms" required>
        <input type="text" name="nickname" placeholder="Nickname (Usuari)" required>
        <input type="password" name="password" placeholder="Contrasenya" required>
        <input type="email" name="email" placeholder="Email" required>
        <input type="tel" name="telefon" placeholder="Telèfon" pattern="[6789][0-9]{8}" required                         title="9 dígits (6xxxxxxxx)">
        <input type="text" name="dni" placeholder="DNI / NIE" pattern="([0-9]{8}[A-Z])|([XYZ][0-9]{7}[A-Z])" style="text-transform: uppercase;" required>
        
        <div class="days-group">
            <span style="font-size: 0.85rem; color: var(--neon-blue);">Dies d'assistència (mínim 1):</span>
            <div class="days-options">
                <label><input type="checkbox" name="dies" value="10"> 10</label>
                <label><input type="checkbox" name="dies" value="11"> 11</label>
                <label><input type="checkbox" name="dies" value="12"> 12</label>
            </div>
        </div>

I amb aquest fragment del codi la verificació per l’apartat dels dies d’assistència:

<script>
    function validarFormulari() {
        // Busquem tots els checkboxes amb el nom 'dies'
        const checkboxes = document.querySelectorAll('input[name="dies"]:checked');
        if (checkboxes.length === 0) {
            alert("Has de seleccionar almenys un dia d'assistència.");
            return false; // Atura l'enviament del formulari
        }
        return true; // Tot correcte
    }
</script>

Hem afegit un inici de sessió amb el botó de baix de finalitzar registre perquè una persona que ja s’hagi registrat pugui entrar a la pàgina sense tornar a registrar-se.

També hi ha una pàgina d’administració on podem veure la base de dades amb els usuaris registrats:

També hem afegit que quan és faci el registre correctament et reencamini al Google Sites informatiu de la LAN Party i el mateix amb l’inici de sessió amb aquesta línia:

URL_DESTI_SITES = "https://sites.google.com/view/lanpartyhub/home"

Aquesta línia assigna l’enllaç del sites dins d’una variable perquè sigui més fàcil de posar com per exemple a la següent línia:

with sqlite3.connect(DB_NAME) as conn:
                conn.execute('''
                    INSERT INTO assistents (nom, cognoms, nickname, email, password, telefon, dni, dies, intolerancies)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', (request.form['nom'], request.form['cognoms'], nickname, 
                     request.form['email'], request.form['password'], request.form['telefon'], 
                     dni, dies, request.form['intolerancies']))
            return redirect(URL_DESTI_SITES)

A l’última línia es pot veure que s’utilitza la variable, perquè t’acabi reencaminant al sites en finalitzar el registre, en comptes de posar l’URL sencera una altra vegada es posa el nom de la variable i és més curt i fàcil.

La pàgina final a la qual t’emporta si fas el registre o inici de sessió correctament és la següent: