Webbserverprogrammering 1

Show sourcecode

The following files exists in this folder. Click to view.

webbserverprogrammering/submissions/projekt-matkort-handler/

.github/
add_logs.php
admin/
api/
card_balance.php
classes/
config/
food_logs.php
forgot_password.php
includes/
index.php
insert_restaurants.php
install.php
login.php
logout.php
public/
register.php
reset_password.php
verify.php

card_balance.php

156 lines UTF-8 Windows (CRLF)
<?php
session_start
();

include_once 
'./config/database.php';
include_once 
'./classes/Restaurant.php';
include_once 
'./classes/CardBalance.php';

if (!isset(
$_SESSION['user_id'])) {
  
header("Location: login.php");
  exit;
}

$error '';
$cardBalanceModel = new CardBalance($pdo);

// Initialize/Reset sequence checking
$currentBalance $cardBalanceModel->getBalanceAmount($_SESSION['user_id']);
$nextResetDate $cardBalanceModel->getNextResetDate();
$periods $cardBalanceModel->getPeriods();
$dailyRate $cardBalanceModel->getDailyBudget($_SESSION['user_id']);

$page_title 'Mitt matkort';
require_once 
'./includes/header.php';
?>

<div class="balance-container">
    <h1>Kvarstående Saldo</h1>

    <div class="balance-card">
        <!-- Main Animated Number -->
        <h2 id="animatedBalance" class="balance-amount">0.00 kr</h2>
        <p class="daily-rate-info">Baserat på <?php echo $dailyRate?> kr/dag på aktiva skoldagar</p>
    </div>

    <!-- Countdown timer to next period reset -->
    <?php if ($nextResetDate): ?>
    <div class="countdown-card">
        <h3><?php echo ($nextResetDate === '2026-06-11') ? 'Skolåret Slutar (Kortet Nollställs)' 'Nästa Passets Nollställning'?></h3>
        <p class="reset-date-label"><?php echo date('Y-m-d'strtotime($nextResetDate)); ?></p>
        <div id="countdownTimer" class="countdown-timer">
            <div class="time-box"><span id="cd-days">0</span> <div>Dagar</div></div>
            <div class="time-box"><span id="cd-hours">0</span> <div>Timmar</div></div>
            <div class="time-box"><span id="cd-minutes">0</span> <div>Minuter</div></div>
            <div class="time-box"><span id="cd-seconds">0</span> <div>Sekunder</div></div>
        </div>
        <p class="warning-text">Kom ihåg: Ditt saldo raderas när passet byts. Glöm inte att spendera det innan lo(v)en börjar!</p>
    </div>
    <?php else: ?>
    <div class="countdown-card">
        <h3>Skolåret är över</h3>
        <p>Det finns inga fler planerade nollställningar eller pass för detta skolår.</p>
    </div>
    <?php endif; ?>

    <!-- Period Schedule Information -->
    <div class="periods-info">
        <h3>Årets Perioder 2025/2026</h3>
        <ul>
            <?php foreach ($periods as $index => $period): ?>
            <li>
                <strong>Pass <?php echo $index 1?>:</strong> Startar <?php echo $period['start']; ?> — Totalt: <?php echo $period['amount']; ?> kr 
                <?php if ($period['start'] == $nextResetDate) echo ' <span class="badge">NÄSTA</span>'?>
            </li>
            <?php endforeach; ?>
        </ul>
    </div>
</div>

<style>
/* Scoped styling directly for component modularity */
.balance-container { max-width: 800px; margin: 40px auto; padding: 20px;}
h1 { text-align: center; color: var(--text-dark); margin-bottom: 30px; }
.balance-card { background: var(--white); color: var(--text-dark); padding: 40px; border-radius: var(--radius-lg); text-align: center; margin-bottom: 30px; border: 1px solid var(--gray-light); box-shadow: var(--shadow-soft); }
.balance-amount { font-size: 4rem; margin: 0; font-weight: 800; color: var(--primary); }
.daily-rate-info { font-size: 1.1rem; opacity: 0.9; margin-top: 10px; color: var(--gray); font-weight: 500;}

