Skip to content

Commit 51b0aa0

Browse files
Improve flex task setup (#84)
Updates include: - Moving the majority of the TasksController logic to Flex so that it can be extended in the parent application - Moving the migration for creation of `flex_tasks` to the Flex engine - Setting up the task views to use locals rather than variables set directly in the controller - Cleaning up the directory structures a little - A few small bug fixes when they were discovered
1 parent e7cf90a commit 51b0aa0

25 files changed

Lines changed: 194 additions & 134 deletions

.rubocop.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,22 @@ AllCops:
2222
# YARD documentation configuration
2323
YARD:
2424
Enabled: true
25-
25+
2626
YARD/CollectionStyle:
2727
Enabled: true
28-
28+
2929
YARD/CollectionType:
3030
Enabled: true
31-
31+
3232
YARD/MeaninglessTag:
3333
Enabled: true
34-
34+
3535
YARD/MismatchName:
3636
Enabled: true
37-
37+
3838
YARD/TagTypePosition:
3939
Enabled: true
40-
40+
4141
YARD/TagTypeSyntax:
4242
Enabled: true
4343

@@ -46,3 +46,4 @@ Style/Documentation:
4646
Exclude:
4747
- "spec/**/*"
4848
- "test/**/*"
49+
- "db/migrate/**/*"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module Flex
2+
# Controller for managing Flex::Task records. Handles listing, filtering, showing, and updating tasks.
3+
# This controller helps a parent application manage tasks by not forcing the parent application to implement the same functionality.
4+
class TasksController < ::ApplicationController
5+
helper DateHelper
6+
7+
before_action :set_task, only: %i[ show update ]
8+
9+
def task_class
10+
controller_path.classify.constantize
11+
end
12+
13+
def index
14+
@tasks = filter_tasks
15+
@task_types = task_class.distinct.pluck(:type)
16+
end
17+
18+
def show
19+
end
20+
21+
def update
22+
if params["task-action"].present?
23+
@task.mark_completed
24+
flash["task-message"] = I18n.t("tasks.messages.task_marked_completed")
25+
end
26+
27+
redirect_to task_path(@task)
28+
end
29+
30+
private
31+
32+
def set_task
33+
@task = task_class.find(params[:id]) if params[:id].present?
34+
end
35+
36+
def index_filter_params
37+
params.permit(:filter_date, :filter_type, :filter_status)
38+
end
39+
40+
def filter_tasks
41+
tasks = filter_tasks_by_date(task_class.all, index_filter_params[:filter_date])
42+
tasks = filter_tasks_by_type(tasks, index_filter_params[:filter_type])
43+
tasks = filter_tasks_by_status(tasks, index_filter_params[:filter_status])
44+
45+
tasks
46+
end
47+
48+
def filter_tasks_by_date(tasks, filter_by)
49+
case filter_by
50+
when "today"
51+
tasks.due_today
52+
when "overdue"
53+
tasks.overdue
54+
when "tomorrow"
55+
tasks.due_tomorrow
56+
when "this_week"
57+
tasks.due_this_week
58+
else
59+
tasks
60+
end
61+
end
62+
63+
def filter_tasks_by_type(tasks, filter_by)
64+
return tasks unless filter_by.present? && filter_by != "all"
65+
66+
tasks.with_type(filter_by)
67+
end
68+
69+
def filter_tasks_by_status(tasks, status)
70+
status == "completed" \
71+
? tasks.completed
72+
: tasks.incomplete
73+
end
74+
end
75+
end

app/views/flex/tasks/_due_date_filter.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<%= form_with method: 'get' do |form| %>
2-
<%= hidden_field_tag(:filter_date, params[:filter_date]) if params[:filter_date].present? %>
2+
<%= hidden_field_tag(:filter_type, params[:filter_type]) if params[:filter_type].present? %>
33
<%= hidden_field_tag(:filter_status, params[:filter_status]) if params[:filter_status].present? %>
44
<%= form.label :filter_date, t("flex.tasks.partials.due_date_filter.label"), class: 'usa-label' %>
55
<%= form.select :filter_date, [

app/views/flex/tasks/_type_filter.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<%= form.label :filter_type, t("flex.tasks.partials.type_filter.label"), class: 'usa-label' %>
55
<%= form.select :filter_type, \
66
[[t("flex.tasks.partials.type_filter.options.all"), ""]].append(
7-
*@distinct_task_types.map { |type| [t("tasks.types.#{type.underscore}"), type] }
7+
*task_types.map { |type| [t("tasks.types.#{type.underscore}"), type] }
88
),
99
{ include_blank: false, selected: params[:filter_type] },
1010
{ class: 'usa-select', onchange: 'this.form.submit()' } %>

app/views/flex/tasks/index.html.erb

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
<%# Index view for displaying a list of tasks
2-
Required locals:
3-
- model_class: Flex::Task or subclass - The model class for the tasks
4-
5-
Example usage:
6-
render template: "flex/application_forms/index", locals: {
7-
model_class: PassportTask
8-
}
9-
%>
10-
<%= content_for :title, model_class.name.titleize %>
2+
Required locals:
3+
- tasks: Flex::Task[] - Array of tasks to display
4+
%>
115
<!-- Main content -->
126
<main class="usa-section">
137
<div class="grid-container">
148
<h1 class="usa-heading-xl"><%= t("flex.tasks.index.title") %></h1>
159
<nav class="display-flex border-bottom-1px border-base-light">
1610
<%
11+
filter_params = params.permit(:filter_date, :filter_type, :filter_status)
1712
[
18-
{ name: t("flex.tasks.index.tabs.assigned"), path: polymorphic_path(model_class, filter_status: nil, filter_date: params[:filter_date], filter_type: params[:filter_type]), active: params[:filter_status] != "completed" },
19-
{ name: t("flex.tasks.index.tabs.completed"), path: polymorphic_path(model_class, filter_status: "completed", filter_date: params[:filter_date], filter_type: params[:filter_type]), active: params[:filter_status] == "completed" }
13+
{
14+
name: t("flex.tasks.index.tabs.assigned"),
15+
path: url_for(filter_params.merge(filter_status: nil)),
16+
active: params[:filter_status] != "completed"
17+
},
18+
{
19+
name: t("flex.tasks.index.tabs.completed"),
20+
path: url_for(filter_params.merge(filter_status: "completed")),
21+
active: params[:filter_status] == "completed"
22+
}
2023
].each do |tab|
2124
%>
2225
<%= render partial: "flex/cases/nav_tab", locals: tab %>
@@ -25,13 +28,13 @@
2528
<!-- Filters -->
2629
<div class="grid-row grid-gap-4 margin-bottom-4">
2730
<div class="grid-col-12 tablet:grid-col-3">
28-
<%= render partial: 'flex/tasks/type_filter' %>
31+
<%= render partial: 'flex/tasks/type_filter', locals: { task_types: task_types } %>
2932
</div>
3033
<div class="grid-col-12 tablet:grid-col-3">
3134
<%= render partial: 'flex/tasks/due_date_filter' %>
3235
</div>
3336
</div>
34-
<% if @tasks.any? %>
37+
<% if tasks.any? %>
3538
<!-- Tasks -->
3639
<table class="usa-table usa-table--striped usa-table--sticky-header width-full">
3740
<thead>
@@ -52,14 +55,14 @@
5255
</thead>
5356
<tbody>
5457
<%
55-
@tasks.each do |task|
58+
tasks.each do |task|
5659
%>
5760
<tr>
5861
<td data-sort-value="<%= time_since_epoch task.due_on %>">
5962
<%= local_en_us task.due_on %>
6063
</td>
6164
<td data-sort-value="<%= task.type %>">
62-
<%= link_to t("tasks.types.#{task.type.underscore}"), passport_task_path(task) %>
65+
<%= link_to t("tasks.types.#{task.type.underscore}"), task_path(task) %>
6366
</td>
6467
<td data-sort-value="<%= task.case_id %>"><%= task.case_id %></td>
6568
<td data-sort-value="<%= time_since_epoch task.created_at %>">

app/views/flex/tasks/show.html.erb

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
<% task_type_with_name = t("tasks.types.#{@task.type.underscore}") + " - " + @application_form.full_name || "TBD" %>
1+
<%# Show view for displaying a task
2+
Required locals:
3+
- assigned_user_display_text: String - The name of the user assigned to this task
4+
- task: Flex::Task - The task object
5+
6+
Optional locals:
7+
- task_details_partial: String - The HTML to render for the task details. If not provided, the view will look for a partial named after the task type within the `details` directory.
8+
9+
Example usage:
10+
render template: 'flex/tasks/default/show', locals: {
11+
task: @task,
12+
assigned_user_display_text: assigned_user&.full_name || "Not Assigned"
13+
}
14+
%>
215
<main class="usa-section">
316
<div class="grid-container">
417
<nav class="usa-breadcrumb" aria-label="Breadcrumbs">
@@ -7,25 +20,25 @@
720
<%= link_to t("flex.tasks.breadcrumbs.home"), "/", class: "usa-breadcrumb__link" %>
821
</li>
922
<li class="usa-breadcrumb__list-item">
10-
<%= link_to t("flex.tasks.breadcrumbs.tasks"), polymorphic_path(model_class), class: "usa-breadcrumb__link" %>
23+
<%= link_to t("flex.tasks.breadcrumbs.tasks"), tasks_path, class: "usa-breadcrumb__link" %>
1124
</li>
1225
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
13-
<span><%= task_type_with_name %></span>
26+
<span><%= t("tasks.types.#{task.type.underscore}") %></span>
1427
</li>
1528
</ol>
1629
</nav>
17-
<h1 class="section-heading-h1"><%= task_type_with_name %></h1>
30+
<h1 class="section-heading-h1"><%= t("tasks.types.#{task.type.underscore}") %></h1>
1831
<div class="border-bottom-1px margin-bottom-1 padding-bottom-1">
1932
<div class="grid-container">
2033
<div class="grid-row grid-gap-2">
2134
<div class="grid-col-auto">
22-
<strong><%= t("flex.tasks.show.details.status") %>:</strong> <%= t("tasks.statuses.#{@task.status}") %>
35+
<strong><%= t("flex.tasks.show.details.status") %>:</strong> <%= t("tasks.statuses.#{task.status}") %>
2336
</div>
2437
<div class="grid-col-auto">
25-
<strong><%= t("flex.tasks.show.details.due_on") %>:</strong> <%= local_en_us @task.due_on %>
38+
<strong><%= t("flex.tasks.show.details.due_on") %>:</strong> <%= local_en_us task.due_on %>
2639
</div>
2740
<div class="grid-col-auto">
28-
<strong><%= t("flex.tasks.show.details.assigned_to") %>:</strong> <%= @assigned_user&.full_name || "N/A" %>
41+
<strong><%= t("flex.tasks.show.details.assigned_to") %>:</strong> <%= assigned_user_display_text %>
2942
</div>
3043
</div>
3144
</div>
@@ -41,6 +54,10 @@
4154
</div>
4255
<% end %>
4356
<h2 class="section-heading-h2"><%= t("flex.tasks.show.headers.task_details") %></h2>
44-
<%= render html: task_details_partial %>
57+
<% if content_for?(:task_details_partial) %>
58+
<%= yield(:task_details_partial) %>
59+
<% else %>
60+
<%= render partial: "tasks/details/#{task.type.underscore}", locals: local_assigns.to_h %>
61+
<% end %>
4562
</section>
4663
</main>

spec/dummy/db/migrate/20250430160812_create_flex_tasks.rb renamed to db/migrate/20250430160812_create_flex_tasks.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ def change
66
t.integer :status, index: true, default: 0
77
t.integer :assignee_id, index: true # not linked to anything yet but will be later
88
t.integer :case_id, index: true
9+
t.date :due_on
910

1011
t.timestamps
1112
end

spec/dummy/app/controllers/passport_tasks_controller.rb

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class TasksController < Flex::TasksController
2+
def task_class
3+
PassportTask
4+
end
5+
6+
def show
7+
super
8+
kase = PassportCase.find(@task.case_id)
9+
@application_form = PassportApplicationForm.find(kase.application_form_id)
10+
end
11+
end

spec/dummy/app/models/user.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# User represents an individual who interacts with the dummy app.
2+
#
3+
# This model stores basic user information such as first and last name but may be extended to hold more.
14
class User < ApplicationRecord
25
attribute :first_name, :string
36
attribute :last_name, :string

0 commit comments

Comments
 (0)