Skip to content

Commit c0ed3e0

Browse files
authored
Sector CRUD (#582)
1 parent 22cabfd commit c0ed3e0

File tree

18 files changed

+588
-39
lines changed

18 files changed

+588
-39
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
class SectorsController < ApplicationController
2+
before_action :set_sector, only: [:show, :edit, :update, :destroy]
3+
4+
def index
5+
per_page = params[:number_of_items_per_page].presence || 25
6+
unpaginated = Sector.all
7+
filtered = unpaginated.sector_name(params[:sector_name])
8+
.published_search(params[:published_search])
9+
.order(:name)
10+
@sectors = filtered.paginate(page: params[:page], per_page: per_page)
11+
12+
@count_display = if @sectors.total_entries == unpaginated.count
13+
unpaginated.count
14+
else
15+
"#{@sectors.total_entries}/#{unpaginated.count}"
16+
end
17+
end
18+
19+
def show
20+
end
21+
22+
def new
23+
@sector = Sector.new
24+
set_form_variables
25+
end
26+
27+
def edit
28+
set_form_variables
29+
end
30+
31+
def create
32+
@sector = Sector.new(sector_params)
33+
34+
if @sector.save
35+
redirect_to sectors_path, notice: "Sector was successfully created."
36+
else
37+
set_form_variables
38+
render :new, status: :unprocessable_content
39+
end
40+
end
41+
42+
def update
43+
if @sector.update(sector_params)
44+
redirect_to sectors_path, notice: "Sector was successfully updated.", status: :see_other
45+
else
46+
set_form_variables
47+
render :edit, status: :unprocessable_content
48+
end
49+
end
50+
51+
def destroy
52+
@sector.destroy!
53+
redirect_to sectors_path, notice: "Sector was successfully destroyed."
54+
end
55+
56+
# Optional hooks for setting variables for forms or index
57+
def set_form_variables
58+
end
59+
60+
private
61+
62+
def set_sector
63+
@sector = Sector.find(params[:id])
64+
end
65+
66+
# Strong parameters
67+
def sector_params
68+
params.require(:sector).permit(
69+
:name, :published
70+
)
71+
end
72+
end

app/helpers/admin_dashboard_cards_helper.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,13 @@ def user_content_cards
4040
# -----------------------------
4141
def reference_cards
4242
[
43-
model_card(:categories, icon: "🗂️", intensity: 100),
44-
custom_card("Service populations", authenticated_root_path, icon: "🏭", color: :lime, intensity: 100),
43+
model_card(:categories, icon: "🗂️",
44+
intensity: 100,
45+
params: { published_search: true }),
46+
model_card(:sectors, icon: "🏭",
47+
intensity: 100,
48+
title: "Service populations",
49+
params: { published_search: true }),
4550
custom_card("Project statuses", authenticated_root_path, icon: "🧮", color: :emerald, intensity: 100),
4651
custom_card("Windows types", windows_types_path, icon: "🪟"),
4752
]
@@ -50,10 +55,10 @@ def reference_cards
5055
# ============================================================
5156
# CARD BUILDERS
5257
# ============================================================
53-
def model_card(key, title: nil, icon:, intensity: 50)
58+
def model_card(key, title: nil, icon:, intensity: 50, params: {})
5459
{
5560
title: title || key.to_s.humanize,
56-
path: polymorphic_path(key.to_s.classify.constantize),
61+
path: polymorphic_path(key.to_s.classify.constantize, params),
5762
icon: icon,
5863
bg_color: DomainTheme.bg_class_for(key, intensity: intensity),
5964
hover_bg_color: DomainTheme.bg_class_for(key, intensity: intensity == 50 ? 100 : intensity + 100, hover: true),

app/models/sector.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ class Sector < ApplicationRecord
1414
validates_presence_of :name, uniqueness: true
1515

1616
# Scopes
17-
scope :published, -> { where(published: true).
18-
order(Arel.sql("CASE WHEN name = 'Other' THEN 1 ELSE 0 END, LOWER(name) ASC")) }
17+
scope :published, ->(published=nil) {
18+
["true", "false"].include?(published) ? where(published: published) : where(published: true) }
19+
scope :published_search, ->(published_search) { published_search.present? ? published(published_search) : all }
20+
scope :sector_name, ->(sector_name) {
21+
sector_name.present? ? where("sectors.name LIKE ?", "%#{sector_name}%") : all }
1922
scope :has_taggings, -> { joins(:sectorable_items).distinct }
23+
2024
end

app/views/categories/_form.html.erb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@
5353

5454
<%= f.button :submit,
5555
"Save Category",
56-
class: "bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700
57-
transition-colors duration-150" %>
56+
class: "btn btn-primary" %>
5857
</div>
5958

6059
</div>

app/views/sectors/_form.html.erb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<%= simple_form_for(@sector) do |f| %>
2+
<div class="space-y-6">
3+
4+
<!-- Errors -->
5+
<%= f.error_notification %>
6+
<%= render "shared/errors", resource: @sector if @sector.errors.any? %>
7+
8+
<!-- One-line Flex Fields -->
9+
<div class="flex flex-wrap gap-6">
10+
11+
<!-- Name -->
12+
<div class="flex-1 min-w-[220px]">
13+
<%= f.input :name,
14+
label: "Name",
15+
input_html: { class: "form-control" } %>
16+
</div>
17+
18+
<div class="flex items-center min-w-[150px] pt-6">
19+
<%= f.input :published,
20+
as: :boolean,
21+
label: "Published?",
22+
wrapper_html: { class: "flex items-center gap-2" },
23+
input_html: { class: "form-checkbox" } %>
24+
</div>
25+
26+
</div>
27+
28+
<!-- Actions -->
29+
<div class="flex flex-wrap justify-end gap-3 pt-6">
30+
31+
<% if @sector.persisted? && current_user.super_user? %>
32+
<%= link_to "Delete",
33+
@sector,
34+
method: :delete,
35+
data: { confirm: "Are you sure you want to delete this category?" },
36+
class: "btn btn-danger-outline" %>
37+
<% end %>
38+
39+
<%= link_to "Cancel", sectors_path,
40+
class: "btn btn-secondary-outline" %>
41+
42+
<%= f.button :submit,
43+
"Save Sector",
44+
class: "btn btn-primary" %>
45+
</div>
46+
47+
</div>
48+
<% end %>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!-- Filters -->
2+
<div class="mb-6 p-4 bg-white border border-gray-200 rounded-lg">
3+
<%= form_with url: sectors_path,
4+
method: :get,
5+
local: true,
6+
class: "grid grid-cols-1 md:grid-cols-5 gap-4 items-end" do |f| %>
7+
8+
<!-- Name Search -->
9+
<div>
10+
<%= f.label :sector_name, "Name Contains",
11+
class: "block text-sm font-medium text-gray-700" %>
12+
13+
<%= f.text_field :sector_name,
14+
value: params[:sector_name],
15+
class: "mt-1 block w-full rounded-md border border-gray-300 p-2",
16+
oninput: "this.form.requestSubmit()" %>
17+
</div>
18+
19+
<!-- Published -->
20+
<div>
21+
<%= f.label :published_search, "Published",
22+
class: "block text-sm font-medium text-gray-700" %>
23+
24+
<%= f.select :published_search,
25+
options_for_select([["All", ""], ["Published", "true"], ["Hidden", "false"]], params[:published_search]),
26+
{},
27+
class: "mt-1 block w-full rounded-md border border-gray-300 p-2",
28+
onchange: "this.form.requestSubmit()" %>
29+
</div>
30+
31+
<!-- Clear -->
32+
<div>
33+
<%= link_to "Clear",
34+
sectors_path,
35+
class: "btn btn-utility-outline whitespace-nowrap" %>
36+
</div>
37+
38+
<% end %>
39+
</div>

app/views/sectors/edit.html.erb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div class="min-h-screen py-8">
2+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
3+
<div class="max-w-5xl mx-auto bg-white border border-gray-200 rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-200 p-6">
4+
<div class="flex items-center justify-between mb-6">
5+
<h1 class="text-2xl font-semibold text-gray-900">Edit Service population</h1>
6+
<%= link_to "Taggings", taggings_path(sector_names: @sector.name),
7+
class: "btn btn-secondary-outline" %>
8+
</div>
9+
10+
<div class="space-y-6">
11+
<div class="mt-4">
12+
<%= render "form", sector: @sector %>
13+
</div>
14+
</div>
15+
</div>
16+
</div>
17+
</div>

app/views/sectors/index.html.erb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<div class="min-h-screen">
2+
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
3+
<div class="max-w-7xl mx-auto <%= DomainTheme.bg_class_for(:sectors) %> border border-gray-200 rounded-xl shadow-lg p-6">
4+
<div class="space-y-6">
5+
6+
<!-- Header -->
7+
<div class="flex items-center justify-between mb-6">
8+
<h1 class="text-2xl font-semibold text-gray-900">
9+
Service populations (<%= @count_display %>)
10+
</h1>
11+
<%= link_to "New #{Sector.model_name.human.downcase}",
12+
new_sector_path,
13+
class: "btn btn-primary-outline" %>
14+
</div>
15+
16+
<%= render "search_boxes" %>
17+
18+
<!-- Table -->
19+
<div class="bg-white rounded">
20+
<div class="overflow-x-auto">
21+
<% if @sectors.any? %>
22+
<table class="w-full table-fixed border-collapse border border-gray-200">
23+
<thead class="bg-gray-100">
24+
<tr>
25+
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/3">
26+
Name
27+
</th>
28+
29+
<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-1/6">
30+
Published
31+
</th>
32+
33+
<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-[80px]">
34+
Actions
35+
</th>
36+
</tr>
37+
</thead>
38+
39+
<tbody class="divide-y divide-gray-200">
40+
<% @sectors.each do |sector| %>
41+
<tr class=" <%= "bg-gray-200" unless sector.published %>
42+
<%= sector.published ? "hover:bg-gray-50" : "hover:bg-gray-100" %> transition-colors duration-150">
43+
44+
<td class="px-4 py-2 text-md text-gray-800 truncate font-bold">
45+
<%= sector.name %>
46+
</td>
47+
48+
<td class="px-4 py-2 text-center">
49+
<% if sector.published? %>
50+
<span class="inline-block px-2 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded">
51+
Yes
52+
</span>
53+
<% else %>
54+
<span class="inline-block px-2 py-1 text-xs font-semibold text-gray-700 bg-gray-200 rounded">
55+
No
56+
</span>
57+
<% end %>
58+
</td>
59+
60+
<!-- Actions -->
61+
<td class="px-4 py-2 text-center">
62+
<%= link_to "Edit",
63+
edit_sector_path(sector),
64+
class: "btn btn-secondary-outline whitespace-nowrap" %>
65+
<%= link_to "Taggings",
66+
taggings_path(sector_names: sector.name),
67+
class: "btn btn-secondary-outline whitespace-nowrap" %>
68+
</td>
69+
70+
</tr>
71+
<% end %>
72+
</tbody>
73+
</table>
74+
<% else %>
75+
<!-- Empty state -->
76+
<p class="text-gray-500 text-center py-6">
77+
No <%= Sector.model_name.human.pluralize %> found.
78+
</p>
79+
<% end %>
80+
</div>
81+
</div>
82+
83+
<!-- Pagination -->
84+
<div class="pagination flex justify-center mt-12">
85+
<%= tailwind_paginate @sectors %>
86+
</div>
87+
88+
</div>
89+
</div>
90+
</div>
91+
</div>

app/views/sectors/new.html.erb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div class="min-h-screen py-8">
2+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
3+
<div class="max-w-5xl mx-auto bg-white border border-gray-200 rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-200 p-6">
4+
<div class="flex items-center justify-between mb-3">
5+
<h1 class="text-2xl font-semibold text-gray-900">New service population</h1>
6+
</div>
7+
8+
<div class="border-b border-gray-300 mb-6"></div>
9+
10+
<div class="space-y-6">
11+
<div class="mt-4">
12+
<%= render "form", sector: @sector %>
13+
</div>
14+
</div>
15+
</div>
16+
</div>
17+
</div>

app/views/sectors/show.html.erb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<div class="min-h-screen py-8">
2+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
3+
<div class="max-w-5xl mx-auto bg-white border border-gray-200 rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-200 p-6">
4+
<div class="flex items-center justify-between mb-6">
5+
<!-- Title -->
6+
<h1 class="text-2xl font-semibold text-gray-900">
7+
Service population details
8+
</h1>
9+
10+
<!-- Buttons Group -->
11+
<div class="flex items-center gap-2">
12+
<%= link_to("Index", sectors_path, class: "btn btn-secondary-outline") %>
13+
<% if current_user.super_user? %>
14+
<%= link_to("Edit", edit_sector_path(@sector), class: "btn btn-primary-outline") %>
15+
<% end %>
16+
</div>
17+
</div>
18+
19+
<div class="space-y-6">
20+
<div class="mt-4">
21+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-3">
22+
23+
<div class="mb-3">
24+
<p class="font-bold text-gray-700">Name:</p>
25+
<p class="text-gray-900"><%= @sector.name %></p>
26+
</div>
27+
<div class="mb-3">
28+
<p class="font-bold text-gray-700">Published:</p>
29+
<p class="text-gray-900"><%= @sector.published %></p>
30+
</div>
31+
32+
</div>
33+
</div>
34+
</div>
35+
</div>
36+
</div>
37+
</div>

0 commit comments

Comments
 (0)