super_admin 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +216 -0
- data/Rakefile +30 -0
- data/app/assets/stylesheets/super_admin/application.css +15 -0
- data/app/assets/stylesheets/super_admin/tailwind.css +1 -0
- data/app/assets/stylesheets/super_admin/tailwind.source.css +25 -0
- data/app/controllers/super_admin/application_controller.rb +89 -0
- data/app/controllers/super_admin/associations_controller.rb +136 -0
- data/app/controllers/super_admin/audit_logs_controller.rb +39 -0
- data/app/controllers/super_admin/base_controller.rb +133 -0
- data/app/controllers/super_admin/dashboard_controller.rb +29 -0
- data/app/controllers/super_admin/exports_controller.rb +109 -0
- data/app/controllers/super_admin/resources_controller.rb +201 -0
- data/app/dashboards/super_admin/base_dashboard.rb +200 -0
- data/app/errors/super_admin/configuration_error.rb +6 -0
- data/app/helpers/super_admin/application_helper.rb +84 -0
- data/app/helpers/super_admin/exports_helper.rb +16 -0
- data/app/helpers/super_admin/resources_helper.rb +204 -0
- data/app/helpers/super_admin/route_helper.rb +7 -0
- data/app/javascript/super_admin/application.js +263 -0
- data/app/jobs/super_admin/application_job.rb +4 -0
- data/app/jobs/super_admin/generate_super_admin_csv_export_job.rb +100 -0
- data/app/mailers/super_admin/application_mailer.rb +6 -0
- data/app/models/super_admin/application_record.rb +5 -0
- data/app/models/super_admin/audit_log.rb +35 -0
- data/app/models/super_admin/csv_export.rb +67 -0
- data/app/services/super_admin/auditing.rb +74 -0
- data/app/services/super_admin/authorization.rb +113 -0
- data/app/services/super_admin/authorization_adapters/base_adapter.rb +100 -0
- data/app/services/super_admin/authorization_adapters/default_adapter.rb +77 -0
- data/app/services/super_admin/authorization_adapters/proc_adapter.rb +65 -0
- data/app/services/super_admin/authorization_adapters/pundit_adapter.rb +81 -0
- data/app/services/super_admin/csv_export_creator.rb +45 -0
- data/app/services/super_admin/dashboard_registry.rb +90 -0
- data/app/services/super_admin/dashboard_resolver.rb +100 -0
- data/app/services/super_admin/filter_builder.rb +185 -0
- data/app/services/super_admin/form_builder.rb +59 -0
- data/app/services/super_admin/form_fields/array_field.rb +35 -0
- data/app/services/super_admin/form_fields/association_field.rb +146 -0
- data/app/services/super_admin/form_fields/base_field.rb +53 -0
- data/app/services/super_admin/form_fields/boolean_field.rb +29 -0
- data/app/services/super_admin/form_fields/date_field.rb +15 -0
- data/app/services/super_admin/form_fields/date_time_field.rb +15 -0
- data/app/services/super_admin/form_fields/enum_field.rb +27 -0
- data/app/services/super_admin/form_fields/factory.rb +102 -0
- data/app/services/super_admin/form_fields/nested_field.rb +120 -0
- data/app/services/super_admin/form_fields/number_field.rb +29 -0
- data/app/services/super_admin/form_fields/text_area_field.rb +19 -0
- data/app/services/super_admin/model_inspector.rb +182 -0
- data/app/services/super_admin/queries/base_query.rb +45 -0
- data/app/services/super_admin/queries/filter_query.rb +188 -0
- data/app/services/super_admin/queries/resource_scope_query.rb +74 -0
- data/app/services/super_admin/queries/search_query.rb +146 -0
- data/app/services/super_admin/queries/sort_query.rb +41 -0
- data/app/services/super_admin/resource_configuration.rb +63 -0
- data/app/services/super_admin/resource_exporter.rb +78 -0
- data/app/services/super_admin/resource_query.rb +40 -0
- data/app/services/super_admin/resources/association_inspector.rb +112 -0
- data/app/services/super_admin/resources/collection_presenter.rb +63 -0
- data/app/services/super_admin/resources/context.rb +63 -0
- data/app/services/super_admin/resources/filter_params.rb +29 -0
- data/app/services/super_admin/resources/permitted_attributes.rb +104 -0
- data/app/services/super_admin/resources/value_normalizer.rb +121 -0
- data/app/services/super_admin/sensitive_attributes.rb +166 -0
- data/app/views/layouts/super_admin.html.erb +74 -0
- data/app/views/super_admin/audit_logs/index.html.erb +143 -0
- data/app/views/super_admin/dashboard/index.html.erb +79 -0
- data/app/views/super_admin/exports/index.html.erb +84 -0
- data/app/views/super_admin/exports/show.html.erb +57 -0
- data/app/views/super_admin/resources/_form.html.erb +42 -0
- data/app/views/super_admin/resources/destroy.turbo_stream.erb +17 -0
- data/app/views/super_admin/resources/edit.html.erb +37 -0
- data/app/views/super_admin/resources/index.html.erb +189 -0
- data/app/views/super_admin/resources/new.html.erb +31 -0
- data/app/views/super_admin/resources/show.html.erb +106 -0
- data/app/views/super_admin/shared/_breadcrumbs.html.erb +12 -0
- data/app/views/super_admin/shared/_custom_styles.html.erb +132 -0
- data/app/views/super_admin/shared/_flash.html.erb +55 -0
- data/app/views/super_admin/shared/_form_field.html.erb +35 -0
- data/app/views/super_admin/shared/_navigation.html.erb +92 -0
- data/app/views/super_admin/shared/_nested_fields.html.erb +59 -0
- data/app/views/super_admin/shared/_nested_record_fields.html.erb +45 -0
- data/config/importmap.rb +4 -0
- data/config/initializers/rack_attack.rb +134 -0
- data/config/initializers/super_admin.rb +117 -0
- data/config/locales/super_admin.en.yml +197 -0
- data/config/locales/super_admin.fr.yml +197 -0
- data/config/routes.rb +22 -0
- data/lib/generators/super_admin/dashboard_generator.rb +50 -0
- data/lib/generators/super_admin/install_generator.rb +58 -0
- data/lib/generators/super_admin/templates/20240101000001_create_super_admin_audit_logs.rb +24 -0
- data/lib/generators/super_admin/templates/20240101000002_create_super_admin_csv_exports.rb +33 -0
- data/lib/generators/super_admin/templates/super_admin.rb +58 -0
- data/lib/super_admin/dashboard_creator.rb +256 -0
- data/lib/super_admin/engine.rb +53 -0
- data/lib/super_admin/install_task.rb +96 -0
- data/lib/super_admin/version.rb +3 -0
- data/lib/super_admin.rb +7 -0
- data/lib/tasks/super_admin_tasks.rake +38 -0
- metadata +239 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<% content_for :title, model_display_name(@model_class) %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<li class="flex items-center">
|
|
4
|
+
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
5
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
<span class="text-gray-900 font-medium"><%= model_display_name(@model_class) %></span>
|
|
8
|
+
</li>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<% applied_filters = @applied_filters || {} %>
|
|
12
|
+
|
|
13
|
+
<div class="max-w-full mx-auto">
|
|
14
|
+
<!-- Flash messages container for Turbo Streams -->
|
|
15
|
+
<div id="flash_messages"></div>
|
|
16
|
+
<!-- Header -->
|
|
17
|
+
<div class="mb-4 sm:mb-6 flex flex-col gap-3 sm:gap-4 md:flex-row md:items-center md:justify-between">
|
|
18
|
+
<div class="min-w-0 flex-1">
|
|
19
|
+
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 truncate"><%= model_display_name(@model_class) %></h1>
|
|
20
|
+
<p class="mt-1 text-xs sm:text-sm text-gray-600">
|
|
21
|
+
<%= t("super_admin.resources.index.records_count", count: @pagy.count, total: number_with_delimiter(@pagy.count)) %>
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="flex flex-wrap gap-2">
|
|
25
|
+
<%= button_to t("super_admin.resources.index.export_csv"), super_admin_exports_path, method: :post, params: { resource: params[:resource], search: params[:search], sort: params[:sort], direction: params[:direction], filters: applied_filters.presence }, form_class: "inline-block", class: "inline-flex items-center px-3 sm:px-4 py-2 border border-gray-300 text-xs sm:text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50" %>
|
|
26
|
+
<%= link_to t("super_admin.resources.index.new"), super_admin_new_resource_path(resource: params[:resource]), class: "inline-flex items-center px-3 sm:px-4 py-2 border border-transparent text-xs sm:text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover-bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus-ring-blue-500" %>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- Search & Filters -->
|
|
31
|
+
<%= form_with url: super_admin_resources_path(resource: params[:resource]), method: :get, local: true, class: "mb-6 sm:mb-8 space-y-4" do %>
|
|
32
|
+
<div class="flex flex-col gap-3 sm:flex-row bg-white rounded-lg shadow-sm border border-gray-200 p-3 sm:p-4">
|
|
33
|
+
<%= text_field_tag :search, params[:search], placeholder: t("super_admin.resources.shared.search_placeholder"), class: "flex-1 rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 text-sm sm:text-base px-3 sm:px-4 py-2 sm:py-2.5" %>
|
|
34
|
+
<div class="flex gap-2">
|
|
35
|
+
<% if params[:sort].present? %>
|
|
36
|
+
<%= hidden_field_tag :sort, params[:sort] %>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% if params[:direction].present? %>
|
|
39
|
+
<%= hidden_field_tag :direction, params[:direction] %>
|
|
40
|
+
<% end %>
|
|
41
|
+
<%= submit_tag t("super_admin.resources.shared.search_button"), class: "inline-flex items-center px-3 sm:px-4 py-2 border border-transparent text-xs sm:text-sm font-medium rounded-md text-white bg-gray-700 hover-bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-colors" %>
|
|
42
|
+
<% if params[:search].present? %>
|
|
43
|
+
<%= link_to t("super_admin.resources.shared.reset_filters"), super_admin_resources_path(resource: params[:resource]), class: "inline-flex items-center px-3 sm:px-4 py-2 border border-gray-300 text-xs sm:text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus-ring-blue-500 transition-colors" %>
|
|
44
|
+
<% end %>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<% if @filter_definitions.any? %>
|
|
49
|
+
<div class="rounded-lg border border-gray-200 bg-white shadow-sm overflow-hidden">
|
|
50
|
+
<div class="bg-gradient-to-r from-gray-50 to-gray-100 px-4 sm:px-6 py-3 sm:py-4 border-b border-gray-200">
|
|
51
|
+
<h2 class="text-xs sm:text-sm font-semibold text-gray-800 flex items-center">
|
|
52
|
+
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-2 text-gray-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
53
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
|
|
54
|
+
</svg>
|
|
55
|
+
<%= t("super_admin.resources.shared.advanced_filters") %>
|
|
56
|
+
</h2>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="px-4 sm:px-6 py-4 sm:py-5">
|
|
59
|
+
<div class="grid gap-4 sm:gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
60
|
+
<% @filter_definitions.each do |definition| %>
|
|
61
|
+
<div class="space-y-2">
|
|
62
|
+
<label class="block text-xs font-semibold text-gray-700 uppercase tracking-wider"><%= definition.label %></label>
|
|
63
|
+
<% case definition.type
|
|
64
|
+
when :string, :text %>
|
|
65
|
+
<%= text_field_tag "filters[#{definition.attribute}_contains]", filter_value(applied_filters, "#{definition.attribute}_contains"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2", placeholder: t("super_admin.resources.shared.contains_placeholder") %>
|
|
66
|
+
<% when :boolean %>
|
|
67
|
+
<% options = [[t("super_admin.resources.shared.boolean_options.placeholder"), ""], [t("super_admin.resources.shared.boolean_options.yes"), "true"], [t("super_admin.resources.shared.boolean_options.no"), "false"]] %>
|
|
68
|
+
<%= select_tag "filters[#{definition.attribute}_equals]", options_for_select(options, filter_value(applied_filters, "#{definition.attribute}_equals")), include_blank: false, class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
69
|
+
<% when :enum %>
|
|
70
|
+
<% enum_options = [[t("super_admin.resources.shared.boolean_options.placeholder"), ""]] + definition.options.map { |opt| [opt.to_s.humanize, opt] } %>
|
|
71
|
+
<%= select_tag "filters[#{definition.attribute}_equals]", options_for_select(enum_options, filter_value(applied_filters, "#{definition.attribute}_equals")), include_blank: false, class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
72
|
+
<% when :integer %>
|
|
73
|
+
<div class="flex gap-2">
|
|
74
|
+
<%= number_field_tag "filters[#{definition.attribute}_min]", filter_value(applied_filters, "#{definition.attribute}_min"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2", placeholder: t("super_admin.resources.shared.min_placeholder"), step: 1 %>
|
|
75
|
+
<%= number_field_tag "filters[#{definition.attribute}_max]", filter_value(applied_filters, "#{definition.attribute}_max"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2", placeholder: t("super_admin.resources.shared.max_placeholder"), step: 1 %>
|
|
76
|
+
</div>
|
|
77
|
+
<% when :float, :decimal %>
|
|
78
|
+
<div class="flex gap-2">
|
|
79
|
+
<%= number_field_tag "filters[#{definition.attribute}_min]", filter_value(applied_filters, "#{definition.attribute}_min"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2", placeholder: t("super_admin.resources.shared.min_placeholder"), step: 0.01 %>
|
|
80
|
+
<%= number_field_tag "filters[#{definition.attribute}_max]", filter_value(applied_filters, "#{definition.attribute}_max"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2", placeholder: t("super_admin.resources.shared.max_placeholder"), step: 0.01 %>
|
|
81
|
+
</div>
|
|
82
|
+
<% when :date %>
|
|
83
|
+
<div class="flex gap-2">
|
|
84
|
+
<%= date_field_tag "filters[#{definition.attribute}_from]", filter_date_value(applied_filters, "#{definition.attribute}_from"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
85
|
+
<%= date_field_tag "filters[#{definition.attribute}_to]", filter_date_value(applied_filters, "#{definition.attribute}_to"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
86
|
+
</div>
|
|
87
|
+
<% when :datetime %>
|
|
88
|
+
<div class="flex gap-2">
|
|
89
|
+
<%= datetime_field_tag "filters[#{definition.attribute}_from]", filter_datetime_value(applied_filters, "#{definition.attribute}_from"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
90
|
+
<%= datetime_field_tag "filters[#{definition.attribute}_to]", filter_datetime_value(applied_filters, "#{definition.attribute}_to"), class: "w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 sm:text-sm bg-white px-3 py-2" %>
|
|
91
|
+
</div>
|
|
92
|
+
<% end %>
|
|
93
|
+
</div>
|
|
94
|
+
<% end %>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="px-4 sm:px-6 mt-4 sm:mt-6 pt-4 border-t border-gray-200 flex flex-col sm:flex-row justify-end gap-2 sm:gap-3">
|
|
97
|
+
<%= link_to t("super_admin.resources.shared.clear_filters"), super_admin_resources_path(resource: params[:resource], search: params[:search], sort: params[:sort], direction: params[:direction]), class: "inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-xs sm:text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus-ring-blue-500 transition-colors" %>
|
|
98
|
+
<%= submit_tag t("super_admin.resources.shared.apply_filters"), class: "inline-flex items-center justify-center px-4 py-2 border border-transparent text-xs sm:text-sm font-medium rounded-md text-white bg-blue-600 hover-bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus-ring-blue-500 shadow-sm transition-colors" %>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<% end %>
|
|
103
|
+
<% end %>
|
|
104
|
+
|
|
105
|
+
<!-- Table & Bulk actions -->
|
|
106
|
+
<%= form_with url: super_admin_bulk_action_path(resource: params[:resource]), method: :post, local: true, data: { controller: "super-admin--bulk-selection" }, class: "bg-white rounded-lg shadow overflow-hidden border border-gray-200" do %>
|
|
107
|
+
<%= hidden_field_tag :search, params[:search] if params[:search].present? %>
|
|
108
|
+
<%= hidden_field_tag :sort, params[:sort] if params[:sort].present? %>
|
|
109
|
+
<%= hidden_field_tag :direction, params[:direction] if params[:direction].present? %>
|
|
110
|
+
<% applied_filters.each do |key, value| %>
|
|
111
|
+
<%= hidden_field_tag "filters[#{key}]", value %>
|
|
112
|
+
<% end %>
|
|
113
|
+
|
|
114
|
+
<div class="flex flex-col gap-2 border-b border-gray-200 bg-gray-50 p-3 sm:p-4 sm:flex-row sm:items-center sm:justify-between">
|
|
115
|
+
<div class="text-xs sm:text-sm text-gray-600" data-super-admin--bulk-selection-target="summary">
|
|
116
|
+
<span data-super-admin--bulk-selection-target="counter">0</span> <%= t("super_admin.resources.shared.bulk.selected_suffix") %>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="flex flex-col sm:flex-row gap-2">
|
|
119
|
+
<%= select_tag :bulk_action, options_for_select([[t("super_admin.resources.shared.bulk.action_placeholder"), ""], [t("super_admin.resources.shared.bulk.destroy_action"), "destroy"]]), class: "rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus-ring-blue-500 text-xs sm:text-sm px-3 py-2" %>
|
|
120
|
+
<%= submit_tag t("super_admin.resources.shared.bulk.apply"), class: "inline-flex items-center justify-center px-3 sm:px-4 py-2 border border-transparent text-xs sm:text-sm font-medium rounded-md text-white bg-red-600 hover-bg-red-700", data: { turbo_confirm: t("super_admin.resources.shared.bulk.confirm") } %>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<% if @resources.any? %>
|
|
125
|
+
<div class="overflow-x-auto -mx-3 sm:mx-0">
|
|
126
|
+
<div class="inline-block min-w-full align-middle">
|
|
127
|
+
<table class="min-w-full divide-y divide-gray-200">
|
|
128
|
+
<thead class="bg-gray-50">
|
|
129
|
+
<tr>
|
|
130
|
+
<th scope="col" class="w-12 px-3 sm:px-4 py-3">
|
|
131
|
+
<%= check_box_tag "select_all", "1", false,
|
|
132
|
+
id: "select_all",
|
|
133
|
+
class: "rounded border-gray-300",
|
|
134
|
+
aria: { label: t("super_admin.resources.shared.bulk.select_all_label") },
|
|
135
|
+
data: { action: "super-admin--bulk-selection#toggleAll", super_admin__bulk_selection_target: "toggle" } %>
|
|
136
|
+
</th>
|
|
137
|
+
<% @attributes.first(8).each do |attr| %>
|
|
138
|
+
<th scope="col" class="px-3 sm:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider whitespace-nowrap">
|
|
139
|
+
<%= @model_class.human_attribute_name(attr) %>
|
|
140
|
+
</th>
|
|
141
|
+
<% end %>
|
|
142
|
+
<th scope="col" class="px-3 sm:px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider whitespace-nowrap">
|
|
143
|
+
<%= t("super_admin.resources.index.actions_header") %>
|
|
144
|
+
</th>
|
|
145
|
+
</tr>
|
|
146
|
+
</thead>
|
|
147
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
|
148
|
+
<% @resources.each do |resource| %>
|
|
149
|
+
<tr id="resource_row_<%= resource.id %>" class="hover:bg-gray-50">
|
|
150
|
+
<td class="px-3 sm:px-4 py-3 sm:py-4">
|
|
151
|
+
<%= check_box_tag "resource_ids[]", resource.id, false,
|
|
152
|
+
id: "resource_#{resource.id}",
|
|
153
|
+
class: "rounded border-gray-300",
|
|
154
|
+
aria: { label: t("super_admin.resources.shared.bulk.select_item_label", id: resource.id) },
|
|
155
|
+
data: { action: "super-admin--bulk-selection#itemChanged", super_admin__bulk_selection_target: "checkbox" } %>
|
|
156
|
+
</td>
|
|
157
|
+
<% @attributes.first(8).each do |attr| %>
|
|
158
|
+
<td class="px-3 sm:px-6 py-3 sm:py-4 text-xs sm:text-sm text-gray-900">
|
|
159
|
+
<div class="max-w-xs truncate">
|
|
160
|
+
<%= truncate(format_attribute_value(resource, attr), length: 50) %>
|
|
161
|
+
</div>
|
|
162
|
+
</td>
|
|
163
|
+
<% end %>
|
|
164
|
+
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap text-right text-xs sm:text-sm font-medium">
|
|
165
|
+
<div class="flex justify-end gap-2 sm:space-x-2">
|
|
166
|
+
<%= link_to t("super_admin.resources.index.view"), super_admin_resource_path(resource: params[:resource], id: resource.id), class: "text-blue-600 hover-text-blue-900" %>
|
|
167
|
+
<%= link_to t("super_admin.resources.index.edit"), super_admin_edit_resource_path(resource: params[:resource], id: resource.id), class: "text-indigo-600 hover:text-indigo-900" %>
|
|
168
|
+
<%= link_to t("super_admin.resources.index.delete"), super_admin_resource_path(resource: params[:resource], id: resource.id), data: { turbo_method: :delete, turbo_confirm: t("super_admin.resources.index.delete_confirm") }, class: "text-red-600 hover-text-red-900" %>
|
|
169
|
+
</div>
|
|
170
|
+
</td>
|
|
171
|
+
</tr>
|
|
172
|
+
<% end %>
|
|
173
|
+
</tbody>
|
|
174
|
+
</table>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<% else %>
|
|
178
|
+
<div class="p-4 sm:p-6 text-center text-xs sm:text-sm text-gray-500">
|
|
179
|
+
<%= t("super_admin.resources.index.empty_state") %>
|
|
180
|
+
</div>
|
|
181
|
+
<% end %>
|
|
182
|
+
|
|
183
|
+
<% if @pagy.pages > 1 %>
|
|
184
|
+
<div class="bg-white px-3 sm:px-4 py-3 border-t border-gray-200 sm:px-6">
|
|
185
|
+
<%== pagy_nav(@pagy) %>
|
|
186
|
+
</div>
|
|
187
|
+
<% end %>
|
|
188
|
+
<% end %>
|
|
189
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<% content_for :title, t("super_admin.resources.new.page_title", model: model_display_name(@model_class)) %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<li class="flex items-center">
|
|
4
|
+
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
5
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
<%= link_to model_display_name(@model_class), super_admin_resources_path(resource: params[:resource]), class: "text-blue-600 hover-text-blue-800" %>
|
|
8
|
+
</li>
|
|
9
|
+
<li class="flex items-center">
|
|
10
|
+
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
11
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
|
12
|
+
</svg>
|
|
13
|
+
<span class="text-gray-900 font-medium"><%= t("super_admin.resources.new.breadcrumb") %></span>
|
|
14
|
+
</li>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<div class="max-w-3xl mx-auto">
|
|
18
|
+
<!-- Header -->
|
|
19
|
+
<div class="mb-6">
|
|
20
|
+
<h1 class="text-3xl font-bold text-gray-900">
|
|
21
|
+
<%= t("super_admin.resources.new.heading", model: @model_class.model_name.human.downcase) %>
|
|
22
|
+
</h1>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<!-- Form Card -->
|
|
26
|
+
<div class="bg-white rounded-lg shadow border border-gray-200">
|
|
27
|
+
<div class="px-6 py-8">
|
|
28
|
+
<%= render "form", resource: @resource, model_class: @model_class, editable_attributes: @editable_attributes %>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<% content_for :title, t("super_admin.resources.show.page_title", model: model_display_name(@model_class)) %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<li class="flex items-center">
|
|
4
|
+
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
5
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
<%= link_to model_display_name(@model_class), super_admin_resources_path(resource: params[:resource]), class: "text-blue-600 hover-text-blue-800" %>
|
|
8
|
+
</li>
|
|
9
|
+
<li class="flex items-center">
|
|
10
|
+
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
11
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
|
12
|
+
</svg>
|
|
13
|
+
<span class="text-gray-900 font-medium"><%= t("super_admin.resources.show.breadcrumb_id", id: @resource.id) %></span>
|
|
14
|
+
</li>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<div class="max-w-4xl mx-auto">
|
|
18
|
+
<!-- Header -->
|
|
19
|
+
<div class="mb-6 flex items-center justify-between">
|
|
20
|
+
<div>
|
|
21
|
+
<h1 class="text-3xl font-bold text-gray-900">
|
|
22
|
+
<%= t("super_admin.resources.show.heading", model: model_display_name(@model_class), id: @resource.id) %>
|
|
23
|
+
</h1>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="flex space-x-2">
|
|
26
|
+
<%= link_to t("super_admin.resources.show.edit"), super_admin_edit_resource_path(resource: params[:resource], id: @resource.id), class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover-bg-blue-700" %>
|
|
27
|
+
<%= button_to t("super_admin.resources.show.delete"), super_admin_resource_path(resource: params[:resource], id: @resource.id), method: :delete, data: { turbo_confirm: t("super_admin.resources.show.delete_confirm") }, class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover-bg-red-700" %>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- Attributes Card -->
|
|
32
|
+
<div class="bg-white rounded-lg shadow border border-gray-200 overflow-hidden mb-6">
|
|
33
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
|
34
|
+
<h2 class="text-lg font-medium text-gray-900"><%= t("super_admin.resources.show.attributes") %></h2>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="px-6 py-4">
|
|
37
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2">
|
|
38
|
+
<% @attributes.each do |attr| %>
|
|
39
|
+
<div>
|
|
40
|
+
<dt class="text-sm font-medium text-gray-500">
|
|
41
|
+
<%= @model_class.human_attribute_name(attr) %>
|
|
42
|
+
</dt>
|
|
43
|
+
<dd class="mt-1 text-sm text-gray-900">
|
|
44
|
+
<%= format_attribute_value(@resource, attr) %>
|
|
45
|
+
</dd>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|
|
48
|
+
</dl>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<!-- Associations -->
|
|
53
|
+
<% if @associations.any? %>
|
|
54
|
+
<div class="bg-white rounded-lg shadow border border-gray-200 overflow-hidden">
|
|
55
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
|
56
|
+
<h2 class="text-lg font-medium text-gray-900"><%= t("super_admin.resources.show.associations") %></h2>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="px-6 py-4">
|
|
59
|
+
<dl class="space-y-4">
|
|
60
|
+
<% @associations.each do |assoc| %>
|
|
61
|
+
<div>
|
|
62
|
+
<dt class="text-sm font-medium text-gray-500">
|
|
63
|
+
<%= assoc.name.to_s.humanize %>
|
|
64
|
+
<span class="ml-2 text-xs text-gray-400">(<%= assoc.macro %>)</span>
|
|
65
|
+
</dt>
|
|
66
|
+
<dd class="mt-1 text-sm text-gray-900">
|
|
67
|
+
<% if assoc.macro == :has_many %>
|
|
68
|
+
<% count = @association_counts.fetch(assoc.name, 0) %>
|
|
69
|
+
<% if count.positive? %>
|
|
70
|
+
<% counter = content_tag(:span, count, class: "font-medium") %>
|
|
71
|
+
<%= t("super_admin.resources.show.association_count_html", count: count, number: counter) %>
|
|
72
|
+
<% else %>
|
|
73
|
+
<span class="text-gray-400 italic"><%= t("super_admin.resources.show.association_none") %></span>
|
|
74
|
+
<% end %>
|
|
75
|
+
<% else %>
|
|
76
|
+
<% begin %>
|
|
77
|
+
<% associated = @resource.public_send(assoc.name) %>
|
|
78
|
+
<% if associated.nil? %>
|
|
79
|
+
<span class="text-gray-400 italic"><%= t("super_admin.resources.show.association_none") %></span>
|
|
80
|
+
<% elsif associated.is_a?(ActiveRecord::Base) %>
|
|
81
|
+
<span class="font-medium">#<%= associated.id %></span>
|
|
82
|
+
<% else %>
|
|
83
|
+
<%= associated.to_s %>
|
|
84
|
+
<% end %>
|
|
85
|
+
<% rescue StandardError => e %>
|
|
86
|
+
<span class="text-red-600 text-xs"><%= t("super_admin.resources.show.association_error", message: e.message) %></span>
|
|
87
|
+
<% end %>
|
|
88
|
+
<% end %>
|
|
89
|
+
</dd>
|
|
90
|
+
</div>
|
|
91
|
+
<% end %>
|
|
92
|
+
</dl>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<% end %>
|
|
96
|
+
|
|
97
|
+
<!-- Timestamps -->
|
|
98
|
+
<div class="mt-6 text-sm text-gray-500 text-center">
|
|
99
|
+
<% if @resource.respond_to?(:created_at) %>
|
|
100
|
+
<%= t("super_admin.resources.show.created_at", date: l(@resource.created_at, format: :long)) %>
|
|
101
|
+
<% end %>
|
|
102
|
+
<% if @resource.respond_to?(:updated_at) && @resource.updated_at != @resource.created_at %>
|
|
103
|
+
• <%= t("super_admin.resources.show.updated_at", date: l(@resource.updated_at, format: :long)) %>
|
|
104
|
+
<% end %>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<nav class="flex overflow-x-auto" aria-label="Breadcrumb">
|
|
2
|
+
<ol class="flex items-center space-x-1 sm:space-x-2 text-xs sm:text-sm whitespace-nowrap">
|
|
3
|
+
<li class="flex-shrink-0">
|
|
4
|
+
<%= link_to super_admin_root_path, class: "text-gray-500 hover:text-gray-700" do %>
|
|
5
|
+
<svg class="h-4 w-4 sm:h-5 sm:w-5" fill="currentColor" viewBox="0 0 20 20">
|
|
6
|
+
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path>
|
|
7
|
+
</svg>
|
|
8
|
+
<% end %>
|
|
9
|
+
</li>
|
|
10
|
+
<%= content_for(:breadcrumbs) if content_for?(:breadcrumbs) %>
|
|
11
|
+
</ol>
|
|
12
|
+
</nav>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
/* SuperAdmin Custom Colors - Embedded for compatibility with Propshaft */
|
|
3
|
+
|
|
4
|
+
/* Background Colors - White & Gray */
|
|
5
|
+
.bg-white { background-color: #ffffff !important; }
|
|
6
|
+
.bg-gray-50 { background-color: #f9fafb !important; }
|
|
7
|
+
.bg-gray-100 { background-color: #f3f4f6 !important; }
|
|
8
|
+
.bg-gray-200 { background-color: #e5e7eb !important; }
|
|
9
|
+
.bg-gray-300 { background-color: #d1d5db !important; }
|
|
10
|
+
.bg-gray-800 { background-color: #1f2937 !important; }
|
|
11
|
+
.bg-gray-900 { background-color: #111827 !important; }
|
|
12
|
+
|
|
13
|
+
/* Background Colors - Blue */
|
|
14
|
+
.bg-blue-50 { background-color: #eff6ff !important; }
|
|
15
|
+
.bg-blue-100 { background-color: #dbeafe !important; }
|
|
16
|
+
.bg-blue-200 { background-color: #bfdbfe !important; }
|
|
17
|
+
.bg-blue-500 { background-color: #3b82f6 !important; }
|
|
18
|
+
.bg-blue-600 { background-color: #2563eb !important; }
|
|
19
|
+
.bg-blue-700 { background-color: #1d4ed8 !important; }
|
|
20
|
+
.bg-blue-900 { background-color: #1e3a8a !important; }
|
|
21
|
+
|
|
22
|
+
/* Background Colors - Red */
|
|
23
|
+
.bg-red-50 { background-color: #fef2f2 !important; }
|
|
24
|
+
.bg-red-100 { background-color: #fee2e2 !important; }
|
|
25
|
+
.bg-red-200 { background-color: #fecaca !important; }
|
|
26
|
+
.bg-red-400 { background-color: #f87171 !important; }
|
|
27
|
+
.bg-red-600 { background-color: #dc2626 !important; }
|
|
28
|
+
.bg-red-700 { background-color: #b91c1c !important; }
|
|
29
|
+
.bg-red-900 { background-color: #7f1d1d !important; }
|
|
30
|
+
|
|
31
|
+
/* Background Colors - Green */
|
|
32
|
+
.bg-green-50 { background-color: #f0fdf4 !important; }
|
|
33
|
+
.bg-green-100 { background-color: #dcfce7 !important; }
|
|
34
|
+
.bg-green-200 { background-color: #bbf7d0 !important; }
|
|
35
|
+
.bg-green-500 { background-color: #22c55e !important; }
|
|
36
|
+
.bg-green-600 { background-color: #16a34a !important; }
|
|
37
|
+
.bg-green-700 { background-color: #15803d !important; }
|
|
38
|
+
.bg-green-900 { background-color: #14532d !important; }
|
|
39
|
+
|
|
40
|
+
/* Background Colors - Yellow */
|
|
41
|
+
.bg-yellow-50 { background-color: #fefce8 !important; }
|
|
42
|
+
.bg-yellow-100 { background-color: #fef9c3 !important; }
|
|
43
|
+
.bg-yellow-200 { background-color: #fef08a !important; }
|
|
44
|
+
.bg-yellow-600 { background-color: #ca8a04 !important; }
|
|
45
|
+
.bg-yellow-700 { background-color: #a16207 !important; }
|
|
46
|
+
.bg-yellow-900 { background-color: #713f12 !important; }
|
|
47
|
+
|
|
48
|
+
/* Gradient Backgrounds */
|
|
49
|
+
.bg-gradient-blue { background: linear-gradient(to right, #eff6ff, #dbeafe) !important; }
|
|
50
|
+
.bg-gradient-green { background: linear-gradient(to right, #f0fdf4, #dcfce7) !important; }
|
|
51
|
+
.bg-gradient-yellow { background: linear-gradient(to right, #fefce8, #fef9c3) !important; }
|
|
52
|
+
.bg-gradient-red { background: linear-gradient(to right, #fef2f2, #fee2e2) !important; }
|
|
53
|
+
|
|
54
|
+
/* Text Colors - White & Gray */
|
|
55
|
+
.text-white { color: #ffffff !important; }
|
|
56
|
+
.text-gray-300 { color: #d1d5db !important; }
|
|
57
|
+
.text-gray-400 { color: #9ca3af !important; }
|
|
58
|
+
.text-gray-500 { color: #6b7280 !important; }
|
|
59
|
+
.text-gray-600 { color: #4b5563 !important; }
|
|
60
|
+
.text-gray-700 { color: #374151 !important; }
|
|
61
|
+
.text-gray-800 { color: #1f2937 !important; }
|
|
62
|
+
.text-gray-900 { color: #111827 !important; }
|
|
63
|
+
|
|
64
|
+
/* Text Colors - Blue */
|
|
65
|
+
.text-blue-400 { color: #60a5fa !important; }
|
|
66
|
+
.text-blue-600 { color: #2563eb !important; }
|
|
67
|
+
.text-blue-700 { color: #1d4ed8 !important; }
|
|
68
|
+
.text-blue-800 { color: #1e40af !important; }
|
|
69
|
+
.text-blue-900 { color: #1e3a8a !important; }
|
|
70
|
+
|
|
71
|
+
/* Text Colors - Red */
|
|
72
|
+
.text-red-400 { color: #f87171 !important; }
|
|
73
|
+
.text-red-600 { color: #dc2626 !important; }
|
|
74
|
+
.text-red-700 { color: #b91c1c !important; }
|
|
75
|
+
.text-red-800 { color: #991b1b !important; }
|
|
76
|
+
.text-red-900 { color: #7f1d1d !important; }
|
|
77
|
+
|
|
78
|
+
/* Text Colors - Green */
|
|
79
|
+
.text-green-600 { color: #16a34a !important; }
|
|
80
|
+
.text-green-700 { color: #15803d !important; }
|
|
81
|
+
.text-green-900 { color: #14532d !important; }
|
|
82
|
+
|
|
83
|
+
/* Text Colors - Yellow */
|
|
84
|
+
.text-yellow-600 { color: #ca8a04 !important; }
|
|
85
|
+
.text-yellow-700 { color: #a16207 !important; }
|
|
86
|
+
.text-yellow-900 { color: #713f12 !important; }
|
|
87
|
+
|
|
88
|
+
/* Border Colors */
|
|
89
|
+
.border-gray-200 { border-color: #e5e7eb !important; }
|
|
90
|
+
.border-gray-300 { border-color: #d1d5db !important; }
|
|
91
|
+
.border-gray-800 { border-color: #1f2937 !important; }
|
|
92
|
+
.border-blue-200 { border-color: #bfdbfe !important; }
|
|
93
|
+
.border-blue-300 { border-color: #93c5fd !important; }
|
|
94
|
+
.border-blue-400 { border-color: #60a5fa !important; }
|
|
95
|
+
.border-green-200 { border-color: #bbf7d0 !important; }
|
|
96
|
+
.border-yellow-200 { border-color: #fef08a !important; }
|
|
97
|
+
.border-red-200 { border-color: #fecaca !important; }
|
|
98
|
+
|
|
99
|
+
/* Hover - Background */
|
|
100
|
+
.hover-bg-gray-50:hover { background-color: #f9fafb !important; }
|
|
101
|
+
.hover-bg-gray-100:hover { background-color: #f3f4f6 !important; }
|
|
102
|
+
.hover-bg-gray-800:hover { background-color: #1f2937 !important; }
|
|
103
|
+
.hover-bg-blue-600:hover { background-color: #2563eb !important; }
|
|
104
|
+
.hover-bg-blue-700:hover { background-color: #1d4ed8 !important; }
|
|
105
|
+
.hover-bg-red-600:hover { background-color: #dc2626 !important; }
|
|
106
|
+
.hover-bg-red-700:hover { background-color: #b91c1c !important; }
|
|
107
|
+
|
|
108
|
+
/* Hover - Text */
|
|
109
|
+
.hover-text-white:hover { color: #ffffff !important; }
|
|
110
|
+
.hover-text-gray-700:hover { color: #374151 !important; }
|
|
111
|
+
.hover-text-gray-900:hover { color: #111827 !important; }
|
|
112
|
+
.hover-text-blue-700:hover { color: #1d4ed8 !important; }
|
|
113
|
+
.hover-text-blue-800:hover { color: #1e40af !important; }
|
|
114
|
+
.hover-text-blue-900:hover { color: #1e3a8a !important; }
|
|
115
|
+
.hover-text-red-900:hover { color: #7f1d1d !important; }
|
|
116
|
+
|
|
117
|
+
/* Hover - Border */
|
|
118
|
+
.hover-border-blue-400:hover { border-color: #60a5fa !important; }
|
|
119
|
+
|
|
120
|
+
/* Focus - Ring */
|
|
121
|
+
.focus-ring-blue-500:focus {
|
|
122
|
+
outline: none !important;
|
|
123
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5) !important;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Special opacity backgrounds for badges */
|
|
127
|
+
.bg-blue-opacity { background-color: rgba(37, 99, 235, 0.1) !important; }
|
|
128
|
+
.bg-green-opacity { background-color: rgba(34, 197, 94, 0.1) !important; }
|
|
129
|
+
.bg-yellow-opacity { background-color: rgba(202, 138, 4, 0.1) !important; }
|
|
130
|
+
.bg-red-opacity { background-color: rgba(220, 38, 38, 0.1) !important; }
|
|
131
|
+
.bg-blue-900-opacity { background-color: rgba(30, 58, 138, 0.5) !important; }
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<%# Tailwind-based flash message used within SuperAdmin without ViewComponent dependency %>
|
|
2
|
+
<% flash_configs = {
|
|
3
|
+
notice: {
|
|
4
|
+
container_classes: "bg-blue-50 border-blue-200 text-blue-900",
|
|
5
|
+
badge_classes: "bg-blue-100 text-blue-700",
|
|
6
|
+
badge_label: "I"
|
|
7
|
+
},
|
|
8
|
+
success: {
|
|
9
|
+
container_classes: "bg-green-50 border-green-200 text-green-900",
|
|
10
|
+
badge_classes: "bg-green-100 text-green-700",
|
|
11
|
+
badge_label: "✓"
|
|
12
|
+
},
|
|
13
|
+
alert: {
|
|
14
|
+
container_classes: "bg-yellow-50 border-yellow-200 text-yellow-900",
|
|
15
|
+
badge_classes: "bg-yellow-100 text-yellow-700",
|
|
16
|
+
badge_label: "!"
|
|
17
|
+
},
|
|
18
|
+
error: {
|
|
19
|
+
container_classes: "bg-red-50 border-red-200 text-red-900",
|
|
20
|
+
badge_classes: "bg-red-100 text-red-700",
|
|
21
|
+
badge_label: "✕"
|
|
22
|
+
}
|
|
23
|
+
}.freeze %>
|
|
24
|
+
<% selected_config = flash_configs[type.to_sym] || flash_configs[:notice] %>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
data-controller="super-admin--flash"
|
|
28
|
+
data-super-admin--flash-auto-dismiss-value="5000"
|
|
29
|
+
class="flash-message w-full overflow-hidden rounded-lg border shadow-md transition-all <%= selected_config[:container_classes] %>"
|
|
30
|
+
role="alert"
|
|
31
|
+
aria-live="polite"
|
|
32
|
+
>
|
|
33
|
+
<div class="flex items-start gap-3 p-4">
|
|
34
|
+
<div class="flex-shrink-0 mt-0.5">
|
|
35
|
+
<div class="flex h-6 w-6 items-center justify-center rounded-full text-xs font-semibold <%= selected_config[:badge_classes] %>">
|
|
36
|
+
<%= selected_config[:badge_label] %>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="flex-1 pt-0.5">
|
|
41
|
+
<p class="text-sm font-medium leading-relaxed"><%= message %></p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<button
|
|
45
|
+
type="button"
|
|
46
|
+
data-action="click->super-admin--flash#close"
|
|
47
|
+
class="flex-shrink-0 inline-flex rounded-lg p-1.5 hover:bg-black/5 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-current transition-colors"
|
|
48
|
+
aria-label="<%= t('super_admin.actions.close') %>"
|
|
49
|
+
>
|
|
50
|
+
<%= tag.svg(viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", class: "h-5 w-5", aria: { hidden: true }) do %>
|
|
51
|
+
<%= tag.path(d: "M6 6l12 12M6 18L18 6", stroke_linecap: "round", stroke_linejoin: "round", stroke_width: "1.5") %>
|
|
52
|
+
<% end %>
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<%# Rendu d'un champ de formulaire selon son type %>
|
|
2
|
+
<%# Locals: form, model_class, attribute_name %>
|
|
3
|
+
|
|
4
|
+
<% builder = SuperAdmin::FormBuilder.new(model_class: model_class, form: form, attribute_name: attribute_name) %>
|
|
5
|
+
|
|
6
|
+
<% if builder.field_type == :nested %>
|
|
7
|
+
<%= builder.build_field %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<div class="mb-4">
|
|
10
|
+
<%= form.label attribute_name, builder.field_label, class: "block text-sm font-medium text-gray-700 mb-1" %>
|
|
11
|
+
|
|
12
|
+
<% case builder.field_type %>
|
|
13
|
+
<% when :boolean %>
|
|
14
|
+
<div class="flex items-center">
|
|
15
|
+
<%= builder.build_field %>
|
|
16
|
+
<span class="ml-2 text-sm text-gray-600"><%= t("super_admin.resources.form.boolean_hint") %></span>
|
|
17
|
+
</div>
|
|
18
|
+
<% else %>
|
|
19
|
+
<%= builder.build_field %>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<% if form.object.errors[attribute_name].any? %>
|
|
23
|
+
<p class="mt-1 text-sm text-red-600">
|
|
24
|
+
<%= form.object.errors[attribute_name].to_sentence %>
|
|
25
|
+
</p>
|
|
26
|
+
<% end %>
|
|
27
|
+
|
|
28
|
+
<% column = model_class.columns_hash[attribute_name.to_s] %>
|
|
29
|
+
<% if column && column.comment.present? %>
|
|
30
|
+
<p class="mt-1 text-xs text-gray-500">
|
|
31
|
+
<%= column.comment %>
|
|
32
|
+
</p>
|
|
33
|
+
<% end %>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|