satis 2.1.46 → 2.1.47
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/app/assets/stylesheets/satis/application.css +1 -0
- data/app/components/satis/attachments/component.css +136 -0
- data/app/components/satis/attachments/component.html.slim +26 -0
- data/app/components/satis/attachments/component.rb +34 -0
- data/app/components/satis/attachments/create.json.jbuilder +5 -0
- data/app/components/satis/card/component.css +2 -2
- data/app/components/satis/card/component.html.slim +1 -1
- data/app/components/satis/dropdown/component_controller.js +2 -5
- data/app/components/satis/editor/component.css +1 -1
- data/app/components/satis/sidebar_menu_item/component.css +4 -0
- data/app/components/satis/tabs/component.html.slim +1 -1
- data/app/controllers/satis/attachments_controller.rb +35 -0
- data/app/helpers/satis/application_helper.rb +0 -8
- data/app/javascript/satis/controllers/attachment_delete_controller.js +49 -0
- data/app/javascript/satis/controllers/attachment_upload_controller.js +109 -0
- data/app/javascript/satis/controllers/index.js +7 -0
- data/config/routes.rb +2 -0
- data/lib/satis/helpers/container.rb +1 -0
- data/lib/satis/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b4aa4a7fdd94865ec844e8389cee2d3d19009f817a48d48bdb5e44b1ab7d12f
|
|
4
|
+
data.tar.gz: 6b6eaa2df3376c21b24bd32ef981963c1a6c6a564f6a7ccb2f8ffa8fe51d699f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3bf96d40e2775fb1042a21be50b0796db96d424a8bd2414934e246675ac60bb8cdadd55b27cb82b232f9f6d0e1bd961e154cf9e44902f14732dbd747fd23cff
|
|
7
|
+
data.tar.gz: 8ebb4d890726d4e1e3d76b65650091bbafa273e7756c01835f30aec4157e1c488539cd04e971158e96aa7b7651f1c50264ac64cf0ed4daa0c638c8a16bd41d03
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
@import '../../../components/satis/appearance_switcher/component.css';
|
|
2
|
+
@import '../../../components/satis/attachments/component.css';
|
|
2
3
|
@import '../../../components/satis/breadcrumbs/component.css';
|
|
3
4
|
@import '../../../components/satis/card/component.css';
|
|
4
5
|
@import '../../../components/satis/call_to_action/component.css';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
.attachments__group {
|
|
2
|
+
@apply grid gap-6 mt-10 justify-start;
|
|
3
|
+
grid-template-columns: repeat(auto-fill, 200px);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.attachments__attachment {
|
|
7
|
+
@apply relative w-[200px] h-[200px] bg-cover bg-center bg-no-repeat rounded-lg shadow-md overflow-hidden;
|
|
8
|
+
@apply dark:bg-gray-900;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.attachments__attachment:hover {
|
|
12
|
+
@apply opacity-90;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.attachments__attachment .attachments__button {
|
|
16
|
+
@apply hidden absolute top-2.5 bg-white bg-opacity-80 p-1.5 rounded-full;
|
|
17
|
+
@apply dark:bg-gray-700 dark:text-white;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.attachments__attachment .attachments__button:first-of-type {
|
|
21
|
+
@apply left-2.5;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.attachments__attachment .attachments__button:last-of-type {
|
|
25
|
+
@apply right-2.5;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.attachments__attachment:hover .attachments__button {
|
|
29
|
+
@apply block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.attachments__attachment .attachments__filename {
|
|
33
|
+
@apply hidden absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 text-white p-1.5 text-center text-xs;
|
|
34
|
+
@apply dark:bg-gray-900 dark:bg-opacity-90;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.attachments__attachment:hover .attachments__filename {
|
|
38
|
+
@apply block;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.preview-text {
|
|
42
|
+
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white bg-opacity-70 p-2.5 rounded text-sm text-gray-800;
|
|
43
|
+
@apply dark:bg-gray-800 dark:bg-opacity-70 dark:text-gray-200;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (max-width: 640px) {
|
|
47
|
+
.attachments__group {
|
|
48
|
+
grid-template-columns: repeat(auto-fill, 150px);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.attachments__attachment {
|
|
52
|
+
@apply w-[150px] h-[150px];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.icon.uploading {
|
|
57
|
+
display: none; /* Ensure this is hidden by default */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.uploading .icon.uploading {
|
|
61
|
+
display: inline-block; /* Show only when uploading */
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.icon.upload {
|
|
65
|
+
display: inline-block; /* Ensure the upload icon is visible */
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.uploading .icon.upload {
|
|
69
|
+
display: none; /* Hide the upload icon during upload */
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.attachment-upload-button {
|
|
73
|
+
display: inline-block;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.upload-btn {
|
|
77
|
+
@apply dark:bg-gray-900 dark:text-white;
|
|
78
|
+
align-items: center;
|
|
79
|
+
padding: 10px 15px;
|
|
80
|
+
background-color: #f0f0f0;
|
|
81
|
+
border: 2px dashed #ccc;
|
|
82
|
+
border-radius: 5px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
transition: all 0.3s ease;
|
|
85
|
+
font-family: Arial, sans-serif;
|
|
86
|
+
color: #333;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.upload-btn:hover {
|
|
90
|
+
@apply dark:bg-gray-900 dark:text-white dark:border-gray-700;
|
|
91
|
+
background-color: #e0e0e0;
|
|
92
|
+
border-color: #999;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.upload-btn:focus {
|
|
96
|
+
outline: none;
|
|
97
|
+
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.upload-btn .icon {
|
|
101
|
+
margin-right: 10px;
|
|
102
|
+
font-size: 18px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.upload-btn .icon.upload {
|
|
106
|
+
color: #4a90e2;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.upload-btn .icon.uploading {
|
|
110
|
+
display: none;
|
|
111
|
+
color: #f39c12;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.uploading .upload-btn .icon.upload {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.uploading .upload-btn .icon.uploading {
|
|
119
|
+
display: inline-block;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.upload-btn .button-text {
|
|
123
|
+
font-size: 14px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.dragging .upload-btn {
|
|
127
|
+
background-color: #e8f0fe;
|
|
128
|
+
border-color: #4285f4;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.uploading .upload-btn {
|
|
132
|
+
@apply dark:bg-gray-800 dark:border-gray-600 dark:text-white;
|
|
133
|
+
background-color: #fcf8e3;
|
|
134
|
+
border-color: #f39c12;
|
|
135
|
+
cursor: not-allowed;
|
|
136
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
div.attachment-upload.upload-btn data-controller="attachment-upload" data-attachment-upload-url="#{Satis::Engine.routes.url_helpers.attachments_path(sgid: model_sgid, attribute: attribute)}" data-attachment-upload-param-name="attachments[]"data-attachment-upload-extra-data='{}' data-turbo="true" turbo-method="post"
|
|
2
|
+
span.icon.upload
|
|
3
|
+
i.fas.fa-upload
|
|
4
|
+
span.icon.uploading
|
|
5
|
+
i.fal.fa-circle-notch.fa-spin
|
|
6
|
+
span.button-text Drag or click to attach files
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
div.attachments__group
|
|
11
|
+
- attachments = model_has_images ? model.images : model.attachments
|
|
12
|
+
- attachments.each do |attachment|
|
|
13
|
+
div.attachments__attachment(style= attachment_style(attachment))
|
|
14
|
+
- unless attachment.representable?
|
|
15
|
+
div.preview-text
|
|
16
|
+
i.fas.fa-file(aria-hidden="true")
|
|
17
|
+
|
|
18
|
+
= link_to Satis::Engine.routes.url_helpers.attachment_path(attachment, sgid: model_sgid, attribute: attribute), data: {turbo_method: :delete, turbo_confirm: "Are you sure you want to delete this attachment?"}, class: 'attachments__button' do
|
|
19
|
+
i.fas.fa-xmark
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
= link_to Rails.application.routes.url_helpers.rails_blob_url(attachment, host: request.host + ":#{request.port}"), data: { turbo: true, turbolinks: false }, class: 'attachments__button', download: true do
|
|
23
|
+
i.fas.fa-download
|
|
24
|
+
|
|
25
|
+
span.attachments__filename
|
|
26
|
+
= attachment.filename
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Satis
|
|
2
|
+
module Attachments
|
|
3
|
+
class Component < Satis::ApplicationComponent
|
|
4
|
+
include Rails.application.routes.url_helpers
|
|
5
|
+
|
|
6
|
+
attr_reader :model, :attribute, :attachments_options
|
|
7
|
+
|
|
8
|
+
def initialize(model, attribute, **options)
|
|
9
|
+
super()
|
|
10
|
+
@model = model
|
|
11
|
+
@attribute = attribute
|
|
12
|
+
@attachments_options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def model_has_images
|
|
16
|
+
model.respond_to?(:images)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def attachment_style(attachment)
|
|
20
|
+
if attachment.representable?
|
|
21
|
+
url = attachment.representation(resize_to_limit: [200, 200]).processed.url
|
|
22
|
+
"background-image: url(#{url})"
|
|
23
|
+
else
|
|
24
|
+
"background-color: f0f0f0"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def model_sgid
|
|
30
|
+
@model.to_sgid(expires_in: nil, for: 'satis_attachments')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
@apply bg-white h-full sm:rounded-lg sm:shadow dark:bg-gray-800 overflow-hidden;
|
|
3
3
|
|
|
4
4
|
&__header {
|
|
5
|
-
@apply px-4 py-5 sm:px-6 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300;
|
|
5
|
+
@apply px-4 py-5 sm:px-6 dark:bg-gray-900 bg-white dark:border-gray-700 dark:text-gray-300;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
&__tabs {
|
|
9
|
-
@apply bg-white px-4 border-b border-gray-200 sm:px-5 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300;
|
|
9
|
+
@apply bg-white px-4 border-b border-gray-200 sm:px-5 bg-white dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300;
|
|
10
10
|
|
|
11
11
|
a:first {
|
|
12
12
|
@apply ml-4;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.sts-card data-controller="satis-tabs" data-satis-tabs-persist-value=persist data-satis-tabs-key-value=key id=identifier
|
|
2
2
|
- if header?
|
|
3
|
-
.sts-card__header class="#{tabs? ? '' : 'border-b border-gray-200'}
|
|
3
|
+
.sts-card__header class="#{tabs? ? '' : 'border-b border-gray-200'}"
|
|
4
4
|
.-ml-4.-mt-4.flex.justify-between.items-center.flex-wrap.sm:flex-nowrap
|
|
5
5
|
- if icon
|
|
6
6
|
.ml-4.mt-4.flex-shrink-0.text-primary-600.dark:text-gray-300
|
|
@@ -520,9 +520,7 @@ export default class DropdownComponentController extends ApplicationController {
|
|
|
520
520
|
let matches = []
|
|
521
521
|
this.itemTargets.forEach((item) => {
|
|
522
522
|
const text = item.getAttribute("data-satis-dropdown-item-text")
|
|
523
|
-
const matched = this.needsExactMatchValue ?
|
|
524
|
-
searchValue.localeCompare(text, undefined, {sensitivity: 'base'}) === 0:
|
|
525
|
-
new RegExp(searchValue, "i").test(text)
|
|
523
|
+
const matched = this.needsExactMatchValue ? searchValue.localeCompare(text, undefined, {sensitivity: 'base'}) === 0 : text.toLowerCase().includes(searchValue.toLowerCase())
|
|
526
524
|
|
|
527
525
|
const isHidden = item.classList.contains("hidden")
|
|
528
526
|
if (!isHidden) {
|
|
@@ -622,8 +620,7 @@ export default class DropdownComponentController extends ApplicationController {
|
|
|
622
620
|
this.itemTargets.forEach((item) => {
|
|
623
621
|
const text = item.getAttribute("data-satis-dropdown-item-text")
|
|
624
622
|
const matched = this.needsExactMatchValue
|
|
625
|
-
? searchValue.localeCompare(text, undefined, { sensitivity: "base" }) === 0
|
|
626
|
-
: new RegExp(searchValue, "i").test(text)
|
|
623
|
+
? searchValue.localeCompare(text, undefined, { sensitivity: "base" }) === 0 : text.toLowerCase().includes(searchValue.toLowerCase())
|
|
627
624
|
|
|
628
625
|
const isHidden = item.classList.contains("hidden")
|
|
629
626
|
if (!isHidden) {
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
display: none;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
.sidebar.close .sts-sidebar-menu-item:hover > .sts-sidebar-menu-item__link {
|
|
43
|
+
width: 40px;
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
|
|
43
47
|
.sidebar.close .sts-sidebar-menu-item [data-satis-sidebar-menu-item-target="submenu"] .sts-sidebar-menu-item{
|
|
44
48
|
display: none;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- tabs.each do |tab|
|
|
6
6
|
option selected=tab.selected? = ct(".#{tab.name}", scope: :tab)
|
|
7
7
|
.hidden.sm:block
|
|
8
|
-
.border-b.border-gray-200.dark:border-opacity-25.dark:bg-gray-900
|
|
8
|
+
.border-b.border-gray-200.bg-white.dark:border-opacity-25.dark:bg-gray-900
|
|
9
9
|
nav.sts-tabs__nav aria-label="Tabs"
|
|
10
10
|
- tabs.each.with_index do |tab, index|
|
|
11
11
|
- id = tab.id.present? ? tab.id : tab.name
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Satis
|
|
4
|
+
class AttachmentsController < ApplicationController
|
|
5
|
+
before_action :set_objects
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
@attachments = @model.public_send(@attachment_type)
|
|
10
|
+
render json: @attachments
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create
|
|
14
|
+
params[:attachments].each do |file|
|
|
15
|
+
@model.public_send(@attachment_type).attach(file)
|
|
16
|
+
end
|
|
17
|
+
redirect_to request.referer || root_path, notice: "Attachment created successfully."
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def destroy
|
|
22
|
+
attachment = @model.public_send(@attachment_type).find_by(id: params[:id])
|
|
23
|
+
attachment&.purge
|
|
24
|
+
|
|
25
|
+
redirect_to request.referer || root_path, notice: "Attachment deleted successfully."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def set_objects
|
|
31
|
+
@attachment_type = params[:attribute] || 'attachments'
|
|
32
|
+
@model = GlobalID::Locator.locate_signed(params[:sgid], for: 'satis_attachments')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -3,13 +3,5 @@ module Satis
|
|
|
3
3
|
def sts
|
|
4
4
|
@_satis_helpers_container ||= Satis::Helpers::Container.new(self)
|
|
5
5
|
end
|
|
6
|
-
|
|
7
|
-
def method_missing(method, *args, **kwargs, &block)
|
|
8
|
-
if method.to_s.ends_with?('_url') || method.to_s.ends_with?('_path') && main_app.respond_to?(method)
|
|
9
|
-
main_app.send(method, *args, **kwargs, &block)
|
|
10
|
-
else
|
|
11
|
-
super
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
6
|
end
|
|
15
7
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import AttachmentUploadController from "./attachment_upload_controller";
|
|
3
|
+
import { get } from '@rails/request.js'
|
|
4
|
+
|
|
5
|
+
/***
|
|
6
|
+
* Delete attachments controller
|
|
7
|
+
*
|
|
8
|
+
* Deletes an attachments
|
|
9
|
+
*/
|
|
10
|
+
export default class AttachmentDeleteController extends Controller {
|
|
11
|
+
connect() {}
|
|
12
|
+
|
|
13
|
+
delete(event) {
|
|
14
|
+
const self = this
|
|
15
|
+
|
|
16
|
+
event.stopPropagation()
|
|
17
|
+
event.preventDefault()
|
|
18
|
+
|
|
19
|
+
const formData = new FormData()
|
|
20
|
+
formData.append("_method", "DELETE")
|
|
21
|
+
|
|
22
|
+
fetch(self.element.getAttribute("href"), {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
|
|
26
|
+
},
|
|
27
|
+
body: formData,
|
|
28
|
+
})
|
|
29
|
+
.then(self.handleErrors)
|
|
30
|
+
.then((response) => {
|
|
31
|
+
window.location.reload(true);
|
|
32
|
+
response.json().then(function (data) {
|
|
33
|
+
let node = document.querySelector(data.selector)
|
|
34
|
+
if (node) {
|
|
35
|
+
node.innerHTML = data.html
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
.catch((error) => {
|
|
40
|
+
console.log(error)
|
|
41
|
+
})
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
handleErrors(response) {
|
|
46
|
+
if (!response.ok) throw new Error(response.status)
|
|
47
|
+
return response
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class AttachmentUploadController extends Controller {
|
|
4
|
+
static targets = ["input"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.createFileInput()
|
|
8
|
+
this.addEventListeners()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
createFileInput() {
|
|
12
|
+
const input = document.createElement("input")
|
|
13
|
+
input.setAttribute("name", this.data.get("param-name") || "file")
|
|
14
|
+
input.setAttribute("type", "file")
|
|
15
|
+
input.setAttribute("multiple", "multiple")
|
|
16
|
+
input.style.display = "none"
|
|
17
|
+
this.element.appendChild(input)
|
|
18
|
+
this.fileInput = input
|
|
19
|
+
|
|
20
|
+
if (!this.data.has("param-name")) {
|
|
21
|
+
console.warn(this.element, "has no data-upload-param attribute, uploads may not work")
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
addEventListeners() {
|
|
26
|
+
this.element.addEventListener("click", this.handleClick.bind(this))
|
|
27
|
+
this.fileInput.addEventListener("change", this.handleChange.bind(this))
|
|
28
|
+
this.element.addEventListener("dragover", this.handleDragOver.bind(this))
|
|
29
|
+
this.element.addEventListener("dragleave", this.handleDragLeave.bind(this))
|
|
30
|
+
this.element.addEventListener("dragenter", this.handleDragEnter.bind(this))
|
|
31
|
+
this.element.addEventListener("drop", this.handleDrop.bind(this))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
handleClick(event) {
|
|
35
|
+
event.preventDefault()
|
|
36
|
+
this.fileInput.click()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
handleChange(event) {
|
|
40
|
+
this.upload(event.target.files)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleDragOver(event) {
|
|
44
|
+
event.preventDefault()
|
|
45
|
+
this.element.classList.add("dragging")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
handleDragLeave(event) {
|
|
49
|
+
event.preventDefault()
|
|
50
|
+
this.element.classList.remove("dragging")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handleDragEnter(event) {
|
|
54
|
+
event.preventDefault()
|
|
55
|
+
this.element.classList.add("dragging")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
handleDrop(event) {
|
|
59
|
+
event.preventDefault()
|
|
60
|
+
this.element.classList.remove("dragging")
|
|
61
|
+
if (event.dataTransfer.files.length > 0) {
|
|
62
|
+
this.upload(event.dataTransfer.files)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
upload(files) {
|
|
67
|
+
// Only proceed if files are selected
|
|
68
|
+
if (files.length === 0) return
|
|
69
|
+
|
|
70
|
+
let formData = new FormData()
|
|
71
|
+
if (this.data.has("extra-data")) {
|
|
72
|
+
for (let [key, value] of Object.entries(JSON.parse(this.data.get("extra-data")))) {
|
|
73
|
+
formData.append(key, value)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < files.length; i++) {
|
|
78
|
+
formData.append(this.data.get("param-name"), files[i])
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.element.classList.add("uploading")
|
|
82
|
+
|
|
83
|
+
fetch(this.data.get("url"), {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
body: formData,
|
|
86
|
+
headers: {
|
|
87
|
+
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content,
|
|
88
|
+
'Accept': 'text/html, application/json'
|
|
89
|
+
},
|
|
90
|
+
redirect: 'follow' // Important: follow redirects
|
|
91
|
+
}).then((response) => {
|
|
92
|
+
// Check if the response is a redirect
|
|
93
|
+
if (response.type === 'opaqueredirect' || response.redirected) {
|
|
94
|
+
window.location.href = response.url
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (response.ok) {
|
|
99
|
+
this.element.classList.remove("uploading")
|
|
100
|
+
window.location.reload(true)
|
|
101
|
+
} else {
|
|
102
|
+
throw new Error(response.statusText)
|
|
103
|
+
}
|
|
104
|
+
}).catch((error) => {
|
|
105
|
+
console.log(error)
|
|
106
|
+
this.element.classList.remove("uploading")
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -56,6 +56,13 @@ application.register("satis-link", LinkController);
|
|
|
56
56
|
import FieldsForController from "satis/controllers/fields_for_controller";
|
|
57
57
|
application.register("satis-fields-for", FieldsForController);
|
|
58
58
|
|
|
59
|
+
import AttachmentUploadController from "satis/controllers/attachment_upload_controller";
|
|
60
|
+
application.register("satis-fields-for", AttachmentUploadController);
|
|
61
|
+
|
|
62
|
+
import AttachmentDeleteController from "satis/controllers/attachment_delete_controller";
|
|
63
|
+
application.register("satis-fields-for", AttachmentDeleteController);
|
|
64
|
+
|
|
65
|
+
|
|
59
66
|
import FormController from "satis/controllers/form_controller";
|
|
60
67
|
application.register("satis-form", FormController);
|
|
61
68
|
|
data/config/routes.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Satis::Engine.routes.draw do
|
|
2
2
|
resources :user_data, only: %i[show update]
|
|
3
3
|
resources :dialogs, only: %[show], constraints: { id: /[A-Za-z0-9\_\-\/]+/ }
|
|
4
|
+
resources :attachments, only: [:index, :create, :destroy]
|
|
4
5
|
|
|
5
6
|
unless Rails.env.production?
|
|
6
7
|
namespace :documentation do
|
|
@@ -15,3 +16,4 @@ Satis::Engine.routes.draw do
|
|
|
15
16
|
resources :documentation
|
|
16
17
|
end
|
|
17
18
|
end
|
|
19
|
+
|
|
@@ -20,6 +20,7 @@ module Satis
|
|
|
20
20
|
add_helper :menu, Satis::Menu::Component
|
|
21
21
|
add_helper :page, Satis::Page::Component
|
|
22
22
|
add_helper :sidebar_menu, Satis::SidebarMenu::Component
|
|
23
|
+
add_helper :attachments, Satis::Attachments::Component
|
|
23
24
|
add_helper :tabs, Satis::Tabs::Component
|
|
24
25
|
add_helper :input, Satis::Input::Component
|
|
25
26
|
add_helper :progress_bar, Satis::ProgressBar::Component
|
data/lib/satis/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: satis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.47
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom de Grunt
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-01-
|
|
11
|
+
date: 2025-01-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: browser
|
|
@@ -751,6 +751,10 @@ files:
|
|
|
751
751
|
- app/components/satis/appearance_switcher/component.rb
|
|
752
752
|
- app/components/satis/appearance_switcher/component_controller.js
|
|
753
753
|
- app/components/satis/application_component.rb
|
|
754
|
+
- app/components/satis/attachments/component.css
|
|
755
|
+
- app/components/satis/attachments/component.html.slim
|
|
756
|
+
- app/components/satis/attachments/component.rb
|
|
757
|
+
- app/components/satis/attachments/create.json.jbuilder
|
|
754
758
|
- app/components/satis/avatar/component.html.slim
|
|
755
759
|
- app/components/satis/avatar/component.rb
|
|
756
760
|
- app/components/satis/breadcrumbs/component.css
|
|
@@ -853,6 +857,7 @@ files:
|
|
|
853
857
|
- app/components/satis/tabs/component.rb
|
|
854
858
|
- app/components/satis/tabs/component_controller.js
|
|
855
859
|
- app/controllers/satis/application_controller.rb
|
|
860
|
+
- app/controllers/satis/attachments_controller.rb
|
|
856
861
|
- app/controllers/satis/dialogs_controller.rb
|
|
857
862
|
- app/controllers/satis/documentation/avatars_controller.rb
|
|
858
863
|
- app/controllers/satis/documentation/cards_controller.rb
|
|
@@ -866,6 +871,8 @@ files:
|
|
|
866
871
|
- app/javascript/satis/application.js
|
|
867
872
|
- app/javascript/satis/controllers/application.js
|
|
868
873
|
- app/javascript/satis/controllers/application_controller.js
|
|
874
|
+
- app/javascript/satis/controllers/attachment_delete_controller.js
|
|
875
|
+
- app/javascript/satis/controllers/attachment_upload_controller.js
|
|
869
876
|
- app/javascript/satis/controllers/fields_for_controller.js
|
|
870
877
|
- app/javascript/satis/controllers/file_controller.js
|
|
871
878
|
- app/javascript/satis/controllers/form_controller.js
|