Fix projects layout

This commit is contained in:
Chris Veleris 2024-09-06 21:53:21 +03:00
parent ff113cfe48
commit 2e30fef430
15 changed files with 164 additions and 83 deletions

View file

@ -10,11 +10,15 @@ class Project < ActiveRecord::Base
validates :name, presence: true
def task_status_counts
status_counts = tasks.group(:status).count
total = status_counts.values.sum
{
total: tasks.count,
in_progress: tasks.where(status: Task.statuses[:in_progress]).count,
done: tasks.where(status: Task.statuses[:done]).count,
not_started: tasks.where(status: Task.statuses[:not_started]).count
total: total,
in_progress: status_counts[Task.statuses[:in_progress]] || 0,
done: status_counts[Task.statuses[:done]] || 0,
not_started: status_counts[Task.statuses[:not_started]] || 0
}
end

View file

@ -1,6 +1,11 @@
class Sinatra::Application
get '/projects' do
@projects_with_tasks = current_user.projects.includes(:tasks, :area).order('areas.name ASC, projects.name ASC')
@projects_with_tasks = current_user.projects.left_joins(:tasks, :area).order('areas.name ASC, projects.name ASC')
@task_status_counts = @projects_with_tasks.each_with_object({}) do |project, counts|
counts[project.id] = project.task_status_counts
end
@grouped_projects = @projects_with_tasks.group_by(&:area)
erb :'projects/index'

View file

@ -58,7 +58,7 @@ module Sinatra
@tasks = @tasks.where(id: tagged_task_ids)
end
@tasks = @tasks.joins(:tags).distinct
@tasks = @tasks.left_joins(:tags).distinct
erb :'tasks/index'
end

View file

@ -1,4 +1,4 @@
<div class="border-0 rounded shadow-sm mb-1 px-3 py-2 d-flex align-items-center note-item" data-note-id="<%= note.id || 'new' %>">
<div class="border-0 rounded shadow-sm px-3 py-1 d-flex align-items-center note-item" data-note-id="<%= note.id || 'new' %>">
<i class="fs-6 bi-journal-text me-2"></i>
<div class="row flex-grow-1 align-items-center">
<div class="col-md-4">

View file

@ -0,0 +1,39 @@
<% projects.each do |project| %>
<% counts = @task_status_counts[project.id] %>
<div class="col-md-4 mb-3">
<a class="text-decoration-none project-card" href="/project/<%= project.id %>">
<div class="card shadow-sm" style="min-height: 177px;">
<div class="d-flex flex-column justify-content-between h-100">
<div>
<div class="rounded" style="height: 100px;"></div>
<div class="card-body p-0">
<div class="card-footer p-0">
<div class="progress rounded-0" style="height: 2px;">
<div class="progress-bar" role="progressbar" style="width: <%= project.progress_percentage %>%" aria-valuenow="<%= project.progress_percentage %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<h5 class="card-title px-3 pt-3 pb-0 mb-1">
<%= project.name.length > 20 ? project.name.upcase[0,25] + "..." : project.name.upcase %>
</h5>
<div class="card-text px-3 small opacity-50">
<%= counts[:total] %> Tasks
<% if counts[:in_progress] > 0 %>
, <i class="bi bi-circle-fill text-success me-1" style="font-size: 0.5em; position: relative; top: -0.3em;"></i> <%= counts[:in_progress] %> in progress
<% end %>
</div>
</div>
</div>
</div>
</div>
</a>
</div>
<% end %>
<div class="col-md-4 mb-3">
<a class="text-decoration-none project-card" href="#" data-bs-toggle="modal" data-bs-target="#newProjectModal">
<div class="card shadow-sm p-0 opacity-25" style="min-height: 177px;">
<div class="card-body rounded px-0 p-0 text-center">
<i class="bi bi-plus opacity-25" style="font-size: 72px; line-height: 175px;"></i>
</div>
</div>
</a>
</div>

View file

@ -0,0 +1,43 @@
<table class="table-dark">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Project Name</th>
<th scope="col">Progress</th>
<th scope="col">Total Tasks</th>
<th scope="col">Tasks in Progress</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<% projects.each_with_index do |project, index| %>
<% counts = @task_status_counts[project.id] %>
<tr>
<th scope="row"><%= index + 1 %></th>
<td><%= project.name %></td>
<td>
<div class="progress" style="height: 2px;">
<div class="progress-bar" role="progressbar" style="width: <%= project.progress_percentage %>%;" aria-valuenow="<%= project.progress_percentage %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<%= project.progress_percentage %>%
</td>
<td><%= counts[:total] %> Tasks</td>
<td>
<% if counts[:in_progress] > 0 %>
<i class="bi bi-circle-fill text-success" style="font-size: 0.5em; position: relative; top: -0.3em;"></i> <%= counts[:in_progress] %>
<% else %>
0
<% end %>
</td>
<td>
<a href="/project/<%= project.id %>" class="btn btn-primary btn-sm">View</a>
</td>
</tr>
<% end %>
</tbody>
</table>
<!-- Button to add new project -->
<div class="mt-3">
<a href="#" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#newProjectModal">Add New Project</a>
</div>

