diff --git a/add_project.php b/add_project.php
index 7975510..db6204f 100644
--- a/add_project.php
+++ b/add_project.php
@@ -6,6 +6,13 @@ header('Content-Type: application/json');
require_login();
$user_id = current_user_id();
+$profile_id = $_SESSION['active_profile_id'] ?? null;
+
+if (!$profile_id) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'No active profile']);
+ exit;
+}
$data = json_decode(file_get_contents('php://input'), true);
$name = trim($data['name'] ?? '');
@@ -16,7 +23,7 @@ if ($name === '') {
exit;
}
-$stmt = $pdo->prepare("INSERT INTO projects (user_id, name) VALUES (?, ?)");
-$stmt->execute([$user_id, $name]);
+$stmt = $pdo->prepare("INSERT INTO projects (user_id, profile_id, name) VALUES (?, ?, ?)");
+$stmt->execute([$user_id, $profile_id, $name]);
echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
diff --git a/get_data.php b/get_data.php
index 4955210..9233f0a 100644
--- a/get_data.php
+++ b/get_data.php
@@ -6,9 +6,16 @@ header('Content-Type: application/json');
require_login();
$user_id = current_user_id();
+$profile_id = $_SESSION['active_profile_id'] ?? null;
-$stmt = $pdo->prepare("SELECT * FROM projects WHERE user_id = ? ORDER BY sort_order ASC");
-$stmt->execute([$user_id]);
+if (!$profile_id) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'No active profile']);
+ exit;
+}
+
+$stmt = $pdo->prepare("SELECT * FROM projects WHERE user_id = ? AND profile_id = ? ORDER BY sort_order ASC");
+$stmt->execute([$user_id, $profile_id]);
$projects = $stmt->fetchAll();
foreach ($projects as &$project) {
@@ -23,4 +30,4 @@ foreach ($projects as &$project) {
}
}
-echo json_encode($projects);
\ No newline at end of file
+echo json_encode($projects);
diff --git a/get_profiles.php b/get_profiles.php
new file mode 100644
index 0000000..3d91a32
--- /dev/null
+++ b/get_profiles.php
@@ -0,0 +1,18 @@
+prepare("SELECT id, name, is_default FROM profiles WHERE user_id = ? ORDER BY is_default DESC, name ASC");
+$stmt->execute([$user_id]);
+$profiles = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+echo json_encode([
+ 'success' => true,
+ 'active_profile_id' => $_SESSION['active_profile_id'] ?? null,
+ 'profiles' => $profiles
+]);
diff --git a/index.php b/index.php
index 1b89a1a..e3851b9 100644
--- a/index.php
+++ b/index.php
@@ -144,8 +144,19 @@
justify-content: center;
}
+ .profile-select {
+ height: 32px; /* match your header-action-btn buttons */
+ font-size: 1.5rem;
+ font-weight: 700; /* or 700 for stronger bold */
+ padding-right: 2.5rem; /* key: reserve space for the arrow */
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
.add-project-btn {
- font-size: 1.4rem;
+ font-size: 1.5rem;
+ font-weight: 600; /* or 700 for stronger bold */
padding: 0.5rem 1rem;
}
@@ -259,37 +270,36 @@
-
@@ -538,131 +548,149 @@
favicon.type = "image/svg+xml";
});
});
-}
+ }
+ function loadProfiles() {
+ fetch('get_profiles.php')
+ .then(r => r.json())
+ .then(data => {
+ if (!data.success) return;
+ const sel = document.getElementById('profileSelect');
+ sel.innerHTML = '';
+ data.profiles.forEach(p => {
+ const opt = document.createElement('option');
+ opt.value = p.id;
+ opt.textContent = p.name;
+ sel.appendChild(opt);
+ });
+ if (data.active_profile_id) {
+ sel.value = String(data.active_profile_id);
+ }
+ });
+ }
- function fetchProjects() {
- fetch('get_data.php')
- .then(res => res.json())
- .then(data => {
+ function fetchProjects() {
+ fetch('get_data.php')
+ .then(res => res.json())
+ .then(data => {
- if (!Array.isArray(data)) {
- if (data && data.error === 'Not authenticated') {
- new bootstrap.Modal(document.getElementById('loginModal')).show();
- return;
- }
- console.error('get_data.php returned non-array:', data);
+ if (!Array.isArray(data)) {
+ if (data && data.error === 'Not authenticated') {
+ new bootstrap.Modal(document.getElementById('loginModal')).show();
return;
}
+ console.error('get_data.php returned non-array:', data);
+ return;
+ }
- const container = document.getElementById('projectGrid');
- container.innerHTML = '';
+ const container = document.getElementById('projectGrid');
+ container.innerHTML = '';
- data.forEach((project, idx) => {
-
- const projectId = `project-${project.id}`;
- const colorClass = `project-color-${idx % 10}`;
-
- const col = document.createElement('div');
+ data.forEach((project, idx) => {
+
+ const projectId = `project-${project.id}`;
+ const colorClass = `project-color-${idx % 10}`;
+
+ const col = document.createElement('div');
- col.className = `col-12 col-sm-6 col-md-4 col-lg-2 col-xl-2 col-xxl-1 g-2 m-0 project-column`;
- col.dataset.projectId = project.id;
- col.classList.add("draggable-project");
- col.innerHTML = `
-
-
-
- ${project.tasks.map(task => `
-
-
${task.name}
-
-
-
-
-
-
- ${task.subtasks.map(sub => `
-
- ${sub.name}
-
-
- `).join('')}
-
- `).join('')}
-
-
- `;
+ col.className = `col-12 col-sm-6 col-md-4 col-lg-2 col-xl-2 col-xxl-1 g-2 m-0 project-column`;
+ col.dataset.projectId = project.id;
+ col.classList.add("draggable-project");
+ col.innerHTML = `
+
+
+
+ ${project.tasks.map(task => `
+
+
${task.name}
+
+
+
+
+
+
+ ${task.subtasks.map(sub => `
+
+ ${sub.name}
+
+
+ `).join('')}
+
+ `).join('')}
+
+
+ `;
- container.appendChild(col);
- });
+ container.appendChild(col);
+ });
-// Create the drop indicator element
-const placeholder = document.createElement('div');
-placeholder.className = 'project-column';
-placeholder.innerHTML = ``;
+ // Create the drop indicator element
+ const placeholder = document.createElement('div');
+ placeholder.className = 'project-column';
+ placeholder.innerHTML = ``;
-// Setup drag events for project reordering
-document.querySelectorAll('.project-column').forEach(col => {
- col.setAttribute('draggable', 'true');
+ // Setup drag events for project reordering
+ document.querySelectorAll('.project-column').forEach(col => {
+ col.setAttribute('draggable', 'true');
- col.addEventListener('dragstart', (e) => {
- e.dataTransfer.setData('text/plain', col.dataset.projectId);
- col.classList.add('dragging');
- });
+ col.addEventListener('dragstart', (e) => {
+ e.dataTransfer.setData('text/plain', col.dataset.projectId);
+ col.classList.add('dragging');
+ });
- col.addEventListener('dragend', () => {
- col.classList.remove('dragging');
- placeholder.remove();
- });
+ col.addEventListener('dragend', () => {
+ col.classList.remove('dragging');
+ placeholder.remove();
+ });
- col.addEventListener('dragover', (e) => {
- e.preventDefault();
- const dragging = document.querySelector('.dragging');
- if (!dragging || dragging === col) return;
+ col.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ const dragging = document.querySelector('.dragging');
+ if (!dragging || dragging === col) return;
- const bounding = col.getBoundingClientRect();
- const offset = e.clientY - bounding.top;
+ const bounding = col.getBoundingClientRect();
+ const offset = e.clientY - bounding.top;
- if (offset < bounding.height / 2) {
- col.parentNode.insertBefore(placeholder, col);
- } else {
- col.parentNode.insertBefore(placeholder, col.nextSibling);
- }
- });
+ if (offset < bounding.height / 2) {
+ col.parentNode.insertBefore(placeholder, col);
+ } else {
+ col.parentNode.insertBefore(placeholder, col.nextSibling);
+ }
+ });
- col.addEventListener('drop', (e) => {
- e.preventDefault();
- const dragging = document.querySelector('.dragging');
- if (placeholder && placeholder.parentNode) {
- placeholder.parentNode.insertBefore(dragging, placeholder);
- placeholder.remove();
- updateProjectOrder(); // This already exists in your code
- }
- });
-});
+ col.addEventListener('drop', (e) => {
+ e.preventDefault();
+ const dragging = document.querySelector('.dragging');
+ if (placeholder && placeholder.parentNode) {
+ placeholder.parentNode.insertBefore(dragging, placeholder);
+ placeholder.remove();
+ updateProjectOrder(); // This already exists in your code
+ }
+ });
+ });
// Enable drag-and-drop sorting
@@ -956,7 +984,23 @@ document.querySelectorAll('.project-column').forEach(col => {
err.style.display = 'block';
});
});
-
+
+ document.getElementById('profileSelect').addEventListener('change', () => {
+ const profileId = parseInt(document.getElementById('profileSelect').value, 10);
+
+ fetch('set_profile.php', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ profile_id: profileId })
+ })
+ .then(r => r.json())
+ .then(data => {
+ if (!data.success) return;
+ fetchProjects();
+ });
+ });
+
+
document.addEventListener('DOMContentLoaded', () => {
loadSettings();
@@ -979,6 +1023,7 @@ document.querySelectorAll('.project-column').forEach(col => {
document.querySelectorAll('.settings-btn').forEach(b => b.style.display = 'none');
}
+ loadProfiles();
fetchProjects();
});
});
diff --git a/login.php b/login.php
index 1f24908..dc9b156 100644
--- a/login.php
+++ b/login.php
@@ -33,6 +33,27 @@ $_SESSION['user'] = [
'can_manage_settings' => intval($user['can_manage_settings']),
];
+// Set active profile for this session (default profile if available)
+$stmt = $pdo->prepare("SELECT id FROM profiles WHERE user_id = ? AND is_default = 1 LIMIT 1");
+$stmt->execute([$_SESSION['user']['id']]);
+$profileId = $stmt->fetchColumn();
+
+if (!$profileId) {
+ // Fallback to first profile
+ $stmt = $pdo->prepare("SELECT id FROM profiles WHERE user_id = ? ORDER BY id ASC LIMIT 1");
+ $stmt->execute([$_SESSION['user']['id']]);
+ $profileId = $stmt->fetchColumn();
+}
+
+if (!$profileId) {
+ // Last-resort: create a default profile if none exist (useful for new users)
+ $stmt = $pdo->prepare("INSERT INTO profiles (user_id, name, is_default) VALUES (?, 'Default', 1)");
+ $stmt->execute([$_SESSION['user']['id']]);
+ $profileId = $pdo->lastInsertId();
+}
+
+$_SESSION['active_profile_id'] = (int)$profileId;
+
echo json_encode([
'success' => true,
'user' => [
@@ -40,5 +61,6 @@ echo json_encode([
'email' => $_SESSION['user']['email'],
'role' => $_SESSION['user']['role'],
'can_manage_settings' => $_SESSION['user']['can_manage_settings'],
- ]
+ ],
+ 'active_profile_id' => $_SESSION['active_profile_id']
]);
diff --git a/register.php b/register.php
index 49d6020..56f1153 100644
--- a/register.php
+++ b/register.php
@@ -33,10 +33,24 @@ if ($role_id <= 0) {
}
try {
+ $pdo->beginTransaction();
+
+ // Create user
$stmt = $pdo->prepare("INSERT INTO users (email, password_hash, role_id) VALUES (?, ?, ?)");
$stmt->execute([$email, $hash, $role_id]);
+ $userId = (int)$pdo->lastInsertId();
+
+ // Create default profile for this user
+ $stmt = $pdo->prepare("INSERT INTO profiles (user_id, name, is_default) VALUES (?, 'Default', 1)");
+ $stmt->execute([$userId]);
+
+ $pdo->commit();
+
echo json_encode(['success' => true]);
+
} catch (Throwable $e) {
+ if ($pdo->inTransaction()) $pdo->rollBack();
+
http_response_code(409);
echo json_encode(['success' => false, 'error' => 'Account already exists']);
}
diff --git a/set_profile.php b/set_profile.php
new file mode 100644
index 0000000..552ad31
--- /dev/null
+++ b/set_profile.php
@@ -0,0 +1,30 @@
+ false, 'error' => 'Invalid profile_id']);
+ exit;
+}
+
+$stmt = $pdo->prepare("SELECT id FROM profiles WHERE id = ? AND user_id = ?");
+$stmt->execute([$profile_id, $user_id]);
+
+if (!$stmt->fetchColumn()) {
+ http_response_code(403);
+ echo json_encode(['success' => false, 'error' => 'Profile not allowed']);
+ exit;
+}
+
+$_SESSION['active_profile_id'] = $profile_id;
+
+echo json_encode(['success' => true, 'active_profile_id' => $profile_id]);