open_fresk 0.1.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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +64 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +248 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +130 -0
  7. data/Rakefile +8 -0
  8. data/app/.DS_Store +0 -0
  9. data/app/assets/.DS_Store +0 -0
  10. data/app/assets/config/manifest.js +20 -0
  11. data/app/assets/images/open_fresk/.keep +0 -0
  12. data/app/assets/images/open_fresk/favicon.png +0 -0
  13. data/app/assets/images/open_fresk/logos/logo.png +0 -0
  14. data/app/assets/stylesheets/open_fresk/application.scss +90 -0
  15. data/app/assets/stylesheets/open_fresk/components/_accordion.scss +3 -0
  16. data/app/assets/stylesheets/open_fresk/components/_btn.scss +25 -0
  17. data/app/assets/stylesheets/open_fresk/components/_btn_toggle.scss +16 -0
  18. data/app/assets/stylesheets/open_fresk/components/_card.scss +6 -0
  19. data/app/assets/stylesheets/open_fresk/components/_checkout.scss +142 -0
  20. data/app/assets/stylesheets/open_fresk/components/_cursors.scss +35 -0
  21. data/app/assets/stylesheets/open_fresk/components/_dropdown_toggle.scss +9 -0
  22. data/app/assets/stylesheets/open_fresk/components/_form.scss +22 -0
  23. data/app/assets/stylesheets/open_fresk/components/_google_maps_autocomplete.scss +3 -0
  24. data/app/assets/stylesheets/open_fresk/components/_hamburger_button.scss +41 -0
  25. data/app/assets/stylesheets/open_fresk/components/_header.scss +57 -0
  26. data/app/assets/stylesheets/open_fresk/components/_iframe.scss +26 -0
  27. data/app/assets/stylesheets/open_fresk/components/_index.scss +22 -0
  28. data/app/assets/stylesheets/open_fresk/components/_nav_pills.scss +26 -0
  29. data/app/assets/stylesheets/open_fresk/components/_nps_progress_bar.scss +97 -0
  30. data/app/assets/stylesheets/open_fresk/components/_pagination.scss +11 -0
  31. data/app/assets/stylesheets/open_fresk/components/_progress_bar.scss +17 -0
  32. data/app/assets/stylesheets/open_fresk/components/_radio_button.scss +25 -0
  33. data/app/assets/stylesheets/open_fresk/components/_range.scss +253 -0
  34. data/app/assets/stylesheets/open_fresk/components/_select_input.scss +9 -0
  35. data/app/assets/stylesheets/open_fresk/components/_slider.scss +61 -0
  36. data/app/assets/stylesheets/open_fresk/components/_slim_select.scss +31 -0
  37. data/app/assets/stylesheets/open_fresk/components/_timeline.scss +165 -0
  38. data/app/assets/stylesheets/open_fresk/components/_tooltip.scss +20 -0
  39. data/app/assets/stylesheets/open_fresk/components/_transformation.scss +4 -0
  40. data/app/assets/stylesheets/open_fresk/config/_belts.scss +38 -0
  41. data/app/assets/stylesheets/open_fresk/config/_bg.scss +6 -0
  42. data/app/assets/stylesheets/open_fresk/config/_border.scss +15 -0
  43. data/app/assets/stylesheets/open_fresk/config/_colors.scss +42 -0
  44. data/app/assets/stylesheets/open_fresk/config/_index.scss +6 -0
  45. data/app/assets/stylesheets/open_fresk/config/_text.scss +24 -0
  46. data/app/assets/stylesheets/open_fresk/config/_typography.scss +26 -0
  47. data/app/assets/stylesheets/open_fresk/external_libraries/_actiontext.scss +30 -0
  48. data/app/assets/stylesheets/open_fresk/external_libraries/_slimselect.min.scss +1 -0
  49. data/app/assets/stylesheets/open_fresk/external_libraries/index.scss +2 -0
  50. data/app/assets/stylesheets/open_fresk/pages/_index.scss +1 -0
  51. data/app/assets/stylesheets/open_fresk/pages/_signin.scss +10 -0
  52. data/app/assets/stylesheets-original/open_fresk/application.css +90 -0
  53. data/app/controllers/.DS_Store +0 -0
  54. data/app/controllers/concerns/.keep +0 -0
  55. data/app/controllers/concerns/open_fresk/authentication.rb +18 -0
  56. data/app/controllers/open_fresk/application_controller.rb +5 -0
  57. data/app/controllers/open_fresk/sessions_controller.rb +38 -0
  58. data/app/controllers/open_fresk/training_sessions_controller.rb +17 -0
  59. data/app/extensions/open_fresk/core_extensions/string.rb +13 -0
  60. data/app/helpers/header_helper.rb +2 -0
  61. data/app/helpers/open_fresk/application_helper.rb +18 -0
  62. data/app/helpers/plateform_access/rights_helper.rb +15 -0
  63. data/app/javascript/manifest.json +15 -0
  64. data/app/javascript/open_fresk/application.js +7 -0
  65. data/app/javascript/open_fresk/controllers/hello_controller.js +7 -0
  66. data/app/jobs/open_fresk/application_job.rb +4 -0
  67. data/app/mailers/open_fresk/application_mailer.rb +6 -0
  68. data/app/models/.DS_Store +0 -0
  69. data/app/models/concerns/.keep +0 -0
  70. data/app/models/open_fresk/application_record.rb +10 -0
  71. data/app/models/open_fresk/training_session.rb +9 -0
  72. data/app/models/open_fresk/user.rb +9 -0
  73. data/app/services/open_fresk/extensions/string_enum.rb +201 -0
  74. data/app/views/layouts/header/_hamburger.html.erb +7 -0
  75. data/app/views/layouts/header/_logo.html.erb +5 -0
  76. data/app/views/layouts/open_fresk/_alert.html.erb +0 -0
  77. data/app/views/layouts/open_fresk/_footer.html.erb +0 -0
  78. data/app/views/layouts/open_fresk/_header.html.erb +12 -0
  79. data/app/views/layouts/open_fresk/_include.html.erb +24 -0
  80. data/app/views/layouts/open_fresk/_meta.html.erb +10 -0
  81. data/app/views/layouts/open_fresk/application.html.erb +23 -0
  82. data/app/views/open_fresk/sessions/_login.html.erb +16 -0
  83. data/app/views/open_fresk/sessions/new.html.erb +8 -0
  84. data/app/views/open_fresk/training_sessions/_card.html.erb +0 -0
  85. data/app/views/open_fresk/training_sessions/index.html.erb +22 -0
  86. data/bin/rails +14 -0
  87. data/config/importmap.rb +6 -0
  88. data/config/locales/en.yml +15 -0
  89. data/config/locales/fr.yml +15 -0
  90. data/config/routes.rb +8 -0
  91. data/lib/.DS_Store +0 -0
  92. data/lib/open_fresk/engine.rb +93 -0
  93. data/lib/open_fresk/version.rb +3 -0
  94. data/lib/open_fresk.rb +6 -0
  95. data/lib/tasks/open_fresk_tasks.rake +4 -0
  96. data/open_fresk.gemspec +29 -0
  97. metadata +250 -0
