cm-admin 4.1.2 → 4.2.1
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 +4 -4
- data/.gem/credentials +5 -0
- data/.github/workflows/linters.yml +4 -2
- data/.github/workflows/release-cm-gem.yml +105 -0
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +4 -2
- data/app/assets/javascripts/cm_admin/exports.js +48 -29
- data/app/assets/stylesheets/cm_admin/base/form.scss +7 -8
- data/app/assets/stylesheets/cm_admin/base/navbar.scss +11 -24
- data/app/assets/stylesheets/cm_admin/base/show.scss +1 -1
- data/app/assets/stylesheets/cm_admin/base/table.scss +1 -1
- data/app/assets/stylesheets/cm_admin/components/_buttons.scss +4 -0
- data/app/controllers/cm_admin/application_controller.rb +0 -8
- data/app/controllers/cm_admin/exports_controller.rb +14 -3
- data/app/controllers/cm_admin/resource_controller.rb +6 -8
- data/app/jobs/generate_export_file_job.rb +15 -0
- data/app/mailers/export_mailer.rb +22 -0
- data/app/models/concerns/exportable.rb +97 -0
- data/app/models/file_export.rb +17 -0
- data/app/views/cm_admin/main/_associated_table.html.slim +2 -0
- data/app/views/cm_admin/main/_card.html.slim +1 -1
- data/app/views/cm_admin/main/_kanban.html.slim +1 -1
- data/app/views/cm_admin/main/_table.html.slim +1 -1
- data/app/views/cm_admin/main/_top_navbar.html.slim +0 -10
- data/app/views/cm_admin/main/associated_index.html.slim +2 -4
- data/app/views/cm_admin/main/index.html.slim +7 -3
- data/app/views/export_mailer/export_email.html.slim +19 -0
- data/app/views/layouts/cm_admin.html.slim +12 -5
- data/app/views/layouts/mailer.html.slim +6 -0
- data/cm_admin.gemspec +5 -3
- data/config/routes.rb +1 -1
- data/lib/cm_admin/models/export.rb +2 -55
- data/lib/cm_admin/version.rb +1 -1
- data/lib/cm_admin/view_helpers.rb +55 -37
- data/lib/generators/cm_admin/install_generator.rb +1 -0
- metadata +31 -9
- data/.rubocop-https---raw-githubusercontent-com-commutatus-cm-linters-main-rubocop-yml +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c44311ddd3f0ebf6e1d4f95955770a545780720121c06fda39b9c54d9c9d0daa
|
4
|
+
data.tar.gz: cb582aaa8202e30c06d7c60bdc880d240d6175b4fb7b44d6636509a0a4da9c63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 756da465d4b28250f4f2fd9ea258660fde6fe75354c39c44d29c5081e47f07dbdaf12a42b2adfcd69f4c12cf6ff37c78f67e486ada7d5e34a1967083f3b4894d
|
7
|
+
data.tar.gz: 84d6e56e30f677f9dc996ff708bf43d0a754f2e776a3185ca22771a985a9bb471d7f1d44a62f959c194fc864f32556a45d3142b842a396d0e5e4455ffb82c2d6
|
data/.gem/credentials
ADDED
@@ -19,9 +19,11 @@ jobs:
|
|
19
19
|
rubocop_extensions: rubocop-rails:2.26.2
|
20
20
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
21
21
|
reporter: github-pr-check # Possible values are github-pr-check, github-pr-review
|
22
|
+
fail_level: none
|
23
|
+
fail_on_error: false
|
22
24
|
- uses: actions/checkout@v4
|
23
25
|
- name: stylelint
|
24
26
|
uses: reviewdog/action-stylelint@v1
|
25
27
|
with:
|
26
|
-
|
27
|
-
|
28
|
+
reporter: github-pr-review # Change reporter.
|
29
|
+
stylelint_input: "**/*.scss"
|
@@ -0,0 +1,105 @@
|
|
1
|
+
name: Bump Gem to CM Gems
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
inputs:
|
6
|
+
bump_type:
|
7
|
+
type: choice
|
8
|
+
description: "Bump Type ( Choosing None will bump build number and keep the same version )"
|
9
|
+
default: "patch"
|
10
|
+
options:
|
11
|
+
- patch
|
12
|
+
- minor
|
13
|
+
- major
|
14
|
+
|
15
|
+
permissions:
|
16
|
+
contents: read
|
17
|
+
|
18
|
+
jobs:
|
19
|
+
# build:
|
20
|
+
# name: Build gem
|
21
|
+
# runs-on: ubuntu-latest
|
22
|
+
|
23
|
+
# permissions:
|
24
|
+
# id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
25
|
+
# contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
26
|
+
|
27
|
+
# steps:
|
28
|
+
# # Set up
|
29
|
+
# - uses: actions/checkout@v4
|
30
|
+
|
31
|
+
# - name: set git config
|
32
|
+
# env:
|
33
|
+
# GH_TOKEN: ${{ github.token }}
|
34
|
+
# run: |
|
35
|
+
# git config --global user.email "${GITHUB_ACTOR_ID}+${GITHUB_ACTOR}@users.noreply.github.com"
|
36
|
+
# git config --global user.name "$(gh api /users/${GITHUB_ACTOR} | jq .name -r)"
|
37
|
+
# git config -l
|
38
|
+
# - name: Set up Ruby
|
39
|
+
# uses: ruby/setup-ruby@v1
|
40
|
+
# with:
|
41
|
+
# bundler-cache: true
|
42
|
+
# ruby-version: ruby
|
43
|
+
|
44
|
+
# - name: Install the gem-release
|
45
|
+
# run: gem install gem-release
|
46
|
+
|
47
|
+
# - name: Bump the gem
|
48
|
+
# run: gem bump --version ${{ github.event.inputs.bump_type }} -p
|
49
|
+
|
50
|
+
# - name: Remove lock on bundle
|
51
|
+
# run: bundle config set frozen false
|
52
|
+
|
53
|
+
# - name: Bundle Install
|
54
|
+
# run: bundle install
|
55
|
+
|
56
|
+
# - name: Git Add files
|
57
|
+
# run: git add Gemfile.lock
|
58
|
+
|
59
|
+
# - name: Git Commit
|
60
|
+
# run: git commit -m "Add bundle files"
|
61
|
+
|
62
|
+
# - name: Push the new version
|
63
|
+
# run: git push origin ${{ github.ref_name }}
|
64
|
+
|
65
|
+
release:
|
66
|
+
name: Release gem
|
67
|
+
runs-on: ubuntu-latest
|
68
|
+
# needs: build
|
69
|
+
permissions:
|
70
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
71
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
72
|
+
|
73
|
+
steps:
|
74
|
+
- uses: actions/checkout@v4
|
75
|
+
|
76
|
+
- name: Git Pull
|
77
|
+
run: git pull
|
78
|
+
|
79
|
+
- name: Set up Ruby
|
80
|
+
uses: ruby/setup-ruby@v1
|
81
|
+
with:
|
82
|
+
ruby-version: ruby
|
83
|
+
bundler-cache: true
|
84
|
+
|
85
|
+
- name: Remove lock on bundle
|
86
|
+
run: bundle config set frozen false
|
87
|
+
|
88
|
+
- name: Update Rubygems
|
89
|
+
run: |
|
90
|
+
gem install rubygems-update
|
91
|
+
update_rubygems
|
92
|
+
gem update --system
|
93
|
+
|
94
|
+
- name: Build gem
|
95
|
+
run: |
|
96
|
+
gem build cm_admin.gemspec
|
97
|
+
GEM_FILE=$(ls *.gem | tail -n 1)
|
98
|
+
echo "GEM_FILE=$GEM_FILE" >> $GITHUB_ENV
|
99
|
+
|
100
|
+
- name: Push gem
|
101
|
+
env:
|
102
|
+
GEM_FILE: ${{ env.GEM_FILE }}
|
103
|
+
CM_GEMS_HOST: ${{ vars.CM_GEMS_HOST }}
|
104
|
+
run: |
|
105
|
+
gem push $GEM_FILE --host $CM_GEMS_HOST
|
data/.rubocop.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
inherit_from:
|
2
|
-
- https://raw.githubusercontent.com/commutatus/cm-linters/main/rubocop.yml
|
2
|
+
- https://raw.githubusercontent.com/commutatus/cm-linters/main/rubocop.yml
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cm-admin (4.1
|
4
|
+
cm-admin (4.2.1)
|
5
5
|
caxlsx_rails
|
6
6
|
cocoon (~> 1.2.15)
|
7
|
-
csv
|
7
|
+
csv (>= 3.3.0)
|
8
|
+
csv-importer (>= 0.8.2)
|
8
9
|
importmap-rails
|
9
10
|
local_time (~> 3.0.2)
|
10
11
|
pagy (~> 4.11.0)
|
@@ -111,6 +112,7 @@ GEM
|
|
111
112
|
concurrent-ruby (1.3.4)
|
112
113
|
connection_pool (2.4.1)
|
113
114
|
crass (1.0.6)
|
115
|
+
csv (3.3.0)
|
114
116
|
csv-importer (0.8.2)
|
115
117
|
virtus
|
116
118
|
date (3.3.4)
|
@@ -1,33 +1,52 @@
|
|
1
|
-
$(document).on(
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
$(document).on("click", '[data-behaviour="export-select-all"]', function (e) {
|
2
|
+
if ($(this).is(":checked")) {
|
3
|
+
$('[data-behaviour="export-checkbox"]').prop("checked", true);
|
4
|
+
} else {
|
5
|
+
$('[data-behaviour="export-checkbox"]').prop("checked", false);
|
6
|
+
}
|
7
7
|
});
|
8
8
|
|
9
|
-
$(document).on(
|
10
|
-
"
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
}
|
9
|
+
$(document).on("click", '[data-behaviour="export-checkbox"]', function (e) {
|
10
|
+
const container = $(this).closest(".row");
|
11
|
+
if (
|
12
|
+
container.find('[data-behaviour="export-checkbox"]:checked').length ==
|
13
|
+
container.find('[data-behaviour="export-checkbox"]').length
|
14
|
+
) {
|
15
|
+
$('[data-behaviour="export-select-all"]').prop("checked", true);
|
16
|
+
} else {
|
17
|
+
$('[data-behaviour="export-select-all"]').prop("checked", false);
|
19
18
|
}
|
20
|
-
);
|
19
|
+
});
|
21
20
|
|
22
|
-
$(document).on(
|
23
|
-
"
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
);
|
21
|
+
$(document).on("click", '[data-behaviour="export-submit"]', function (e) {
|
22
|
+
$('[data-behaviour="export-form-submit"]').submit();
|
23
|
+
const exportFormBody = $("[data-behaviour='export-form-container']");
|
24
|
+
const exportProcessing = $("[data-behaviour='export-processing']");
|
25
|
+
const exportTitle = $("[data-behaviour='export-modal-title']");
|
26
|
+
const exportFooter = $("[data-behaviour='export-modal-footer']");
|
27
|
+
const exportContent = $("[data-behaviour='export-modal-content']");
|
28
|
+
exportTitle.html("Export Processing");
|
29
|
+
exportFormBody.addClass("hidden");
|
30
|
+
exportFooter.addClass("hidden");
|
31
|
+
exportProcessing.removeClass("hidden");
|
32
|
+
exportContent.removeClass("export-modal-content");
|
33
|
+
exportContent.addClass("modal-content");
|
34
|
+
});
|
35
|
+
|
36
|
+
document.addEventListener("turbo:load", function () {
|
37
|
+
document
|
38
|
+
.querySelector('[data-behaviour="export-modal"]')
|
39
|
+
.addEventListener("hidden.bs.modal", function () {
|
40
|
+
const exportFormBody = $("[data-behaviour='export-form-container']");
|
41
|
+
const exportProcessing = $("[data-behaviour='export-processing']");
|
42
|
+
const exportTitle = $("[data-behaviour='export-modal-title']");
|
43
|
+
const exportFooter = $("[data-behaviour='export-modal-footer']");
|
44
|
+
const exportContent = $("[data-behaviour='export-modal-content']");
|
45
|
+
exportTitle.html("Export Data");
|
46
|
+
exportProcessing.addClass("hidden");
|
47
|
+
exportFormBody.removeClass("hidden");
|
48
|
+
exportFooter.removeClass("hidden");
|
49
|
+
exportContent.addClass("export-modal-content");
|
50
|
+
exportContent.removeClass("modal-content");
|
51
|
+
});
|
52
|
+
});
|
@@ -50,10 +50,11 @@
|
|
50
50
|
|
51
51
|
.field-label {
|
52
52
|
@extend .form-label;
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
}
|
54
|
+
|
55
|
+
.required-label::after {
|
56
|
+
content: " *";
|
57
|
+
color: $red;
|
57
58
|
}
|
58
59
|
|
59
60
|
.field-control {
|
@@ -77,8 +78,6 @@
|
|
77
78
|
}
|
78
79
|
}
|
79
80
|
|
80
|
-
|
81
|
-
|
82
81
|
//Form card UI
|
83
82
|
.form-card {
|
84
83
|
@extend .card, .mt-3;
|
@@ -164,7 +163,7 @@
|
|
164
163
|
|
165
164
|
.bulk-action-button {
|
166
165
|
@extend .d-flex, .align-items-center, .gap-2;
|
167
|
-
|
166
|
+
|
168
167
|
&:hover {
|
169
168
|
text-decoration: none;
|
170
169
|
}
|
@@ -175,4 +174,4 @@
|
|
175
174
|
gap: 8px;
|
176
175
|
align-items: center;
|
177
176
|
}
|
178
|
-
}
|
177
|
+
}
|
@@ -7,30 +7,17 @@
|
|
7
7
|
line-height: 22px;
|
8
8
|
}
|
9
9
|
}
|
10
|
-
|
11
10
|
&__actions {
|
12
|
-
@
|
13
|
-
.export-container {
|
14
|
-
position: relative;
|
15
|
-
.export-popup {
|
16
|
-
top: 45px;
|
17
|
-
width: 156px;
|
18
|
-
padding: 8px 0;
|
19
|
-
animation: fadeIn 0.2s ease-in-out;
|
20
|
-
.popup-option {
|
21
|
-
padding: 8px 16px;
|
22
|
-
@include font($size: $t4-text, $color: $primary-text-clr);
|
23
|
-
line-height: 22px;
|
24
|
-
transition: all 0.2s linear;
|
25
|
-
cursor: pointer;
|
26
|
-
&:hover {
|
27
|
-
background-color: $grey-lighter-clr;
|
28
|
-
}
|
29
|
-
span {
|
30
|
-
margin-right: 8px;
|
31
|
-
}
|
32
|
-
}
|
33
|
-
}
|
34
|
-
}
|
11
|
+
@include flex();
|
35
12
|
}
|
36
13
|
}
|
14
|
+
|
15
|
+
.export-container {
|
16
|
+
position: relative;
|
17
|
+
margin: 0 24px 0 auto;
|
18
|
+
}
|
19
|
+
|
20
|
+
.export-modal-content {
|
21
|
+
@extend .modal-content;
|
22
|
+
height: 80vh;
|
23
|
+
}
|
@@ -40,6 +40,10 @@
|
|
40
40
|
@extend .btn, .btn-primary, .btn-sm, .btn-icon-spacing, .disabled;
|
41
41
|
}
|
42
42
|
|
43
|
+
.cm-admin .btn-dark {
|
44
|
+
@extend .btn, .btn-outline-dark, .btn-sm;
|
45
|
+
}
|
46
|
+
|
43
47
|
// button mixin - We can use if needed
|
44
48
|
// @include button-variant($background: #6554e0, $border: #6554e0);
|
45
49
|
|
@@ -1,13 +1,5 @@
|
|
1
1
|
module CmAdmin
|
2
2
|
class ApplicationController < ::ActionController::Base
|
3
|
-
|
4
|
-
# before_action :check_cm_admin
|
5
3
|
layout 'cm_admin'
|
6
|
-
|
7
|
-
|
8
|
-
def check_cm_admin
|
9
|
-
redirect_to CmAdmin::Engine.mount_path + '/access-denied' unless defined?(::Current) && ::Current.cm_admin_user
|
10
|
-
end
|
11
|
-
|
12
4
|
end
|
13
5
|
end
|
@@ -6,9 +6,20 @@ require 'cm_admin/view_helpers'
|
|
6
6
|
|
7
7
|
module CmAdmin
|
8
8
|
class ExportsController < ApplicationController
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
before_action :set_current_user_permission
|
10
|
+
|
11
|
+
def send_export_email
|
12
|
+
export_params = params.permit(:select_all, :class_name, :associated_class_name, :parent_id, :child_records,
|
13
|
+
:action_name, :filters, columns: []).to_h
|
14
|
+
associated_model_name = params[:associated_class_name].presence || params[:class_name]
|
15
|
+
expires_at = DateTime.now + 1.day
|
16
|
+
FileExport.create!(associated_model_name:, exported_by: Current.user, expires_at:, params: export_params)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def set_current_user_permission
|
22
|
+
CmCurrent.user_permissions = Current.user.cm_role.cm_permissions if Current.user.cm_role.present?
|
12
23
|
end
|
13
24
|
end
|
14
25
|
end
|
@@ -8,11 +8,6 @@ module CmAdmin
|
|
8
8
|
skip_before_action :verify_authenticity_token, only: :reset_sort_columns
|
9
9
|
before_action :set_current_user_permission
|
10
10
|
|
11
|
-
|
12
|
-
def set_current_user_permission
|
13
|
-
CmCurrent.user_permissions = Current.user.cm_role.cm_permissions if Current.user.cm_role.present?
|
14
|
-
end
|
15
|
-
|
16
11
|
def cm_index(params)
|
17
12
|
@current_action = CmAdmin::Models::Action.find_by(@model, name: 'index')
|
18
13
|
# Based on the params the filter and pagination object to be set
|
@@ -138,7 +133,7 @@ module CmAdmin
|
|
138
133
|
|
139
134
|
if @action.execution_mode.to_sym == :individual
|
140
135
|
flash[:bulk_action_success] = "#{@bulk_action_processor.success_records.size} rows were successful for #{action_name}" if @bulk_action_processor.success_records.present?
|
141
|
-
flash[:alert] = "<b>#{@bulk_action_processor.invalid_records.size} rows were unsuccessful for #{action_name}</b>"
|
136
|
+
flash[:alert] = "<b>#{@bulk_action_processor.invalid_records.size} rows were unsuccessful for #{action_name}</b>"
|
142
137
|
flash[:bulk_action_error] = "<div>
|
143
138
|
<div>
|
144
139
|
<div><b>#{@bulk_action_processor.invalid_records.size} rows were unsuccessful for #{action_name}</b></div>
|
@@ -151,7 +146,7 @@ module CmAdmin
|
|
151
146
|
<div><b>#{action_name} encountered the following errors:</b></div>
|
152
147
|
<ul>#{error_messages}</ul>
|
153
148
|
</div>"
|
154
|
-
end
|
149
|
+
end
|
155
150
|
format.html { redirect_to request.referrer}
|
156
151
|
end
|
157
152
|
end
|
@@ -322,7 +317,6 @@ module CmAdmin
|
|
322
317
|
end
|
323
318
|
|
324
319
|
@associated_ar_object = if child_records.is_a? ActiveRecord::Relation
|
325
|
-
|
326
320
|
filter_by(params, child_records, parent_record: @ar_object, filter_params: @associated_model.filter_params(params))
|
327
321
|
else
|
328
322
|
child_records
|
@@ -444,6 +438,10 @@ module CmAdmin
|
|
444
438
|
|
445
439
|
private
|
446
440
|
|
441
|
+
def set_current_user_permission
|
442
|
+
CmCurrent.user_permissions = Current.user.cm_role.cm_permissions if Current.user.cm_role.present?
|
443
|
+
end
|
444
|
+
|
447
445
|
def attachment_fields(model_object)
|
448
446
|
model_object.reflect_on_all_associations.map do |reflection|
|
449
447
|
next if reflection.options[:polymorphic]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class GenerateExportFileJob < ApplicationJob
|
2
|
+
queue_as :default
|
3
|
+
|
4
|
+
def perform(file_export)
|
5
|
+
Current.user = file_export.exported_by
|
6
|
+
CmCurrent.user_permissions = Current.user.cm_role.cm_permissions if Current.user.cm_role.present?
|
7
|
+
|
8
|
+
file_export.attach_export_file
|
9
|
+
ExportMailer.export_email(file_export).deliver_now
|
10
|
+
file_export.success!
|
11
|
+
rescue StandardError => e
|
12
|
+
file_export&.failed!
|
13
|
+
raise e
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ExportMailer < ApplicationMailer
|
2
|
+
def export_email(file_export)
|
3
|
+
set_current_for_development
|
4
|
+
expires_in = (file_export.expires_at - Time.now).to_i
|
5
|
+
@model = file_export.associated_model_name&.pluralize
|
6
|
+
@link = file_export.export_file.url(expires_in:)
|
7
|
+
@expires_at = file_export.expires_at.utc
|
8
|
+
mail(to: file_export&.exported_by&.email, subject: "#{@model} Export is ready for download")
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def set_current_for_development
|
14
|
+
return unless Rails.env.development?
|
15
|
+
|
16
|
+
url = Rails.application.credentials[:be_url]
|
17
|
+
host = URI.parse(url).host
|
18
|
+
protocol = URI.parse(url).scheme
|
19
|
+
port = URI.parse(url).port
|
20
|
+
ActiveStorage::Current.url_options = { host:, protocol:, port: }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Exportable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def attach_export_file
|
5
|
+
file_path = generate_excel
|
6
|
+
filename = file_path&.split('/')&.last
|
7
|
+
export_file.attach(io: File.open(file_path), filename:)
|
8
|
+
rescue StandardError => e
|
9
|
+
failed!
|
10
|
+
raise e
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_excel
|
14
|
+
params = self.params.with_indifferent_access
|
15
|
+
klass_name = params[:class_name]
|
16
|
+
associated_klass_name = params[:associated_class_name]
|
17
|
+
action_name = params[:action_name] || 'index'
|
18
|
+
|
19
|
+
model = CmAdmin::Model.find_by({ name: klass_name })
|
20
|
+
klass = klass_name.constantize
|
21
|
+
current_action = CmAdmin::Models::Action.find_by(model, name: action_name)
|
22
|
+
|
23
|
+
if associated_klass_name.present? && params[:parent_id].present?
|
24
|
+
scoped_model = "CmAdmin::#{model.name}Policy::#{action_name.classify}Scope".constantize.new(Current.user, klass).resolve
|
25
|
+
parent_model = model
|
26
|
+
model = CmAdmin::Model.find_by({ name: associated_klass_name })
|
27
|
+
parent_record = fetch_ar_object(scoped_model, params[:parent_id])
|
28
|
+
child_records = parent_record.send(params[:child_records])
|
29
|
+
end
|
30
|
+
selected_column_names = params[:columns] || []
|
31
|
+
records = if associated_klass_name.present?
|
32
|
+
child_records
|
33
|
+
else
|
34
|
+
"CmAdmin::#{klass_name}Policy::Scope".constantize.new(Current.user, klass).resolve
|
35
|
+
end
|
36
|
+
records.includes(current_action.eager_load_associations) if current_action.eager_load_associations.present?
|
37
|
+
filter_params = JSON.parse(params[:filters] || '{}')
|
38
|
+
filtered_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, model.filters)
|
39
|
+
formatted_data = format_records(filtered_data, model, selected_column_names, parent_model:, action_name:)
|
40
|
+
file_path = "#{Rails.root}/tmp/#{model.name}_data_#{DateTime.now.strftime('%Y-%m-%d_%H-%M-%S')}.xlsx"
|
41
|
+
create_workbook(model, formatted_data, file_path)
|
42
|
+
file_path
|
43
|
+
end
|
44
|
+
|
45
|
+
def exportable_columns(klass, action_name: :index)
|
46
|
+
klass.available_fields[action_name].select(&:exportable)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def fetch_ar_object(model_object, id)
|
52
|
+
return model_object.friendly.find(id) if model_object.respond_to?(:friendly)
|
53
|
+
|
54
|
+
model_object.find(id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_records(records, model, selected_column_names, parent_model:, action_name: :index)
|
58
|
+
deserialized_columns = CmAdmin::Utils.deserialize_csv_columns(selected_column_names, :as_json_params)
|
59
|
+
main_model = parent_model || model
|
60
|
+
available_fields = main_model.available_fields[action_name.to_sym]
|
61
|
+
# This includes isn't recursve, a full solution should be recursive
|
62
|
+
records_arr = []
|
63
|
+
records.includes(deserialized_columns[:include].keys).find_each do |record|
|
64
|
+
record_hash = record.as_json({ only: selected_column_names.map(&:to_sym) })
|
65
|
+
selected_column_names.each do |column_name|
|
66
|
+
break unless available_fields.map(&:field_name).include?(column_name.to_sym)
|
67
|
+
|
68
|
+
column = CmAdmin::Models::Column.find_by(model, :index, { name: column_name.to_sym })
|
69
|
+
record_hash[column.field_name] = if column.field_type == :custom
|
70
|
+
send(column.helper_method, record, column.field_name).to_s
|
71
|
+
else
|
72
|
+
record.send(column.field_name).to_s
|
73
|
+
end
|
74
|
+
end
|
75
|
+
records_arr << record_hash
|
76
|
+
end
|
77
|
+
records_arr
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_workbook(cm_model, records, file_path)
|
81
|
+
flattened_records = records.map { |record| CmAdmin::Utils.flatten_hash(record) }
|
82
|
+
columns = exportable_columns(cm_model).select do |column|
|
83
|
+
flattened_records.first.keys.include?(column.field_name.to_s)
|
84
|
+
end
|
85
|
+
size_arr = []
|
86
|
+
columns.size.times { size_arr << 22 }
|
87
|
+
xl = Axlsx::Package.new
|
88
|
+
xl.workbook.add_worksheet do |sheet|
|
89
|
+
sheet.add_row columns&.map(&:header), b: true
|
90
|
+
flattened_records.each do |record|
|
91
|
+
sheet.add_row(columns.map { |column| record[column.field_name.to_s] })
|
92
|
+
end
|
93
|
+
sheet.column_widths(*size_arr)
|
94
|
+
end
|
95
|
+
xl.serialize(file_path)
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class FileExport < ApplicationRecord
|
2
|
+
include CmAdmin::CustomHelper
|
3
|
+
include Exportable
|
4
|
+
|
5
|
+
enum :status, { in_progress: 0, success: 1, failed: 2 }
|
6
|
+
|
7
|
+
has_one_attached :export_file
|
8
|
+
belongs_to :exported_by, polymorphic: true
|
9
|
+
|
10
|
+
after_create :generate_export_file
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def generate_export_file
|
15
|
+
GenerateExportFileJob.perform_later(self)
|
16
|
+
end
|
17
|
+
end
|
@@ -60,3 +60,5 @@
|
|
60
60
|
.pagination-bar
|
61
61
|
p.count-text.m-0 Showing #{number_with_delimiter(@associated_ar_object.pagy.from.to_i)} to #{number_with_delimiter(@associated_ar_object.pagy.to.to_i)} out of #{number_with_delimiter(@associated_ar_object.pagy.count.to_i)}
|
62
62
|
== render partial: 'cm_admin/main/cm_pagy_nav', locals: { pagy: @associated_ar_object.pagy }
|
63
|
+
|
64
|
+
= export_modal(@model, action_name: @action.name.to_sym, associated_klass: @associated_model)
|
@@ -55,5 +55,5 @@
|
|
55
55
|
p.count-text.m-0 Showing #{@ar_object.pagy.from} to #{@ar_object.pagy.to} out of #{@ar_object.pagy.count}
|
56
56
|
== render partial: 'cm_admin/main/cm_pagy_nav', locals: { pagy: @ar_object.pagy }
|
57
57
|
|
58
|
-
=
|
58
|
+
= export_modal(@model)
|
59
59
|
= manage_column_pop_up(@model)
|
@@ -53,5 +53,5 @@
|
|
53
53
|
p.count-text.m-0 Showing #{number_with_delimiter(@ar_object.pagy.from.to_i)} to #{number_with_delimiter(@ar_object.pagy.to.to_i)} out of #{number_with_delimiter(@ar_object.pagy.count.to_i)}
|
54
54
|
== render partial: 'cm_admin/main/cm_pagy_nav', locals: { pagy: @ar_object.pagy }
|
55
55
|
|
56
|
-
=
|
56
|
+
= export_modal(@model)
|
57
57
|
= manage_column_pop_up(@model)
|
@@ -6,16 +6,6 @@
|
|
6
6
|
h4 = action_title
|
7
7
|
.entity-header__actions
|
8
8
|
- if @model.current_action.name == 'index'
|
9
|
-
- if has_valid_policy(@ar_object, :export)
|
10
|
-
.export-container
|
11
|
-
.dropdown
|
12
|
-
button.btn-secondary.dropdown-toggle data-bs-toggle='dropdown'
|
13
|
-
i.fa.fa-arrow-down
|
14
|
-
| Export
|
15
|
-
ul.dropdown-menu.export-popup
|
16
|
-
li
|
17
|
-
.popup-option.pointer data-bs-toggle='modal' data-bs-target='#exportmodal'
|
18
|
-
span Export
|
19
9
|
- if @model.importer && has_valid_policy(@ar_object, :importable)
|
20
10
|
= link_to 'Import', cm_admin.send(:"cm_import_#{@model.name.underscore}_path"), class: 'btn-primary ms-2'
|
21
11
|
- new_action = @model.available_actions.select{|act| act if act.action_type.eql?(:default) && act.name.eql?('new')}
|
@@ -1,15 +1,19 @@
|
|
1
|
-
|
2
1
|
= hidden_field_tag :view_type, (@current_action.view_type || params[:view_type])
|
3
2
|
.cm-index-page.cm-page-container
|
4
3
|
.sticky-container.page-top-bar
|
5
4
|
== render 'cm_admin/main/top_navbar'
|
6
|
-
- if @model.filters.present? && @action.partial.nil?
|
5
|
+
- if (@model.filters.present? || has_valid_policy(@ar_object, :export)) && @action.partial.nil?
|
7
6
|
.cm-index-page__filters
|
8
7
|
== render partial: 'cm_admin/main/filters', locals: { filters: @model.filters }
|
8
|
+
- if has_valid_policy(@ar_object, :export)
|
9
|
+
.export-container
|
10
|
+
button.btn-dark data-bs-toggle="modal" data-bs-target="#exportModal"
|
11
|
+
i.fa-regular.fa-file-export.me-1
|
12
|
+
| Export
|
9
13
|
.cm-index-page__table-container
|
10
14
|
- if @action.partial
|
11
15
|
== render @action.partial
|
12
|
-
- elsif params[:view_type] == 'table' || @current_action.view_type == :table
|
16
|
+
- elsif params[:view_type] == 'table' || @current_action.view_type == :table
|
13
17
|
== render 'cm_admin/main/table'
|
14
18
|
- elsif params[:view_type] == 'card' || @current_action.view_type == :card
|
15
19
|
== render "cm_admin/#{@model.name.underscore}/card"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
table style="background: #F2F4F6; padding: 56px 0px; width: 100%; border-spacing: 24px;"
|
2
|
+
tr
|
3
|
+
td colspan="3" style="text-align: center; margin-bottom: 24px;"
|
4
|
+
img src="#{Rails.configuration.x.project_settings.logo_url}" alt="logo" style="height: auto; width: 50px;"
|
5
|
+
tr style="width: fit-content; style=width: 100px;"
|
6
|
+
td
|
7
|
+
td style="background: #fff; padding: 40px; padding-bottom: 56px;"
|
8
|
+
p style="color: #333333; font-weight: 700; font-size: 24px; line-height: 36px; margin: 0; margin-bottom: 24px;"
|
9
|
+
= "#{@model} Export is ready"
|
10
|
+
div style="margin-bottom: 40px;"
|
11
|
+
p
|
12
|
+
| The download link will expire at <strong>#{@expires_at.strftime("%H:%M:%S")} UTC</strong> on <strong>#{@expires_at.strftime("%d %B %Y")}.</strong>
|
13
|
+
p Make sure to save the file before the link expires.
|
14
|
+
a style="border: none; background: #6D0094; color: #fff; padding: 16px 32px; width: fit-content; border-radius: 5px; text-decoration: none;" href="#{@link}"
|
15
|
+
| Download Excel
|
16
|
+
td
|
17
|
+
tr
|
18
|
+
td colspan="3" style="text-align: center; color: #5F6166; font-size: 16px; margin-top: 24px;"
|
19
|
+
= "© #{Time.current.year} #{Rails.configuration.x.project_settings.name}"
|
@@ -26,12 +26,12 @@ html
|
|
26
26
|
script src="https://kit.fontawesome.com/9c93dde7a7.js" data-mutate-approach="sync"
|
27
27
|
= stylesheet_link_tag 'cm_admin/custom', media: 'all', 'data-turbo-track': 'reload'
|
28
28
|
link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /
|
29
|
-
|
29
|
+
|
30
30
|
script src="https://raw.githack.com/SortableJS/Sortable/master/Sortable.js"
|
31
31
|
|
32
32
|
link[rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css"]
|
33
33
|
link[rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.rtl.min.css"]
|
34
|
-
|
34
|
+
|
35
35
|
body
|
36
36
|
- if CmAdmin.config.enable_tracking
|
37
37
|
/ Google Tag Manager (noscript)
|
@@ -54,13 +54,20 @@ html
|
|
54
54
|
== render 'cm_admin/main/top_navbar'
|
55
55
|
- if @ar_object.model_name
|
56
56
|
== render 'cm_admin/main/tabs', via_xhr: false
|
57
|
-
- if @associated_model && @associated_model.filters.present?
|
58
|
-
.
|
59
|
-
|
57
|
+
- if @associated_model && (@associated_model.filters.present? || has_valid_policy(@associated_ar_object, :export))
|
58
|
+
.cm-index-page__filters
|
59
|
+
.filters-bar
|
60
|
+
== render partial: 'cm_admin/main/filters', locals: { filters: @associated_model.filters }
|
61
|
+
- if has_valid_policy(@associated_ar_object, :export)
|
62
|
+
.export-container
|
63
|
+
button.btn-dark data-bs-toggle="modal" data-bs-target="#exportModal"
|
64
|
+
i.fa-regular.fa-file-export.me-1
|
65
|
+
| Export
|
60
66
|
= yield
|
61
67
|
- else
|
62
68
|
= yield
|
63
69
|
div data-behaviour="flash-container"
|
64
70
|
= render 'layouts/cm_flash_message'
|
71
|
+
div data-behaviour="export-modal-container"
|
65
72
|
- unless (@current_action&.view_type == :kanban || params[:view_type] == 'kanban')
|
66
73
|
= render 'layouts/custom_action_modals'
|
data/cm_admin.gemspec
CHANGED
@@ -10,13 +10,14 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = 'CmAdmin providing a streamlined and efficient solution for building customized admin panels within the context of Rails applications. Its robust features empower developers to effortlessly generate and manage administrative interfaces with precision and ease.'
|
11
11
|
spec.homepage = 'https://github.com/commutatus/cm-admin'
|
12
12
|
spec.license = 'MIT'
|
13
|
-
spec.required_ruby_version = '>=
|
13
|
+
spec.required_ruby_version = '>= 3.3.0'
|
14
14
|
|
15
15
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
16
16
|
|
17
17
|
spec.metadata = {
|
18
18
|
'homepage_uri' => spec.homepage,
|
19
|
-
'source_code_uri' => 'https://github.com/commutatus/cm-admin'
|
19
|
+
'source_code_uri' => 'https://github.com/commutatus/cm-admin',
|
20
|
+
'github_repo' => 'ssh://github.com/commutatus/cm-admin'
|
20
21
|
}
|
21
22
|
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
22
23
|
|
@@ -30,11 +31,12 @@ Gem::Specification.new do |spec|
|
|
30
31
|
spec.require_paths = ['lib']
|
31
32
|
spec.add_runtime_dependency 'caxlsx_rails'
|
32
33
|
spec.add_runtime_dependency 'cocoon', '~> 1.2.15'
|
33
|
-
spec.add_runtime_dependency 'csv-importer', '
|
34
|
+
spec.add_runtime_dependency 'csv-importer', '>= 0.8.2'
|
34
35
|
spec.add_runtime_dependency 'local_time', '~> 3.0.2'
|
35
36
|
spec.add_runtime_dependency 'pagy', '~> 4.11.0'
|
36
37
|
spec.add_runtime_dependency 'pundit', '~> 2.2.0'
|
37
38
|
spec.add_runtime_dependency 'rails', '>= 7.0'
|
38
39
|
spec.add_runtime_dependency 'slim', '>= 4.1.0'
|
40
|
+
spec.add_runtime_dependency 'csv', '>= 3.3.0'
|
39
41
|
spec.add_dependency 'importmap-rails'
|
40
42
|
end
|
data/config/routes.rb
CHANGED
@@ -2,62 +2,9 @@ module CmAdmin
|
|
2
2
|
module Models
|
3
3
|
class Export
|
4
4
|
class << self
|
5
|
-
def
|
6
|
-
klass
|
7
|
-
selected_column_names = params[:columns] || []
|
8
|
-
# filter_params = params[:filters]
|
9
|
-
model = CmAdmin::Model.find_by({name: klass_name})
|
10
|
-
# records = get_records(klass, model, columns, helpers)
|
11
|
-
records = "CmAdmin::#{klass_name}Policy::Scope".constantize.new(Current.user, klass).resolve
|
12
|
-
filtered_data = CmAdmin::Models::Filter.filtered_data(model.filter_params(params), records, model.filters)
|
13
|
-
formatted_data = format_records(filtered_data, model, selected_column_names, helpers)
|
14
|
-
file_path = "#{Rails.root}/tmp/#{klass}_data_#{DateTime.now.strftime("%Y-%m-%d_%H-%M-%S")}.xlsx"
|
15
|
-
create_workbook(model, formatted_data, selected_column_names, file_path)
|
16
|
-
return file_path
|
5
|
+
def exportable_columns(klass, action_name: :index)
|
6
|
+
klass.available_fields[action_name].select(&:exportable)
|
17
7
|
end
|
18
|
-
|
19
|
-
def format_records(records, model, selected_column_names, helpers)
|
20
|
-
deserialized_columns = CmAdmin::Utils.deserialize_csv_columns(selected_column_names, :as_json_params)
|
21
|
-
# This includes isn't recursve, a full solution should be recursive
|
22
|
-
records_arr = []
|
23
|
-
records.includes(deserialized_columns[:include].keys).find_each do |record|
|
24
|
-
record_hash = record.as_json({ only: selected_column_names.map(&:to_sym) })
|
25
|
-
selected_column_names.each do |column_name|
|
26
|
-
break unless model.available_fields[:index].map(&:field_name).include?(column_name.to_sym)
|
27
|
-
|
28
|
-
column = CmAdmin::Models::Column.find_by(model, :index, { name: column_name.to_sym })
|
29
|
-
if column.field_type == :custom
|
30
|
-
record_hash[column.field_name] = helpers.send(column.helper_method, record, column.field_name).to_s
|
31
|
-
else
|
32
|
-
record_hash[column.field_name] = record.send(column.field_name).to_s
|
33
|
-
end
|
34
|
-
end
|
35
|
-
records_arr << record_hash
|
36
|
-
end
|
37
|
-
records_arr
|
38
|
-
end
|
39
|
-
|
40
|
-
def create_workbook(cm_model, records, _class_name, file_path)
|
41
|
-
flattened_records = records.map { |record| CmAdmin::Utils.flatten_hash(record) }
|
42
|
-
# columns = flattened_records.map{|x| x.keys}.flatten.uniq.sort
|
43
|
-
columns = exportable_columns(cm_model).select { |column| flattened_records.first.keys.include?(column.field_name.to_s) }
|
44
|
-
size_arr = []
|
45
|
-
columns.size.times { size_arr << 22 }
|
46
|
-
xl = Axlsx::Package.new
|
47
|
-
xl.workbook.add_worksheet do |sheet|
|
48
|
-
sheet.add_row columns&.map(&:header), b: true
|
49
|
-
flattened_records.each do |record|
|
50
|
-
sheet.add_row(columns.map { |column| record[column.field_name.to_s] })
|
51
|
-
end
|
52
|
-
sheet.column_widths(*size_arr)
|
53
|
-
end
|
54
|
-
xl.serialize(file_path)
|
55
|
-
end
|
56
|
-
|
57
|
-
def exportable_columns(klass)
|
58
|
-
klass.available_fields[:index].select(&:exportable)
|
59
|
-
end
|
60
|
-
|
61
8
|
end
|
62
9
|
end
|
63
10
|
end
|
data/lib/cm_admin/version.rb
CHANGED
@@ -15,66 +15,84 @@ module CmAdmin
|
|
15
15
|
include ActionView::Helpers::TagHelper
|
16
16
|
include ActionView::Helpers::NumberHelper
|
17
17
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
tag.div class: 'modal-content' do
|
29
|
-
concat pop_ups(klass, required_filters)
|
18
|
+
def export_modal(klass, action_name: :index, associated_klass: nil, required_filters: nil)
|
19
|
+
has_default_email = Rails.configuration.x.project_settings.default_from_email.present?
|
20
|
+
tag.div class: 'modal fade form-modal', id: 'exportModal', role: 'dialog', data: { behaviour: 'export-modal' }, aria: { labelledby: 'exportModalLabel' } do
|
21
|
+
tag.div class: 'modal-dialog modal-dialog-centered modal-dialog-scrollable', role: 'document' do
|
22
|
+
tag.div class: "#{has_default_email ? 'export-modal-content' : 'modal-content'}", data: { behaviour: 'export-modal-content' } do
|
23
|
+
if has_default_email
|
24
|
+
modal_content(klass, action_name, associated_klass, required_filters)
|
25
|
+
else
|
26
|
+
modal_content_with_error
|
27
|
+
end
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
33
|
+
def modal_content(klass, action_name, associated_klass, required_filters)
|
34
|
+
concat modal_header
|
35
|
+
concat modal_body(klass, action_name, associated_klass)
|
36
|
+
concat modal_footer
|
40
37
|
end
|
41
38
|
|
42
|
-
def
|
39
|
+
def modal_header
|
43
40
|
tag.div class: 'modal-header' do
|
44
|
-
tag.
|
45
|
-
|
46
|
-
end
|
47
|
-
tag.h4 'Select columns to export', class: 'modal-title', id: 'exportModal'
|
41
|
+
concat tag.h5 'Export Data', class: 'modal-title', id: 'exportModalLabel', data: { behaviour: 'export-modal-title' }
|
42
|
+
concat tag.button type: 'button', class: 'btn-close', data: { bs_dismiss: 'modal' }, aria: { label: 'Close' }
|
48
43
|
end
|
49
44
|
end
|
50
45
|
|
51
|
-
def
|
52
|
-
tag.div class: 'modal-body' do
|
53
|
-
form_tag cm_admin.send('
|
54
|
-
concat
|
55
|
-
|
56
|
-
concat '
|
46
|
+
def modal_body(klass, action_name, associated_klass)
|
47
|
+
tag.div class: 'modal-body', data: { behaviour: 'export-modal-body-form' } do
|
48
|
+
form_tag cm_admin.send('send_export_email_path'), id: 'export-to-file-form', style: 'width: 100%;', class: 'cm-admin-csv-export-form', data: { turbo: false, behaviour: 'export-form-submit' } do
|
49
|
+
concat tag.p 'You’ll receive the exported file in your email soon.', class: 'hidden', data: { behaviour: 'export-processing' }
|
50
|
+
concat(content_tag(:div, class: 'export-form-container', data: { behaviour: 'export-form-container' }) do
|
51
|
+
concat tag.label 'Select columns to export', class: 'required-label mb-2'
|
52
|
+
concat(content_tag(:div, class: 'column export-select-container') do
|
53
|
+
concat check_box_tag('select_all', '1', false, class: 'form-check-input', data: { behaviour: 'export-select-all' })
|
54
|
+
concat 'All'
|
55
|
+
end)
|
56
|
+
concat hidden_field_tag 'class_name', klass.name.to_s
|
57
|
+
concat hidden_field_tag 'associated_class_name', associated_klass&.name.to_s.presence
|
58
|
+
concat hidden_field_tag 'parent_id', params[:id]
|
59
|
+
concat hidden_field_tag 'child_records', @current_action.child_records
|
60
|
+
concat hidden_field_tag 'action_name', @current_action.name
|
61
|
+
concat hidden_field_tag 'filters', params[:filters].to_json
|
62
|
+
concat checkbox_row(klass, action_name)
|
57
63
|
end)
|
58
|
-
concat hidden_field_tag 'class_name', klass.name.to_s, id: 'export-to-file-klass'
|
59
|
-
concat checkbox_row(klass)
|
60
|
-
concat tag.hr
|
61
|
-
# TODO: export-to-file-btn class is used for JS functionality, Have to remove
|
62
|
-
concat submit_tag 'Export', class: 'btn-primary export-to-file-btn'
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
67
|
-
def
|
68
|
+
def modal_footer
|
69
|
+
tag.div class: 'modal-footer', data: { behaviour: 'export-modal-footer' } do
|
70
|
+
concat tag.button 'Close', type: 'button', class: 'btn btn-secondary', data: { bs_dismiss: 'modal' }
|
71
|
+
concat tag.button 'Export', type: 'submit', class: 'btn btn-primary', data: { behaviour: 'export-submit' }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def modal_content_with_error
|
76
|
+
concat(content_tag(:div, class: 'modal-header') do
|
77
|
+
concat tag.h5 'Export Data', class: 'modal-title', id: 'exportModalLabel', data: { behaviour: 'export-modal-title' }
|
78
|
+
concat tag.button type: 'button', class: 'btn-close', data: { bs_dismiss: 'modal' }, aria: { label: 'Close' }
|
79
|
+
end)
|
80
|
+
concat(content_tag(:div, class: 'modal-body') do
|
81
|
+
p "Your application does not have a default sender, so we can't send out any emails, please contact a developer."
|
82
|
+
end)
|
83
|
+
end
|
84
|
+
|
85
|
+
def checkbox_row(klass, action_name)
|
68
86
|
tag.div class: 'row' do
|
69
|
-
CmAdmin::Models::Export.exportable_columns(klass).each do |column|
|
87
|
+
CmAdmin::Models::Export.exportable_columns(klass, action_name:).each do |column|
|
70
88
|
concat create_checkbox(column)
|
71
89
|
end
|
72
90
|
end
|
73
91
|
end
|
74
92
|
|
75
93
|
def create_checkbox(column)
|
76
|
-
tag.div
|
77
|
-
concat check_box_tag('columns[]', column.field_name, false, id: column.field_name.to_s.gsub('/', '-'), data: { behaviour: 'export-checkbox' })
|
94
|
+
tag.div do
|
95
|
+
concat check_box_tag('columns[]', column.field_name, false, id: column.field_name.to_s.gsub('/', '-'), class: 'form-check-input', data: { behaviour: 'export-checkbox' })
|
78
96
|
concat " #{column.header.to_s.gsub('/', '_').humanize}"
|
79
97
|
end
|
80
98
|
end
|
@@ -14,6 +14,7 @@ module CmAdmin
|
|
14
14
|
copy_file 'application_policy.rb', 'app/policies/application_policy.rb'
|
15
15
|
route 'mount CmAdmin::Engine => "/admin"'
|
16
16
|
generate 'migration', 'CreateFileImport associated_model_name:string added_by:references{polymorphic} error_report:jsonb completed_at:datetime status:integer'
|
17
|
+
generate 'migration', 'CreateFileExport associated_model_name:string exported_by:references{polymorphic} expires_at:datetime status:integer params:jsonb'
|
17
18
|
rake 'db:migrate'
|
18
19
|
end
|
19
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cm-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1
|
4
|
+
version: 4.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael
|
@@ -11,10 +11,10 @@ authors:
|
|
11
11
|
- Pranav
|
12
12
|
- Mahaveer
|
13
13
|
- Austin
|
14
|
-
autorequire:
|
14
|
+
autorequire:
|
15
15
|
bindir: exe
|
16
16
|
cert_chain: []
|
17
|
-
date: 2024-12-
|
17
|
+
date: 2024-12-16 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: caxlsx_rails
|
@@ -48,14 +48,14 @@ dependencies:
|
|
48
48
|
name: csv-importer
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - "
|
51
|
+
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.8.2
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - "
|
58
|
+
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 0.8.2
|
61
61
|
- !ruby/object:Gem::Dependency
|
@@ -128,6 +128,20 @@ dependencies:
|
|
128
128
|
- - ">="
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: 4.1.0
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: csv
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 3.3.0
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 3.3.0
|
131
145
|
- !ruby/object:Gem::Dependency
|
132
146
|
name: importmap-rails
|
133
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,17 +172,18 @@ executables: []
|
|
158
172
|
extensions: []
|
159
173
|
extra_rdoc_files: []
|
160
174
|
files:
|
175
|
+
- ".gem/credentials"
|
161
176
|
- ".github/CODEOWNERS"
|
162
177
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
163
178
|
- ".github/ISSUE_TEMPLATE/config.yml"
|
164
179
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
165
180
|
- ".github/workflows/deploy-yard-docs.yml"
|
166
181
|
- ".github/workflows/linters.yml"
|
182
|
+
- ".github/workflows/release-cm-gem.yml"
|
167
183
|
- ".github/workflows/release-gem.yml"
|
168
184
|
- ".github/workflows/test.yml"
|
169
185
|
- ".gitignore"
|
170
186
|
- ".rspec"
|
171
|
-
- ".rubocop-https---raw-githubusercontent-com-commutatus-cm-linters-main-rubocop-yml"
|
172
187
|
- ".rubocop.yml"
|
173
188
|
- ".stylelintrc.json"
|
174
189
|
- ".vscode/settings.json"
|
@@ -347,12 +362,16 @@ files:
|
|
347
362
|
- app/helpers/cm_admin/custom_helper.rb
|
348
363
|
- app/helpers/cm_admin/permission_helper.rb
|
349
364
|
- app/jobs/file_import_processor_job.rb
|
365
|
+
- app/jobs/generate_export_file_job.rb
|
366
|
+
- app/mailers/export_mailer.rb
|
350
367
|
- app/models/cm_current.rb
|
351
368
|
- app/models/cm_permission.rb
|
352
369
|
- app/models/cm_role.rb
|
353
370
|
- app/models/concerns/cm_admin/bulk_action_processor.rb
|
354
371
|
- app/models/concerns/cm_admin/cm_role.rb
|
355
372
|
- app/models/concerns/cm_admin/file_import.rb
|
373
|
+
- app/models/concerns/exportable.rb
|
374
|
+
- app/models/file_export.rb
|
356
375
|
- app/models/file_import.rb
|
357
376
|
- app/policies/cm_admin/file_import_policy.rb
|
358
377
|
- app/views/cm_admin/main/_actions_dropdown.html.slim
|
@@ -387,6 +406,7 @@ files:
|
|
387
406
|
- app/views/cm_admin/static/dashboard.html.slim
|
388
407
|
- app/views/cm_admin/static/error_401.html.slim
|
389
408
|
- app/views/cm_admin/static/error_403.html.slim
|
409
|
+
- app/views/export_mailer/export_email.html.slim
|
390
410
|
- app/views/layouts/_cm_flash_message.html.slim
|
391
411
|
- app/views/layouts/_custom_action_modal.html.slim
|
392
412
|
- app/views/layouts/_custom_action_modals.html.slim
|
@@ -395,6 +415,7 @@ files:
|
|
395
415
|
- app/views/layouts/_left_sidebar_nav.html.slim
|
396
416
|
- app/views/layouts/_quick_links.html.slim
|
397
417
|
- app/views/layouts/cm_admin.html.slim
|
418
|
+
- app/views/layouts/mailer.html.slim
|
398
419
|
- app/views/layouts/static.html.slim
|
399
420
|
- bin/console
|
400
421
|
- bin/importmap
|
@@ -485,7 +506,8 @@ licenses:
|
|
485
506
|
metadata:
|
486
507
|
homepage_uri: https://github.com/commutatus/cm-admin
|
487
508
|
source_code_uri: https://github.com/commutatus/cm-admin
|
488
|
-
|
509
|
+
github_repo: ssh://github.com/commutatus/cm-admin
|
510
|
+
post_install_message:
|
489
511
|
rdoc_options: []
|
490
512
|
require_paths:
|
491
513
|
- lib
|
@@ -493,7 +515,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
493
515
|
requirements:
|
494
516
|
- - ">="
|
495
517
|
- !ruby/object:Gem::Version
|
496
|
-
version:
|
518
|
+
version: 3.3.0
|
497
519
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
498
520
|
requirements:
|
499
521
|
- - ">="
|
@@ -501,7 +523,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
501
523
|
version: '0'
|
502
524
|
requirements: []
|
503
525
|
rubygems_version: 3.5.22
|
504
|
-
signing_key:
|
526
|
+
signing_key:
|
505
527
|
specification_version: 4
|
506
528
|
summary: CmAdmin is a robust gem designed to assist in creating admin panels for Rails
|
507
529
|
applications
|
@@ -1,20 +0,0 @@
|
|
1
|
-
AllCops:
|
2
|
-
DisabledByDefault: false
|
3
|
-
|
4
|
-
Style/FrozenStringLiteralComment:
|
5
|
-
Enabled: false
|
6
|
-
|
7
|
-
Style/Documentation:
|
8
|
-
Enabled: false
|
9
|
-
|
10
|
-
Layout/IndentationStyle:
|
11
|
-
Enabled: false
|
12
|
-
|
13
|
-
Layout/LineLength:
|
14
|
-
Enabled: false
|
15
|
-
|
16
|
-
Metrics/MethodLength:
|
17
|
-
Enabled: false
|
18
|
-
|
19
|
-
Metrics/AbcSize:
|
20
|
-
Enabled: false
|