Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions app/controllers/categories_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class CategoriesController < ApplicationController
before_action :set_category, only: [:show, :edit, :update, :destroy]

def index
per_page = params[:number_of_items_per_page].presence || 25
@category_types = CategoryType.order(:name)

unpaginated = Category.joins(:category_type)
filtered = unpaginated.category_type_id(params[:category_type_id])
.category_name(params[:category_name])
.published_search(params[:published_search])
.order("metadata.name ASC, categories.name ASC")
@categories = filtered.paginate(page: params[:page], per_page: per_page)

@count_display = if @categories.total_entries == unpaginated.count
unpaginated.count
else
"#{@categories.total_entries}/#{unpaginated.count}"
end
end

def show
end

def new
@category = Category.new
set_form_variables
end

def edit
set_form_variables
end

def create
@category = Category.new(category_params)

if @category.save
redirect_to categories_path, notice: "Category was successfully created."
else
set_form_variables
render :new, status: :unprocessable_content
end
end

def update
if @category.update(category_params)
redirect_to categories_path, notice: "Category was successfully updated.", status: :see_other
else
set_form_variables
render :edit, status: :unprocessable_content
end
end

def destroy
@category.destroy!
redirect_to categories_path, notice: "Category was successfully destroyed."
end

# Optional hooks for setting variables for forms or index
def set_form_variables
@category_types = CategoryType.order(:name)
end

private

def set_category
@category = Category.find(params[:id])
end