@@ -0,0 +1,30 @@
1
+ /*
2
+ * Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
3
+ * the trix-editor content (whether displayed or under editing). Feel free to incorporate this
4
+ * inclusion directly in any other asset bundle and remove this file.
5
+ *
6
+ */
7
+
8
+ /*
9
+ * We need to override trix.css’s image gallery styles to accommodate the
10
+ * <action-text-attachment> element we wrap around attachments. Otherwise,
11
+ * images in galleries will be squished by the max-width: 33%; rule.
12
+ */
13
+ .trix-content .attachment-gallery > action-text-attachment,
14
+ .trix-content .attachment-gallery > .attachment {
15
+ flex: 1 0 33%;
16
+ padding: 0 0.5em;
17
+ max-width: 33%;
18
+ }
19
+
20
+ .trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
21
+ .trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
22
+ .trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
23
+ flex-basis: 50%;
24
+ max-width: 50%;
25
+ }
26
+
27
+ .trix-content action-text-attachment .attachment {
28
+ padding: 0 !important;
29
+ max-width: 100% !important;
30
+ }
@@ -0,0 +1 @@
1
+ :root{--ss-primary-color:#5897fb;--ss-bg-color:#ffffff;--ss-font-color:#4d4d4d;--ss-font-placeholder-color:#8d8d8d;--ss-disabled-color:#dcdee2;--ss-border-color:#dcdee2;--ss-highlight-color:#fffb8c;--ss-success-color:#00b755;--ss-error-color:#dc3545;--ss-main-height:30px;--ss-content-height:300px;--ss-spacing-l:7px;--ss-spacing-m:5px;--ss-spacing-s:3px;--ss-animation-timing:0.2s;--ss-border-radius:4px}@keyframes ss-valueIn{0%{transform:scale(0);opacity:0}100%{transform:scale(1);opacity:1}}@keyframes ss-valueOut{0%{transform:scale(1);opacity:1}100%{transform:scale(0);opacity:0}}.ss-hide{display:none!important}.ss-main{display:flex;flex-direction:row;position:relative;user-select:none;color:var(--ss-font-color);min-height:var(--ss-main-height);width:100%;padding:var(--ss-spacing-s);cursor:pointer;border:1px solid var(--ss-border-color);border-radius:var(--ss-border-radius);background-color:var(--ss-bg-color);outline:0;box-sizing:border-box;transition:background-color var(--ss-animation-timing);overflow:hidden}.ss-main:focus{box-shadow:0 0 5px var(--ss-primary-color)}.ss-main.ss-disabled{background-color:var(--ss-disabled-color);cursor:not-allowed}.ss-main.ss-disabled .ss-values .ss-disabled{color:var(--ss-font-color)}.ss-main.ss-disabled .ss-values .ss-value .ss-value-delete{cursor:not-allowed}.ss-main.ss-open-above{border-top-left-radius:0;border-top-right-radius:0}.ss-main.ss-open-below{border-bottom-left-radius:0;border-bottom-right-radius:0}.ss-main .ss-values{display:inline-flex;flex-wrap:wrap;gap:var(--ss-spacing-m);flex:1 1 100%}.ss-main .ss-values .ss-placeholder{display:flex;padding:var(--ss-spacing-s) var(--ss-spacing-m) var(--ss-spacing-s) var(--ss-spacing-m);margin:auto 0 auto 0;line-height:1em;align-items:center;width:100%;color:var(--ss-font-placeholder-color);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ss-main .ss-values .ss-max{display:flex;user-select:none;align-items:center;width:fit-content;font-size:12px;color:var(--ss-bg-color);line-height:1;padding:var(--ss-spacing-s) var(--ss-spacing-m);background-color:var(--ss-primary-color);border-radius:var(--ss-border-radius)}.ss-main .ss-values .ss-single{display:flex;margin:auto 0 auto var(--ss-spacing-s)}.ss-main .ss-values .ss-value{display:flex;user-select:none;align-items:center;width:fit-content;background-color:var(--ss-primary-color);border-radius:var(--ss-border-radius);animation-name:ss-valueIn;animation-duration:var(--ss-animation-timing);animation-timing-function:ease-out;animation-fill-mode:both}.ss-main .ss-values .ss-value.ss-value-out{animation-name:ss-valueOut;animation-duration:var(--ss-animation-timing);animation-timing-function:ease-out}.ss-main .ss-values .ss-value .ss-value-text{font-size:12px;color:var(--ss-bg-color);line-height:1;padding:var(--ss-spacing-s) var(--ss-spacing-m)}.ss-main .ss-values .ss-value .ss-value-delete{display:flex;align-items:center;height:var(--ss-spacing-l);width:var(--ss-spacing-l);padding:var(--ss-spacing-s) var(--ss-spacing-m);cursor:pointer;border-left:solid 1px var(--ss-bg-color);box-sizing:content-box}.ss-main .ss-values .ss-value .ss-value-delete svg{height:var(--ss-spacing-l);width:var(--ss-spacing-l)}.ss-main .ss-values .ss-value .ss-value-delete svg path{fill:none;stroke:var(--ss-bg-color);stroke-width:18;stroke-linecap:round;stroke-linejoin:round}.ss-main .ss-deselect{display:flex;align-self:center;justify-content:flex-end;flex:0 1 auto;width:8px;height:8px;margin:0 var(--ss-spacing-m) 0 var(--ss-spacing-m)}.ss-main .ss-deselect svg{width:8px;height:8px}.ss-main .ss-deselect svg path{fill:none;stroke:var(--ss-font-color);stroke-width:20;stroke-linecap:round;stroke-linejoin:round}.ss-main .ss-arrow{display:flex;align-items:center;justify-content:flex-end;flex:0 1 auto;width:12px;height:12px;margin:auto var(--ss-spacing-m) auto var(--ss-spacing-m)}.ss-main .ss-arrow path{fill:none;stroke:var(--ss-font-color);stroke-width:18;stroke-linecap:round;stroke-linejoin:round;transition-timing-function:ease-out;transition:var(--ss-animation-timing)}.ss-content{position:absolute;display:flex;height:auto;flex-direction:column;width:auto;max-height:var(--ss-content-height);box-sizing:border-box;border:solid 1px var(--ss-border-color);background-color:var(--ss-bg-color);transition:transform var(--ss-animation-timing),opacity var(--ss-animation-timing);opacity:0;transform:scaleY(0);transform-origin:center top;overflow:hidden;z-index:10000}.ss-content.ss-relative{position:relative;height:100%}.ss-content.ss-open-above{flex-direction:column-reverse;opacity:1;transform:scaleY(1);transform-origin:center bottom;border-top-left-radius:var(--ss-border-radius);border-top-right-radius:var(--ss-border-radius)}.ss-content.ss-open-below{opacity:1;transform:scaleY(1);transform-origin:center top;border-bottom-left-radius:var(--ss-border-radius);border-bottom-right-radius:var(--ss-border-radius)}.ss-content .ss-search{flex:0 1 auto;display:flex;flex-direction:row;padding:var(--ss-spacing-l) var(--ss-spacing-l) var(--ss-spacing-m) var(--ss-spacing-l)}.ss-content .ss-search input{display:inline-flex;font-size:inherit;line-height:inherit;flex:1 1 auto;width:100%;min-width:0;padding:var(--ss-spacing-m) var(--ss-spacing-l);margin:0;border:1px solid var(--ss-border-color);border-radius:var(--ss-border-radius);background-color:var(--ss-bg-color);outline:0;text-align:left;box-sizing:border-box}.ss-content .ss-search input::placeholder{color:var(--ss-font-placeholder-color);vertical-align:middle}.ss-content .ss-search input:focus{box-shadow:0 0 5px var(--ss-primary-color)}.ss-content .ss-search .ss-addable{display:inline-flex;justify-content:center;align-items:center;cursor:pointer;flex:0 0 auto;height:auto;margin:0 0 0 var(--ss-spacing-m);border:1px solid var(--ss-border-color);border-radius:var(--ss-border-radius)}.ss-content .ss-search .ss-addable svg{display:flex;align-items:center;justify-content:flex-end;flex:0 1 auto;width:12px;height:12px;margin:auto var(--ss-spacing-m) auto var(--ss-spacing-m)}.ss-content .ss-search .ss-addable svg path{fill:none;stroke:var(--ss-font-color);stroke-width:18;stroke-linecap:round;stroke-linejoin:round}.ss-content .ss-list{flex:1 1 auto;height:auto;overflow-x:hidden;overflow-y:auto}.ss-content .ss-list .ss-error{color:var(--ss-error-color);padding:var(--ss-spacing-l)}.ss-content .ss-list .ss-searching{color:var(--ss-font-color);padding:var(--ss-spacing-l)}.ss-content .ss-list .ss-optgroup.ss-close .ss-option{display:none!important}.ss-content .ss-list .ss-optgroup .ss-optgroup-label{display:flex;flex-direction:row;align-items:center;justify-content:space-between;padding:var(--ss-spacing-m) var(--ss-spacing-l) var(--ss-spacing-m) var(--ss-spacing-l)}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-label-text{flex:1 1 auto;font-weight:700;color:var(--ss-font-color)}.ss-content .ss-list .ss-optgroup .ss-optgroup-label:has(.ss-arrow){cursor:pointer}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions{flex:0 1 auto;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:var(--ss-spacing-m)}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall{flex:0 0 auto;display:flex;flex-direction:row;cursor:pointer}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall:hover{opacity:.5}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall.ss-selected svg path{stroke:var(--ss-error-color)}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall span{flex:0 1 auto;display:flex;align-items:center;justify-content:center;font-size:60%;text-align:center;padding:0 var(--ss-spacing-s) 0 0}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall svg{flex:0 1 auto;width:13px;height:13px}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall svg path{fill:none;stroke:var(--ss-success-color);stroke-linecap:round;stroke-linejoin:round}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall svg:first-child{stroke-width:5}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-selectall svg:last-child{stroke-width:11}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-closable{flex:0 1 auto;display:flex;flex-direction:row;cursor:pointer}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-closable .ss-arrow{flex:1 1 auto;width:10px;height:10px}.ss-content .ss-list .ss-optgroup .ss-optgroup-label .ss-optgroup-actions .ss-closable .ss-arrow path{fill:none;stroke:var(--ss-font-color);stroke-width:18;stroke-linecap:round;stroke-linejoin:round;transition-timing-function:ease-out;transition:var(--ss-animation-timing)}.ss-content .ss-list .ss-optgroup .ss-option{padding:var(--ss-spacing-s) var(--ss-spacing-s) var(--ss-spacing-s) calc(var(--ss-spacing-l)*3)}.ss-content .ss-list .ss-option{display:flex;padding:var(--ss-spacing-m) var(--ss-spacing-l) var(--ss-spacing-m) var(--ss-spacing-l);color:var(--ss-font-color);cursor:pointer;user-select:none}.ss-content .ss-list .ss-option:hover{color:var(--ss-bg-color);background-color:var(--ss-primary-color)}.ss-content .ss-list .ss-option.ss-highlighted,.ss-content .ss-list .ss-option:not(.ss-disabled).ss-selected{color:var(--ss-bg-color);background-color:var(--ss-primary-color)}.ss-content .ss-list .ss-option.ss-disabled{cursor:not-allowed;background-color:var(--ss-disabled-color)}.ss-content .ss-list .ss-option.ss-disabled:hover{color:var(--ss-font-color)}.ss-content .ss-list .ss-option .ss-search-highlight{background-color:var(--ss-highlight-color)}
@@ -0,0 +1,2 @@
1
+ @import "actiontext";
2
+ @import "slimselect.min";
@@ -0,0 +1 @@
1
+ @import "signin";
@@ -0,0 +1,10 @@
1
+ .form-signin {
2
+ width: 100%;
3
+ max-width: 400px;
4
+ padding: 15px;
5
+ margin: auto;
6
+ }
7
+
8
+ .navbar-collapse .dropdown-menu {
9
+ min-width: 250px !important;
10
+ }
@@ -0,0 +1,90 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ */
14
+
15
+ label.required:after {
16
+ content: "*";
17
+ }
18
+
19
+ // Main stylesheet
20
+
21
+ // CSS partials
22
+ @import "config/index";
23
+ @import "external_libraries/index";
24
+ @import "components/index";
25
+ @import "pages/index";
26
+ @import "font_awesome5_webfont";
27
+ $fa-font-path: "@fortawesome/fontawesome-free/webfonts";
28
+ @import "@fortawesome/fontawesome-free/scss/fontawesome";
29
+ @import "@fortawesome/fontawesome-free/scss/brands";
30
+ @import "@fortawesome/fontawesome-free/scss/solid";
31
+ @import "@fortawesome/fontawesome-free/scss/regular";
32
+ @import "@fortawesome/fontawesome-free/scss/v4-shims";
33
+ @import "bootstrap";
34
+
35
+ // External libraries
36
+ body,
37
+ html {
38
+ width: 100%;
39
+ height: 100%;
40
+ }
41
+
42
+ * {
43
+ font-family: "Roboto", sans-serif;
44
+ }
45
+
46
+ body {
47
+ display: flex !important;
48
+ align-items: center;
49
+ padding-top: 0px;
50
+ padding-bottom: 0px;
51
+ font-weight: 400 !important;
52
+ margin-bottom: 20px !important;
53
+ margin-top: 20px !important;
54
+ }
55
+
56
+ img {
57
+ aspect-ratio: attr(width) / attr(height);
58
+ }
59
+
60
+ p.copyright {
61
+ margin: 15px 0 0;
62
+ }
63
+
64
+ .rounded-4 {
65
+ border-radius: 8px;
66
+ }
67
+ .rounded-5 {
68
+ border-radius: 10px;
69
+ }
70
+
71
+ .square-50 {
72
+ width: 50px;
73
+ height: 50px;
74
+ display: flex;
75
+ flex-direction: column;
76
+ justify-content: center;
77
+ }
78
+
79
+ .nav-item.dropdown .dropdown-item:active {
80
+ background-color: transparent !important;
81
+ }
82
+
83
+ .list-style-none {
84
+ list-style-type: none;
85
+ }
86
+
87
+ input[type="radio"] {
88
+ width: 16px;
89
+ height: 16px;
90
+ }
Binary file
File without changes
@@ -0,0 +1,18 @@
1
+ module OpenFresk::Authentication
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ helper_method :current_user
6
+ before_action :authenticate_user!
7
+ end
8
+
9
+ def current_user
10
+ @current_user ||= ::User.find_by(id: session[:user_id])
11
+ end
12
+
13
+ def authenticate_user!
14
+ return if current_user
15
+ session[:forwarding_url] = request.fullpath if request.get?
16
+ redirect_to new_session_path, alert: t("please_log_in")
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module OpenFresk
2
+ class ApplicationController < ::ApplicationController
3
+ include OpenFresk::Authentication
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ module OpenFresk
2
+ class SessionsController < ApplicationController
3
+ skip_before_action :authenticate_user!
4
+
5
+ def new
6
+ end
7
+
8
+ def create
9
+ user = ::User.where(email: params[:email]&.downcase).first
10
+ if user&.authenticate(params[:password])
11
+ session[:user_id] = user.id
12
+
13
+ # Allow host app to add custom behavior
14
+ after_sign_in_hook(user)
15
+
16
+ redirect_to redirection_url(user)
17
+ else
18
+ @alert = t("sessions.invalid_credentials")
19
+ respond_to do |format|
20
+ format.html { redirect_to open_fresk.new_session_path, alert: @alert }
21
+ format.turbo_stream do
22
+ render turbo_stream: turbo_stream.prepend("signin", partial: "wrong_credentials", locals: {alert: @alert})
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def redirection_url(user)
31
+ root_url
32
+ end
33
+
34
+ def after_sign_in_hook(user)
35
+ # No-op by default
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module OpenFresk
2
+ class TrainingSessionsController < ApplicationController
3
+ def index
4
+ end
5
+
6
+ private
7
+
8
+ def sessions_lists
9
+ public_opportunities
10
+ end
11
+
12
+ def public_opportunities
13
+ training_sessions = TrainingSession.futur
14
+ @my_training_sessions = training_sessions.my_sessions(current_user)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module OpenFresk
2
+ module CoreExtensions
3
+ module String
4
+ def capitalize_with_dash
5
+ capitalize.gsub(/-[a-z]/) { |s| s.upcase }
6
+ end
7
+
8
+ def titleize_with_dashes
9
+ humanize.gsub(/\b('?[a-z])/) { |s| s.capitalize }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ module HeaderHelper
2
+ end
@@ -0,0 +1,18 @@
1
+ module OpenFresk
2
+ module ApplicationHelper
3
+ def current_language
4
+ "Français"
5
+ end
6
+
7
+ def language_name(locale)
8
+ case locale
9
+ when "en"
10
+ "English"
11
+ when "fr"
12
+ "Français"
13
+ when "es"
14
+ "Español"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module PlateformAccess
2
+ module RightsHelper
3
+ def current_user_rights_animator_and_up?
4
+ current_user&.super_admin? || current_user&.administrator? || current_user&.organiser? || current_user&.animator?
5
+ end
6
+
7
+ def current_user_is_admin?
8
+ current_user&.super_admin? || current_user&.administrator?
9
+ end
10
+
11
+ def current_user_can_create_general_public_sessions?
12
+ current_user&.super_admin? || current_user&.administrator? || current_user&.organiser?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "OpenFresk",
3
+ "short_name": "Fresk",
4
+ "start_url": "/",
5
+ "display": "standalone",
6
+ "background_color": "#ffffff",
7
+ "theme_color": "#ffffff",
8
+ "icons": [
9
+ {
10
+ "src": "/assets/open_fresk/favicon.png",
11
+ "sizes": "192x192",
12
+ "type": "image/png"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,7 @@
1
+ import { Application } from "@hotwired/stimulus";
2
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
3
+
4
+ const application = Application.start();
5
+ eagerLoadControllersFrom("open_fresk/controllers", application);
6
+
7
+ console.log("🔌 open_fresk.js loaded");
@@ -0,0 +1,7 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class HelloController extends Controller {
4
+ connect() {
5
+ console.log("hello world from OpenFresk")
6
+ }
7
+ }
@@ -0,0 +1,4 @@
1
+ module OpenFresk
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module OpenFresk
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
Binary file
File without changes
@@ -0,0 +1,10 @@
1
+ module OpenFresk
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ include OpenFresk::Extensions::StringEnum
4
+ self.abstract_class = true
5
+
6
+ def self.[](attribute)
7
+ arel_table[attribute]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module OpenFresk
2
+ class TrainingSession < ApplicationRecord
3
+ self.table_name = 'training_sessions'
4
+
5
+ scope :futur, -> { where("end_time >= ?", DateTime.current.beginning_of_day).order(date: :asc) }
6
+ scope :organized_by, ->(user) { where(created_by_user_id: user&.id) }
7
+ scope :my_sessions, ->(user) { where(id: organized_by(user)) }
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenFresk
2
+ class User < ApplicationRecord
3
+ self.table_name = 'users'
4
+
5
+ def fullname
6
+ "#{firstname&.titleize_with_dashes} #{lastname&.titleize_with_dashes}"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenFresk
4
+ module Extensions
5
+ module StringEnum
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ # string_enum allow a developer to define a set of named constants
10
+ #
11
+ # ==== Examples
12
+ #
13
+ # class User
14
+ # include StringEnum
15
+ #
16
+ # string_enum role: %i[employee admin manager]
17
+ #
18
+ # # automaticaly include validation
19
+ # # validates(:role, inclusion: { in: ['EMPLOYEE', 'ADMIN', 'MANAGER'] }, allow_nil: true)
20
+ # end
21
+ #
22
+ # define all methods bellow:
23
+ #
24
+ # user.employee?
25
+ # user.admin?
26
+ # user.manager?
27
+ #
28
+ # user.employee!
29
+ # user.admin!
30
+ # user.manager!
31
+ #
32
+ # User::Employee # => 'EMPLOYEE'
33
+ # User::Admin # => 'ADMIN'
34
+ # User::Manager # => 'MANAGER'
35
+ #
36
+ # User.employees # => [<#User role='EMPLOYEE'>, <#User role='EMPLOYEE'>]
37
+ # User.admin # => [<#User role='ADMIN'>, <#User role='ADMIN'>]
38
+ # User.manager # => [<#User role='MANAGER'>, <#User role='MANAGER'>]
39
+ #
40
+ # User.role_with(<KEYS>)
41
+ # User.role_with([:admin, :manager]) # => [<#User role='MANAGER'>, <#User role='ADMIN'>]
42
+ #
43
+ # User.role_keys # => [:employee, :admin, :manager]
44
+ # User.role_values # => ['EMPLOYEE', 'ADMIN', 'MANAGER']
45
+ #
46
+ # User.enum_value_for(role: <KEY>)
47
+ # User.enum_value_for(role: :employee) # => 'EMPLOYEE'
48
+ #
49
+ # User.role_value_for(<key>)
50
+ # User.role_value_for(:employee) # => 'EMPLOYEE'
51
+ #
52
+ def string_enum(validation: true, prefix: false, **enum_hash)
53
+ # enum_name = :role
54
+ enum_name = enum_hash.keys.first
55
+
56
+ # enum_keys = [:employee, :admin, :manager]
57
+ enum_keys = enum_hash[enum_name].freeze
58
+
59
+ # enum_keys_values = { employee: 'EMPLOYEE', admin: 'ADMIN', manager: 'MANAGER' }
60
+ enum_keys_values = enum_keys.index_with(&key_to_value)
61
+
62
+ # enum_values = ['EMPLOYEE', 'ADMIN', 'MANAGER']
63
+ enum_values = enum_keys_values.values
64
+
65
+ define_enum_methods(enum_name, enum_keys, enum_values, enum_keys_values, prefix)
66
+
67
+ # HACK: we want validation to be overridable while implementing framework
68
+ # To do that, each time we are overriding string enum, we must
69
+ # * Remove validations callback already set by ancestor's definition of string enum
70
+ # * Remove the validations from the list of classes validators
71
+ #
72
+ # find previously defined inclusion validation callback:
73
+ previous_validation_callback =
74
+ send(:get_callbacks, :validate)
75
+ .send(:chain)
76
+ .filter do |c|
77
+ c.filter.is_a?(ActiveModel::Validations::InclusionValidator) && c.filter.attributes.include?(enum_name)
78
+ end.first
79
+
80
+ # if such callback exists
81
+ if previous_validation_callback.present?
82
+ # get rid of it
83
+ send(:get_callbacks, :validate).send(:chain).delete(previous_validation_callback)
84
+ # remove the validation from classes validators
85
+ _validators[enum_name].delete_if { |v| v.instance_of?(ActiveModel::Validations::InclusionValidator) }
86
+ end
87
+
88
+ # implement validation based on current enum
89
+ # if validation is set to false, we've cleared inclusion validations on current enum
90
+ #
91
+ # validates(:role, inclusion: { in: ['EMPLOYEE', 'ADMIN', 'MANAGER'] }, allow_nil: true)
92
+ validates(enum_name, inclusion: {in: enum_values}, allow_nil: true) if validation
93
+ end
94
+
95
+ private
96
+
97
+ def key_to_value
98
+ ->(key) { key.to_s.upcase }
99
+ end
100
+
101
+ def define_enum_methods(enum_name, enum_keys, enum_values, enum_keys_values, prefix)
102
+ # for each keys define named methodes
103
+ enum_keys_values.each { |enum_key, enum_value| define_named_methodes(enum_name, enum_key, enum_value, prefix) }
104
+
105
+ # class.role_with(<KEYS>)
106
+ #
107
+ # def self.role_with([:admin, :manager])
108
+ # where(role: ['ADMIN', 'MANAGER'])
109
+ # end
110
+ define_singleton_method("#{enum_name}_with") do |*keys|
111
+ where(enum_name => Array(keys).flatten.map(&key_to_value))
112
+ end
113
+
114
+ # class.role_keys
115
+ #
116
+ # def self.role_keys
117
+ # [:employee, :admin, :manager]
118
+ # end
119
+ define_singleton_method("#{enum_name}_keys") { enum_keys }
120
+
121
+ # instance.role_keys
122
+ delegate("#{enum_name}_keys".to_sym, to: :class)
123
+
124
+ # class.role_values
125
+ #
126
+ # def self.role_values
127
+ # ['EMPLOYEE', 'ADMIN', 'MANAGER']
128
+ # end
129
+ define_singleton_method("#{enum_name}_values") { enum_values }
130
+
131
+ # instance.role_values
132
+ delegate("#{enum_name}_values".to_sym, to: :class)
133
+
134
+ define_enum_value_methods(enum_name, enum_keys, enum_keys_values)
135
+ end
136
+
137
+ def define_enum_value_methods(enum_name, enum_keys, enum_keys_values)
138
+ # class.enum_value_for(<enum_name>: <key>)
139
+ #
140
+ # def self.enum_value_for(role: :employee)
141
+ # 'EMPLOYEE'
142
+ # end
143
+ define_singleton_method(:enum_value_for) do |**args|
144
+ public_send("#{args.keys.first}_enum_value_for", args.values.first)
145
+ end
146
+
147
+ # class.<enum_name>_enum_value_for(<key>)
148
+ #
149
+ # def self.role_enum_value_for(:employee)
150
+ # 'EMPLOYEE'
151
+ # end
152
+ define_singleton_method("#{enum_name}_enum_value_for") do |enum_key|
153
+ enum_key = enum_key.to_sym
154
+ raise(ArgumentError, "unknown enum_key: #{enum_key}") unless enum_keys.include?(enum_key)
155
+
156
+ enum_keys_values[enum_key]
157
+ end
158
+ end
159
+
160
+ def define_named_methodes(enum_name, enum_key, enum_value, prefix)
161
+ # instance.<key>?
162
+ #
163
+ # def employee?
164
+ # role == 'EMPLOYEE'
165
+ # end
166
+ define_method("#{enum_key}?") { public_send(enum_name) == enum_value }
167
+
168
+ # instance.<key>!
169
+ #
170
+ # def employee!
171
+ # self.role = 'EMPLOYEE'
172
+ # end
173
+ define_method("#{enum_key}!") { public_send("#{enum_name}=", enum_value) }
174
+
175
+ # class::<Key>
176
+ # the class name follow by key as camelcase
177
+ #
178
+ # class::Employee
179
+ # => 'EMPLOYEE'
180
+ define_constant(enum_key, prefix ? enum_name : false)
181
+
182
+ # def self.employees
183
+ # where(role: 'EMPLOYEE')
184
+ # end
185
+ define_singleton_method(enum_key.to_s.pluralize) { where(enum_name => enum_value) }
186
+ end
187
+
188
+ def define_constant(enum_key, prefix)
189
+ prefix = prefix ? prefix.to_s + "_" : ""
190
+ constant_name = (prefix + enum_key.to_s).camelcase
191
+ return if const_defined?(constant_name, false)
192
+
193
+ const_set(
194
+ constant_name,
195
+ enum_key.to_s.underscore.upcase
196
+ )
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,7 @@
1
+ <button class="navbar-toggler" id="navbarSideCollapse" onclick="this.classList.toggle('opened');this.setAttribute('aria-expanded', this.classList.contains('opened'))" aria-label="Main Menu">
2
+ <svg width="30" height="30" viewBox="0 0 100 100">
3
+ <path class="line line1" d="M 20,29.000046 H 80.000231 C 80.000231,29.000046 94.498839,28.817352 94.532987,66.711331 94.543142,77.980673 90.966081,81.670246 85.259173,81.668997 79.552261,81.667751 75.000211,74.999942 75.000211,74.999942 L 25.000021,25.000058" />
4
+ <path class="line line2" d="M 20,50 H 80" />
5
+ <path class="line line3" d="M 20,70.999954 H 80.000231 C 80.000231,70.999954 94.498839,71.182648 94.532987,33.288669 94.543142,22.019327 90.966081,18.329754 85.259173,18.331003 79.552261,18.332249 75.000211,25.000058 75.000211,25.000058 L 25.000021,74.999942" />
6
+ </svg>
7
+ </button>
@@ -0,0 +1,5 @@
1
+ <%= link_to root_path(language: params[:language]), class: "navbar-brand" do %>
2
+ <div class="d-flex fs-6 fw-bold lh-sm">
3
+ <%= image_tag "open_fresk/logos/logo.png" %>
4
+ </div>
5
+ <% end %>
File without changes
File without changes
@@ -0,0 +1,12 @@
1
+ <div class="container-fluid px-0 header bg-secondary bg-opacity-25">
2
+ <nav class="navbar navbar-expand-lg px-3 py-0 shadow-sm">
3
+ <%= render "layouts/header/hamburger" %>
4
+ <%= render "layouts/header/logo" %>
5
+ <div class="navbar-collapse offcanvas-collapse on_top_of_iframe">
6
+ <ul class="navbar-nav me-auto mb-2 mb-lg-0">
7
+ <%= root_path(t("header.home")) %>
8
+ </ul>
9
+ <%= link_to fa_icon("globe", text: current_language), "#languageModal", class: "nav-item nav-link link-dark", data: { bs_toggle: "modal", bs_target: "#languageModal"} %>
10
+ </div>
11
+ </nav>
12
+ </div>
@@ -0,0 +1,24 @@
1
+ <%= stylesheet_link_tag "open_fresk/application", media: "all", "data-turbo-track": "reload" %>
2
+ <%= csrf_meta_tags %>
3
+
4
+ <%= favicon_link_tag "open_fresk/favicon.png", type: "image/png" %>
5
+ <%= favicon_link_tag "open_fresk/favicon.png", rel: "apple-touch-icon", type: "image/png" %>
6
+
7
+ <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/cupertino/jquery-ui.min.css">
8
+ <link rel="manifest" href="<%= asset_path 'manifest.json' %>">
9
+
10
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.8.2/dist/es-module-shims.js" data-turbo-track="reload"></script>
11
+
12
+
13
+ <%= javascript_include_tag "open_fresk/application",
14
+ "data-turbo-track": "reload",
15
+ defer: true %>
16
+
17
+ <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
18
+ <script src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"></script>
19
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,400;0,700;1,100;1,400;1,700&display=swap" rel="stylesheet">
20
+
21
+ <%#— host-only extras hook —%>
22
+ <% if lookup_context.template_exists?("layouts/open_fresk/include_extras", [], true) %>
23
+ <%= render partial: "layouts/open_fresk/include_extras" %>
24
+ <% end %>