Skip to content

Commit 5b8358d

Browse files
Add task view index and seeds
- Added the tasks index view into Flex - Updated the Flex::Task model - Made `case_id` readonly because I cannot foresee a task getting moved to a different case after creation - Exposed `type` as a readonly attribute - Since this is automatically set, we shouldn't be manually setting this ever - Added scopes to help with easier querying elsewhere - It's my first time using them. I tried researching best practices and such, but if you have any feedback @lorenyu let me know. - Added a Flex::TasksHelper to help extract logic and such from the task index view - Added `passport_tasks` route and a `PassportTasksController` - Created `PassportTask` that extends from `Flex::Task`, then 2 subclasses that extend from `PassportTask` (`PassportPhotoTask` and `PassportVerifyInfoTask`) - Added a couple of migrations: - Added the `due_on` column to `flex_tasks` - Added the missing `test_application_forms` and `test_cases` tables, that were taken out previously and extracted to a different branch to work on - When re-running migrations, the local tables disappeared and caused problems, so I had to add this migration back into this branch - **(Fun addition)**: seeds 🌱 . When setting up the db locally we will have some cases and tasks created for us. - Updated the `task_spec` test file to reflect changes
1 parent b3a151c commit 5b8358d

21 files changed

Lines changed: 306 additions & 28 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module Flex
2+
module DateHelper
3+
def local_en_us(date)
4+
date.to_formatted_s(:local_en_us)
5+
end
6+
7+
def time_since_epoch(date)
8+
date.to_time.to_i
9+
end
10+
end
11+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module Flex
2+
module TasksHelper
3+
def find_task_type_human_readable_name(task_type_map, task_type)
4+
_, human_readable_name = task_type_map.find { |type, _| type.name == task_type }
5+
6+
human_readable_name
7+
end
8+
9+
def task_type_options_for_select(task_type_map = {}, selected = nil)
10+
safe_join([
11+
tag.option("All tasks", value: ""),
12+
task_type_map.map do |task_type, human_readable_task_type|
13+
tag.option(
14+
human_readable_task_type,
15+
value: task_type.name,
16+
selected: task_type.name == selected
17+
)
18+
end
19+
])
20+
end
21+
22+
def hidden_params_field(name)
23+
hidden_field_tag(name, params[name]) if params[name].present?
24+
end
25+
end
26+
end

template/{{app_name}}/engines/flex/app/models/flex/task.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
module Flex
22
class Task < ApplicationRecord
33
attribute :description, :text
4+
attribute :due_on, :date
5+
attr_readonly :case_id
6+
attr_readonly :type
47

58
attribute :assignee_id, :string
69
protected attr_writer :assignee_id
710

8-
attribute :case_id, :string
9-
protected attr_writer :case_id
10-
1111
attribute :status, :integer, default: 0
1212
protected attr_writer :status
1313
enum :status, pending: 0, completed: 1
1414

1515
validates :case_id, presence: true
1616

17-
def set_case(case_id)
18-
self[:case_id] = case_id
19-
save!
20-
end
17+
default_scope -> { order(due_on: :desc) }
18+
scope :due_today, -> { where(due_on: Date.today) }
19+
scope :due_tomorrow, -> { where(due_on: Date.tomorrow) }
20+
scope :due_this_week, -> { where(due_on: Date.today.beginning_of_week..Date.today.end_of_week) }
21+
scope :overdue, -> { where("due_on < ?", Date.today) }
22+
scope :completed, -> { where(status: :completed) }
23+
scope :incomplete, -> { where.not(status: :completed) }
24+
scope :with_type, ->(type) { where(type: type) }
2125

