Implementeer live Microsoft 365 Outlook en Atlassian Jira integraties met agendasync en taakimport
This commit is contained in:
+237
-58
@@ -1,15 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* Fociz Dagplanning, Focus Modus & Time-blocking
|
||||
* Gekoppeld aan Microsoft Outlook 365 (Agenda) en Atlassian Jira (Tickets)
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
require_once __DIR__ . '/includes/integrations/microsoft.php';
|
||||
require_once __DIR__ . '/includes/integrations/jira.php';
|
||||
|
||||
$db = get_db_connection();
|
||||
$userId = $currentUser['id'];
|
||||
$today = date('Y-m-d');
|
||||
|
||||
// Haal dagplanning op of maak aan
|
||||
// 1. Haal dagplanning op of maak aan
|
||||
$stmtPlan = $db->prepare("SELECT * FROM daily_plans WHERE user_id = ? AND plan_date = ?");
|
||||
$stmtPlan->execute([$userId, $today]);
|
||||
$dailyPlan = $stmtPlan->fetch();
|
||||
@@ -24,7 +27,7 @@ if (!$dailyPlan) {
|
||||
$dailyPlan = ['id' => $planId, 'plan_date' => $today, 'focus_goal' => 'Mijn belangrijkste doel van vandaag behalen', 'notes' => ''];
|
||||
}
|
||||
|
||||
// Haal taken op voor vandaag
|
||||
// 2. Haal taken op voor vandaag
|
||||
$stmtTasks = $db->prepare("
|
||||
SELECT * FROM tasks
|
||||
WHERE user_id = ? AND (daily_plan_id = ? OR daily_plan_id IS NULL)
|
||||
@@ -33,35 +36,75 @@ $stmtTasks = $db->prepare("
|
||||
$stmtTasks->execute([$userId, $dailyPlan['id']]);
|
||||
$tasks = $stmtTasks->fetchAll();
|
||||
|
||||
// Outlook demo / gesynchroniseerde events
|
||||
$outlookEvents = [
|
||||
[
|
||||
'id' => 'evt-1',
|
||||
'subject' => 'Daily Team Standup & Focus Sync',
|
||||
'start' => '09:00',
|
||||
'end' => '09:30',
|
||||
'location' => 'Microsoft Teams',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
],
|
||||
[
|
||||
'id' => 'evt-2',
|
||||
'subject' => 'Sprint & Roadmap Review met Stakeholders',
|
||||
'start' => '11:00',
|
||||
'end' => '12:00',
|
||||
'location' => 'Vergaderruimte A & Teams',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
],
|
||||
[
|
||||
'id' => 'evt-3',
|
||||
'subject' => 'Architectuur & MariaDB Database Evaluatie',
|
||||
'start' => '14:30',
|
||||
'end' => '15:30',
|
||||
'location' => 'Online Teams Call',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
]
|
||||
];
|
||||
// 3. Haal integratiestatussen op
|
||||
$stmtInt = $db->prepare("SELECT * FROM integrations WHERE user_id = ?");
|
||||
$stmtInt->execute([$userId]);
|
||||
$userIntegrations = [];
|
||||
foreach ($stmtInt->fetchAll() as $row) {
|
||||
$userIntegrations[$row['provider']] = $row;
|
||||
}
|
||||
|
||||
$hours = ["08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00"];
|
||||
$isOutlookConnected = !empty($userIntegrations['OUTLOOK']['access_token']) && empty($userIntegrations['OUTLOOK']['is_demo_mode']);
|
||||
$outlookEvents = [];
|
||||
$isLiveOutlook = false;
|
||||
|
||||
if ($isOutlookConnected) {
|
||||
$outlookEvents = microsoft_fetch_today_events($userId, $today);
|
||||
$isLiveOutlook = !empty($outlookEvents) || true; // Live koppeling is actief
|
||||
}
|
||||
|
||||
// Fallback naar sandbox data indien Outlook niet live gekoppeld is
|
||||
if (!$isOutlookConnected || empty($outlookEvents)) {
|
||||
$outlookEvents = [
|
||||
[
|
||||
'id' => 'evt-1',
|
||||
'subject' => 'Daily Team Standup & Focus Sync',
|
||||
'start' => '09:00',
|
||||
'end' => '09:30',
|
||||
'location' => 'Microsoft Teams',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
[
|
||||
'id' => 'evt-2',
|
||||
'subject' => 'Sprint & Roadmap Review met Stakeholders',
|
||||
'start' => '11:00',
|
||||
'end' => '12:00',
|
||||
'location' => 'Vergaderruimte A & Teams',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
'source' => 'SANDBOX',
|
||||
],
|
||||
[
|
||||
'id' => 'evt-3',
|
||||
'subject' => 'Architectuur & MariaDB Database Evaluatie',
|
||||
'start' => '14:30',
|
||||
'end' => '15:30',
|
||||
'location' => 'Online Teams Call',
|
||||
'meeting_url' => 'https://teams.microsoft.com',
|
||||
'source' => 'SANDBOX',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 4. Haal Jira issues op voor de snelle import widget
|
||||
$jiraData = jira_get_user_issues($userId);
|
||||
$jiraIssues = $jiraData['issues'] ?? [];
|
||||
$isJiraLive = !empty($jiraData['success']) && empty($jiraData['is_demo']);
|
||||
|
||||
// 5. Tijdlijn uren bepalen
|
||||
$startHour = 8;
|
||||
$endHour = 18;
|
||||
foreach ($outlookEvents as $e) {
|
||||
if (!empty($e['start'])) {
|
||||
$h = (int)explode(':', $e['start'])[0];
|
||||
if ($h < $startHour && $h >= 6) $startHour = $h;
|
||||
if ($h > $endHour && $h <= 22) $endHour = $h + 1;
|
||||
}
|
||||
}
|
||||
$hours = [];
|
||||
for ($h = $startHour; $h <= $endHour; $h++) {
|
||||
$hours[] = sprintf('%02d:00', $h);
|
||||
}
|
||||
|
||||
// Nederlandse datumformattering
|
||||
$dutchDays = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'];
|
||||
@@ -70,7 +113,7 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
?>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Top Banner: Datum & Hoofddoel -->
|
||||
<!-- Top Banner: Datum, Hoofddoel & Integraties Status -->
|
||||
<div class="bg-gradient-to-r from-slate-900 via-[#0e1626] to-slate-900 border border-slate-800 rounded-2xl p-6 shadow-2xl">
|
||||
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||||
<div>
|
||||
@@ -83,11 +126,34 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<a href="/integrations.php" class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-blue-950/60 border border-blue-800/60 text-blue-300 hover:text-white hover:bg-blue-900/60 text-xs font-medium transition">
|
||||
<i data-lucide="refresh-cw" class="w-3.5 h-3.5"></i>
|
||||
<span>Outlook & Teams Verbonden</span>
|
||||
</a>
|
||||
<!-- Integratie Status Badges -->
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<?php if ($isOutlookConnected): ?>
|
||||
<div class="flex items-center space-x-2 px-3 py-1.5 rounded-xl bg-blue-950/60 border border-blue-800/60 text-blue-300 text-xs font-medium">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
|
||||
<span>Outlook 365 Live</span>
|
||||
<button onclick="location.reload()" title="Agenda Verversen" class="p-1 text-blue-400 hover:text-white rounded transition">
|
||||
<i data-lucide="refresh-cw" class="w-3 h-3"></i>
|
||||
</button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="/integrations.php" class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-slate-800/80 border border-slate-700 text-slate-300 hover:text-white hover:bg-slate-700 text-xs font-medium transition">
|
||||
<i data-lucide="link-2" class="w-3.5 h-3.5 text-blue-400"></i>
|
||||
<span>Koppel Outlook</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isJiraLive): ?>
|
||||
<a href="#jiraPanel" class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-sky-950/60 border border-sky-800/60 text-sky-300 hover:text-white text-xs font-medium transition">
|
||||
<span class="w-2 h-2 rounded-full bg-sky-400 animate-pulse"></span>
|
||||
<span>Jira: <?= count($jiraIssues) ?> tickets</span>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="/integrations.php" class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-slate-800/80 border border-slate-700 text-slate-300 hover:text-white hover:bg-slate-700 text-xs font-medium transition">
|
||||
<i data-lucide="trello" class="w-3.5 h-3.5 text-sky-400"></i>
|
||||
<span>Koppel Jira</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -117,7 +183,7 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Grid: Timer + Takenlijst (Links) & Timeblocking (Rechts) -->
|
||||
<!-- Main Grid: Timer + Takenlijst + Jira (Links) & Timeblocking (Rechts) -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||
|
||||
<!-- Linker kolom: Focus Timer & Taken -->
|
||||
@@ -212,7 +278,7 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
<div class="space-y-2" id="tasksContainer">
|
||||
<?php if (empty($tasks)): ?>
|
||||
<div class="text-center py-8 text-slate-500 text-xs">
|
||||
Nog geen taken gepland voor vandaag. Klik op 'Nieuwe Taak' om te beginnen.
|
||||
Nog geen taken gepland voor vandaag. Klik op 'Nieuwe Taak' of importeer een Jira ticket hieronder.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
@@ -225,15 +291,31 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
</button>
|
||||
|
||||
<div class="truncate">
|
||||
<span class="text-xs font-medium block truncate <?= $task['is_completed'] ? 'line-through text-slate-500' : 'text-slate-200' ?>">
|
||||
<?= htmlspecialchars($task['title']) ?>
|
||||
</span>
|
||||
<?php if (!empty($task['time_block_start'])): ?>
|
||||
<span class="text-[10px] text-slate-400 font-mono flex items-center space-x-1 mt-0.5">
|
||||
<i data-lucide="clock" class="w-2.5 h-2.5"></i>
|
||||
<span><?= htmlspecialchars($task['time_block_start']) ?></span>
|
||||
<div class="flex items-center space-x-1.5 truncate">
|
||||
<?php if ($task['source'] === 'JIRA'): ?>
|
||||
<span class="px-1.5 py-0.2 rounded bg-sky-500/10 border border-sky-500/20 text-sky-400 font-mono text-[9px] font-bold">
|
||||
JIRA
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<span class="text-xs font-medium truncate <?= $task['is_completed'] ? 'line-through text-slate-500' : 'text-slate-200' ?>">
|
||||
<?= htmlspecialchars($task['title']) ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2 mt-0.5">
|
||||
<?php if (!empty($task['time_block_start'])): ?>
|
||||
<span class="text-[10px] text-slate-400 font-mono flex items-center space-x-1">
|
||||
<i data-lucide="clock" class="w-2.5 h-2.5"></i>
|
||||
<span><?= htmlspecialchars($task['time_block_start']) ?></span>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($task['external_url'])): ?>
|
||||
<a href="<?= htmlspecialchars($task['external_url']) ?>" target="_blank" class="text-[10px] text-sky-400 hover:underline flex items-center space-x-0.5">
|
||||
<span>Open in Jira</span>
|
||||
<i data-lucide="external-link" class="w-2.5 h-2.5"></i>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -258,6 +340,61 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Jira Snelle Import Widget -->
|
||||
<div id="jiraPanel" class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-xl backdrop-blur-sm">
|
||||
<div class="flex items-center justify-between pb-3 border-b border-slate-800 mb-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<i data-lucide="trello" class="w-4 h-4 text-sky-400"></i>
|
||||
<h3 class="text-sm font-bold text-white uppercase tracking-wider">
|
||||
Mijn Jira Tickets
|
||||
</h3>
|
||||
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold <?= $isJiraLive ? 'bg-sky-500/20 text-sky-400 border border-sky-500/30' : 'bg-slate-800 text-slate-400' ?>">
|
||||
<?= count($jiraIssues) ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<a href="/integrations.php" class="text-xs text-slate-400 hover:text-sky-300 transition flex items-center space-x-1">
|
||||
<span>Beheren</span>
|
||||
<i data-lucide="chevron-right" class="w-3.5 h-3.5"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (empty($jiraIssues)): ?>
|
||||
<div class="text-center py-6 text-slate-500 text-xs">
|
||||
Geen openstaande tickets gevonden in Jira.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="space-y-2 max-h-64 overflow-y-auto pr-1">
|
||||
<?php foreach (array_slice($jiraIssues, 0, 6) as $issue): ?>
|
||||
<div class="flex items-center justify-between p-2.5 rounded-xl bg-slate-950/60 border border-slate-800/80 hover:border-slate-700 transition">
|
||||
<div class="truncate mr-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="font-mono text-[10px] font-bold text-sky-400 bg-sky-500/10 px-1.5 py-0.5 rounded">
|
||||
<?= htmlspecialchars($issue['key']) ?>
|
||||
</span>
|
||||
<span class="text-xs font-semibold text-white truncate">
|
||||
<?= htmlspecialchars($issue['summary']) ?>
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-[10px] text-slate-500 mt-0.5 block">
|
||||
<?= htmlspecialchars($issue['status']) ?> • <?= htmlspecialchars($issue['priority']) ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onclick="quickImportJira(this, '<?= htmlspecialchars(addslashes($issue['summary'])) ?>', '<?= htmlspecialchars($issue['key']) ?>', '<?= htmlspecialchars(addslashes($issue['url'] ?? '')) ?>')"
|
||||
class="flex items-center space-x-1 text-[11px] font-bold text-emerald-400 hover:text-emerald-300 bg-emerald-500/10 hover:bg-emerald-500/20 border border-emerald-500/20 px-2 py-1 rounded-lg transition flex-shrink-0"
|
||||
title="Plan in als dagtaak"
|
||||
>
|
||||
<i data-lucide="plus" class="w-3 h-3"></i>
|
||||
<span>Plan</span>
|
||||
</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Dagnotities & Reflectie -->
|
||||
<div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-xl backdrop-blur-sm">
|
||||
<div class="flex items-center space-x-2 mb-3">
|
||||
@@ -282,19 +419,25 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
<i data-lucide="clock" class="w-5 h-5 text-emerald-400"></i>
|
||||
<h3 class="text-sm font-bold text-white uppercase tracking-wider">Tijdlijn & Time-blocking</h3>
|
||||
</div>
|
||||
<span class="text-xs text-slate-400">08:00 – 18:00</span>
|
||||
<div class="flex items-center space-x-2">
|
||||
<?php if ($isOutlookConnected): ?>
|
||||
<span class="px-2 py-0.5 rounded bg-blue-500/10 border border-blue-500/20 text-blue-400 text-[10px] font-bold">Outlook 365</span>
|
||||
<?php endif; ?>
|
||||
<span class="text-xs text-slate-400 font-mono"><?= $hours[0] ?> – <?= end($hours) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<?php foreach ($hours as $hour):
|
||||
$hourInt = (int)explode(':', $hour)[0];
|
||||
|
||||
// Zoek Outlook meetings
|
||||
// Zoek Outlook meetings die binnen dit uur vallen
|
||||
$slotEvents = array_filter($outlookEvents, function($e) use ($hourInt) {
|
||||
if (empty($e['start'])) return false;
|
||||
return (int)explode(':', $e['start'])[0] === $hourInt;
|
||||
});
|
||||
|
||||
// Zoek taken in dit blok
|
||||
// Zoek taken gepland in dit uur
|
||||
$slotTasks = array_filter($tasks, function($t) use ($hourInt) {
|
||||
return !empty($t['time_block_start']) && (int)explode(':', $t['time_block_start'])[0] === $hourInt;
|
||||
});
|
||||
@@ -307,17 +450,21 @@ $formattedDate = $dutchDays[date('w')] . ' ' . date('j') . ' ' . $dutchMonths[(i
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<!-- Outlook meetings -->
|
||||
<?php foreach ($slotEvents as $evt): ?>
|
||||
<div class="flex items-center justify-between p-2 rounded-xl bg-blue-950/40 border border-blue-800/50 text-blue-200 text-xs shadow-sm">
|
||||
<div class="flex items-center justify-between p-2 rounded-xl bg-blue-950/50 border border-blue-800/60 text-blue-100 text-xs shadow-sm">
|
||||
<div class="flex items-center space-x-2 truncate mr-2">
|
||||
<span class="px-1.5 py-0.5 rounded bg-blue-500/20 text-blue-400 font-mono text-[10px] font-bold">
|
||||
<?= $evt['start'] ?> - <?= $evt['end'] ?>
|
||||
<?= htmlspecialchars($evt['start']) ?> - <?= htmlspecialchars($evt['end']) ?>
|
||||
</span>
|
||||
<span class="font-semibold truncate" title="<?= htmlspecialchars($evt['subject']) ?>">
|
||||
<?= htmlspecialchars($evt['subject']) ?>
|
||||
</span>
|
||||
<span class="font-semibold truncate"><?= htmlspecialchars($evt['subject']) ?></span>
|
||||
</div>
|
||||
<a href="<?= htmlspecialchars($evt['meeting_url']) ?>" target="_blank" class="flex items-center space-x-1 text-[10px] text-blue-400 hover:text-blue-300 bg-blue-500/20 px-2 py-0.5 rounded-lg flex-shrink-0">
|
||||
<i data-lucide="video" class="w-3 h-3"></i>
|
||||
<span>Deelnemen</span>
|
||||
</a>
|
||||
<?php if (!empty($evt['meeting_url'])): ?>
|
||||
<a href="<?= htmlspecialchars($evt['meeting_url']) ?>" target="_blank" class="flex items-center space-x-1 text-[10px] text-blue-300 hover:text-white bg-blue-600/30 hover:bg-blue-600/50 px-2 py-0.5 rounded-lg flex-shrink-0 transition">
|
||||
<i data-lucide="video" class="w-3 h-3"></i>
|
||||
<span>Deelnemen</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@@ -374,7 +521,6 @@ function setTimerPreset(minutes) {
|
||||
totalDuration = minutes * 60;
|
||||
updateTimerDisplay();
|
||||
|
||||
// Toggle styling
|
||||
document.getElementById('timerToggleText').innerText = 'Start Focus';
|
||||
document.getElementById('btnPreset25').className = minutes === 25 ? 'px-2.5 py-1 rounded-lg font-bold bg-emerald-500 text-slate-950' : 'px-2.5 py-1 rounded-lg text-slate-400 hover:text-slate-200';
|
||||
document.getElementById('btnPreset50').className = minutes === 50 ? 'px-2.5 py-1 rounded-lg font-bold bg-emerald-500 text-slate-950' : 'px-2.5 py-1 rounded-lg text-slate-400 hover:text-slate-200';
|
||||
@@ -470,6 +616,40 @@ async function submitNewTask() {
|
||||
}
|
||||
}
|
||||
|
||||
async function quickImportJira(btn, title, jiraKey, url) {
|
||||
const originalHtml = btn.innerHTML;
|
||||
btn.innerText = '...';
|
||||
btn.disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/planner.php?action=add_task', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
title: `[${jiraKey}] ${title}`,
|
||||
priority: 'P1',
|
||||
source: 'JIRA',
|
||||
external_id: jiraKey,
|
||||
external_url: url,
|
||||
date: '<?= $today ?>'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
btn.innerHTML = '<i data-lucide="check" class="w-3 h-3"></i>';
|
||||
location.reload();
|
||||
} else {
|
||||
btn.innerHTML = originalHtml;
|
||||
btn.disabled = false;
|
||||
alert(data.error || 'Kon taak niet toevoegen');
|
||||
}
|
||||
} catch(err) {
|
||||
btn.innerHTML = originalHtml;
|
||||
btn.disabled = false;
|
||||
alert('Netwerkfout bij toevoegen');
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleTask(taskId, isCompleted) {
|
||||
await fetch('/api/planner.php?action=toggle_task', {
|
||||
method: 'POST',
|
||||
@@ -491,4 +671,3 @@ async function deleteTask(taskId) {
|
||||
</script>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user