Add roadmap management, service worker, and user invitation features

- Implemented roadmap.php for managing roadmap items with filtering options by quarter and status.
- Added a service worker (sw.js) to handle push notifications and click events for calendar appointments.
- Created users.php for admin to manage team members and invitations, including user creation and deletion functionalities.
This commit is contained in:
2026-09-09 15:54:24 +02:00
commit 9262b09c4d
41 changed files with 10579 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
<?php
/**
* MariaDB PDO Databaseverbinding & Automatische Migratie
*/
require_once __DIR__ . '/config.php';
function get_db_connection(): PDO {
static $pdo = null;
if ($pdo !== null) {
return $pdo;
}
// 1. Controleer of de PDO MySQL/MariaDB driver aanwezig is in PHP
$availableDrivers = PDO::getAvailableDrivers();
if (!in_array('mysql', $availableDrivers)) {
$driversList = !empty($availableDrivers) ? implode(', ', $availableDrivers) : 'geen';
die("
<div style='font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; background: #0c0f17; color: #f1f5f9; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px;'>
<div style='max-width: 580px; background: #111827; border: 1px solid #ef4444; border-radius: 16px; padding: 30px; box-shadow: 0 20px 25px -5px rgba(0,0,0,0.5);'>
<h2 style='color: #ef4444; margin-top: 0;'>⚠️ PHP MySQL Driver Ontbreekt (pdo_mysql)</h2>
<p style='color: #94a3b8; font-size: 14px;'>De extensie <strong>pdo_mysql</strong> is niet actief in deze PHP-omgeving.</p>
</div>
</div>
");
}
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_TIMEOUT => 5,
];
if (defined('PDO::MYSQL_ATTR_MULTI_STATEMENTS')) {
$options[PDO::MYSQL_ATTR_MULTI_STATEMENTS] = true;
}
$dsnWithDb = sprintf(
'mysql:host=%s;port=%s;dbname=%s;charset=%s',
DB_HOST,
DB_PORT,
DB_NAME,
DB_CHARSET
);
// 2. CHECK: Bestaat de database al?
// We proberen eerst direct verbinding te maken met de database.
// Als de database al bestaat, lukt dit direct zonder database-aanmaakrechten te vereisen!
try {
$pdo = new PDO($dsnWithDb, DB_USER, DB_PASS, $options);
// De database bestaat al! Controleer enkel of de tabellen al aanwezig zijn
ensure_tables_exist($pdo);
return $pdo;
} catch (PDOException $e) {
// Foutcode 1049 betekent: Database 'DB_NAME' bestaat nog NIET op de MariaDB server
if ($e->getCode() == 1049 || str_contains($e->getMessage(), 'Unknown database')) {
// Probeer de database eenmalig aan te maken indien de gebruiker CREATE rechten heeft
try {
$serverDsn = sprintf('mysql:host=%s;port=%s;charset=%s', DB_HOST, DB_PORT, DB_CHARSET);
$serverPdo = new PDO($serverDsn, DB_USER, DB_PASS, $options);
$serverPdo->exec(sprintf(
'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',
DB_NAME
));
// Nu opnieuw verbinden met de nieuw aangemaakte database
$pdo = new PDO($dsnWithDb, DB_USER, DB_PASS, $options);
ensure_tables_exist($pdo);
return $pdo;
} catch (PDOException $createEx) {
die("
<div style='font-family: sans-serif; background: #0c0f17; color: #fff; padding: 40px; text-align: center; min-height: 100vh; display: flex; align-items: center; justify-content: center;'>
<div style='max-width: 500px; background: #111827; border: 1px solid #f59e0b; border-radius: 16px; padding: 30px; text-align: left;'>
<h2 style='color: #f59e0b; margin-top: 0;'>⚠️ Database '" . htmlspecialchars(DB_NAME) . "' bestaat nog niet</h2>
<p style='color: #94a3b8; font-size: 14px; line-height: 1.6;'>
De database <strong>" . htmlspecialchars(DB_NAME) . "</strong> bestaat nog niet op MariaDB server <strong>" . htmlspecialchars(DB_HOST) . "</strong> en de gebruiker <em>" . htmlspecialchars(DB_USER) . "</em> heeft geen rechten om automatisch databases aan te maken.
</p>
<p style='color: #cbd5e1; font-size: 13px;'>
Maak de database <code>" . htmlspecialchars(DB_NAME) . "</code> eenmalig aan via phpMyAdmin, HeidiSQL of de MariaDB CLI:
</p>
<code style='display: block; background: #000; color: #4ade80; padding: 10px; border-radius: 8px; font-size: 12px; margin: 10px 0;'>
CREATE DATABASE IF NOT EXISTS `" . htmlspecialchars(DB_NAME) . "` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
</code>
</div>
</div>
");
}
}
// Andere verbindingsfout (bijv. verkeerd wachtwoord, poort of host onbereikbaar)
die("
<div style='font-family: sans-serif; background: #0c0f17; color: #fff; padding: 40px; text-align: center; min-height: 100vh; display: flex; align-items: center; justify-content: center;'>
<div style='max-width: 520px; background: #111827; border: 1px solid #ef4444; border-radius: 16px; padding: 30px; text-align: left;'>
<h2 style='color: #ef4444; margin-top: 0;'>MariaDB Verbindingsfout (" . htmlspecialchars((string)$e->getCode()) . ")</h2>
<p style='color: #cbd5e1; font-size: 14px;'>" . htmlspecialchars($e->getMessage()) . "</p>
<div style='background: #0b0f17; border: 1px solid #1f2937; padding: 15px; border-radius: 10px; font-size: 13px; margin-top: 15px;'>
Host: <strong>" . htmlspecialchars(DB_HOST) . "</strong><br>
Poort: <strong>" . htmlspecialchars(DB_PORT) . "</strong><br>
Database: <strong>" . htmlspecialchars(DB_NAME) . "</strong><br>
Gebruiker: <strong>" . htmlspecialchars(DB_USER) . "</strong>
</div>
</div>
</div>
");
}
}
/**
* Zorgt ervoor dat alle benodigde tabellen uit schema.sql aanwezig zijn in MariaDB
* Voert niets uit als de tabellen al bestaan!
*/
function ensure_tables_exist(PDO $pdo): void {
try {
// Controleer of de hoofdtabel 'users' al bestaat
$result = $pdo->query("SHOW TABLES LIKE 'users'");
if ($result && $result->rowCount() > 0) {
// Tabellen bestaan al. Voer eventuele niet-destructieve kolom-upgrades uit indien nodig
try {
$pdo->exec("ALTER TABLE tasks MODIFY external_id VARCHAR(255) NULL");
} catch (Throwable $t) {
// Kolom bestaat wellicht al of heeft al de juiste lengte
}
return;
}
// Tabellen ontbreken nog: voer schema.sql uit statement voor statement
$schemaFile = file_exists(__DIR__ . '/schema.sql') ? (__DIR__ . '/schema.sql') : (__DIR__ . '/../schema.sql');
if (file_exists($schemaFile)) {
$sql = file_get_contents($schemaFile);
$statements = array_filter(array_map('trim', explode(';', $sql)));
foreach ($statements as $stmt) {
// Verwijder eventuele SQL commentaren aan het begin
$stmtClean = preg_replace('/^--.*$/m', '', $stmt);
$stmtClean = trim($stmtClean);
if (!empty($stmtClean)) {
$pdo->exec($stmtClean);
}
}
}
} catch (Exception $e) {
error_log("Fout bij aanmaken tabellen: " . $e->getMessage());
}
}
+135
View File
@@ -0,0 +1,135 @@
-- Fociz MariaDB Databaseschema
-- UTF-8 / utf8mb4 ondersteuning voor emoticons, sticky notes en internationale tekens
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(120) NOT NULL,
`email` VARCHAR(191) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) NOT NULL,
`role` ENUM('ADMIN', 'MEMBER') NOT NULL DEFAULT 'MEMBER',
`two_factor_secret` VARCHAR(120) NULL,
`two_factor_enabled` TINYINT(1) NOT NULL DEFAULT 0,
`backup_codes` TEXT NULL,
`remember_token` VARCHAR(64) NULL,
`remember_token_expires_at` DATETIME NULL,
`notification_settings` TEXT NULL,
`avatar_url` VARCHAR(255) NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `invites` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`token` VARCHAR(64) NOT NULL UNIQUE,
`email` VARCHAR(191) NOT NULL,
`role` ENUM('ADMIN', 'MEMBER') NOT NULL DEFAULT 'MEMBER',
`expires_at` DATETIME NOT NULL,
`used_at` DATETIME NULL,
`created_by_id` INT NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX (`token`),
CONSTRAINT `fk_invites_user` FOREIGN KEY (`created_by_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `daily_plans` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NOT NULL,
`plan_date` DATE NOT NULL,
`focus_goal` TEXT NULL,
`secondary_goals` TEXT NULL,
`notes` TEXT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `uk_user_date` (`user_id`, `plan_date`),
CONSTRAINT `fk_dailyplans_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `tasks` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NOT NULL,
`daily_plan_id` INT NULL,
`title` VARCHAR(255) NOT NULL,
`description` TEXT NULL,
`status` ENUM('TODO', 'IN_PROGRESS', 'DONE', 'CANCELLED') NOT NULL DEFAULT 'TODO',
`priority` ENUM('P1', 'P2', 'P3') NOT NULL DEFAULT 'P2',
`time_block_start` VARCHAR(10) NULL,
`time_block_end` VARCHAR(10) NULL,
`duration_minutes` INT NULL DEFAULT 30,
`is_completed` TINYINT(1) NOT NULL DEFAULT 0,
`source` VARCHAR(50) NOT NULL DEFAULT 'FOCIZ',
`external_id` VARCHAR(255) NULL,
`external_url` TEXT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX (`user_id`),
CONSTRAINT `fk_tasks_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_tasks_plan` FOREIGN KEY (`daily_plan_id`) REFERENCES `daily_plans` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `brainstorm_sessions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`description` TEXT NULL,
`theme` VARCHAR(100) NOT NULL DEFAULT 'Algemeen',
`created_by_id` INT NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX (`created_by_id`),
CONSTRAINT `fk_brainstorm_user` FOREIGN KEY (`created_by_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `brainstorm_cards` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`session_id` INT NOT NULL,
`content` TEXT NOT NULL,
`category` VARCHAR(100) NOT NULL DEFAULT 'Ideeën',
`color` VARCHAR(20) NOT NULL DEFAULT 'amber',
`votes` INT NOT NULL DEFAULT 0,
`author_name` VARCHAR(120) NULL,
`converted_to_roadmap_item_id` INT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX (`session_id`),
CONSTRAINT `fk_cards_session` FOREIGN KEY (`session_id`) REFERENCES `brainstorm_sessions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `roadmap_items` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`description` TEXT NULL,
`type` VARCHAR(50) NOT NULL DEFAULT 'FEATURE',
`status` VARCHAR(50) NOT NULL DEFAULT 'PLANNED',
`quarter` VARCHAR(20) NOT NULL DEFAULT 'Q3',
`priority` VARCHAR(20) NOT NULL DEFAULT 'MEDIUM',
`progress` INT NOT NULL DEFAULT 0,
`start_date` DATE NULL,
`due_date` DATE NULL,
`jira_issue_key` VARCHAR(50) NULL,
`origin_brainstorm_card_id` INT NULL,
`created_by_id` INT NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX (`created_by_id`),
CONSTRAINT `fk_roadmap_user` FOREIGN KEY (`created_by_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `integrations` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NOT NULL,
`provider` ENUM('OUTLOOK', 'TEAMS', 'JIRA') NOT NULL,
`is_enabled` TINYINT(1) NOT NULL DEFAULT 1,
`is_demo_mode` TINYINT(1) NOT NULL DEFAULT 1,
`access_token` TEXT NULL,
`refresh_token` TEXT NULL,
`settings` TEXT NULL,
`last_sync_at` DATETIME NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `uk_user_provider` (`user_id`, `provider`),
CONSTRAINT `fk_integrations_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `system_settings` (
`setting_key` VARCHAR(64) PRIMARY KEY,
`setting_value` TEXT NULL,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;