View file

@ -1,67 +1,38 @@
<h2 class="mb-5"><i class="bi bi-hexagon ms-3 me-2"></i>Areas & Projects</h2>
<h2 class="mb-5"><i class="bi bi-hexagon ms-3 me-2"></i>Projects</h2>
<div class="row px-3">
<% @grouped_projects.each do |area, projects| %>
<div class="mb-5">
<div class="area-item d-flex align-items-center mb-3">
<a href="#area_<%= area.id %>_projects" class="nav-link link-dark" data-bs-toggle="collapse" aria-expanded="false">
<h4 class="mb-0 pb-0 fw-bold"><%= area.name %></h4>
</a>
<div class="dropdown area-options ms-2">
<button class="btn btn-link link-dark p-0" type="button" id="dropdownMenuButton<%= area.id %>" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton<%= area.id %>">
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#editAreaModal<%= area.id %>">Edit</a></li>
<li>
<form action="/area/<%= area.id %>" method="post" onsubmit="return confirm('Are you sure you want to delete this area?');">
<input type="hidden" name="_method" value="delete">
<button type="submit" class="dropdown-item">Delete</button>
</form>
</li>
</ul>
</div>
</div>
<div class="row">
<% projects.each do |project| %>
<div class="col-md-4 mb-3">
<a class="text-decoration-none project-card" href="/project/<%= project.id %>">
<div class="card shadow-sm" style="min-height: 177px;">
<div class="d-flex flex-column justify-content-between h-100">
<div>
<div class="rounded" style="height: 100px;"></div>
<div class="card-body p-0">
<div class="card-footer p-0">
<div class="progress rounded-0" style="height: 2px;">
<div class="progress-bar" role="progressbar" style="width: <%= project.progress_percentage %>%" aria-valuenow="<%= project.progress_percentage %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<h5 class="card-title px-3 pt-3 pb-0 mb-1"><%= project.name.upcase %></h5>
<div class="card-text px-3 small opacity-50">
<%= project.task_status_counts[:total] %> Tasks
<% if project.task_status_counts[:in_progress] > 0 %>
, <i class="bi bi-circle-fill text-success me-1" style="font-size: 0.5em; position: relative; top: -0.3em;"></i> <%= project.task_status_counts[:in_progress] %> in progress
<% end %>
</div>
</div>
</div>
</div>
</div>
</a>
<% projects.each_with_index do |project, index| %>
<% counts = @task_status_counts[project.id] %>
<div class="border-0 rounded bg-transparent px-2 py-1 d-flex align-items-center task-item" data-project-id="<%= project.id %>" onclick="window.location.href='/project/<%= project.id %>'" style="cursor: pointer;">
<div class="row flex-grow-1 align-items-top">
<div class="col-md-6 d-flex align-items-center">
<i class="bi bi-circle me-2"></i>
<span class="fw-light"><%= project.name %></span>
<span>— <%= area&.name || 'No area' %></span>
</div>
<% end %>
<div class="col-md-4 mb-3">
<a class="text-decoration-none project-card" href="#" data-bs-toggle="modal" data-bs-target="#newProjectModal">
<div class="card shadow-sm p-0 opacity-25" style="min-height: 177px;">
<div class="card-body rounded px-0 p-0 text-center">
<i class="bi bi-plus opacity-25" style="font-size: 72px; line-height: 175px;"></i>
</div>
<div class="col-md-6 text-end">
<div class="progress" style="height: 5px; width: 100px; display: inline-block; vertical-align: middle;">
<div class="progress-bar bg-success" role="progressbar" style="width: <%= project.progress_percentage %>%;" aria-valuenow="<%= project.progress_percentage %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</a>
<span>
<%= counts[:total] %> /
<% if counts[:in_progress] > 0 %>
<i class="bi bi-circle-fill text-success" style="font-size: 0.5em; position: relative; top: -0.3em;"></i> <%= counts[:in_progress] %>
<% else %>
0
<% end %>
</span>
</div>
</div>
</div>
</div>
<% end %>
<% end %>
</div>
<div class="mt-3">
<a href="#" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#newProjectModal">Add New Project</a>
</div>
<%= partial :'tasks/_edit_task_modal' %>
<% current_user.areas.each do |area| %>
<%= partial :'areas/_edit_area_modal', locals: { area: area } %>

View file

@ -44,7 +44,7 @@
<li class="border-top my-3"></li>
<li class="nav-item d-flex justify-content-between align-items-center w-100 <%= nav_link('/projects') %>">
<a href="/projects" class="text-decoration-none flex-grow-1 <%= nav_link_active?('/projects') ? 'text-light' : 'link-dark' %>">
<i class="bi bi-hexagon-fill me-1"></i> Areas & Projects
<i class="bi bi-hexagon-fill me-1"></i> Projects
</a>
<button class="btn btn-link text-secondary p-0 ms-2" type="button" id="addNewDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-plus-lg"></i>

View file