.countdown-card { background: var(--white); padding: 25px; border-radius: var(--radius-lg); border: 1px solid var(--gray-light); text-align: center; box-shadow: var(--shadow-soft); margin-bottom: 30px; }
.countdown-card h3 { margin-top: 0; color: var(--text-dark); }
.reset-date-label { font-size: 1.2rem; font-weight: bold; color: var(--primary); margin-bottom: 20px; }
.countdown-timer { display: flex; justify-content: center; gap: 20px; margin-bottom: 20px; }
.time-box { background: var(--bg-color); padding: 15px 20px; border-radius: var(--radius-sm); min-width: 80px; }
.time-box span { font-size: 2rem; font-weight: bold; color: var(--text-dark); display: block; }
.time-box div { font-size: 0.8rem; color: var(--gray); text-transform: uppercase; margin-top: 5px; font-weight: bold; }
.warning-text { color: var(--primary); font-size: 0.9rem; max-width: 500px; margin: 0 auto; line-height: 1.5; font-style: italic;}

.periods-info { background: var(--white); padding: 25px; border-radius: var(--radius-lg); border: 1px solid var(--gray-light); box-shadow: var(--shadow-soft); }
.periods-info ul { list-style: none; padding: 0; margin: 0; }
.periods-info li { padding: 12px 15px; border-bottom: 1px solid var(--gray-light); font-size: 1.1rem; color: var(--text-dark);}
.periods-info li:last-child { border-bottom: none; }
.badge { background: var(--primary); color: var(--white); padding: 3px 8px; border-radius: 4px; font-size: 0.7rem; font-weight: bold; vertical-align: middle;}
</style>

<script>
document.addEventListener("DOMContentLoaded", function() {
    
    // --- 1. COUNT UP ANIMATION ---
    const targetBalance = <?php echo json_encode((float)$currentBalance); ?>;
    const balanceElement = document.getElementById("animatedBalance");
    let currentVal = 0;
    
    // Determine animation speed/steps
    const duration = 1500; // 1.5 seconds
    const fps = 60;
    const frames = Math.round((duration / 1000) * fps);
    const increment = targetBalance / frames;
    
    let frame = 0;
    const counterInterval = setInterval(() => {
        frame++;
        currentVal += increment;
        
        // Prevent floating point errors from overshooting
        if(frame >= frames || currentVal >= targetBalance) {
            currentVal = targetBalance;
            clearInterval(counterInterval);
        }
        
        balanceElement.innerText = currentVal.toFixed(2) + " kr";
    }, 1000 / fps);


    // --- 2. COUNTDOWN TIMER ---
    <?php if ($nextResetDate): ?>
    
    // Combine date with midnight time for exact countdown
    const countdownDate = new Date("<?php echo $nextResetDate?>T00:00:00").getTime();
    
    function updateCountdown() {
        const now = new Date().getTime();
        const distance = countdownDate - now;
        
        if (distance < 0) {
            document.getElementById("countdownTimer").innerHTML = "<h3 style='color:green;'>Saldot nollställdes idag!</h3>";
            return;
        }
        
        // Time calculations
        const days = Math.floor(distance / (1000 * 60 * 60 * 24));
        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((distance % (1000 * 60)) / 1000);
        
        document.getElementById("cd-days").innerText = days;
        document.getElementById("cd-hours").innerText = hours;
        document.getElementById("cd-minutes").innerText = minutes;
        document.getElementById("cd-seconds").innerText = seconds;
    }
    
    updateCountdown(); // Run immediately right away
    setInterval(updateCountdown, 1000); // And scale every second
    
    <?php endif; ?>
});
</script>

<?php require_once './includes/footer.php'?>