# Strong parameters
def category_params
params.require(:category).permit(
:name, :category_type_id, :metadatum_id, :published
)
end
end
2 changes: 1 addition & 1 deletion app/helpers/admin_dashboard_cards_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def user_content_cards
# -----------------------------
def reference_cards
[
custom_card("Categories", authenticated_root_path, icon: "🗂️", color: :lime, intensity: 100),
model_card(:categories, icon: "🗂️", intensity: 100),
custom_card("Service populations", authenticated_root_path, icon: "🏭", color: :lime, intensity: 100),
custom_card("Project statuses", authenticated_root_path, icon: "🧮", color: :emerald, intensity: 100),
custom_card("Windows types", windows_types_path, icon: "🪟"),
Expand Down
9 changes: 9 additions & 0 deletions app/models/category.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ class Category < ApplicationRecord

# Validations
validates_presence_of :name, uniqueness: true

# Scopes
scope :category_type_id, ->(category_type_id) {
category_type_id.present? ? where(metadatum_id: category_type_id) : all }
scope :category_name, ->(category_name) {
category_name.present? ? where("categories.name LIKE ?", "%#{category_name}%") : all }
scope :published, ->(published=nil) {
["true", "false"].include?(published) ? where(published: published) : where(published: true) }
scope :published_search, ->(published_search) { published_search.present? ? published(published_search) : all }
end
61 changes: 61 additions & 0 deletions app/views/categories/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<%= simple_form_for(@category) do |f| %>
<div class="space-y-6">

<!-- Errors -->
<%= f.error_notification %>
<%= render "shared/errors", resource: @category if @category.errors.any? %>

<!-- One-line Flex Fields -->
<div class="flex flex-wrap gap-6">

<!-- Name -->
<div class="flex-1 min-w-[220px]">
<%= f.input :name,
label: "Name",
input_html: { class: "form-control" } %>
</div>

<!-- Category Type -->
<div class="flex-1 min-w-[220px]">
<%= f.input :metadatum_id,
label: "Category Type",
collection: @category_types,
label_method: :name,
value_method: :id,
include_blank: "Select a Type",
input_html: { class: "form-control" } %>
</div>

<!-- Published -->
<div class="flex items-center min-w-[150px] pt-6">
<%= f.input :published,
as: :boolean,
label: "Published?",
wrapper_html: { class: "flex items-center gap-2" },
input_html: { class: "form-checkbox" } %>
</div>

</div>

<!-- Actions -->
<div class="flex flex-wrap justify-end gap-3 pt-6">

<% if @category.persisted? && current_user.super_user? %>
<%= link_to "Delete",
@category,
method: :delete,
data: { confirm: "Are you sure you want to delete this category?" },
class: "btn btn-danger-outline" %>
<% end %>

<%= link_to "Cancel", categories_path,
class: "btn btn-secondary-outline" %>

<%= f.button :submit,
"Save Category",
class: "bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700
transition-colors duration-150" %>
</div>

</div>
<% end %>
52 changes: 52 additions & 0 deletions app/views/categories/_search_boxes.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!-- Filters -->
<div class="mb-6 p-4 bg-white border border-gray-200 rounded-lg">
<%= form_with url: categories_path,
method: :get,
local: true,
class: "grid grid-cols-1 md:grid-cols-5 gap-4 items-end" do |f| %>

<!-- Category Type -->
<div>
<%= f.label :category_type_id, "Category Type",
class: "block text-sm font-medium text-gray-700" %>

<%= f.select :category_type_id,
options_from_collection_for_select(@category_types, :id, :name, params[:category_type_id]),
{ include_blank: "All types" },
class: "mt-1 block w-full rounded-md border border-gray-300 p-2",
onchange: "this.form.requestSubmit()" %>
</div>

<!-- Name Search -->
<div>
<%= f.label :category_name, "Name Contains",
class: "block text-sm font-medium text-gray-700" %>

<%= f.text_field :category_name,
value: params[:category_name],
placeholder: "e.g. Art, Music…",
class: "mt-1 block w-full rounded-md border border-gray-300 p-2",
oninput: "this.form.requestSubmit()" %>
</div>

<!-- Published -->
<div>
<%= f.label :published_search, "Published",
class: "block text-sm font-medium text-gray-700" %>

<%= f.select :published_search,
options_for_select([["All", ""], ["Published", "true"], ["Hidden", "false"]], params[:published_search]),
{},
class: "mt-1 block w-full rounded-md border border-gray-300 p-2",
onchange: "this.form.requestSubmit()" %>
</div>

<!-- Clear -->
<div>
<%= link_to "Clear",
categories_path,
class: "btn btn-utility-outline whitespace-nowrap" %>
</div>

<% end %>
</div>
17 changes: 17 additions & 0 deletions app/views/categories/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="min-h-screen py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<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">
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-semibold text-gray-900">Edit <%= @category.class.model_name.human %></h1>
<%= link_to "Taggings", taggings_path(category_names: @category.name),
class: "btn btn-secondary-outline" %>
</div>

<div class="space-y-6">
<div class="mt-4">
<%= render "form", category: @category %>
</div>
</div>
</div>
</div>
</div>
99 changes: 99 additions & 0 deletions app/views/categories/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<div class="min-h-screen">
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto <%= DomainTheme.bg_class_for(:categories) %> border border-gray-200 rounded-xl shadow-lg p-6">
<div class="space-y-6">

<!-- Header -->
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-semibold text-gray-900">
<%= Category.model_name.human.pluralize %> (<%= @count_display %>)
</h1>
<%= link_to "New #{Category.model_name.human.downcase}",
new_category_path,
class: "btn btn-primary-outline" %>
</div>

<%= render "search_boxes" %>

<!-- Table -->
<div class="bg-white rounded">
<div class="overflow-x-auto">
<% if @categories.any? %>
<table class="w-full table-fixed border-collapse border border-gray-200">
<thead class="bg-gray-100">
<tr>
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/3">
Name
</th>

<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/4">
Type
</th>

<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-1/6">
Published
</th>

<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-[80px]">
Actions
</th>
</tr>
</thead>

<tbody class="divide-y divide-gray-200">
<% @categories.each do |category| %>
<tr class=" <%= "bg-gray-200" unless category.published %>
<%= category.published ? "hover:bg-gray-50" : "hover:bg-gray-100" %> transition-colors duration-150">

<td class="px-4 py-2 text-md text-gray-800 truncate font-bold">
<%= category.name %>
</td>

<td class="px-4 py-2 text-sm text-gray-800 truncate">
<%= category.category_type&.name || "—" %>
</td>

<td class="px-4 py-2 text-center">
<% if category.published? %>
<span class="inline-block px-2 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded">
Yes
</span>
<% else %>
<span class="inline-block px-2 py-1 text-xs font-semibold text-gray-700 bg-gray-200 rounded">
No
</span>
<% end %>
</td>

<!-- Actions -->
<td class="px-4 py-2 text-center">
<%= link_to "Edit",
edit_category_path(category),
class: "btn btn-secondary-outline whitespace-nowrap" %>
<%= link_to "Taggings",
taggings_path(category_names: category.name),
class: "btn btn-secondary-outline whitespace-nowrap" %>
</td>

</tr>
<% end %>
</tbody>
</table>
<% else %>
<!-- Empty state -->
<p class="text-gray-500 text-center py-6">
No <%= Category.model_name.human.pluralize %> found.
</p>
<% end %>
</div>
</div>

<!-- Pagination -->
<div class="pagination flex justify-center mt-12">
<%= tailwind_paginate @categories %>
</div>

</div>
</div>
</div>
</div>
17 changes: 17 additions & 0 deletions app/views/categories/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="min-h-screen py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<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">
<div class="flex items-center justify-between mb-3">
<h1 class="text-2xl font-semibold text-gray-900">New <%= @category.class.model_name.human %></h1>
</div>

<div class="border-b border-gray-300 mb-6"></div>

<div class="space-y-6">
<div class="mt-4">
<%= render "form", category: @category %>
</div>
</div>
</div>
</div>
</div>
41 changes: 41 additions & 0 deletions app/views/categories/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div class="min-h-screen py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<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">
<div class="flex items-center justify-between mb-6">
<!-- Title -->
<h1 class="text-2xl font-semibold text-gray-900">
<%= @category.class.model_name.human %> Details
</h1>

<!-- Buttons Group -->
<div class="flex items-center gap-2">
<%= link_to("Index", categories_path, class: "btn btn-secondary-outline") %>
<% if current_user.super_user? %>
<%= link_to("Edit", edit_category_path(@category), class: "btn btn-primary-outline") %>
<% end %>
</div>
</div>

<div class="space-y-6">
<div class="mt-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-3">

<div class="mb-3">
<p class="font-bold text-gray-700">Name:</p>
<p class="text-gray-900"><%= @category.name %></p>
</div>
<div class="mb-3">
<p class="font-bold text-gray-700">Category type:</p>
<p class="text-gray-900"><%= @category.category_type.name %></p>
</div>
<div class="mb-3">
<p class="font-bold text-gray-700">Published:</p>
<p class="text-gray-900"><%= @category.published %></p>
</div>

</div>
</div>
</div>
</div>
</div>
</div>
Loading