lato 0.1.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/MIT-LICENSE +20 -0
- data/README.md +95 -0
- data/Rakefile +8 -0
- data/app/assets/config/lato_manifest.js +5 -0
- data/app/assets/images/lato/user-150x150.jpg +0 -0
- data/app/assets/images/lato/user-300x300.jpg +0 -0
- data/app/assets/images/lato/user-600x600.jpg +0 -0
- data/app/assets/images/lato/user-900x900.jpg +0 -0
- data/app/assets/javascripts/lato/application.js +6 -0
- data/app/assets/javascripts/lato/controllers/application.js +9 -0
- data/app/assets/javascripts/lato/controllers/index.js +11 -0
- data/app/assets/javascripts/lato/controllers/lato_action_controller.js +92 -0
- data/app/assets/javascripts/lato/controllers/lato_aside_opener_controller.js +21 -0
- data/app/assets/javascripts/lato/controllers/lato_feedback_controller.js +13 -0
- data/app/assets/javascripts/lato/controllers/lato_form_controller.js +60 -0
- data/app/assets/javascripts/lato/controllers/lato_hello_controller.js +12 -0
- data/app/assets/javascripts/lato/controllers/lato_operation_controller.js +24 -0
- data/app/assets/stylesheets/lato/application.scss +73 -0
- data/app/controllers/concerns/lato/componentable.rb +52 -0
- data/app/controllers/concerns/lato/layoutable.rb +27 -0
- data/app/controllers/concerns/lato/sessionable.rb +45 -0
- data/app/controllers/lato/account_controller.rb +82 -0
- data/app/controllers/lato/application_controller.rb +21 -0
- data/app/controllers/lato/authentication_controller.rb +121 -0
- data/app/controllers/lato/operations_controller.rb +23 -0
- data/app/helpers/lato/application_helper.rb +5 -0
- data/app/helpers/lato/components_helper.rb +183 -0
- data/app/jobs/lato/application_job.rb +59 -0
- data/app/mailers/lato/application_mailer.rb +6 -0
- data/app/mailers/lato/user_mailer.rb +23 -0
- data/app/models/lato/application_record.rb +5 -0
- data/app/models/lato/operation.rb +96 -0
- data/app/models/lato/session.rb +36 -0
- data/app/models/lato/user.rb +164 -0
- data/app/views/lato/account/_alert-accepted-privacy-policy-version.html.erb +20 -0
- data/app/views/lato/account/_alert-accepted-terms-and-conditions-version.html.erb +20 -0
- data/app/views/lato/account/_form-destroy.html.erb +21 -0
- data/app/views/lato/account/_form-password.html.erb +28 -0
- data/app/views/lato/account/_form-user.html.erb +40 -0
- data/app/views/lato/account/index.html.erb +65 -0
- data/app/views/lato/authentication/_form-recover-password.html.erb +20 -0
- data/app/views/lato/authentication/_form-signin.html.erb +31 -0
- data/app/views/lato/authentication/_form-signup.html.erb +47 -0
- data/app/views/lato/authentication/_form-update-password.html.erb +30 -0
- data/app/views/lato/authentication/_form-verify-email.html.erb +22 -0
- data/app/views/lato/authentication/recover_password.html.erb +13 -0
- data/app/views/lato/authentication/signin.html.erb +13 -0
- data/app/views/lato/authentication/signout.html.erb +11 -0
- data/app/views/lato/authentication/signup.html.erb +13 -0
- data/app/views/lato/authentication/update_password.html.erb +13 -0
- data/app/views/lato/authentication/verify_email.html.erb +7 -0
- data/app/views/lato/components/_index.html.erb +89 -0
- data/app/views/lato/components/_navbar_nav_item.html.erb +5 -0
- data/app/views/lato/components/_operation.html.erb +68 -0
- data/app/views/lato/components/_page_head.html.erb +15 -0
- data/app/views/lato/components/_sidebar_nav_item.html.erb +5 -0
- data/app/views/lato/mailer/user/email_verification_mail.html.erb +9 -0
- data/app/views/lato/mailer/user/password_update_mail.html.erb +9 -0
- data/app/views/lato/operations/show.html.erb +3 -0
- data/app/views/layouts/lato/_action.html.erb +17 -0
- data/app/views/layouts/lato/_aside-opener.html.erb +7 -0
- data/app/views/layouts/lato/_content.html.erb +5 -0
- data/app/views/layouts/lato/_feedbacks.html.erb +16 -0
- data/app/views/layouts/lato/_footer.html.erb +11 -0
- data/app/views/layouts/lato/_mailer-foot_content.html.erb +2 -0
- data/app/views/layouts/lato/_mailer-head_content.html.erb +2 -0
- data/app/views/layouts/lato/_navbar-brand_content.html.erb +1 -0
- data/app/views/layouts/lato/_navbar-nav_content.html.erb +15 -0
- data/app/views/layouts/lato/_navbar.html.erb +16 -0
- data/app/views/layouts/lato/_sidebar-nav_content.html.erb +3 -0
- data/app/views/layouts/lato/_sidebar.html.erb +5 -0
- data/app/views/layouts/lato/application.html.erb +44 -0
- data/app/views/layouts/lato/mailer.html.erb +3 -0
- data/config/importmap.rb +8 -0
- data/config/locales/it.yml +235 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20221022205744_create_lato_users.rb +14 -0
- data/db/migrate/20221118072130_create_lato_operations.rb +14 -0
- data/lib/lato/btstrap.rb +36 -0
- data/lib/lato/config.rb +39 -0
- data/lib/lato/engine.rb +13 -0
- data/lib/lato/version.rb +3 -0
- data/lib/lato.rb +27 -0
- data/lib/tasks/lato_tasks.rake +28 -0
- metadata +199 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0d0fcbdd5ba0523af03858edd0646045fabe36a832f39c3420c60745d4794ef6
|
|
4
|
+
data.tar.gz: 6debf36698efb7f3e2a64963b22679725c0db5f0561bf79c0f39aa1b017e8705
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ad25978acd813be6f01c18d48e5c4baeb6976770d99206175c0ba2ea4244a8e1b50a6f2943bc405a29921f64b24c3cfbee4068970b0ce9e8cd9bc681336d6adc
|
|
7
|
+
data.tar.gz: '0735888ffc4bbfcd489729f684e558affa2b2843e10cdb2aeebb8f442c7a0f3159b0f7319f8180a3dc9255a588a1fed406adc38063f1f9de4b9b419899d8276a'
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2022 Gregorio Galante
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Lato
|
|
2
|
+
Basic engine for all Lato projects.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
Add required dependencies to your application's Gemfile:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
|
|
9
|
+
gem "importmap-rails" # NOTE: Probably already installed in default rails 7 project
|
|
10
|
+
|
|
11
|
+
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
|
|
12
|
+
gem "turbo-rails" # NOTE: Probably already installed in default rails 7 project
|
|
13
|
+
|
|
14
|
+
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
|
|
15
|
+
gem "stimulus-rails" # NOTE: Probably already installed in default rails 7 project
|
|
16
|
+
|
|
17
|
+
# Use Kredis as Redis interface [https://github.com/rails/kredis]
|
|
18
|
+
# NOTE: Installation -> https://github.com/rails/kredis#installation
|
|
19
|
+
gem "kredis"
|
|
20
|
+
|
|
21
|
+
# Use Sass to process CSS
|
|
22
|
+
gem "sassc-rails"
|
|
23
|
+
|
|
24
|
+
# Use lato as application panel
|
|
25
|
+
gem "lato"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Install gem and run required tasks:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
$ bundle
|
|
32
|
+
$ rails active_storage:install
|
|
33
|
+
$ rails lato:install:application
|
|
34
|
+
$ rails lato:install:migrations
|
|
35
|
+
$ rails db:migrate
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Mount lato routes on the **config/routes.rb** file:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
Rails.application.routes.draw do
|
|
42
|
+
mount Lato::Engine => "/lato"
|
|
43
|
+
|
|
44
|
+
# ....
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Import Lato Scss on **app/assets/stylesheets/application.scss** file:
|
|
49
|
+
```scss
|
|
50
|
+
@import 'lato/application';
|
|
51
|
+
|
|
52
|
+
// ....
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Import Lato Js on **app/javascript/application.js** file:
|
|
56
|
+
```js
|
|
57
|
+
import "lato/application";
|
|
58
|
+
|
|
59
|
+
// ....
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Setup italian locale to the application (currently Lato works with IT locale) on the **config/application.rb** file:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
module MyApplication
|
|
66
|
+
class Application < Rails::Application
|
|
67
|
+
config.i18n.available_locales = [:it]
|
|
68
|
+
config.i18n.default_locale = :it
|
|
69
|
+
|
|
70
|
+
# ...
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
Be sure to have Redis locally installed and running.
|
|
79
|
+
|
|
80
|
+
Clone repository, install dependencies, run migrations and start:
|
|
81
|
+
|
|
82
|
+
```shell
|
|
83
|
+
$ git clone https://github.com/Lato-GAM/lato
|
|
84
|
+
$ cd lato
|
|
85
|
+
$ bundle
|
|
86
|
+
$ rails db:migrate
|
|
87
|
+
$ foreman start -f Procfile.dev
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
92
|
+
|
|
93
|
+
## To do
|
|
94
|
+
- Gestione abbonamenti e acquisti singoli con integrazione Stripe
|
|
95
|
+
- Gestione input file operazione
|
data/Rakefile
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Import and register all your controllers from the importmap under controllers/*
|
|
2
|
+
|
|
3
|
+
import { application } from "controllers/application"
|
|
4
|
+
|
|
5
|
+
// Eager load all controllers defined in the import map under controllers/**/*_controller
|
|
6
|
+
// import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
|
|
7
|
+
// eagerLoadControllersFrom("controllers", application)
|
|
8
|
+
|
|
9
|
+
// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
|
|
10
|
+
import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
|
|
11
|
+
lazyLoadControllersFrom("controllers", application)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = [
|
|
5
|
+
'trigger',
|
|
6
|
+
'modal',
|
|
7
|
+
'modalBody',
|
|
8
|
+
'modalTitle',
|
|
9
|
+
'modalDialog'
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Stimulus
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
connect() {
|
|
17
|
+
this.bsModals = []
|
|
18
|
+
this.usedModals = []
|
|
19
|
+
|
|
20
|
+
this.modalTargets.forEach((modalTarget, index) => {
|
|
21
|
+
this.bsModals.push(new bootstrap.Modal(modalTarget))
|
|
22
|
+
|
|
23
|
+
modalTarget.addEventListener('show.bs.modal', () => {
|
|
24
|
+
this.usedModals.push(index)
|
|
25
|
+
})
|
|
26
|
+
modalTarget.addEventListener('hide.bs.modal', () => {
|
|
27
|
+
this.usedModals = this.usedModals.filter((i) => i != index)
|
|
28
|
+
this.modalBodyTargets[index].innerHTML = ''
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
disconnect() {
|
|
34
|
+
this.bsModals.forEach((bsModal) => {
|
|
35
|
+
bsModal.dispose()
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
triggerTargetConnected(element) {
|
|
40
|
+
element.addEventListener('click', (e) => this.openAction(this.loadOptionsFromTriggerElement(element)))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Functions
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
openAction(options) {
|
|
48
|
+
const index = this.getFreeModalIndex()
|
|
49
|
+
|
|
50
|
+
if (options.turboFrame && options.turboFrame != '_top') {
|
|
51
|
+
this.modalBodyTargets[index].innerHTML = `<turbo-frame id="${options.turboFrame}"></turbo-frame>`
|
|
52
|
+
|
|
53
|
+
if (options.actionTitle) {
|
|
54
|
+
this.modalTitleTargets[index].innerHTML = options.actionTitle
|
|
55
|
+
} else {
|
|
56
|
+
this.modalTitleTargets[index].innerHTML = ''
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.actionSize) {
|
|
60
|
+
this.modalDialogTargets[index].classList.add(`modal-${options.actionSize}`)
|
|
61
|
+
} else {
|
|
62
|
+
this.modalDialogTargets[index].classList.remove('modal-lg')
|
|
63
|
+
this.modalDialogTargets[index].classList.remove('modal-xl')
|
|
64
|
+
this.modalDialogTargets[index].classList.remove('modal-sm')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.bsModals[index].show()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
loadOptionsFromTriggerElement(element) {
|
|
72
|
+
const options = {}
|
|
73
|
+
options.turboFrame = element.getAttribute('data-turbo-frame')
|
|
74
|
+
options.actionTitle = element.getAttribute('data-action-title')
|
|
75
|
+
options.actionSize = element.getAttribute('data-action-size')
|
|
76
|
+
return options
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getFreeModalIndex() {
|
|
80
|
+
for (let i = 0; i < this.modalTargets.length; i++) {
|
|
81
|
+
if (!this.usedModals.includes(i)) return i
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Actions
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
onTriggerClick(e) {
|
|
90
|
+
this.openAction(this.loadOptionsFromTriggerElement(e.currentTarget))
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Functions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
toggle() {
|
|
10
|
+
document.body.classList.toggle('aside-open')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Actions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
onClickToggle(e) {
|
|
18
|
+
e.preventDefault()
|
|
19
|
+
document.body.classList.toggle('aside-open')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = [
|
|
5
|
+
'submit',
|
|
6
|
+
'input'
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Stimulus
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
connect() {
|
|
14
|
+
this.originalFormData = this.loadFormData()
|
|
15
|
+
this.disableSubmit()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Functions
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
disableSubmit() {
|
|
23
|
+
if (!this.hasSubmitTarget) return
|
|
24
|
+
this.submitTarget.setAttribute('disabled', true)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
enableSubmit() {
|
|
28
|
+
if (!this.hasSubmitTarget) return
|
|
29
|
+
this.submitTarget.removeAttribute('disabled')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
loadFormData() {
|
|
33
|
+
const formData = {}
|
|
34
|
+
|
|
35
|
+
this.inputTargets.forEach((input) => {
|
|
36
|
+
if (input.type == 'checkbox') {
|
|
37
|
+
formData[input.name] = input.checked
|
|
38
|
+
} else {
|
|
39
|
+
formData[input.name] = input.value
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return formData
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Actions
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
onInputChange(e) {
|
|
51
|
+
e.target.classList.remove('is-invalid')
|
|
52
|
+
|
|
53
|
+
const formData = this.loadFormData()
|
|
54
|
+
if (JSON.stringify(formData) != JSON.stringify(this.originalFormData)) {
|
|
55
|
+
this.enableSubmit()
|
|
56
|
+
} else {
|
|
57
|
+
this.disableSubmit()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = [
|
|
5
|
+
'update',
|
|
6
|
+
'outputFile'
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Stimulus
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
connect() {
|
|
14
|
+
if (this.hasUpdateTarget) {
|
|
15
|
+
setTimeout(() => {
|
|
16
|
+
this.updateTarget.click()
|
|
17
|
+
}, 2000)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
outputFileTargetConnected(element) {
|
|
22
|
+
element.click()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Import dependencies */
|
|
2
|
+
|
|
3
|
+
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css");
|
|
4
|
+
@import "bootstrap";
|
|
5
|
+
|
|
6
|
+
/** Turbo loading **/
|
|
7
|
+
|
|
8
|
+
.turbo-progress-bar {
|
|
9
|
+
background-color: $primary;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Pagination (minimal style for gem kaminari) */
|
|
13
|
+
|
|
14
|
+
.pagination {}
|
|
15
|
+
|
|
16
|
+
.pagination > span {
|
|
17
|
+
display: inline-block;
|
|
18
|
+
margin: 0 4px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.pagination .current {
|
|
22
|
+
font-weight: bold;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Custom style */
|
|
26
|
+
|
|
27
|
+
main {
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main, aside {
|
|
32
|
+
margin-top: 54px;
|
|
33
|
+
min-height: calc(100vh - 54px);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.aside-opener {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media screen and (max-width: 768px) {
|
|
41
|
+
aside {
|
|
42
|
+
position: fixed;
|
|
43
|
+
bottom: 0;
|
|
44
|
+
left: 0;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: calc(100% - 54px);
|
|
47
|
+
z-index: 99;
|
|
48
|
+
transform: translateX(-100%);
|
|
49
|
+
transition: transform 0.35s ease-in-out;
|
|
50
|
+
|
|
51
|
+
.aside-open & {
|
|
52
|
+
transform: translateX(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.aside-opener {
|
|
57
|
+
display: flex;
|
|
58
|
+
position: fixed;
|
|
59
|
+
bottom: 15px;
|
|
60
|
+
right: 15px;
|
|
61
|
+
z-index: 100;
|
|
62
|
+
background-color: $primary;
|
|
63
|
+
color: $white;
|
|
64
|
+
border: none;
|
|
65
|
+
padding: 0;
|
|
66
|
+
width: 60px;
|
|
67
|
+
height: 60px;
|
|
68
|
+
font-size: 20px;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
align-items: center;
|
|
71
|
+
border-radius: 50%;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Lato
|
|
2
|
+
module Componentable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
def lato_index_collection(collection, options = {})
|
|
6
|
+
# load options
|
|
7
|
+
# NOTE: instance variables are for options used by "lato_index" component helper
|
|
8
|
+
key = options[:key] || 'default'
|
|
9
|
+
pagination = options[:pagination] || false
|
|
10
|
+
@_lato_index ||= {}
|
|
11
|
+
@_lato_index[key] = {
|
|
12
|
+
columns: options[:columns] || collection.column_names || [],
|
|
13
|
+
sortable_columns: options[:sortable_columns] || [],
|
|
14
|
+
searchable_columns: options[:searchable_columns] || []
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# manage sort by parameter
|
|
18
|
+
unless params[:sort_by].blank?
|
|
19
|
+
sort_by_splitted = params[:sort_by].split('|')
|
|
20
|
+
sort_by_column = sort_by_splitted.first
|
|
21
|
+
sort_by_order = sort_by_splitted.last
|
|
22
|
+
|
|
23
|
+
if collection.respond_to?(:lato_index_order)
|
|
24
|
+
collection = collection.lato_index_order(sort_by_column.to_sym, sort_by_order.to_sym)
|
|
25
|
+
else
|
|
26
|
+
collection = collection.order("#{sort_by_column} #{sort_by_order}")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# manage search by parameter
|
|
31
|
+
unless params[:search].blank?
|
|
32
|
+
search = params[:search].to_s
|
|
33
|
+
if collection.respond_to?(:lato_index_search)
|
|
34
|
+
collection = collection.lato_index_search(search)
|
|
35
|
+
else
|
|
36
|
+
query = @_lato_index[key][:searchable_columns].map { |k| "lower(#{k}) LIKE :search" }
|
|
37
|
+
collection = collection.where(query.join(' OR '), search: "%#{search.downcase.strip}%")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# manage pagination
|
|
42
|
+
if pagination || params[:page] || params[:per_page]
|
|
43
|
+
page = params[:page]&.to_i || 1
|
|
44
|
+
per_page = params[:per_page]&.to_i || 25
|
|
45
|
+
per_page = 100 if per_page > 100
|
|
46
|
+
collection = collection.page(page).per(per_page)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
collection
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Lato
|
|
2
|
+
module Layoutable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action do
|
|
7
|
+
show_sidebar
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def show_sidebar
|
|
12
|
+
@layout_sidebar = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def hide_sidebar
|
|
16
|
+
@layout_sidebar = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def active_sidebar(key)
|
|
20
|
+
@sidebar_key = key
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def active_navbar(key)
|
|
24
|
+
@navbar_key = key
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Lato
|
|
2
|
+
module Sessionable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action do
|
|
7
|
+
@session = Lato::Session.new(cookies.encrypted[:lato_session])
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def authenticate_session
|
|
12
|
+
return true if @session.valid?
|
|
13
|
+
|
|
14
|
+
respond_to do |format|
|
|
15
|
+
format.html { redirect_to lato.root_path }
|
|
16
|
+
format.json { render plain: '', status: :unauthorized }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def not_authenticate_session
|
|
23
|
+
return true unless @session.valid?
|
|
24
|
+
|
|
25
|
+
respond_to do |format|
|
|
26
|
+
format.html { redirect_to lato.root_path }
|
|
27
|
+
format.json { render plain: '', status: :unauthorized }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def session_create(user_id)
|
|
34
|
+
cookies.encrypted[:lato_session] = { value: Lato::Session.generate_session_per_user(user_id), expires: Lato.config.session_lifetime.from_now }
|
|
35
|
+
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def session_destroy
|
|
40
|
+
cookies.encrypted[:lato_session] = nil
|
|
41
|
+
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Lato
|
|
2
|
+
class AccountController < ApplicationController
|
|
3
|
+
before_action :authenticate_session
|
|
4
|
+
before_action { active_navbar(:account) }
|
|
5
|
+
|
|
6
|
+
def index; end
|
|
7
|
+
|
|
8
|
+
def update_user_action
|
|
9
|
+
respond_to do |format|
|
|
10
|
+
if @session.user.update(params.require(:user).permit(:first_name, :last_name, :email))
|
|
11
|
+
format.html { redirect_to lato.account_path, notice: 'Informazioni account aggiornate correttamente' }
|
|
12
|
+
format.json { render json: @session.user }
|
|
13
|
+
else
|
|
14
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
15
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def request_verify_email_action
|
|
21
|
+
respond_to do |format|
|
|
22
|
+
if @session.user.request_verify_email
|
|
23
|
+
format.html { redirect_to lato.account_path, notice: 'Ti abbiamo inviato una email con i passaggi da seguire per completare la procedura' }
|
|
24
|
+
format.json { render json: @session.user }
|
|
25
|
+
else
|
|
26
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
27
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def update_password_action
|
|
33
|
+
respond_to do |format|
|
|
34
|
+
if @session.user.update(params.require(:user).permit(:password, :password_confirmation))
|
|
35
|
+
format.html { redirect_to lato.account_path, notice: 'Password aggiornate correttamente' }
|
|
36
|
+
format.json { render json: @session.user }
|
|
37
|
+
else
|
|
38
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
39
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def destroy_action
|
|
45
|
+
respond_to do |format|
|
|
46
|
+
if @session.user.destroy_with_confirmation(params.require(:user).permit(:email_confirmation))
|
|
47
|
+
session_destroy
|
|
48
|
+
|
|
49
|
+
format.html { redirect_to lato.root_path }
|
|
50
|
+
format.json { render json: {} }
|
|
51
|
+
else
|
|
52
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
53
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_accepted_privacy_policy_version_action
|
|
59
|
+
respond_to do |format|
|
|
60
|
+
if @session.user.update(params.require(:user).permit(:accepted_privacy_policy_version))
|
|
61
|
+
format.html { redirect_to lato.account_path }
|
|
62
|
+
format.json { render json: @session.user }
|
|
63
|
+
else
|
|
64
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
65
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def update_accepted_terms_and_conditions_version_action
|
|
71
|
+
respond_to do |format|
|
|
72
|
+
if @session.user.update(params.require(:user).permit(:accepted_terms_and_conditions_version))
|
|
73
|
+
format.html { redirect_to lato.account_path }
|
|
74
|
+
format.json { render json: @session.user }
|
|
75
|
+
else
|
|
76
|
+
format.html { render :index, status: :unprocessable_entity }
|
|
77
|
+
format.json { render json: @session.user.errors, status: :unprocessable_entity }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|