2226
def assign(user_id)
2327
self[:assignee_id] = user_id
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="usa-alert usa-alert--info usa-alert--no-icon">
2+
<div class="usa-alert__body">
3+
<p class="usa-alert__text">
4+
No tasks!
5+
</p>
6+
</div>
7+
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<%= form_with method: 'get' do |form| %>
2+
<%= hidden_params_field :filter_type %>
3+
<%= hidden_params_field :filter_status %>
4+
<%= form.label :filter_date, "Filter by due date", class: 'usa-label' %>
5+
<%= form.select :filter_date, [
6+
['All dates', 'all'],
7+
['Overdue', 'overdue'],
8+
['Today', 'today'],
9+
['Tomorrow', 'tomorrow'],
10+
['This week', 'this_week']
11+
],
12+
{ include_blank: false, selected: params[:filter_date] },
13+
{ class: 'usa-select', onchange: 'this.form.submit()' } %>
14+
<% end %>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<%= form_with method: 'get' do |form| %>
2+
<%= hidden_params_field :filter_date %>
3+
<%= hidden_params_field :filter_status %>
4+
<%= form.label :filter_type, "Filter by due date", class: 'usa-label' %>
5+
<%= form.select :filter_type, \
6+
task_type_options_for_select(task_type_map, params[:filter_type]),
7+
{ include_blank: false },
8+
{ class: 'usa-select', onchange: 'this.form.submit()' } %>
9+
<% end %>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<%# 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+
- task_type_map: Hash - A mapping of task types to human-readable names
5+
6+
Example usage:
7+
render template: "flex/application_forms/index", locals: {
8+
model_class: PassportTask,
9+
task_type_map: { PassportPhotoTask => 'Verify Photo', PassportVerifyInfoTask => 'Verify Info' }
10+
}
11+
%>
12+
<%= content_for :title, model_class.name.titleize %>
13+
<!-- Main content -->
14+
<main class="usa-section">
15+
<div class="grid-container">
16+
<h1 class="usa-heading-xl"><%= model_class.name.titleize.sub('Task', ' Tasks') %></h1>
17+
<nav class="display-flex border-bottom-1px border-base-light">
18+
<%
19+
[
20+
{ name: "Assigned", path: polymorphic_path(model_class, filter_status: nil, filter_date: params[:filter_date], filter_type: params[:filter_type]), active: params[:filter_status] != "completed" },
21+
{ name: "Completed", path: polymorphic_path(model_class, filter_status: "completed", filter_date: params[:filter_date], filter_type: params[:filter_type]), active: params[:filter_status] == "completed" }
22+
].each do |tab|
23+
%>
24+
<%= render partial: "flex/cases/nav_tab", locals: tab %>
25+
<% end %>
26+
</nav>
27+
<!-- Filters -->
28+
<div class="grid-row grid-gap-4 margin-bottom-4">
29+
<div class="grid-col-12 tablet:grid-col-3">
30+
<%= render partial: 'flex/tasks/task_type_filter', locals: { task_type_map: task_type_map } %>
31+
</div>
32+
<div class="grid-col-12 tablet:grid-col-3">
33+
<%= render partial: 'flex/tasks/task_due_date_filter' %>
34+
</div>
35+
</div>
36+
<% if @tasks.any? %>
37+
<!-- Tasks -->
38+
<table class="usa-table usa-table--striped usa-table--sticky-header">
39+
<thead>
40+
<tr>
41+
<th data-sortable scope="col" role="columnheader" aria-sort="descending">
42+
Task Due Date
43+
</th>
44+
<th data-sortable scope="col" role="columnheader">
45+
Task Type
46+
</th>
47+
<th data-sortable scope="col" role="columnheader">
48+
Case ID
49+
</th>
50+
<th data-sortable scope="col" role="columnheader">
51+
Task created date
52+
</th>
53+
</tr>
54+
</thead>
55+
<tbody>
56+
<%
57+
@tasks.each do |task|
58+
%>
59+
<tr>
60+
<td data-sort-value="<%= time_since_epoch task.due_on %>">
61+
<%= local_en_us task.due_on %>
62+
</td>
63+
<td data-sort-value="<%= task.type %>"><%= find_task_type_human_readable_name(task_type_map, task.type) %></td>
64+
<td data-sort-value="<%= task.case_id %>"><%= task.case_id %></td>
65+
<td data-sort-value="<%= time_since_epoch task.created_at %>">
66+
<%= local_en_us task.created_at.to_date %>
67+
</td>
68+
</tr>
69+
<% end %>
70+
</tbody>
71+
</table>
72+
<div class="usa-sr-only usa-table__announcement-region" aria-live="polite"></div>
73+
<% else %>
74+
<%= render partial: 'flex/tasks/no_tasks_alert' %>
75+
<% end %>
76+
</div>
77+
</div>
78+
</main>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Date format flags and directives: https://apidock.com/ruby/DateTime/strftime
2+
# Date formats of different countries: https://en.wikipedia.org/wiki/List_of_date_formats_by_country
3+
Date::DATE_FORMATS[:local_en_us] = "%m/%d/%Y"
4+
Date::DATE_FORMATS[:local_es_us_pr] = "%d/%m/%Y" # Spanish-speaking individuals in Puerto Rico uses the international format
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
class PassportTasksController < ApplicationController
2+
helper Flex::TasksHelper
3+
helper Flex::DateHelper
4+
5+
def index
6+
filter_tasks
7+
end
8+
9+
private
10+
def index_filter_params
11+
params.permit(:filter_date, :filter_type, :filter_status)
12+
end
13+
14+
def tasks
15+
@tasks ||= Flex::Task
16+
end
17+
18+
def filter_tasks
19+
if index_filter_params[:filter_date].present?
20+
@tasks = filter_tasks_by_date(index_filter_params[:filter_date])
21+
end
22+
if index_filter_params[:filter_type].present?
23+
@tasks = filter_tasks_by_type(index_filter_params[:filter_type])
24+
end
25+
@tasks = filter_tasks_by_status
26+
end
27+
28+
def filter_tasks_by_date(filter_by)
29+
case filter_by
30+
when "today"
31+
tasks.due_today
32+
when "overdue"
33+
tasks.overdue
34+
when "tomorrow"
35+
tasks.due_tomorrow
36+
when "this_week"
37+
tasks.due_this_week
38+
else
39+
tasks.all
40+
end
41+
end
42+
43+
def filter_tasks_by_type(filter_by)
44+
filter_by == "all" ? tasks.all : tasks.with_type(filter_by)
45+
end
46+
47+
def filter_tasks_by_status
48+
index_filter_params[:filter_status] == "completed" \
49+
? tasks.completed
50+
: tasks.incomplete
51+
end
52+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class PassportPhotoTask < PassportTask
2+
end

0 commit comments

Comments
 (0)