@ -1,34 +1,32 @@
<div class="border-0 rounded shadow-sm mb-1 px-3 py-2 d-flex align-items-center task-item <%= 'opacity-50' if task.done? %>" data-task-id="<%= task.id %>">
<div class="border-0 rounded bg-transparent px-2 py-1 d-flex align-items-center task-item <%= 'opacity-50' if task.done? %>" data-task-id="<%= task.id %>">
<div class="row flex-grow-1 align-items-top">
<div class="col-md-6">
<span onclick="toggleTaskCompletion(event, <%= task.id %>)" class="toggle-completion">
<i class="fs-6 bi <%= task.done? ? 'bi-check-circle-fill' : 'bi-circle' %> <%= priority_class(task) %> me-2"></i>
</span>
<span class="fw-light"><%= task.name %></span>
</div>
<div class="col-md-6 text-end">
<% if task.tags.any? %>
<div class="ms-3 opacity-75 d-inline-block">
<% task.tags.each do |tag| %>
<a href="<%= "/tasks?#{update_query_params('tag', tag.name)}" %>" class="badge bg-primary-subtle link-primary text-decoration-none">
<a href="<%= "/tasks?#{update_query_params('tag', tag.name)}" %>" class="badge bg-primary-subtle py-1 link-primary text-decoration-none">
<i class="bi bi-tag-fill me-1 opacity-50"></i><%= tag.name %>
</a>
<% end %>
</div>
<% end %>
</div>
<div class="col-md-3 text-end">
<% if task.project && params[:id].blank? %>
<a href="/project/<%= task.project.id %>" class="badge border border-secondary text-decoration-none link-dark fw-light">
<%= task.project.name %>
<a href="/project/<%= task.project.id %>" class="badge border border-secondary text-decoration-none py-1 link-dark fw-normal">
<%= task.project.name.length > 25 ? task.project.name[0,25] + "..." : task.project.name %>
</a>
<% end %>
</div>
<div class="col-md-3 text-end">
<% if task.due_date %>
<span class="badge <%= due_date_badge_class(task.due_date) %> fw-light">
<span class="badge py-1 <%= due_date_badge_class(task.due_date) %>" style="width: 95px">
<i class="bi bi-clock me-2"></i> <%= format_due_date(task.due_date) %>
</span>
<% end %>
<span class="badge <%= status_badge_class(task.status) %> fw-light">
<span class="badge py-1 <%= status_badge_class(task.status) %>">
<i class="bi bi-circle-fill me-1" style="font-size: 0.6em; position: relative; top: -0.15em;"></i>
<span class="text-dark"><%= task.status.gsub('_', ' ').capitalize %></span>
</span>

View file

@ -29,7 +29,7 @@
<% unless params[:status] == 'done' %>
<%= partial :'tasks/_minimal_form', locals: { task: Task.new } %>
<% end %>
<div class="mx-3">
<div class="mt-2 mx-3">
<% if @tasks.any? %>
<% @tasks.each do |task| %>
<div id="edit_task_form_<%= task.id %>" class="d-none">

4
console.rb Normal file
View file

@ -0,0 +1,4 @@
require 'irb'
require './app'
IRB.start

3
create_migration.sh Executable file
View file

@ -0,0 +1,3 @@
#! /bin/bash
bundle exec ../bin/rake db:create_migration "$1"

View file

@ -0,0 +1,5 @@
class AddActiveToProjects < ActiveRecord::Migration[7.1]
def change
add_column :projects, :active, :boolean, default: false
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2023_11_27_094906) do
ActiveRecord::Schema[7.1].define(version: 2024_03_26_093339) do
create_table "areas", force: :cascade do |t|
t.string "name"
t.integer "user_id", null: false
@ -44,6 +44,7 @@ ActiveRecord::Schema[7.1].define(version: 2023_11_27_094906) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "description"
t.boolean "active", default: false
t.index ["area_id"], name: "index_projects_on_area_id"
t.index ["user_id"], name: "index_projects_on_user_id"
end

View file

@ -1,7 +1,7 @@
body {
font-family: 'Poppins', sans-serif;
font-size: 0.85rem;
background: #f8f8f8;
font-size: 0.86rem;
background: #fafafa;
}
h1 {
@ -114,6 +114,14 @@ h6 {
color: #fff;
}
.nav-link:hover, .nav-link a:hover {
color: #666 !important;
}
.nav-link.active-link:hover, .nav-link.active-link a:hover {
color: #ccc !important;
}
.dark-mode .nav-link {
color: #fff;
}
@ -126,22 +134,22 @@ h6 {
.task-item,
.note-item {
background: var(--bs-white);
/* background: var(--bs-white); */
}
.dark-mode .task-item,
.dark-mode .note-item {
background: var(--bs-gray-dark) !important;
/* background: var(--bs-gray-dark) !important; */
}
.task-item:hover,
.note-item:hover {
background: #fafafa !important;
background: #eee !important;
}
.dark-mode .task-item:hover,
.dark-mode .note-item:hover {
background: var(--bs-black) !important;
background: #292929 !important;
}
/* task form */
@ -169,7 +177,7 @@ a.project-card .card:hover {
/* dark mode */
.dark-mode {
background-color: var(--bs-dark);
background-color: #191919;
color: var(--bs-light);
}