smriti 0.5.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/LICENSE +21 -0
- data/README.md +168 -0
- data/Rakefile +15 -0
- data/app/assets/images/smriti/android-chrome-192x192.png +0 -0
- data/app/assets/images/smriti/android-chrome-512x512.png +0 -0
- data/app/assets/images/smriti/apple-touch-icon.png +0 -0
- data/app/assets/images/smriti/favicon-16x16.png +0 -0
- data/app/assets/images/smriti/favicon-32x32.png +0 -0
- data/app/assets/images/smriti/favicon-48x48.png +0 -0
- data/app/assets/images/smriti/favicon.ico +0 -0
- data/app/assets/images/smriti/favicon.svg +18 -0
- data/app/assets/images/smriti/logo.svg +18 -0
- data/app/assets/images/smriti/mask-icon.svg +5 -0
- data/app/assets/stylesheets/smriti/application.css +1040 -0
- data/app/controllers/smriti/admin/application_controller.rb +135 -0
- data/app/controllers/smriti/admin/dashboard_controller.rb +32 -0
- data/app/controllers/smriti/admin/mat_view_definitions_controller.rb +372 -0
- data/app/controllers/smriti/admin/mat_view_runs_controller.rb +185 -0
- data/app/controllers/smriti/admin/preferences_controller.rb +91 -0
- data/app/helpers/smriti/admin/datatable_helper.rb +249 -0
- data/app/helpers/smriti/admin/localized_digit_helper.rb +70 -0
- data/app/helpers/smriti/admin/ui_helper.rb +539 -0
- data/app/javascript/smriti/application.js +8 -0
- data/app/javascript/smriti/controllers/application.js +10 -0
- data/app/javascript/smriti/controllers/body_setup_controller.js +120 -0
- data/app/javascript/smriti/controllers/datatable_controller.js +351 -0
- data/app/javascript/smriti/controllers/details_controller.js +200 -0
- data/app/javascript/smriti/controllers/drawer_controller.js +470 -0
- data/app/javascript/smriti/controllers/flash_controller.js +112 -0
- data/app/javascript/smriti/controllers/index.js +10 -0
- data/app/javascript/smriti/controllers/mv_confirm_controller.js +435 -0
- data/app/javascript/smriti/controllers/tabs_controller.js +184 -0
- data/app/javascript/smriti/controllers/tooltip_controller.js +525 -0
- data/app/javascript/smriti/controllers/turbo_frame_lifecycle_controller.js +342 -0
- data/app/jobs/smriti/application_job.rb +144 -0
- data/app/jobs/smriti/create_view_job.rb +87 -0
- data/app/jobs/smriti/delete_view_job.rb +89 -0
- data/app/jobs/smriti/refresh_view_job.rb +94 -0
- data/app/models/concerns/smriti_i18n.rb +139 -0
- data/app/models/concerns/smriti_paginate.rb +70 -0
- data/app/models/concerns/smriti_query_helper.rb +36 -0
- data/app/models/smriti/application_record.rb +39 -0
- data/app/models/smriti/mat_view_definition.rb +254 -0
- data/app/models/smriti/mat_view_run.rb +275 -0
- data/app/views/layouts/smriti/_footer.html.erb +47 -0
- data/app/views/layouts/smriti/_header.html.erb +25 -0
- data/app/views/layouts/smriti/admin.html.erb +47 -0
- data/app/views/layouts/smriti/turbo_frame.html.erb +3 -0
- data/app/views/smriti/admin/dashboard/index.html.erb +38 -0
- data/app/views/smriti/admin/mat_view_definitions/_definition_actions.html.erb +94 -0
- data/app/views/smriti/admin/mat_view_definitions/_dt-index-empty-row.html.erb +11 -0
- data/app/views/smriti/admin/mat_view_definitions/_dt-index-row.html.erb +27 -0
- data/app/views/smriti/admin/mat_view_definitions/empty.html.erb +1 -0
- data/app/views/smriti/admin/mat_view_definitions/form.html.erb +79 -0
- data/app/views/smriti/admin/mat_view_definitions/index.html.erb +10 -0
- data/app/views/smriti/admin/mat_view_definitions/show.html.erb +40 -0
- data/app/views/smriti/admin/mat_view_runs/_dt-index-empty-row.html.erb +11 -0
- data/app/views/smriti/admin/mat_view_runs/_dt-index-row.html.erb +41 -0
- data/app/views/smriti/admin/mat_view_runs/index.html.erb +1 -0
- data/app/views/smriti/admin/mat_view_runs/show.html.erb +64 -0
- data/app/views/smriti/admin/preferences/show.html.erb +49 -0
- data/app/views/smriti/admin/ui/_card.html.erb +15 -0
- data/app/views/smriti/admin/ui/_datatable.html.erb +34 -0
- data/app/views/smriti/admin/ui/_datatable_filters.html.erb +45 -0
- data/app/views/smriti/admin/ui/_datatable_tbody.html.erb +11 -0
- data/app/views/smriti/admin/ui/_datatable_tfoot.html.erb +70 -0
- data/app/views/smriti/admin/ui/_datatable_thead.html.erb +105 -0
- data/app/views/smriti/admin/ui/_details.html.erb +10 -0
- data/app/views/smriti/admin/ui/_flash.html.erb +6 -0
- data/app/views/smriti/admin/ui/_table.html.erb +8 -0
- data/config/importmap.rb +9 -0
- data/config/locales/ar.yml +223 -0
- data/config/locales/de.yml +230 -0
- data/config/locales/en-AU-ocker.yml +223 -0
- data/config/locales/en-AU.yml +202 -0
- data/config/locales/en-BORK.yml +225 -0
- data/config/locales/en-CA.yml +223 -0
- data/config/locales/en-GB.yml +223 -0
- data/config/locales/en-LOL.yml +219 -0
- data/config/locales/en-SCOT.yml +223 -0
- data/config/locales/en-SHAKESPEARE.yml +225 -0
- data/config/locales/en-US-pirate.yml +222 -0
- data/config/locales/en-US.yml +225 -0
- data/config/locales/en-YODA.yml +221 -0
- data/config/locales/en.yml +223 -0
- data/config/locales/es.yml +226 -0
- data/config/locales/fa.yml +223 -0
- data/config/locales/fr-CA.yml +227 -0
- data/config/locales/fr.yml +227 -0
- data/config/locales/he.yml +218 -0
- data/config/locales/hi.yml +223 -0
- data/config/locales/it.yml +225 -0
- data/config/locales/ja-JP.yml +215 -0
- data/config/locales/pt.yml +225 -0
- data/config/locales/ru.yml +228 -0
- data/config/locales/ur.yml +225 -0
- data/config/locales/zh-CN.yml +214 -0
- data/config/locales/zh-TW.yml +214 -0
- data/config/routes.rb +36 -0
- data/lib/ext/exception.rb +20 -0
- data/lib/generators/smriti/install/install_generator.rb +86 -0
- data/lib/generators/smriti/install/templates/create_mat_view_definitions.rb +29 -0
- data/lib/generators/smriti/install/templates/create_mat_view_runs.rb +32 -0
- data/lib/generators/smriti/install/templates/smriti_initializer.rb +23 -0
- data/lib/smriti/admin/auth_bridge.rb +93 -0
- data/lib/smriti/admin/default_auth.rb +62 -0
- data/lib/smriti/configuration.rb +58 -0
- data/lib/smriti/engine.rb +82 -0
- data/lib/smriti/helpers/ui_test_ids.rb +49 -0
- data/lib/smriti/jobs/adapter.rb +81 -0
- data/lib/smriti/service_response.rb +75 -0
- data/lib/smriti/services/base_service.rb +471 -0
- data/lib/smriti/services/check_matview_exists.rb +76 -0
- data/lib/smriti/services/concurrent_refresh.rb +94 -0
- data/lib/smriti/services/create_view.rb +173 -0
- data/lib/smriti/services/delete_view.rb +111 -0
- data/lib/smriti/services/regular_refresh.rb +90 -0
- data/lib/smriti/services/swap_refresh.rb +181 -0
- data/lib/smriti/version.rb +21 -0
- data/lib/smriti.rb +64 -0
- data/lib/tasks/helpers.rb +185 -0
- data/lib/tasks/smriti_tasks.rake +151 -0
- metadata +206 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="mv-toolbar start">
|
|
2
|
+
<%= mv_drawer_link(new_admin_mat_view_definition_path(frame_id: 'mv-drawer'), t("smriti.mat_view_definition.new_definition"),
|
|
3
|
+
variant: :primary,
|
|
4
|
+
testid: 'NEW_DEFINITION_LINK',
|
|
5
|
+
tooltip: t("smriti.mat_view_definition.new_definition_tooltip")) do %>
|
|
6
|
+
<%= mv_icon(:plus_circle) %>
|
|
7
|
+
<%= t("smriti.mat_view_definition.new_definition") %>
|
|
8
|
+
<% end %>
|
|
9
|
+
</div>
|
|
10
|
+
<%= render "smriti/admin/ui/datatable", dt_config: @dt_config, row_values: @data %>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<% humanize_attr = ->(attr) { Smriti::MatViewDefinition.human_attribute_name(attr) } %>
|
|
2
|
+
<% humanize_enum_attr = ->(col, val) { Smriti::MatViewDefinition.human_enum_name(col, val) } %>
|
|
3
|
+
|
|
4
|
+
<input type="hidden" id="mv-drawer-title-text" value="<%= t("smriti.view_var", name: @definition.name) %>"/>
|
|
5
|
+
<input type="hidden" id="mv-drawer-open-url-identifier" value="definitions_view_<%= @definition.id %>"/>
|
|
6
|
+
<%= render "smriti/admin/ui/card" do %>
|
|
7
|
+
<div class="mv-field">
|
|
8
|
+
<div class="mv-label"><%= humanize_attr.call(:refresh_strategy) %></div>
|
|
9
|
+
<div><%= humanize_enum_attr.call(:refresh_strategy, @definition.refresh_strategy) %></div>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="mv-field">
|
|
12
|
+
<div class="mv-label"><%= humanize_attr.call(:schedule_cron) %></div>
|
|
13
|
+
<div><%= @definition.schedule_cron.presence || "-" %></div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="mv-field">
|
|
16
|
+
<div class="mv-label"><%= humanize_attr.call(:unique_index_columns) %></div>
|
|
17
|
+
<div><%= Array(@definition.unique_index_columns).join(", ").presence || "-" %></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="mv-field">
|
|
20
|
+
<div class="mv-label"><%= humanize_attr.call(:dependencies) %></div>
|
|
21
|
+
<div><%= Array(@definition.dependencies).join(", ").presence || "-" %></div>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="mv-field">
|
|
24
|
+
<div class="mv-label"><%= humanize_attr.call(:last_run_at) %></div>
|
|
25
|
+
<div><%= @definition.last_run ? l_with_digits(@definition.last_run.started_at.in_time_zone, format: :datetime12hour) : "-" %></div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="mv-field">
|
|
28
|
+
<%= render "smriti/admin/ui/details",
|
|
29
|
+
tooltip_text: t("smriti.mat_view_definition.sql_summary_tooltip"),
|
|
30
|
+
title: content_tag(:div, humanize_attr.call(:sql), class: "mv-label") do %>
|
|
31
|
+
<pre><%= @definition.sql -%></pre>
|
|
32
|
+
<% end %>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<% content_for :footer do %>
|
|
36
|
+
<div class="mv-header-row even">
|
|
37
|
+
<%= render "definition_actions", defn: @definition, mv_exists: @mv_exists, frame_id: "mv-drawer" %>
|
|
38
|
+
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<tr class="mv-tr">
|
|
2
|
+
<td class="mv-td" colspan="<%= dt_config[:columns].size %>">
|
|
3
|
+
<div class="mv-cell">
|
|
4
|
+
<% if params[:dtfilter] || params[:dtsearch] %>
|
|
5
|
+
<%= t("smriti.mat_view_run.no_runs_if_filtered") %>
|
|
6
|
+
<% else %>
|
|
7
|
+
<%= t("smriti.mat_view_run.no_runs") %>
|
|
8
|
+
<% end %>
|
|
9
|
+
</div>
|
|
10
|
+
</td>
|
|
11
|
+
</tr>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<% dt_humanize_ref = dt_config[:dt_humanize_ref]
|
|
2
|
+
dt_humanize_ref_klass = dt_humanize_ref.constantize
|
|
3
|
+
humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
|
|
4
|
+
humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) }
|
|
5
|
+
mv_exists_map ||= {} %>
|
|
6
|
+
|
|
7
|
+
<tr class="mv-tr" data-testid="RUN_ROW_<%= row_value.id %>">
|
|
8
|
+
<td class="mv-td"><div class="mv-cell"><%= humanize_enum_attr.call(:operation, row_value.operation) %></div></td>
|
|
9
|
+
<td class="mv-td">
|
|
10
|
+
<div class="mv-cell">
|
|
11
|
+
<%= mv_drawer_link(admin_mat_view_definition_path(row_value.mat_view_definition, frame_id: "mv-drawer"), t("smriti.view_var", name: row_value.mat_view_definition.name),
|
|
12
|
+
variant: :ghost,
|
|
13
|
+
underline: true,
|
|
14
|
+
testid: 'VIEW_LINK',
|
|
15
|
+
testid_identifier: "defn-#{row_value.mat_view_definition.id}",
|
|
16
|
+
tooltip: t("smriti.mat_view_definition.view_tooltip")) do %>
|
|
17
|
+
<%= row_value.mat_view_definition.name %>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
20
|
+
</td>
|
|
21
|
+
<td class="mv-td"><div class="mv-cell"><%= l_with_digits(row_value.started_at.in_time_zone, format: :datetime12hour) %></div></td>
|
|
22
|
+
<td class="mv-td">
|
|
23
|
+
<div class="mv-cell">
|
|
24
|
+
<%= mv_badge(row_value.status, humanize_enum_attr.call(:status, row_value.status)) %>
|
|
25
|
+
</div>
|
|
26
|
+
</td>
|
|
27
|
+
<td class="mv-td"><div class="mv-cell"><%= row_value.duration_ms ? localized_digits(t("smriti.x_miliseconds", count: row_value.duration_ms)) : "-" %></div></td>
|
|
28
|
+
<td class="mv-td"><div class="mv-cell"><%= localized_digits(row_value.row_count_before || "-") %>/<%= localized_digits(row_value.row_count_after || "-") %></div></td>
|
|
29
|
+
<td class="mv-td">
|
|
30
|
+
<div class="mv-cell">
|
|
31
|
+
<%= mv_drawer_link(admin_mat_view_run_path(id: row_value.id, frame_id: 'mv-drawer'), t('smriti.mat_view_run.view_details'),
|
|
32
|
+
variant: :ghost,
|
|
33
|
+
underline: true,
|
|
34
|
+
testid: 'VIEW_LINK',
|
|
35
|
+
testid_identifier: "run-#{row_value.id}",
|
|
36
|
+
tooltip: t("smriti.mat_view_run.view_tooltip")) do %>
|
|
37
|
+
<%= t("smriti.mat_view_run.view_details") %>
|
|
38
|
+
<% end %>
|
|
39
|
+
</div>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render "smriti/admin/ui/datatable", dt_config: @dt_config, row_values: @data %>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<% humanize_attr = ->(attr) { Smriti::MatViewRun.human_attribute_name(attr) } %>
|
|
2
|
+
<% humanize_enum_attr = ->(col, val) { Smriti::MatViewRun.human_enum_name(col, val) } %>
|
|
3
|
+
<input type="hidden" id="mv-drawer-open-url-identifier" value="runs_view_<%= @run.id %>"/>
|
|
4
|
+
<div class="mv-card">
|
|
5
|
+
<div class="mv-card-b">
|
|
6
|
+
<div class="mv-field">
|
|
7
|
+
<div class="mv-label"><%= t("smriti.definition") %></div>
|
|
8
|
+
<div>
|
|
9
|
+
<%= mv_drawer_link(admin_mat_view_definition_path(@run.mat_view_definition, frame_id: "mv-drawer"), t("smriti.view_var", name: @run.mat_view_definition.name),
|
|
10
|
+
variant: :ghost,
|
|
11
|
+
underline: true,
|
|
12
|
+
tooltip: t("smriti.mat_view_definition.view_tooltip")) do %>
|
|
13
|
+
<%= @run.mat_view_definition.name %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="mv-field">
|
|
18
|
+
<div class="mv-label"><%= humanize_attr.call(:operation) %></div>
|
|
19
|
+
<div><%= humanize_enum_attr.call(:operation, @run.operation) %></div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="mv-field">
|
|
22
|
+
<div class="mv-label"><%= humanize_attr.call(:started_at) %></div>
|
|
23
|
+
<div><%= l_with_digits(@run.started_at.in_time_zone, format: :datetime12hour) %></div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="mv-field">
|
|
26
|
+
<div class="mv-label"><%= humanize_attr.call(:finished_at) %></div>
|
|
27
|
+
<div><%= l_with_digits(@run.finished_at&.in_time_zone, format: :datetime12hour) || "-" %></div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="mv-field">
|
|
30
|
+
<div class="mv-label"><%= humanize_attr.call(:status) %></div>
|
|
31
|
+
<div><%= mv_badge(@run.status, humanize_enum_attr.call(:status, @run.status)) %></div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="mv-field">
|
|
34
|
+
<div class="mv-label"><%= humanize_attr.call(:duration_ms) %></div>
|
|
35
|
+
<div><%= @run.duration_ms ? localized_digits(t("smriti.x_miliseconds", count: @run.duration_ms)) : "-" %></div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="mv-field">
|
|
38
|
+
<div class="mv-label"><%= humanize_attr.call(:row_count_before) %></div>
|
|
39
|
+
<div><%= localized_digits(@run.row_count_before || "-") %></div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="mv-field">
|
|
42
|
+
<div class="mv-label"><%= humanize_attr.call(:row_count_after) %></div>
|
|
43
|
+
<div><%= localized_digits(@run.row_count_after || "-") %></div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="mv-field">
|
|
47
|
+
<%= render "smriti/admin/ui/details",
|
|
48
|
+
tooltip_text: t("smriti.mat_view_run.meta_tooltip"),
|
|
49
|
+
title: content_tag(:div, humanize_attr.call(:meta), class: "mv-label") do %>
|
|
50
|
+
<pre><%= JSON.pretty_generate(@run.meta || {}).strip -%></pre>
|
|
51
|
+
<% end %>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<% if @run.try(:error).present? %>
|
|
55
|
+
<div class="mv-field">
|
|
56
|
+
<%= render "smriti/admin/ui/details",
|
|
57
|
+
tooltip_text: t("smriti.mat_view_run.error_tooltip"),
|
|
58
|
+
title: content_tag(:div, humanize_attr.call(:error), class: "mv-label") do %>
|
|
59
|
+
<pre><%= JSON.pretty_generate(@run.error || {}).strip -%></pre>
|
|
60
|
+
<% end %>
|
|
61
|
+
</div>
|
|
62
|
+
<% end %>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<input type="hidden" id="mv-drawer-open-url-identifier" value="preferences_edit" %>
|
|
2
|
+
<div class="mv-card">
|
|
3
|
+
<div class="mv-card-b">
|
|
4
|
+
<%= form_with url: admin_preferences_path(frame_id: 'mv-drawer'), method: :put, data: { turbo_frame: :"mv-drawer" } do %>
|
|
5
|
+
<!-- Theme -->
|
|
6
|
+
<div class="mv-field">
|
|
7
|
+
<div class="mv-label"><%= t("smriti.settings.theme") %></div>
|
|
8
|
+
<p class="mv-label mv-label--sublabel">
|
|
9
|
+
<%= t("smriti.settings.theme_hint") %>
|
|
10
|
+
</p>
|
|
11
|
+
<div class="mv-actions" style="justify-content:flex-start">
|
|
12
|
+
<% [["auto", t("smriti.settings.auto")], ["light", t("smriti.settings.light")], ["dark", t("smriti.settings.dark")]].each do |value, label| %>
|
|
13
|
+
<label for="lang-<%= label %>" class="mv-btn mv-btn--secondary mv-btn--sm" style="gap:.4rem;">
|
|
14
|
+
<input id="lang-<%= label %>" type="radio" name="theme" value="<%= value %>" <%= "checked" if @theme == value %> style="margin:0"/>
|
|
15
|
+
<span><%= label %></span>
|
|
16
|
+
</label>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<br/>
|
|
21
|
+
<!-- Language -->
|
|
22
|
+
<div class="mv-field">
|
|
23
|
+
<label for="locale" class="mv-label"><%= t("smriti.settings.language") %></label>
|
|
24
|
+
<p class="mv-label mv-label--sublabel">
|
|
25
|
+
<%= t("smriti.settings.language_hint") %>
|
|
26
|
+
</p>
|
|
27
|
+
<select name="locale" id="locale" class="mv-select">
|
|
28
|
+
<% @locales.each do |loc| %>
|
|
29
|
+
<% label = Smriti::Engine.locale_code_mapping[loc.to_sym] %>
|
|
30
|
+
<option value="<%= loc %>" <%= "selected" if @locale == loc %>>
|
|
31
|
+
<%= label %>
|
|
32
|
+
</option>
|
|
33
|
+
<% end %>
|
|
34
|
+
</select>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="mv-actions">
|
|
37
|
+
<%= mv_submit_button class: "mv-btn mv-btn--primary mv-btn--md",
|
|
38
|
+
testid: 'PREFERENCES_SAVE_BUTTON' do %>
|
|
39
|
+
<%= t("smriti.save_changes") %>
|
|
40
|
+
<% end %>
|
|
41
|
+
|
|
42
|
+
<%= mv_cancel_button variant: :ghost, data: { action: "click->drawer#close" },
|
|
43
|
+
testid: 'PREFERENCES_CANCEL_BUTTON' do %>
|
|
44
|
+
<%= t("smriti.cancel") %>
|
|
45
|
+
<% end %>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% body = capture { yield } %>
|
|
2
|
+
<div class="mv-card">
|
|
3
|
+
<% if content_for?(:title) %>
|
|
4
|
+
<div class="mv-card-h">
|
|
5
|
+
<%= yield :title %>
|
|
6
|
+
</div>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<div class="mv-card-b"><%= body %></div>
|
|
10
|
+
<% if content_for?(:footer) %>
|
|
11
|
+
<div class="mv-card-f">
|
|
12
|
+
<%= yield :footer %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<% dt_config = @dt_config
|
|
2
|
+
dt_humanize_ref = dt_config[:dt_humanize_ref]
|
|
3
|
+
dt_cols = dt_config[:columns]
|
|
4
|
+
dt_humanize_ref_klass = dt_humanize_ref.constantize
|
|
5
|
+
humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
|
|
6
|
+
humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) } %>
|
|
7
|
+
|
|
8
|
+
<div
|
|
9
|
+
data-controller="datatable"
|
|
10
|
+
data-datatable-index-url-value="<%= dt_config[:index_url] %>"
|
|
11
|
+
data-datatable-perpage-default="<%= dt_config[:pagination][:per_page_default]%>"
|
|
12
|
+
>
|
|
13
|
+
<table class="mv-table with-filters" id="<%= dt_config[:id] %>">
|
|
14
|
+
<%= render "smriti/admin/ui/datatable_thead" %>
|
|
15
|
+
<tbody id="datatable-body-<%= dt_config[:id] %>">
|
|
16
|
+
<tr class="mv-tr">
|
|
17
|
+
<td class="mv-td" colspan="<%= dt_cols.size %>">
|
|
18
|
+
<div class="mv-cell">
|
|
19
|
+
<%= t("smriti.loading") %>
|
|
20
|
+
</div>
|
|
21
|
+
</td>
|
|
22
|
+
</tr>
|
|
23
|
+
</tbody>
|
|
24
|
+
<tfoot>
|
|
25
|
+
<tr id="datatable-tfoot-<%= dt_config[:id] %>" class="mv-tr">
|
|
26
|
+
<td class="mv-td" colspan="<%= dt_cols.size %>">
|
|
27
|
+
<div class="dt-pagination-section" data-datatable-target="pagination" data-testid="DT_PAGINATION" data-testid-identifier="<%= dt_config[:id] %>-pagination">
|
|
28
|
+
<%= t("smriti.loading") %>
|
|
29
|
+
</div>
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</tfoot>
|
|
33
|
+
</table>
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<% dt_config = @dt_config
|
|
2
|
+
dt_humanize_ref = dt_config[:dt_humanize_ref]
|
|
3
|
+
dt_cols = dt_config[:columns]
|
|
4
|
+
dtfilter_map = params[:dtfilter].present? ? params[:dtfilter].split(",").map { |f| f.split(":") }.to_h : {}
|
|
5
|
+
dt_humanize_ref_klass = dt_humanize_ref.constantize
|
|
6
|
+
humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) } %>
|
|
7
|
+
<div class="mv-input-group" id="datatable-filters-<%= dt_config[:id] %>">
|
|
8
|
+
<div class="mv-field-inline">
|
|
9
|
+
<%= t("smriti.filter_by") %>
|
|
10
|
+
</div>
|
|
11
|
+
<% dt_cols.keys.each do |key| %>
|
|
12
|
+
<% next unless dt_cols[key][:filter].present? %>
|
|
13
|
+
|
|
14
|
+
<% value = dt_cols[key]
|
|
15
|
+
th_label =
|
|
16
|
+
if value[:label_type] == "i18n"
|
|
17
|
+
t("smriti.#{value[:label_ref]}")
|
|
18
|
+
elsif value[:label_type] == "humanize_attr"
|
|
19
|
+
humanize_attr.call(value[:label_ref])
|
|
20
|
+
else
|
|
21
|
+
value[:label_ref]
|
|
22
|
+
end %>
|
|
23
|
+
|
|
24
|
+
<% selected_value = dtfilter_map[key.to_s] || "no_filter"
|
|
25
|
+
options =
|
|
26
|
+
[[t("smriti.no_filter"), "no_filter"]] +
|
|
27
|
+
dt_humanize_ref_klass.send("filter_options_for_#{key.to_s}").map { |v| v[1].present? ? v : [t("smriti.no_value"), "no_value"] } %>
|
|
28
|
+
<div class="mv-field-inline">
|
|
29
|
+
<%= label_tag key, th_label, class: "mv-label" %>
|
|
30
|
+
<%= select_tag key,
|
|
31
|
+
options_for_select(options, selected_value),
|
|
32
|
+
class: "mv-select",
|
|
33
|
+
autocomplete: "off",
|
|
34
|
+
spellcheck: "false",
|
|
35
|
+
autocapitalize: "none",
|
|
36
|
+
autocorrect: "off",
|
|
37
|
+
data: {
|
|
38
|
+
action: "change->datatable#onFilterChange",
|
|
39
|
+
datatable_target: "filterField",
|
|
40
|
+
testid: "#{dt_config[:id]}-#{Smriti::Helpers::UiTestIds::DT_FILTER_SELECT}-#{key}",
|
|
41
|
+
key: key,
|
|
42
|
+
} %>
|
|
43
|
+
</div>
|
|
44
|
+
<% end %>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<% dt_config = @dt_config
|
|
2
|
+
row_values = Array(@data) %>
|
|
3
|
+
<tbody id="datatable-body-<%= dt_config[:id] %>">
|
|
4
|
+
<% if row_values.size.positive? %>
|
|
5
|
+
<% row_values.each do |row| %>
|
|
6
|
+
<%= render dt_config[:row_partial_name], dt_config: dt_config, row_value: row, **row_meta %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<%= render dt_config[:empty_row_partial_name], dt_config: dt_config, **row_meta %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</tbody>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<% dt_config = @dt_config %>
|
|
2
|
+
<% dt_cols = dt_config[:columns] %>
|
|
3
|
+
<tr id="datatable-tfoot-<%= dt_config[:id] %>" class="mv-tr">
|
|
4
|
+
<td class="mv-td" colspan="<%= dt_cols.size %>">
|
|
5
|
+
<div class="dt-pagination-section">
|
|
6
|
+
<%= label_tag :per_page, t("smriti.per_page"), class: "mv-label mr-2" %>
|
|
7
|
+
<%= select_tag :per_page,
|
|
8
|
+
options_for_select(dt_config[:pagination][:per_page_options].map { |opt| [opt, opt] }, @dt_per_page),
|
|
9
|
+
data: {
|
|
10
|
+
action: "change->datatable#changePerPage",
|
|
11
|
+
"datatable-target": "perPageSelect",
|
|
12
|
+
testid: "dt_pagination_btn-#{dt_config[:id]}-per-page",
|
|
13
|
+
},
|
|
14
|
+
class: "mv-input mv-input-sm",
|
|
15
|
+
aria: {
|
|
16
|
+
label: t("smriti.per_page"),
|
|
17
|
+
} %>
|
|
18
|
+
|
|
19
|
+
<%= mv_button data: { action: "click->datatable#goToPage", page: 1,
|
|
20
|
+
'datatable-target': 'pageBtn' },
|
|
21
|
+
testid: "DT_PAGINATION_BTN",
|
|
22
|
+
testid_identifier: "#{dt_config[:id]}-page-first",
|
|
23
|
+
disabled: @dt_page == 1 do %>
|
|
24
|
+
<%= mv_icon(:double_arrow_left) %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<%= mv_button variant: :ghost,
|
|
27
|
+
data: { action: "click->datatable#goToPage",
|
|
28
|
+
page: @dt_page - 1,
|
|
29
|
+
'datatable-target': 'pageBtn' },
|
|
30
|
+
testid: "DT_PAGINATION_BTN",
|
|
31
|
+
testid_identifier: "#{dt_config[:id]}-page-previous",
|
|
32
|
+
disabled: @dt_page == 1 do %>
|
|
33
|
+
<%= mv_icon(:arrow_left) %>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% pages = pagination_window(current_page: @dt_page, total_pages: @dt_total_pages) %>
|
|
36
|
+
<% pages.each_with_index do |page, index| %>
|
|
37
|
+
<% if page == :gap %>
|
|
38
|
+
<span class="mv-text-muted" data-testid="dt_pagination_btn_gap_<%= index %>">...</span>
|
|
39
|
+
<% else %>
|
|
40
|
+
<%= mv_button variant: (page == @dt_page ? :primary : :ghost),
|
|
41
|
+
data: { action: "click->datatable#goToPage",
|
|
42
|
+
page: page,
|
|
43
|
+
'datatable-target': 'pageBtn' },
|
|
44
|
+
testid: "DT_PAGINATION_BTN",
|
|
45
|
+
testid_identifier: "#{dt_config[:id]}-page-#{page}" do %>
|
|
46
|
+
<%= localized_digits(page) %>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% end %>
|
|
49
|
+
<% end %>
|
|
50
|
+
<%= mv_button variant: :ghost,
|
|
51
|
+
data: { action: "click->datatable#goToPage",
|
|
52
|
+
page: @dt_page + 1,
|
|
53
|
+
'datatable-target': 'pageBtn' },
|
|
54
|
+
testid: "DT_PAGINATION_BTN",
|
|
55
|
+
testid_identifier: "#{dt_config[:id]}-page-next",
|
|
56
|
+
disabled: @dt_page == pages.last do %>
|
|
57
|
+
<%= mv_icon(:arrow_right) %>
|
|
58
|
+
<% end %>
|
|
59
|
+
<%= mv_button data: { action: "click->datatable#goToPage",
|
|
60
|
+
page: pages.last,
|
|
61
|
+
'datatable-target': 'pageBtn' },
|
|
62
|
+
|
|
63
|
+
testid: "DT_PAGINATION_BTN",
|
|
64
|
+
testid_identifier: "#{dt_config[:id]}-page-last",
|
|
65
|
+
disabled: @dt_page == pages.last do %>
|
|
66
|
+
<%= mv_icon(:double_arrow_right) %>
|
|
67
|
+
<% end %>
|
|
68
|
+
</div>
|
|
69
|
+
</td>
|
|
70
|
+
</tr>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<% dt_config = @dt_config
|
|
2
|
+
dt_humanize_ref = dt_config[:dt_humanize_ref]
|
|
3
|
+
dt_cols = dt_config[:columns]
|
|
4
|
+
dt_humanize_ref_klass = dt_humanize_ref.constantize
|
|
5
|
+
search_enabled = dt_config[:search_enabled]
|
|
6
|
+
filter_enabled = dt_config[:filter_enabled]
|
|
7
|
+
humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
|
|
8
|
+
humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) } %>
|
|
9
|
+
<colgroup>
|
|
10
|
+
<% dt_cols.keys.each do |key| %>
|
|
11
|
+
<col class="col-<%= key.to_s.dasherize %>">
|
|
12
|
+
<% end %>
|
|
13
|
+
</colgroup>
|
|
14
|
+
<thead>
|
|
15
|
+
<% if search_enabled %>
|
|
16
|
+
<tr class="mv-tr">
|
|
17
|
+
<th class="mv-th" colspan="<%= dt_cols.size %>">
|
|
18
|
+
<div class="dt-filter-section">
|
|
19
|
+
<div class="mv-input-group">
|
|
20
|
+
<input
|
|
21
|
+
id="<%= dt_config[:id] %>-search-input"
|
|
22
|
+
name="dtsearch"
|
|
23
|
+
type="text"
|
|
24
|
+
class="mv-input mv-input--full-width"
|
|
25
|
+
placeholder="<%= t('smriti.datatable.search_placeholder') %>"
|
|
26
|
+
data-datatable-target="searchInput"
|
|
27
|
+
data-action="input->datatable#onSearchInput"
|
|
28
|
+
value="<%= params[:dtsearch].presence || '' %>"
|
|
29
|
+
autocomplete="off"
|
|
30
|
+
spellcheck="false"
|
|
31
|
+
autocapitalize="none"
|
|
32
|
+
autocorrect="off"
|
|
33
|
+
aria-label="<%= t('smriti.datatable.search_aria_label') %>"
|
|
34
|
+
data-testid='<%= "#{Smriti::Helpers::UiTestIds::DT_SEARCH_INPUT}-#{dt_config[:id]}-search" %>'
|
|
35
|
+
/>
|
|
36
|
+
<%= mv_button variant: :tertiary, class: 'btn-clear mv-btn--no-padding', padding: false, data: { action: "click->datatable#clearSearchInput" },
|
|
37
|
+
aria: { label: t('smriti.datatable.clear_search_aria_label') },
|
|
38
|
+
testid: 'DT_CLEAR_SEARCH_BTN',
|
|
39
|
+
testid_identifier: "#{dt_config[:id]}-clear-search" do %>
|
|
40
|
+
<%= mv_icon(:x_circle) %>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</th>
|
|
45
|
+
</tr>
|
|
46
|
+
|
|
47
|
+
<% end %>
|
|
48
|
+
<% if filter_enabled %>
|
|
49
|
+
<tr class="mv-tr">
|
|
50
|
+
<th class="mv-th" colspan="<%= dt_cols.size %>">
|
|
51
|
+
<div class="dt-filter-section">
|
|
52
|
+
<div class="mv-input-group" id="datatable-filters-<%= dt_config[:id] %>">
|
|
53
|
+
<%= t("smriti.loading") %>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</th>
|
|
57
|
+
</tr>
|
|
58
|
+
<% end %>
|
|
59
|
+
<tr class="mv-tr">
|
|
60
|
+
<% dt_cols.keys.each do |key|
|
|
61
|
+
value = dt_cols[key]
|
|
62
|
+
sort = value[:sort]
|
|
63
|
+
sort_enabled = sort.present?
|
|
64
|
+
th_label = if value[:label_type] == 'i18n'
|
|
65
|
+
t("smriti.#{value[:label_ref]}")
|
|
66
|
+
elsif value[:label_type] == 'humanize_attr'
|
|
67
|
+
humanize_attr.call(value[:label_ref])
|
|
68
|
+
else
|
|
69
|
+
value[:label_ref]
|
|
70
|
+
end
|
|
71
|
+
data = {}
|
|
72
|
+
if sort_enabled
|
|
73
|
+
data = {
|
|
74
|
+
'datatable-target': 'th',
|
|
75
|
+
key: key,
|
|
76
|
+
testid: 'TOGGLE_SORT_BUTTON',
|
|
77
|
+
testid_identifier: "#{dt_config[:id]}-#{key}",
|
|
78
|
+
}
|
|
79
|
+
end %>
|
|
80
|
+
<th class="mv-th" id="<%= dt_config[:id] %>-<%= key %>" <%= data.map { |k, v| "data-#{k.to_s.dasherize}=\"#{v}\"" }.join(' ').html_safe %>>
|
|
81
|
+
<div class="mv-cell" <%= value[:th_style].nil? ? '' : "style=\"#{value[:th_style]}\"".html_safe %>>
|
|
82
|
+
<% if sort_enabled %>
|
|
83
|
+
<span>
|
|
84
|
+
<%= th_label %>
|
|
85
|
+
</span>
|
|
86
|
+
<%= mv_button variant: :ghost, data: { action: "click->datatable#toggleSort" },
|
|
87
|
+
class: 'mv-btn--no-padding has-annotation',
|
|
88
|
+
padding: false,
|
|
89
|
+
testid: 'TOGGLE_SORT_BUTTON',
|
|
90
|
+
testid_identifier: "#{dt_config[:id]}-#{key}" do %>
|
|
91
|
+
<%= mv_icon(:sort_neutral, size: 24, class_name: "muted sort-neutral") %>
|
|
92
|
+
<%= mv_icon(:sort_asc, size: 24, class_name: "active hidden sort-asc") %>
|
|
93
|
+
<%= mv_icon(:sort_desc, size: 24, class_name: "active hidden sort-desc") %>
|
|
94
|
+
<span class="mv-annotation hidden"></span>
|
|
95
|
+
<% end %>
|
|
96
|
+
<% else %>
|
|
97
|
+
<span>
|
|
98
|
+
<%= th_label %>
|
|
99
|
+
</span>
|
|
100
|
+
<% end %>
|
|
101
|
+
</div>
|
|
102
|
+
</th>
|
|
103
|
+
<% end %>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% body = capture { yield } %>
|
|
2
|
+
<details data-controller="details" data-details-duration-value="200" class="mv-details"
|
|
3
|
+
open>
|
|
4
|
+
<summary data-action="click->details#toggle" data-controller="tooltip" data-tooltip-text-value="<%= tooltip_text %>">
|
|
5
|
+
<%= mv_icon(:arrow_right, class_name: "mv-chevron") %>
|
|
6
|
+
<%= title %>
|
|
7
|
+
</summary>
|
|
8
|
+
|
|
9
|
+
<div data-details-target="content" class="mv-details__content"><%= body -%></div>
|
|
10
|
+
</details>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
enable_integrity!
|
|
4
|
+
pin 'smriti/application'
|
|
5
|
+
pin '@hotwired/turbo-rails', to: 'turbo.min.js'
|
|
6
|
+
pin '@hotwired/stimulus', to: 'stimulus.min.js'
|
|
7
|
+
pin '@hotwired/stimulus-loading', to: 'stimulus-loading.js'
|
|
8
|
+
|
|
9
|
+
pin_all_from Smriti::Engine.root.join('app/javascript/smriti/controllers'), under: 'smriti/controllers'
|