Update Sprint & Task Management style
This commit is contained in:
@@ -6,7 +6,8 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash,
|
||||
from models import (db, Company, User, Role, Team, Project, TimeEntry, SystemSettings,
|
||||
SystemEvent, BrandingSettings, Task, SubTask, TaskDependency, Sprint,
|
||||
Comment, UserPreferences, UserDashboard, WorkConfig, CompanySettings,
|
||||
CompanyWorkConfig, ProjectCategory)
|
||||
CompanyWorkConfig, ProjectCategory, Note, NoteFolder, NoteShare,
|
||||
Announcement, CompanyInvitation)
|
||||
from routes.auth import system_admin_required
|
||||
from flask import session
|
||||
from sqlalchemy import func
|
||||
@@ -226,6 +227,34 @@ def delete_company(company_id):
|
||||
db.session.query(User.id).filter(User.company_id == company_id)
|
||||
)).delete(synchronize_session=False)
|
||||
|
||||
# Delete notes and note-related data
|
||||
user_ids_subquery = db.session.query(User.id).filter(User.company_id == company_id).subquery()
|
||||
|
||||
# Delete note shares
|
||||
NoteShare.query.filter(NoteShare.created_by_id.in_(user_ids_subquery)).delete(synchronize_session=False)
|
||||
|
||||
# Delete notes
|
||||
Note.query.filter(Note.created_by_id.in_(user_ids_subquery)).delete(synchronize_session=False)
|
||||
|
||||
# Delete note folders
|
||||
NoteFolder.query.filter(NoteFolder.created_by_id.in_(user_ids_subquery)).delete(synchronize_session=False)
|
||||
|
||||
# Delete announcements
|
||||
Announcement.query.filter(Announcement.created_by_id.in_(user_ids_subquery)).delete(synchronize_session=False)
|
||||
|
||||
# Delete invitations
|
||||
CompanyInvitation.query.filter(
|
||||
(CompanyInvitation.invited_by_id.in_(user_ids_subquery)) |
|
||||
(CompanyInvitation.accepted_by_user_id.in_(user_ids_subquery))
|
||||
).delete(synchronize_session=False)
|
||||
|
||||
# Delete system events associated with users from this company
|
||||
SystemEvent.query.filter(SystemEvent.user_id.in_(user_ids_subquery)).delete(synchronize_session=False)
|
||||
|
||||
# Clear branding settings updated_by references
|
||||
BrandingSettings.query.filter(BrandingSettings.updated_by_id.in_(user_ids_subquery)).update(
|
||||
{BrandingSettings.updated_by_id: None}, synchronize_session=False)
|
||||
|
||||
# Delete users
|
||||
User.query.filter_by(company_id=company_id).delete()
|
||||
|
||||
|
||||
@@ -530,8 +530,8 @@ button {
|
||||
|
||||
/* Button Outline Variants */
|
||||
.btn-outline {
|
||||
border: 1px solid #007bff;
|
||||
color: #007bff;
|
||||
border: 1px solid #6c757d;
|
||||
color: #495057;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="category-card" data-category-id="{{ category.id }}">
|
||||
<div class="category-header" style="background: linear-gradient(135deg, {{ category.color }}20 0%, {{ category.color }}10 100%); border-left: 4px solid {{ category.color }};">
|
||||
<div class="category-title">
|
||||
<span class="category-icon">{{ category.icon or '<i class="ti ti-folder"></i>' }}</span>
|
||||
<span class="category-icon">{{ category.icon|safe if category.icon else '<i class="ti ti-folder"></i>'|safe }}</span>
|
||||
<span class="category-name">{{ category.name }}</span>
|
||||
</div>
|
||||
<div class="category-stats">
|
||||
@@ -141,7 +141,7 @@
|
||||
{% if project.category %}
|
||||
<div class="project-category">
|
||||
<span class="category-badge" style="background-color: {{ project.category.color }}20; color: {{ project.category.color }};">
|
||||
{{ project.category.icon or '<i class="ti ti-folder"></i>' }} {{ project.category.name }}
|
||||
{{ project.category.icon|safe if project.category.icon else '<i class="ti ti-folder"></i>'|safe }} {{ project.category.name }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -226,7 +226,7 @@
|
||||
<td>
|
||||
{% if project.category %}
|
||||
<span class="category-badge" style="background-color: {{ project.category.color }}20; color: {{ project.category.color }};">
|
||||
{{ project.category.icon or '<i class="ti ti-folder"></i>' }} {{ project.category.name }}
|
||||
{{ project.category.icon|safe if project.category.icon else '<i class="ti ti-folder"></i>'|safe }} {{ project.category.name }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
|
||||
@@ -2,8 +2,16 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="page-container timetrack-container">
|
||||
<div class="page-header analytics-header">
|
||||
<h2><i class="ti ti-chart-bar"></i> Time Analytics</h2>
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<h1 class="page-title">
|
||||
<span class="page-icon"><i class="ti ti-chart-bar"></i></span>
|
||||
Time Analytics
|
||||
</h1>
|
||||
<p class="page-subtitle">Analyze time tracking data and generate insights</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="mode-switcher">
|
||||
<button class="mode-btn {% if mode == 'personal' %}active{% endif %}"
|
||||
onclick="switchMode('personal')">Personal</button>
|
||||
@@ -13,6 +21,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unified Filter Panel -->
|
||||
<div class="filter-panel">
|
||||
|
||||
@@ -1,43 +1,56 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="management-container sprint-management-container">
|
||||
<div class="page-container">
|
||||
<!-- Header Section -->
|
||||
<div class="management-header sprint-header">
|
||||
<h1>🏃♂️ Sprint Management</h1>
|
||||
<div class="management-controls sprint-controls">
|
||||
<!-- View Switcher -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<h1 class="page-title">
|
||||
<span class="page-icon"><i class="ti ti-run"></i></span>
|
||||
Sprint Management
|
||||
</h1>
|
||||
<p class="page-subtitle">Manage sprints and track progress</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button id="refresh-sprints" class="btn btn-secondary">
|
||||
<i class="ti ti-refresh"></i>
|
||||
Refresh
|
||||
</button>
|
||||
<button id="add-sprint-btn" class="btn btn-primary">
|
||||
<i class="ti ti-plus"></i>
|
||||
New Sprint
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Section -->
|
||||
<div class="filter-section">
|
||||
<div class="view-switcher">
|
||||
<button class="view-btn active" data-view="active">Active Sprints</button>
|
||||
<button class="view-btn" data-view="all">All Sprints</button>
|
||||
<button class="view-btn" data-view="planning">Planning</button>
|
||||
<button class="view-btn" data-view="completed">Completed</button>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="management-actions sprint-actions">
|
||||
<button id="add-sprint-btn" class="btn btn-primary">+ New Sprint</button>
|
||||
<button id="refresh-sprints" class="btn btn-secondary">🔄 Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sprint Statistics -->
|
||||
<div class="management-stats sprint-stats">
|
||||
<div class="stats-section">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-sprints">0</div>
|
||||
<div class="stat-value" id="total-sprints">0</div>
|
||||
<div class="stat-label">Total Sprints</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="active-sprints">0</div>
|
||||
<div class="stat-value" id="active-sprints">0</div>
|
||||
<div class="stat-label">Active</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="completed-sprints">0</div>
|
||||
<div class="stat-value" id="completed-sprints">0</div>
|
||||
<div class="stat-label">Completed</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-tasks">0</div>
|
||||
<div class="stat-value" id="total-tasks">0</div>
|
||||
<div class="stat-label">Total Tasks</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,7 +125,7 @@
|
||||
<div class="hybrid-date-input">
|
||||
<input type="date" id="sprint-start-date-native" class="date-input-native" required>
|
||||
<input type="text" id="sprint-start-date" class="date-input-formatted" required placeholder="{{ "YYYY-MM-DD" if (g.user.preferences.date_format if g.user.preferences else "ISO") == "ISO" else "MM/DD/YYYY" if (g.user.preferences.date_format if g.user.preferences else "ISO") == "US" else "DD/MM/YYYY" if (g.user.preferences.date_format if g.user.preferences else "ISO") in ["EU", "UK"] else "Mon, Dec 25, 2024" }}">
|
||||
<button type="button" class="calendar-picker-btn" onclick="openCalendarPicker('sprint-start-date')" title="Open calendar">📅</button>
|
||||
<button type="button" class="calendar-picker-btn" onclick="openCalendarPicker('sprint-start-date')" title="Open calendar"><i class="ti ti-calendar"></i></button>
|
||||
</div>
|
||||
<div class="date-error" id="sprint-start-date-error" style="display: none; color: #dc3545; font-size: 0.8rem; margin-top: 0.25rem;"></div>
|
||||
</div>
|
||||
@@ -121,7 +134,7 @@
|
||||
<div class="hybrid-date-input">
|
||||
<input type="date" id="sprint-end-date-native" class="date-input-native" required>
|
||||
<input type="text" id="sprint-end-date" class="date-input-formatted" required placeholder="{{ "YYYY-MM-DD" if (g.user.preferences.date_format if g.user.preferences else "ISO") == "ISO" else "MM/DD/YYYY" if (g.user.preferences.date_format if g.user.preferences else "ISO") == "US" else "DD/MM/YYYY" if (g.user.preferences.date_format if g.user.preferences else "ISO") in ["EU", "UK"] else "Mon, Dec 25, 2024" }}">
|
||||
<button type="button" class="calendar-picker-btn" onclick="openCalendarPicker('sprint-end-date')" title="Open calendar">📅</button>
|
||||
<button type="button" class="calendar-picker-btn" onclick="openCalendarPicker('sprint-end-date')" title="Open calendar"><i class="ti ti-calendar"></i></button>
|
||||
</div>
|
||||
<div class="date-error" id="sprint-end-date-error" style="display: none; color: #dc3545; font-size: 0.8rem; margin-top: 0.25rem;"></div>
|
||||
</div>
|
||||
@@ -143,38 +156,23 @@
|
||||
|
||||
<!-- Styles -->
|
||||
<style>
|
||||
.sprint-management-container {
|
||||
padding: 1rem;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* Container styles - using default page spacing */
|
||||
|
||||
.sprint-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/* Header styles handled by common page-header classes */
|
||||
|
||||
.filter-section {
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.sprint-header h1 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.sprint-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.view-switcher {
|
||||
display: flex;
|
||||
background: #f8f9fa;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
@@ -198,37 +196,7 @@
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.sprint-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sprint-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
/* Statistics styles handled by common stats-section classes */
|
||||
|
||||
.sprint-grid {
|
||||
display: grid;
|
||||
@@ -731,7 +699,7 @@ class SprintManager {
|
||||
</div>
|
||||
|
||||
<div class="sprint-dates">
|
||||
📅 ${formatUserDate(sprint.start_date)} - ${formatUserDate(sprint.end_date)}
|
||||
<i class="ti ti-calendar"></i> ${formatUserDate(sprint.start_date)} - ${formatUserDate(sprint.end_date)}
|
||||
${sprint.days_remaining > 0 ? `(${sprint.days_remaining} days left)` : ''}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,12 +1,36 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="management-container task-management-container">
|
||||
<div class="page-container">
|
||||
<!-- Header Section -->
|
||||
<div class="management-header task-header">
|
||||
<h1><i class="ti ti-clipboard-list"></i> Task Management</h1>
|
||||
<div class="management-controls task-controls">
|
||||
<!-- Smart Search -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<h1 class="page-title">
|
||||
<span class="page-icon"><i class="ti ti-clipboard-list"></i></span>
|
||||
Task Management
|
||||
</h1>
|
||||
<p class="page-subtitle">Manage and track all tasks across projects</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button id="refresh-tasks" class="btn btn-secondary">
|
||||
<i class="ti ti-refresh"></i>
|
||||
Refresh
|
||||
</button>
|
||||
<button id="toggle-archived" class="btn btn-outline" title="Show/Hide Archived Tasks">
|
||||
<i class="ti ti-archive"></i>
|
||||
Show Archived
|
||||
</button>
|
||||
<button id="add-task-btn" class="btn btn-primary">
|
||||
<i class="ti ti-plus"></i>
|
||||
Add Task
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Section -->
|
||||
<div class="search-section">
|
||||
<div class="smart-search-container">
|
||||
<div class="smart-search-box">
|
||||
<input type="text" id="smart-search-input" class="smart-search-input" placeholder="Search tasks... (e.g., my-tasks priority:high, project:TimeTrack, overdue)">
|
||||
@@ -16,36 +40,28 @@
|
||||
<!-- Suggestions will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="management-actions task-actions">
|
||||
<button id="add-task-btn" class="btn btn-primary"><i class="ti ti-plus"></i> Add Task</button>
|
||||
<button id="refresh-tasks" class="btn btn-secondary"><i class="ti ti-refresh"></i> Refresh</button>
|
||||
<button id="toggle-archived" class="btn btn-outline" title="Show/Hide Archived Tasks"><i class="ti ti-archive"></i> Show Archived</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Task Statistics -->
|
||||
<div class="management-stats task-stats">
|
||||
<div class="stats-section">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-tasks">0</div>
|
||||
<div class="stat-value" id="total-tasks">0</div>
|
||||
<div class="stat-label">Total Tasks</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="completed-tasks">0</div>
|
||||
<div class="stat-value" id="completed-tasks">0</div>
|
||||
<div class="stat-label">Completed</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="in-progress-tasks">0</div>
|
||||
<div class="stat-value" id="in-progress-tasks">0</div>
|
||||
<div class="stat-label">In Progress</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="overdue-tasks">0</div>
|
||||
<div class="stat-value" id="overdue-tasks">0</div>
|
||||
<div class="stat-label">Overdue</div>
|
||||
</div>
|
||||
<div class="stat-card" id="archived-stat-card" style="display: none;">
|
||||
<div class="stat-number" id="archived-tasks">0</div>
|
||||
<div class="stat-value" id="archived-tasks">0</div>
|
||||
<div class="stat-label">Archived</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,12 +145,26 @@
|
||||
|
||||
<!-- Styles -->
|
||||
<style>
|
||||
/* Header adjustments for Task Management */
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Search Section */
|
||||
.search-section {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Smart Search Styles */
|
||||
.smart-search-container {
|
||||
margin-bottom: 1rem;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.smart-search-box {
|
||||
@@ -237,71 +267,9 @@
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Task Management Layout */
|
||||
.task-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
/* Task Management specific styles removed - using common page styles */
|
||||
|
||||
.task-controls .smart-search-container {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
max-width: 600px;
|
||||
margin-bottom: 0; /* Remove margin to align with buttons */
|
||||
}
|
||||
|
||||
.task-controls .management-actions {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Ensure all buttons and search input have same height */
|
||||
.smart-search-input,
|
||||
.task-controls .btn {
|
||||
height: 38px; /* Standard height for consistency */
|
||||
}
|
||||
|
||||
.task-controls .btn {
|
||||
padding: 0.5rem 1rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap; /* Prevent button text from wrapping */
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 992px) {
|
||||
.task-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.task-controls .smart-search-container {
|
||||
max-width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.task-controls .management-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.task-controls .management-actions {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.task-controls .btn {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
}
|
||||
/* Responsive adjustments handled by common page styles */
|
||||
|
||||
/* Subtask progress styles */
|
||||
.task-subtasks {
|
||||
@@ -336,18 +304,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.task-controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.task-controls .smart-search-container {
|
||||
min-width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user