Upload files to "/"
This commit is contained in:
commit
90859a47ee
791
todolist.php
Normal file
791
todolist.php
Normal file
@ -0,0 +1,791 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Database configuration for PostgreSQL (menggunakan konfigurasi dari dokumen)
|
||||
$host = '202.46.28.160';
|
||||
$port = '45432';
|
||||
$username = '5803024011';
|
||||
$password = 'pw5803024011';
|
||||
$database = 'tgs01_5803024011';
|
||||
|
||||
try {
|
||||
// Create PDO connection for PostgreSQL
|
||||
$dsn = "pgsql:host=$host;port=$port;dbname=$database";
|
||||
$pdo = new PDO($dsn, $username, $password, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
|
||||
]);
|
||||
|
||||
// Create tables if they don't exist
|
||||
$create_groups_table = "CREATE TABLE IF NOT EXISTS groups (
|
||||
group_id SERIAL PRIMARY KEY,
|
||||
group_name VARCHAR(100) NOT NULL
|
||||
)";
|
||||
|
||||
$create_tasks_table = "CREATE TABLE IF NOT EXISTS tasks (
|
||||
task_id SERIAL PRIMARY KEY,
|
||||
task_name VARCHAR(255) NOT NULL,
|
||||
task_description TEXT,
|
||||
group_id INTEGER NOT NULL,
|
||||
is_done BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE CASCADE
|
||||
)";
|
||||
|
||||
$pdo->exec($create_groups_table);
|
||||
$pdo->exec($create_tasks_table);
|
||||
|
||||
} catch(PDOException $e) {
|
||||
die("Connection failed: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if (isset($_POST['add_group'])) {
|
||||
$group_name = trim($_POST['group_name']);
|
||||
if (!empty($group_name)) {
|
||||
$stmt = $pdo->prepare("INSERT INTO groups (group_name) VALUES (?)");
|
||||
$stmt->execute([$group_name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['add_task'])) {
|
||||
$task_name = trim($_POST['task_name']);
|
||||
$task_description = trim($_POST['task_description']);
|
||||
$group_id = $_POST['group_id'];
|
||||
|
||||
if (!empty($task_name) && !empty($group_id)) {
|
||||
$stmt = $pdo->prepare("INSERT INTO tasks (task_name, task_description, group_id) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$task_name, $task_description, $group_id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['update_task'])) {
|
||||
$task_id = $_POST['task_id'];
|
||||
$task_name = trim($_POST['task_name']);
|
||||
$task_description = trim($_POST['task_description']);
|
||||
$group_id = $_POST['group_id'];
|
||||
|
||||
if (!empty($task_name) && !empty($group_id)) {
|
||||
$stmt = $pdo->prepare("UPDATE tasks SET task_name = ?, task_description = ?, group_id = ? WHERE task_id = ?");
|
||||
$stmt->execute([$task_name, $task_description, $group_id, $task_id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['toggle_task'])) {
|
||||
$task_id = $_POST['task_id'];
|
||||
$stmt = $pdo->prepare("UPDATE tasks SET is_done = NOT is_done WHERE task_id = ?");
|
||||
$stmt->execute([$task_id]);
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_task'])) {
|
||||
$task_id = $_POST['delete_task'];
|
||||
$stmt = $pdo->prepare("DELETE FROM tasks WHERE task_id = ?");
|
||||
$stmt->execute([$task_id]);
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_group'])) {
|
||||
$group_id = $_POST['delete_group'];
|
||||
$stmt = $pdo->prepare("DELETE FROM groups WHERE group_id = ?");
|
||||
$stmt->execute([$group_id]);
|
||||
}
|
||||
|
||||
// Redirect to prevent form resubmission
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get task to edit if requested
|
||||
$edit_task = null;
|
||||
if (isset($_GET['edit_task'])) {
|
||||
$task_id = $_GET['edit_task'];
|
||||
$stmt = $pdo->prepare("SELECT * FROM tasks WHERE task_id = ?");
|
||||
$stmt->execute([$task_id]);
|
||||
$edit_task = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// Fetch groups and tasks organized by groups
|
||||
$groups = $pdo->query("SELECT * FROM groups ORDER BY group_name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch tasks grouped by group_id
|
||||
$tasks_by_group = [];
|
||||
if (!empty($groups)) {
|
||||
$stmt = $pdo->query("SELECT t.*, g.group_name FROM tasks t
|
||||
JOIN groups g ON t.group_id = g.group_id
|
||||
ORDER BY t.is_done ASC, t.created_at DESC");
|
||||
$all_tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($all_tasks as $task) {
|
||||
$tasks_by_group[$task['group_id']][] = $task;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
$total_tasks = 0;
|
||||
$completed_tasks = 0;
|
||||
foreach ($tasks_by_group as $tasks) {
|
||||
foreach ($tasks as $task) {
|
||||
$total_tasks++;
|
||||
if ($task['is_done']) $completed_tasks++;
|
||||
}
|
||||
}
|
||||
$pending_tasks = $total_tasks - $completed_tasks;
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Royal Todo List</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Cormorant+Garamond:wght@300;400;600&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #0f0f0f 100%);
|
||||
color: #f4e4bc;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image:
|
||||
radial-gradient(circle at 25% 25%, rgba(212, 175, 55, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 75% 75%, rgba(212, 175, 55, 0.05) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
color: #d4af37;
|
||||
text-shadow: 0 0 20px rgba(212, 175, 55, 0.5);
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.3rem;
|
||||
color: #f4e4bc;
|
||||
font-weight: 300;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.royal-divider {
|
||||
width: 200px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #d4af37, transparent);
|
||||
margin: 20px auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.royal-divider::before {
|
||||
content: '♦';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #0f0f0f;
|
||||
color: #d4af37;
|
||||
padding: 0 10px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid #d4af37;
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 8px 32px rgba(212, 175, 55, 0.1);
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 1.8rem;
|
||||
color: #d4af37;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #d4af37;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
input, textarea, select {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 2px solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 8px;
|
||||
background: rgba(15, 15, 15, 0.7);
|
||||
color: #f4e4bc;
|
||||
font-size: 1rem;
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: #d4af37;
|
||||
box-shadow: 0 0 15px rgba(212, 175, 55, 0.3);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: linear-gradient(145deg, #d4af37, #b8941f);
|
||||
color: #0f0f0f;
|
||||
border: none;
|
||||
padding: 12px 25px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Cinzel', serif;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: linear-gradient(145deg, #b8941f, #d4af37);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 6px 12px;
|
||||
font-size: 0.8rem;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: linear-gradient(145deg, #4a4a4a, #2a2a2a);
|
||||
color: #f4e4bc;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: linear-gradient(145deg, #2a2a2a, #4a4a4a);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(145deg, #8b0000, #a50000);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: linear-gradient(145deg, #a50000, #8b0000);
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
margin: 30px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #d4af37;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(244, 228, 188, 0.8);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.group-container {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
background: linear-gradient(145deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.1));
|
||||
border: 1px solid rgba(212, 175, 55, 0.5);
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-name {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #d4af37;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.group-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tasks-container {
|
||||
background: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid rgba(212, 175, 55, 0.3);
|
||||
border-top: none;
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
background: rgba(15, 15, 15, 0.7);
|
||||
border: 1px solid rgba(212, 175, 55, 0.2);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
border-color: rgba(212, 175, 55, 0.5);
|
||||
box-shadow: 0 5px 15px rgba(212, 175, 55, 0.1);
|
||||
}
|
||||
|
||||
.task-item.completed {
|
||||
opacity: 0.6;
|
||||
background: rgba(15, 15, 15, 0.4);
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.task-name {
|
||||
font-family: 'Cinzel', serif;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: #d4af37;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.task-name.completed {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.task-description {
|
||||
color: #f4e4bc;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(244, 228, 188, 0.6);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.no-tasks {
|
||||
text-align: center;
|
||||
color: rgba(244, 228, 188, 0.6);
|
||||
font-style: italic;
|
||||
font-size: 1.1rem;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
background: rgba(212, 175, 55, 0.1);
|
||||
border: 2px solid #d4af37;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.ornament {
|
||||
color: #d4af37;
|
||||
font-size: 1.5rem;
|
||||
margin: 0 10px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.crown {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 15px;
|
||||
color: #d4af37;
|
||||
font-size: 1rem;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.group-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stats {
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1 class="title">
|
||||
<span class="ornament">♔</span>
|
||||
Royal Todo List
|
||||
<span class="ornament">♔</span>
|
||||
</h1>
|
||||
<p class="subtitle">Manage your tasks with royal elegance</p>
|
||||
<div class="royal-divider"></div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo count($groups); ?></span>
|
||||
<span class="stat-label">Groups</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo $total_tasks; ?></span>
|
||||
<span class="stat-label">Total Tasks</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo $completed_tasks; ?></span>
|
||||
<span class="stat-label">Completed</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number"><?php echo $pending_tasks; ?></span>
|
||||
<span class="stat-label">Pending</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Task Form (appears when editing) -->
|
||||
<?php if ($edit_task): ?>
|
||||
<div class="edit-form">
|
||||
<h2 class="form-title">♛ Edit Task</h2>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="task_id" value="<?php echo $edit_task['task_id']; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_task_name">Task Name</label>
|
||||
<input type="text" id="edit_task_name" name="task_name" value="<?php echo htmlspecialchars($edit_task['task_name']); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_task_description">Description</label>
|
||||
<textarea id="edit_task_description" name="task_description"><?php echo htmlspecialchars($edit_task['task_description']); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit_group_id">Group</label>
|
||||
<select id="edit_group_id" name="group_id" required>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<option value="<?php echo $group['group_id']; ?>" <?php echo $group['group_id'] == $edit_task['group_id'] ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($group['group_name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" name="update_task" class="btn">Update Task</button>
|
||||
<a href="<?php echo $_SERVER['PHP_SELF']; ?>" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Add Group Form -->
|
||||
<div class="form-section">
|
||||
<h2 class="form-title">♛ Create New Group</h2>
|
||||
<form method="POST">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="group_name">Group Name</label>
|
||||
<input type="text" id="group_name" name="group_name" required placeholder="e.g., Work, Personal, Urgent...">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" name="add_group" class="btn">Create Group</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Add Task Form -->
|
||||
<?php if (!empty($groups)): ?>
|
||||
<div class="form-section">
|
||||
<h2 class="form-title">♜ Add New Task</h2>
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label for="task_name">Task Name</label>
|
||||
<input type="text" id="task_name" name="task_name" required placeholder="Enter task name...">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="task_description">Description (Optional)</label>
|
||||
<textarea id="task_description" name="task_description" placeholder="Enter task description..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="group_id">Select Group</label>
|
||||
<select id="group_id" name="group_id" required>
|
||||
<option value="">Choose a group...</option>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<option value="<?php echo $group['group_id']; ?>">
|
||||
<?php echo htmlspecialchars($group['group_name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" name="add_task" class="btn">Add Task</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Display Groups and Tasks -->
|
||||
<?php if (empty($groups)): ?>
|
||||
<div class="form-section">
|
||||
<div class="no-tasks">
|
||||
<span style="font-size: 3rem; display: block; margin-bottom: 20px;">👑</span>
|
||||
Your royal kingdom awaits! Create your first group to start organizing tasks.
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<div class="group-container">
|
||||
<div class="group-header">
|
||||
<h3 class="group-name">♠ <?php echo htmlspecialchars($group['group_name']); ?></h3>
|
||||
<div class="group-actions">
|
||||
<form method="POST" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this group and all its tasks?')">
|
||||
<button type="submit" name="delete_group" value="<?php echo $group['group_id']; ?>" class="btn btn-small btn-danger">
|
||||
Delete Group
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tasks-container">
|
||||
<?php if (empty($tasks_by_group[$group['group_id']])): ?>
|
||||
<div class="no-tasks">
|
||||
No tasks in this group yet. Add some tasks to get started!
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($tasks_by_group[$group['group_id']] as $task): ?>
|
||||
<div class="task-item <?php echo $task['is_done'] ? 'completed' : ''; ?>">
|
||||
<div class="crown">♔</div>
|
||||
|
||||
<div class="task-header">
|
||||
<h4 class="task-name <?php echo $task['is_done'] ? 'completed' : ''; ?>">
|
||||
<?php echo htmlspecialchars($task['task_name']); ?>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($task['task_description'])): ?>
|
||||
<div class="task-description">
|
||||
<?php echo nl2br(htmlspecialchars($task['task_description'])); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="task-meta">
|
||||
Created: <?php echo date('M j, Y g:i A', strtotime($task['created_at'])); ?>
|
||||
| Status: <strong><?php echo $task['is_done'] ? 'Completed' : 'Pending'; ?></strong>
|
||||
</div>
|
||||
|
||||
<div class="task-actions">
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="task_id" value="<?php echo $task['task_id']; ?>">
|
||||
<button type="submit" name="toggle_task" class="btn btn-small">
|
||||
<?php echo $task['is_done'] ? '↶ Undo' : '✓ Complete'; ?>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a href="<?php echo $_SERVER['PHP_SELF']; ?>?edit_task=<?php echo $task['task_id']; ?>" class="btn btn-small btn-secondary">
|
||||
✎ Edit
|
||||
</a>
|
||||
|
||||
<form method="POST" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this task?')">
|
||||
<button type="submit" name="delete_task" value="<?php echo $task['task_id']; ?>" class="btn btn-small btn-danger">
|
||||
🗑 Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Add royal sparkle effects
|
||||
function createSparkle() {
|
||||
const sparkle = document.createElement('div');
|
||||
sparkle.style.position = 'fixed';
|
||||
sparkle.style.width = '4px';
|
||||
sparkle.style.height = '4px';
|
||||
sparkle.style.background = '#d4af37';
|
||||
sparkle.style.borderRadius = '50%';
|
||||
sparkle.style.pointerEvents = 'none';
|
||||
sparkle.style.opacity = Math.random();
|
||||
sparkle.style.left = Math.random() * window.innerWidth + 'px';
|
||||
sparkle.style.top = Math.random() * window.innerHeight + 'px';
|
||||
sparkle.style.zIndex = '1000';
|
||||
sparkle.style.boxShadow = '0 0 6px #d4af37';
|
||||
|
||||
document.body.appendChild(sparkle);
|
||||
|
||||
// Animate sparkle
|
||||
const animation = sparkle.animate([
|
||||
{ transform: 'translateY(0px)', opacity: sparkle.style.opacity },
|
||||
{ transform: 'translateY(-100px)', opacity: 0 }
|
||||
], {
|
||||
duration: 3000,
|
||||
easing: 'ease-out'
|
||||
});
|
||||
|
||||
animation.onfinish = () => sparkle.remove();
|
||||
}
|
||||
|
||||
// Create sparkles periodically
|
||||
setInterval(createSparkle, 2500);
|
||||
|
||||
// Add form validation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const requiredFields = form.querySelectorAll('[required]');
|
||||
let isValid = true;
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
if (!field.value.trim()) {
|
||||
field.style.borderColor = '#8b0000';
|
||||
field.style.boxShadow = '0 0 10px rgba(139, 0, 0, 0.3)';
|
||||
isValid = false;
|
||||
} else {
|
||||
field.style.borderColor = 'rgba(212, 175, 55, 0.3)';
|
||||
field.style.boxShadow = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault();
|
||||
alert('Please fill in all required fields.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-focus on form inputs
|
||||
const inputs = document.querySelectorAll('input, textarea, select');
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('focus', function() {
|
||||
this.style.transform = 'scale(1.02)';
|
||||
});
|
||||
|
||||
input.addEventListener('blur', function() {
|
||||
this.style.transform = 'scale(1)';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user