lesli 5.1.8 → 5.1.9
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/lesli/application.css +0 -161
- data/app/assets/stylesheets/lesli/application.tailwind.css +68 -0
- data/app/helpers/lesli/html_helper.rb +1 -1
- data/app/helpers/lesli/navigation_helper.rb +82 -16
- data/app/views/lesli/apps/show.html.erb +24 -26
- data/app/views/lesli/layouts/application-devise.html.erb +2 -1
- data/app/views/lesli/partials/_application-lesli-assets.html.erb +3 -2
- data/app/views/lesli/partials/_application-lesli-header.html.erb +102 -78
- data/config/locales/translations.en.yml +9 -0
- data/config/locales/translations.es.yml +9 -0
- data/lib/lesli/version.rb +2 -2
- data/lib/lesli.rb +2 -0
- data/lib/tasks/lesli/db.rake +20 -28
- data/readme.md +136 -73
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ee3156ee2bbab3464a0f87d5464bf3d1bc0822eb26365ade32c75cdc40cfafa
|
|
4
|
+
data.tar.gz: 2ae503e439f2571995336e2f27e818ebfd5aee07d3e12b2e2cba147145e5aba2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e50643cd811ecc96b89276098798cf66962dba64a3d69cc17135d60a751a9b7ab4d5138c218e93f85a133dc5385edef161a04654afcaa866b84c323caa42dfb
|
|
7
|
+
data.tar.gz: 064ac6c21289d1e15ad25d9014e3f00ed4bb0bd975775a40a65be12c633fd847f1c7ac3db610e0921bcfdd46acb5a11d1acd50c067bd7806751b76c604a61251
|
|
@@ -29,164 +29,3 @@ Building a better future, one line of code at a time.
|
|
|
29
29
|
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
30
|
// ·
|
|
31
31
|
*/
|
|
32
|
-
/*
|
|
33
|
-
Lesli
|
|
34
|
-
|
|
35
|
-
Copyright (c) 2025, Lesli Technologies, S. A.
|
|
36
|
-
|
|
37
|
-
This program is free software: you can redistribute it and/or modify
|
|
38
|
-
it under the terms of the GNU General Public License as published by
|
|
39
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
40
|
-
(at your option) any later version.
|
|
41
|
-
|
|
42
|
-
This program is distributed in the hope that it will be useful,
|
|
43
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
44
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
45
|
-
GNU General Public License for more details.
|
|
46
|
-
|
|
47
|
-
You should have received a copy of the GNU General Public License
|
|
48
|
-
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
49
|
-
|
|
50
|
-
Lesli · Ruby on Rails SaaS Development Framework.
|
|
51
|
-
|
|
52
|
-
Made with ♥ by LesliTech
|
|
53
|
-
Building a better future, one line of code at a time.
|
|
54
|
-
|
|
55
|
-
@contact hello@lesli.tech
|
|
56
|
-
@website https://www.lesli.tech
|
|
57
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
58
|
-
|
|
59
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
60
|
-
// ·
|
|
61
|
-
*/
|
|
62
|
-
/**
|
|
63
|
-
Lesli
|
|
64
|
-
|
|
65
|
-
Copyright (c) 2025, Lesli Technologies, S. A.
|
|
66
|
-
|
|
67
|
-
This program is free software: you can redistribute it and/or modify
|
|
68
|
-
it under the terms of the GNU General Public License as published by
|
|
69
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
70
|
-
(at your option) any later version.
|
|
71
|
-
|
|
72
|
-
This program is distributed in the hope that it will be useful,
|
|
73
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
74
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
75
|
-
GNU General Public License for more details.
|
|
76
|
-
|
|
77
|
-
You should have received a copy of the GNU General Public License
|
|
78
|
-
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
79
|
-
|
|
80
|
-
Lesli - SCSS Utilities for websites, apps and web applications
|
|
81
|
-
|
|
82
|
-
Made with ♥ by LesliTech
|
|
83
|
-
Building a better future, one line of code at a time.
|
|
84
|
-
|
|
85
|
-
@contact hello@lesli.tech
|
|
86
|
-
@website https://www.lesli.tech
|
|
87
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
88
|
-
|
|
89
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
90
|
-
// ·
|
|
91
|
-
*/
|
|
92
|
-
/*
|
|
93
|
-
Lesli
|
|
94
|
-
|
|
95
|
-
Copyright (c) 2025, Lesli Technologies, S. A.
|
|
96
|
-
|
|
97
|
-
This program is free software: you can redistribute it and/or modify
|
|
98
|
-
it under the terms of the GNU General Public License as published by
|
|
99
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
100
|
-
(at your option) any later version.
|
|
101
|
-
|
|
102
|
-
This program is distributed in the hope that it will be useful,
|
|
103
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
104
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
105
|
-
GNU General Public License for more details.
|
|
106
|
-
|
|
107
|
-
You should have received a copy of the GNU General Public License
|
|
108
|
-
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
109
|
-
|
|
110
|
-
Lesli - SCSS Utilities for websites, apps and web applications
|
|
111
|
-
|
|
112
|
-
Made with ♥ by LesliTech
|
|
113
|
-
Building a better future, one line of code at a time.
|
|
114
|
-
|
|
115
|
-
@contact hello@lesli.tech
|
|
116
|
-
@website https://www.lesli.tech
|
|
117
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
118
|
-
|
|
119
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
120
|
-
// ·
|
|
121
|
-
*/
|
|
122
|
-
/**
|
|
123
|
-
Lesli
|
|
124
|
-
|
|
125
|
-
Copyright (c) 2025, Lesli Technologies, S. A.
|
|
126
|
-
|
|
127
|
-
This program is free software: you can redistribute it and/or modify
|
|
128
|
-
it under the terms of the GNU General Public License as published by
|
|
129
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
130
|
-
(at your option) any later version.
|
|
131
|
-
|
|
132
|
-
This program is distributed in the hope that it will be useful,
|
|
133
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
134
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
135
|
-
GNU General Public License for more details.
|
|
136
|
-
|
|
137
|
-
You should have received a copy of the GNU General Public License
|
|
138
|
-
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
139
|
-
|
|
140
|
-
Lesli - SCSS Utilities for websites, apps and web applications
|
|
141
|
-
|
|
142
|
-
Made with ♥ by LesliTech
|
|
143
|
-
Building a better future, one line of code at a time.
|
|
144
|
-
|
|
145
|
-
@contact hello@lesli.tech
|
|
146
|
-
@website https://www.lesli.tech
|
|
147
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
148
|
-
|
|
149
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
150
|
-
// ·
|
|
151
|
-
*/
|
|
152
|
-
body.lesli.apps.show .lesli-element-header {
|
|
153
|
-
margin-bottom: 5rem !important;
|
|
154
|
-
}
|
|
155
|
-
body.lesli.apps.show .engines {
|
|
156
|
-
gap: 24px;
|
|
157
|
-
margin-inline-end: auto;
|
|
158
|
-
margin-inline-start: auto;
|
|
159
|
-
}
|
|
160
|
-
body.lesli.apps.show .engines a {
|
|
161
|
-
flex-basis: 318px;
|
|
162
|
-
text-align: center;
|
|
163
|
-
padding: 2rem 0.4rem 2.8rem;
|
|
164
|
-
border: white 1px solid;
|
|
165
|
-
border-radius: 6px;
|
|
166
|
-
background-color: white;
|
|
167
|
-
transition: all ease-in-out 0.2s;
|
|
168
|
-
box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.08) 0px 0px 0px 1px;
|
|
169
|
-
}
|
|
170
|
-
body.lesli.apps.show .engines a:hover {
|
|
171
|
-
border-color: var(--lesli-color-primary);
|
|
172
|
-
transform: translateY(-2px);
|
|
173
|
-
}
|
|
174
|
-
body.lesli.apps.show .engines a.is-active {
|
|
175
|
-
background-color: #F0F4FF;
|
|
176
|
-
border-color: var(--lesli-color-primary);
|
|
177
|
-
}
|
|
178
|
-
body.lesli.apps.show .engines a svg {
|
|
179
|
-
margin-right: 0.4rem;
|
|
180
|
-
fill: var(--lesli-color-primary);
|
|
181
|
-
}
|
|
182
|
-
body.lesli.apps.show .engines a p {
|
|
183
|
-
font-size: 14;
|
|
184
|
-
font-family: "OpenSans";
|
|
185
|
-
}
|
|
186
|
-
body.lesli.apps.show .engines a span {
|
|
187
|
-
font-weight: 600;
|
|
188
|
-
font-size: 20px;
|
|
189
|
-
}
|
|
190
|
-
body.lesli.apps.show .engines a span, body.lesli.apps.show .engines a p {
|
|
191
|
-
color: var(--lesli-color-primary);
|
|
192
|
-
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.1 | MIT License | https://tailwindcss.com */
|
|
2
|
+
/*!
|
|
3
|
+
|
|
4
|
+
Lesli
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
7
|
+
|
|
8
|
+
This program is free software: you can redistribute it and/or modify
|
|
9
|
+
it under the terms of the GNU General Public License as published by
|
|
10
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
(at your option) any later version.
|
|
12
|
+
|
|
13
|
+
This program is distributed in the hope that it will be useful,
|
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
GNU General Public License for more details.
|
|
17
|
+
|
|
18
|
+
You should have received a copy of the GNU General Public License
|
|
19
|
+
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
20
|
+
|
|
21
|
+
Lesli · Ruby on Rails SaaS Development Framework.
|
|
22
|
+
|
|
23
|
+
Made with ♥ by LesliTech
|
|
24
|
+
Building a better future, one line of code at a time.
|
|
25
|
+
|
|
26
|
+
@contact hello@lesli.tech
|
|
27
|
+
@website https://www.lesli.tech
|
|
28
|
+
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
29
|
+
|
|
30
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
31
|
+
// ·
|
|
32
|
+
*/
|
|
33
|
+
@layer theme, utilities;
|
|
34
|
+
@layer theme {
|
|
35
|
+
:root, :host {
|
|
36
|
+
--spacing: 0.25rem;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
@layer utilities {
|
|
40
|
+
.grid {
|
|
41
|
+
display: grid;
|
|
42
|
+
}
|
|
43
|
+
.hidden {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
.\[grid-template-columns\:repeat\(auto-fit\,minmax\(260px\,318px\)\)\] {
|
|
47
|
+
grid-template-columns: repeat(auto-fit,minmax(260px,318px));
|
|
48
|
+
}
|
|
49
|
+
.justify-center {
|
|
50
|
+
justify-content: center;
|
|
51
|
+
}
|
|
52
|
+
.gap-5 {
|
|
53
|
+
gap: calc(var(--spacing) * 5);
|
|
54
|
+
}
|
|
55
|
+
.py-6 {
|
|
56
|
+
padding-block: calc(var(--spacing) * 6);
|
|
57
|
+
}
|
|
58
|
+
.sm\:gap-6 {
|
|
59
|
+
@media (width >= 40rem) {
|
|
60
|
+
gap: calc(var(--spacing) * 6);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
.sm\:py-8 {
|
|
64
|
+
@media (width >= 40rem) {
|
|
65
|
+
padding-block: calc(var(--spacing) * 8);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -99,7 +99,7 @@ module Lesli
|
|
|
99
99
|
engine_code = lesli_engine(:code)
|
|
100
100
|
|
|
101
101
|
# Rails main host app stylesheets
|
|
102
|
-
return
|
|
102
|
+
return stylesheet if engine_code == 'root'
|
|
103
103
|
|
|
104
104
|
# Rails engines stylesheets
|
|
105
105
|
"#{engine_code}/#{stylesheet}"
|
|
@@ -33,6 +33,22 @@ Building a better future, one line of code at a time.
|
|
|
33
33
|
module Lesli
|
|
34
34
|
module NavigationHelper
|
|
35
35
|
|
|
36
|
+
# Applications are rendered in this order on the engine selector page.
|
|
37
|
+
NAVIGATION_ENGINES = %i[
|
|
38
|
+
dashboard
|
|
39
|
+
admin
|
|
40
|
+
mailer
|
|
41
|
+
bell
|
|
42
|
+
contacts
|
|
43
|
+
calendar
|
|
44
|
+
papers
|
|
45
|
+
support
|
|
46
|
+
shield
|
|
47
|
+
security
|
|
48
|
+
audit
|
|
49
|
+
babel
|
|
50
|
+
].freeze
|
|
51
|
+
|
|
36
52
|
def navigation_partial
|
|
37
53
|
engine = lesli_engine[:code]
|
|
38
54
|
path = engine == "root" ? "partials/navigation" : "#{engine}/partials/navigation"
|
|
@@ -48,26 +64,44 @@ module Lesli
|
|
|
48
64
|
|
|
49
65
|
# Prints a html link inside a list item
|
|
50
66
|
def navigation_item(path, label, icon = nil, reload: false)
|
|
51
|
-
content_tag(:li
|
|
67
|
+
content_tag(:li) do
|
|
52
68
|
navigation_link(path, label, icon, reload:reload)
|
|
53
69
|
end
|
|
54
70
|
end
|
|
55
71
|
|
|
56
72
|
def navigation_link(path, label, icon = nil, reload: false)
|
|
73
|
+
active = current_page?(path)
|
|
74
|
+
|
|
75
|
+
item_class = class_names(
|
|
76
|
+
"group flex items-center gap-4 rounded-2xl px-4 py-3 text-base font-medium transition focus:outline-none focus:ring-2 focus:ring-sky-500/30",
|
|
77
|
+
"lg:gap-2 lg:rounded-xl lg:px-4 lg:py-2 lg:text-md",
|
|
78
|
+
active ? "bg-sky-50 text-sky-700" : "text-slate-600 hover:bg-slate-50 hover:text-slate-950 lg:text-slate-500"
|
|
79
|
+
)
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
icon_class = class_names(
|
|
82
|
+
icon,
|
|
83
|
+
"text-[24px] leading-none transition lg:text-[18px]",
|
|
84
|
+
active ? "text-sky-600" : "text-slate-500 group-hover:text-slate-900"
|
|
85
|
+
)
|
|
62
86
|
|
|
63
|
-
|
|
87
|
+
link_to(path, { class: item_class }, data: { turbo_frame: "_top" }) do
|
|
64
88
|
if icon
|
|
65
|
-
concat content_tag(:
|
|
66
|
-
concat content_tag(:span, label, class: "text")
|
|
89
|
+
concat content_tag(:i, nil, class: icon_class)
|
|
67
90
|
end
|
|
91
|
+
|
|
92
|
+
concat content_tag(:span, label)
|
|
68
93
|
end
|
|
69
94
|
end
|
|
70
95
|
|
|
96
|
+
# Render the cards for installed engines while preserving their display order.
|
|
97
|
+
def navigation_engines
|
|
98
|
+
cards = NAVIGATION_ENGINES.filter_map do |engine|
|
|
99
|
+
public_send("navigation_engine_#{engine}")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
safe_join(cards)
|
|
103
|
+
end
|
|
104
|
+
|
|
71
105
|
# ADMINISTRATION
|
|
72
106
|
|
|
73
107
|
# 01.01 Admin engine
|
|
@@ -306,18 +340,50 @@ module Lesli
|
|
|
306
340
|
private
|
|
307
341
|
|
|
308
342
|
# build a html link for an engine path
|
|
309
|
-
def navigation_engine_item(title, subtitle, icon, path, is_active)
|
|
310
|
-
# get hidden modules if there are modules to hide
|
|
343
|
+
def navigation_engine_item(title, subtitle, icon, path, is_active = false)
|
|
311
344
|
modules_hidden = [] # Rails.application.config.lesli.dig(:modules_hidden) || []
|
|
312
345
|
|
|
313
|
-
# stop rendering module navigation if need to hide the module
|
|
314
346
|
return nil if modules_hidden.include?(path)
|
|
315
347
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
348
|
+
card_classes = class_names(
|
|
349
|
+
"group relative overflow-hidden",
|
|
350
|
+
"flex min-h-[210px] w-full flex-col items-center justify-center",
|
|
351
|
+
"rounded-2xl border bg-white px-6 py-8 text-center shadow-sm",
|
|
352
|
+
"transition-all duration-200 ease-out",
|
|
353
|
+
"hover:-translate-y-0.5 hover:border-[var(--lesli-color-primary)] hover:shadow-lg",
|
|
354
|
+
"focus:outline-none focus-visible:ring-4 focus-visible:ring-[var(--lesli-color-primary)]/15",
|
|
355
|
+
is_active ? "is-active border-[var(--lesli-color-primary)] bg-sky-50" : "border-slate-200"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
content_tag(:a, id: path.gsub("/", ""), href: path, class: card_classes, data: { turbo: false }) do
|
|
359
|
+
safe_join([
|
|
360
|
+
content_tag(:div, class: [
|
|
361
|
+
"mb-5 flex h-16 w-16 items-center justify-center",
|
|
362
|
+
"transition-all duration-200",
|
|
363
|
+
"group-hover:scale-105",
|
|
364
|
+
"[&_svg]:h-12 [&_svg]:w-12",
|
|
365
|
+
"[&_svg]:fill-[var(--lesli-color-primary)]",
|
|
366
|
+
"[&_svg_path]:fill-[var(--lesli-color-primary)]"
|
|
367
|
+
].join(" ")) do
|
|
368
|
+
lesli_svg("engine-#{icon}")
|
|
369
|
+
end,
|
|
370
|
+
|
|
371
|
+
content_tag(:div, class: "px-2") do
|
|
372
|
+
safe_join([
|
|
373
|
+
content_tag(:span, title, class: "block text-xl font-semibold leading-6 tracking-tight text-[var(--lesli-color-primary)]"),
|
|
374
|
+
content_tag(:p, subtitle, class: "mt-2 font-[OpenSans] text-sm font-normal leading-5 text-slate-500")
|
|
375
|
+
])
|
|
376
|
+
end,
|
|
377
|
+
|
|
378
|
+
content_tag(:span, "arrow_forward", {
|
|
379
|
+
class: class_names(
|
|
380
|
+
"material-symbols absolute right-5 top-5 text-lg text-[var(--lesli-color-primary)]",
|
|
381
|
+
"transition-all duration-200 group-hover:translate-x-0.5 group-hover:opacity-100",
|
|
382
|
+
is_active ? "opacity-100" : "opacity-0"
|
|
383
|
+
),
|
|
384
|
+
aria: { hidden: true }
|
|
385
|
+
})
|
|
386
|
+
])
|
|
321
387
|
end
|
|
322
388
|
end
|
|
323
389
|
end
|
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
if (window.lesliFilterNavigationEngines) {
|
|
3
|
+
document.removeEventListener('turbo:load', window.lesliFilterNavigationEngines)
|
|
4
|
+
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
window.lesliFilterNavigationEngines = function() {
|
|
7
|
+
let data = { engines: [] }
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const stored = localStorage.getItem('engineData')
|
|
11
|
+
data = stored ? JSON.parse(stored) : data
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.warn('Unable to read the engine navigation preferences.', error)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const hiddenEngines = Array.isArray(data.engines) ? data.engines : []
|
|
17
|
+
|
|
18
|
+
hiddenEngines.forEach(id => {
|
|
7
19
|
const el = document.getElementById(id.replace("lesli"))
|
|
8
|
-
if (el) el.
|
|
20
|
+
if (el) el.hidden = true
|
|
9
21
|
})
|
|
10
|
-
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
document.addEventListener('turbo:load', window.lesliFilterNavigationEngines)
|
|
11
25
|
</script>
|
|
12
|
-
<%
|
|
13
|
-
@navigation_engines = [
|
|
14
|
-
:dashboard,
|
|
15
|
-
:admin,
|
|
16
|
-
:mailer,
|
|
17
|
-
:bell,
|
|
18
|
-
:contacts,
|
|
19
|
-
:calendar,
|
|
20
|
-
:papers,
|
|
21
|
-
:support,
|
|
22
|
-
:shield,
|
|
23
|
-
:security,
|
|
24
|
-
:audit,
|
|
25
|
-
:babel
|
|
26
|
-
]
|
|
27
|
-
%>
|
|
28
26
|
|
|
29
27
|
<%= render(LesliView::Layout::Container.new("lesli-apps", dashboard: false)) do %>
|
|
30
28
|
<%= render("lesli_dashboard/dashboards/shared/header") if defined?(LesliDashboard) %>
|
|
31
|
-
<section class="
|
|
32
|
-
|
|
33
|
-
<%=
|
|
34
|
-
|
|
29
|
+
<section class="lesli-engines py-6 sm:py-8">
|
|
30
|
+
<div class="grid justify-center gap-5 sm:gap-6 [grid-template-columns:repeat(auto-fit,minmax(260px,318px))]">
|
|
31
|
+
<%= navigation_engines %>
|
|
32
|
+
</div>
|
|
35
33
|
</section>
|
|
36
34
|
<% end %>
|
|
@@ -36,7 +36,8 @@ Building a better future, one line of code at a time.
|
|
|
36
36
|
<html lang="en">
|
|
37
37
|
<head>
|
|
38
38
|
<%= render(partial: "lesli/partials/application-lesli-head") %>
|
|
39
|
-
<%= stylesheet_link_tag(lesli_asset_path(:
|
|
39
|
+
<%= stylesheet_link_tag(lesli_asset_path(:lesli_assets, 'public.tailwind'), media: "all") %>
|
|
40
|
+
<%= stylesheet_link_tag(lesli_asset_path(:lesli_shield, public_app_controller.concat(".tailwind")), media: "all") %>
|
|
40
41
|
<%# Disable back button in browser after Logout using JavaScript %>
|
|
41
42
|
<% if flash[:logout] == true %>
|
|
42
43
|
<script>
|
|
@@ -50,8 +50,9 @@ Building a better future, one line of code at a time.
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
<%# Loading stylesheets from engines %>
|
|
53
|
-
<%= stylesheet_link_tag(lesli_asset_path(:lesli_assets), media: "all") %>
|
|
54
|
-
<%= stylesheet_link_tag(lesli_asset_path(), media: "all") %>
|
|
53
|
+
<%= stylesheet_link_tag(lesli_asset_path(:lesli_assets, 'application.tailwind'), media: "all") %>
|
|
54
|
+
<%= stylesheet_link_tag(lesli_asset_path(:lesli_assets, 'view.tailwind'), media: "all") %>
|
|
55
|
+
<%= stylesheet_link_tag(lesli_asset_path(nil,'application.tailwind'), media: "all") %>
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
<%# Loading javascripts from engines %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lesli
|
|
4
4
|
|
|
5
|
-
Copyright (c)
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
6
|
|
|
7
7
|
This program is free software: you can redistribute it and/or modify
|
|
8
8
|
it under the terms of the GNU General Public License as published by
|
|
@@ -30,92 +30,116 @@ Building a better future, one line of code at a time.
|
|
|
30
30
|
// ·
|
|
31
31
|
%>
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
33
|
+
<%
|
|
34
|
+
engine_navigation_partial = navigation_partial
|
|
35
|
+
dashboard_path = defined?(LesliDashboard) ? lesli_dashboard.root_path : main_app.root_path
|
|
36
|
+
notifications_count = @lesli&.dig(:notifications).to_i
|
|
37
|
+
|
|
38
|
+
header_action_class = "relative flex h-10 w-10 shrink-0 items-center justify-center rounded-xl text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/40"
|
|
39
|
+
logout_action_class = "relative flex h-10 w-10 shrink-0 items-center justify-center rounded-xl text-slate-500 transition-colors hover:bg-red-50 hover:text-red-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500/30"
|
|
40
|
+
|
|
41
|
+
dashboard_label = I18n.t("lesli.header.dashboard", default: "Lesli Dashboard")
|
|
42
|
+
notifications_label = if notifications_count.positive?
|
|
43
|
+
I18n.t("lesli.header.notifications_unread", count: notifications_count, default: "%{count} unread notifications")
|
|
44
|
+
else
|
|
45
|
+
I18n.t("lesli.header.notifications", default: "Notifications")
|
|
46
|
+
end
|
|
47
|
+
applications_label = I18n.t("lesli.header.applications", default: "Applications")
|
|
48
|
+
profile_label = I18n.t("lesli.header.profile", default: "Profile")
|
|
49
|
+
logout_label = I18n.t("lesli.header.logout", default: "Logout")
|
|
50
|
+
|
|
51
|
+
action_class = ->(path) {
|
|
52
|
+
class_names(
|
|
53
|
+
header_action_class,
|
|
54
|
+
current_page?(path) && "bg-sky-50 text-sky-700"
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
%>
|
|
58
|
+
|
|
59
|
+
<header class="border-b border-slate-200/80 bg-white">
|
|
60
|
+
<div class="mx-auto flex h-20 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
|
61
|
+
|
|
62
|
+
<!-- Logo -->
|
|
63
|
+
<%= link_to dashboard_path,
|
|
64
|
+
class: "flex items-center",
|
|
65
|
+
aria: { label: dashboard_label },
|
|
66
|
+
data: { turbo_frame: "_top" } do %>
|
|
67
|
+
|
|
68
|
+
<div class="flex items-center [&_img]:h-12 [&_img]:w-auto">
|
|
69
|
+
<%= customization_instance_logo_tag(
|
|
70
|
+
logo: "app-logo",
|
|
71
|
+
options: { alt: "Lesli app logo" }
|
|
72
|
+
) %>
|
|
57
73
|
</div>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
<% end %>
|
|
75
|
+
|
|
76
|
+
<%# Application actions %>
|
|
77
|
+
<nav class="flex items-center gap-2" aria-label="Application actions">
|
|
78
|
+
|
|
79
|
+
<% if defined?(LesliBell) %>
|
|
80
|
+
<%= link_to lesli_bell.notifications_path,
|
|
81
|
+
class: action_class.call(lesli_bell.notifications_path),
|
|
82
|
+
aria: {
|
|
83
|
+
label: notifications_label,
|
|
84
|
+
current: current_page?(lesli_bell.notifications_path) ? "page" : nil
|
|
85
|
+
},
|
|
86
|
+
title: notifications_label,
|
|
87
|
+
data: { turbo_frame: "_top" } do %>
|
|
88
|
+
|
|
89
|
+
<i class="<%= notifications_count.positive? ? 'ri-notification-3-fill text-sky-600' : 'ri-notification-3-line' %> text-[24px] leading-none" aria-hidden="true"></i>
|
|
90
|
+
|
|
91
|
+
<% if notifications_count.positive? %>
|
|
92
|
+
<span class="absolute -right-0 -top-0 flex min-h-4 min-w-4 items-center justify-center rounded-full bg-sky-600 px-1 text-[9px] font-semibold leading-none text-white ring-2 ring-white" aria-hidden="true">
|
|
93
|
+
<%= notifications_count > 99 ? "99+" : notifications_count %>
|
|
94
|
+
</span>
|
|
69
95
|
<% end %>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
96
|
+
<% end %>
|
|
97
|
+
<% end %>
|
|
98
|
+
|
|
99
|
+
<%= link_to lesli.apps_path,
|
|
100
|
+
class: action_class.call(lesli.apps_path),
|
|
101
|
+
aria: {
|
|
102
|
+
label: applications_label,
|
|
103
|
+
current: current_page?(lesli.apps_path) ? "page" : nil
|
|
104
|
+
},
|
|
105
|
+
title: applications_label,
|
|
106
|
+
data: { turbo_frame: "_top" } do %>
|
|
107
|
+
<i class="ri-apps-2-line text-[24px] leading-none" aria-hidden="true"></i>
|
|
108
|
+
<% end %>
|
|
109
|
+
|
|
110
|
+
<% if defined?(LesliAdmin) %>
|
|
111
|
+
<%= link_to lesli_admin.profile_path,
|
|
112
|
+
class: action_class.call(lesli_admin.profile_path),
|
|
113
|
+
aria: {
|
|
114
|
+
label: profile_label,
|
|
115
|
+
current: current_page?(lesli_admin.profile_path) ? "page" : nil
|
|
116
|
+
},
|
|
117
|
+
title: profile_label,
|
|
118
|
+
data: { turbo_frame: "_top" } do %>
|
|
119
|
+
<i class="ri-user-smile-line text-[24px] leading-none" aria-hidden="true"></i>
|
|
120
|
+
<% end %>
|
|
121
|
+
<% end %>
|
|
122
|
+
|
|
123
|
+
<% if defined?(LesliShield) %>
|
|
124
|
+
<%= link_to main_app.destroy_user_session_path,
|
|
125
|
+
class: logout_action_class,
|
|
126
|
+
aria: { label: logout_label },
|
|
127
|
+
title: logout_label do %>
|
|
128
|
+
<i class="ri-logout-circle-r-line text-[24px] leading-none" aria-hidden="true"></i>
|
|
129
|
+
<% end %>
|
|
130
|
+
<% end %>
|
|
131
|
+
|
|
108
132
|
</nav>
|
|
109
133
|
</div>
|
|
110
134
|
</header>
|
|
111
135
|
|
|
112
136
|
<%# Build the engine navigation %>
|
|
113
137
|
<% if engine_navigation_partial %>
|
|
114
|
-
<nav class="
|
|
115
|
-
<div class="
|
|
116
|
-
<
|
|
138
|
+
<nav class="border-b border-slate-200/80 bg-white" aria-label="Module navigation">
|
|
139
|
+
<div class="mx-auto max-w-7xl overflow-x-auto px-4 sm:px-6 lg:px-8">
|
|
140
|
+
<ul class="flex min-h-14 w-max min-w-full items-center justify-start gap-1 py-2 sm:justify-center">
|
|
117
141
|
<%= render(engine_navigation_partial) %>
|
|
118
|
-
</
|
|
142
|
+
</ul>
|
|
119
143
|
</div>
|
|
120
144
|
</nav>
|
|
121
145
|
<% end %>
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
:en:
|
|
3
3
|
lesli:
|
|
4
|
+
header:
|
|
5
|
+
applications: Applications
|
|
6
|
+
dashboard: Lesli Dashboard
|
|
7
|
+
logout: Logout
|
|
8
|
+
notifications: Notifications
|
|
9
|
+
notifications_unread:
|
|
10
|
+
one: "%{count} unread notification"
|
|
11
|
+
other: "%{count} unread notifications"
|
|
12
|
+
profile: Profile
|
|
4
13
|
shared:
|
|
5
14
|
message_error_not_found: Not found
|
|
6
15
|
message_error_resource_not_found: The resource you are looking for was not found.
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
:es:
|
|
3
3
|
lesli:
|
|
4
|
+
header:
|
|
5
|
+
applications: Aplicaciones
|
|
6
|
+
dashboard: Panel de Lesli
|
|
7
|
+
logout: Cerrar sesión
|
|
8
|
+
notifications: Notificaciones
|
|
9
|
+
notifications_unread:
|
|
10
|
+
one: "%{count} notificación sin leer"
|
|
11
|
+
other: "%{count} notificaciones sin leer"
|
|
12
|
+
profile: Perfil
|
|
4
13
|
shared:
|
|
5
14
|
message_error_not_found: ":lesli.shared.message_error_not_found:"
|
|
6
15
|
message_error_resource_not_found: El recurso que estás buscando no fue encontrado
|
data/lib/lesli/version.rb
CHANGED
data/lib/lesli.rb
CHANGED
data/lib/tasks/lesli/db.rake
CHANGED
|
@@ -34,41 +34,24 @@ Building a better future, one line of code at a time.
|
|
|
34
34
|
namespace :lesli do
|
|
35
35
|
namespace :db do
|
|
36
36
|
|
|
37
|
-
desc "Drop, create, migrate, seed &
|
|
38
|
-
task :
|
|
37
|
+
desc "Drop, create, migrate, seed & configure the Lesli database (development only)"
|
|
38
|
+
task :rebuild => :environment do |task, args|
|
|
39
39
|
drop()
|
|
40
40
|
create()
|
|
41
41
|
migrate()
|
|
42
42
|
seed()
|
|
43
|
-
|
|
43
|
+
configure()
|
|
44
44
|
status()
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
desc "
|
|
48
|
-
task :
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
seed()
|
|
52
|
-
setup()
|
|
53
|
-
status()
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
desc "Migrate, setup && user the Lesli database"
|
|
57
|
-
task :deploy => :environment do |task, args|
|
|
58
|
-
create()
|
|
59
|
-
migrate()
|
|
60
|
-
setup()
|
|
61
|
-
status()
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
desc "Migrate & setup the Lesli database"
|
|
65
|
-
task :upgrade => :environment do |task, args|
|
|
66
|
-
migrate()
|
|
67
|
-
setup()
|
|
47
|
+
desc "Migrate, configure && user the Lesli database"
|
|
48
|
+
task :prepare => :environment do |task, args|
|
|
49
|
+
prepare()
|
|
50
|
+
configure()
|
|
68
51
|
status()
|
|
69
52
|
end
|
|
70
53
|
|
|
71
|
-
desc "Seed &
|
|
54
|
+
desc "Seed & configure Lesli database"
|
|
72
55
|
task :seed => :environment do |task, args|
|
|
73
56
|
seed()
|
|
74
57
|
status()
|
|
@@ -79,7 +62,7 @@ namespace :lesli do
|
|
|
79
62
|
def drop
|
|
80
63
|
|
|
81
64
|
# do not execute this task if we are at production level
|
|
82
|
-
|
|
65
|
+
raise "Cannot drop database in production" if Rails.env.production?
|
|
83
66
|
|
|
84
67
|
# print a message to let the users show the action running
|
|
85
68
|
Termline.m("Drop the Lesli database (development only)")
|
|
@@ -112,6 +95,15 @@ namespace :lesli do
|
|
|
112
95
|
Rake::Task['db:migrate'].invoke
|
|
113
96
|
end
|
|
114
97
|
|
|
98
|
+
# Create the Lesli database (development only)
|
|
99
|
+
def prepare
|
|
100
|
+
|
|
101
|
+
# print a message to let the users show the action running
|
|
102
|
+
Termline.info("Prepare the Lesli database")
|
|
103
|
+
|
|
104
|
+
Rake::Task['db:prepare'].invoke
|
|
105
|
+
end
|
|
106
|
+
|
|
115
107
|
def seed
|
|
116
108
|
|
|
117
109
|
# print a message to let the users show the action running
|
|
@@ -129,7 +121,7 @@ namespace :lesli do
|
|
|
129
121
|
Termline.br(2)
|
|
130
122
|
end
|
|
131
123
|
|
|
132
|
-
def
|
|
124
|
+
def configure
|
|
133
125
|
|
|
134
126
|
# print a message to let the users show the action running
|
|
135
127
|
Termline.info("Setup the Lesli database")
|
|
@@ -137,7 +129,7 @@ namespace :lesli do
|
|
|
137
129
|
# scan rails routes to build the controllers index
|
|
138
130
|
Rake::Task['lesli:resources:build'].invoke
|
|
139
131
|
|
|
140
|
-
Lesli::Account.
|
|
132
|
+
Lesli::Account.find_each do |account|
|
|
141
133
|
account.initialize_account
|
|
142
134
|
account.initialize_engines
|
|
143
135
|
end
|
data/readme.md
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
<img
|
|
4
|
+
width="150"
|
|
5
|
+
alt="Lesli"
|
|
6
|
+
src="./app/assets/images/lesli/lesli-logo.svg" />
|
|
7
|
+
</h1>
|
|
8
|
+
<h3>The Open-Source SaaS Development Framework for Ruby on Rails.</h3>
|
|
4
9
|
</div>
|
|
5
10
|
|
|
6
11
|
<br />
|
|
7
|
-
<hr/>
|
|
8
12
|
|
|
9
13
|
<div align="center">
|
|
10
|
-
<a target="_blank" href="https://github.com/LesliTech/Lesli/actions">
|
|
11
|
-
<img
|
|
14
|
+
<a target="_blank" href="https://github.com/LesliTech/Lesli/actions/workflows/lesli-ci-tests.yaml">
|
|
15
|
+
<img
|
|
16
|
+
alt="Lesli test status"
|
|
17
|
+
src="https://img.shields.io/github/actions/workflow/status/LesliTech/Lesli/lesli-ci-tests.yaml?branch=master&style=for-the-badge&logo=github&label=tests">
|
|
12
18
|
</a>
|
|
13
19
|
<a target="_blank" href="https://rubygems.org/gems/lesli">
|
|
14
20
|
<img alt="Gem Version" src="https://img.shields.io/gem/v/lesli?style=for-the-badge&logo=ruby">
|
|
@@ -21,114 +27,173 @@
|
|
|
21
27
|
</a>
|
|
22
28
|
</div>
|
|
23
29
|
|
|
24
|
-
<hr/>
|
|
25
30
|
<br />
|
|
26
31
|
|
|
27
32
|
<div align="center">
|
|
28
33
|
<img
|
|
29
34
|
style="width:100%;max-width:800px;border-radius:6px;"
|
|
30
|
-
alt="Lesli
|
|
35
|
+
alt="Lesli application dashboard"
|
|
36
|
+
src="./docs/images/screenshot.png" />
|
|
31
37
|
</div>
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
<br />
|
|
34
40
|
|
|
35
41
|
## Introduction
|
|
36
42
|
|
|
37
43
|
Lesli is an open-source framework built to create scalable, secure, and modular software products.
|
|
38
44
|
|
|
39
|
-
Built on
|
|
40
|
-
|
|
41
|
-
Lesli is not a starter kit, admin panel, or boilerplate.
|
|
45
|
+
Built on **Ruby on Rails, Hotwire, and Tailwind CSS**, with support for **SQLite and PostgreSQL**, Lesli provides the infrastructure, architecture, and reusable components needed to build production-ready SaaS platforms faster.
|
|
42
46
|
|
|
43
|
-
It is a **modular foundation** that allows you to build applications using independent engines, shared infrastructure, and proven SaaS patterns.
|
|
47
|
+
Lesli is not a starter kit, admin panel, or boilerplate. It is a **modular foundation** that allows you to build applications using independent engines, shared infrastructure, and proven SaaS patterns.
|
|
44
48
|
|
|
45
|
-
With Lesli, you focus on features
|
|
49
|
+
With Lesli, you focus on features—not infrastructure.
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
<br />
|
|
48
52
|
|
|
49
53
|
## Why Lesli?
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
- Flexible authentication, detailed privileges, and access control
|
|
53
|
-
- System auditing with activities and version tracking
|
|
54
|
-
- Attachments that can be added to any resource
|
|
55
|
-
- Full-text search across modules and records
|
|
56
|
-
- Multi-language support for both core and custom modules
|
|
57
|
-
- Designed for long-term SaaS scalability and maintainability
|
|
55
|
+
Lesli core and its official engines provide:
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
- Modular architecture powered by standard Rails engines
|
|
58
|
+
- Flexible authentication, detailed privileges, and access control
|
|
59
|
+
- System auditing with activities and version tracking
|
|
60
|
+
- Attachments that can be added to any resource
|
|
61
|
+
- Full-text search across modules and records
|
|
62
|
+
- Multi-language support for both core and custom modules
|
|
63
|
+
- An architecture designed for long-term SaaS scalability and maintainability
|
|
64
|
+
|
|
65
|
+
<br />
|
|
66
|
+
|
|
67
|
+
## Try Lesli
|
|
68
|
+
|
|
69
|
+
- [Try the online demo](https://demo.lesli.dev/)
|
|
70
|
+
- [Run the Docker demo](https://github.com/LesliTech/lesli-docker-demo)
|
|
71
|
+
|
|
72
|
+
<br />
|
|
60
73
|
|
|
61
74
|
## Quick Start
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
### Requirements
|
|
77
|
+
|
|
78
|
+
- Ruby 3.2 or newer
|
|
79
|
+
- Rails 8.1
|
|
80
|
+
- SQLite, included in the default Rails application
|
|
81
|
+
|
|
82
|
+
Optional tooling:
|
|
83
|
+
|
|
84
|
+
- PostgreSQL for applications that prefer it over SQLite
|
|
85
|
+
- Node.js 20.x for advanced frontend development tasks
|
|
86
|
+
|
|
87
|
+
The default Lesli application can be built and run using only Rails and the Lesli gems.
|
|
88
|
+
|
|
89
|
+
### Create the application
|
|
90
|
+
|
|
91
|
+
Create a Rails application using Rails' default SQLite database:
|
|
92
|
+
|
|
93
|
+
```shell
|
|
94
|
+
rails new LesliApp
|
|
95
|
+
cd LesliApp
|
|
66
96
|
```
|
|
67
97
|
|
|
68
|
-
###
|
|
98
|
+
### Install Lesli
|
|
99
|
+
|
|
100
|
+
Add the gem and run the installation generator:
|
|
69
101
|
|
|
70
102
|
```shell
|
|
71
|
-
# Add Lesli
|
|
72
103
|
bundle add lesli
|
|
104
|
+
bin/rails generate lesli:install
|
|
105
|
+
```
|
|
73
106
|
|
|
74
|
-
|
|
75
|
-
rails generate lesli:install
|
|
107
|
+
### Prepare the database
|
|
76
108
|
|
|
77
|
-
|
|
78
|
-
rake lesli:db:reset
|
|
109
|
+
Prepare the development database and verify the installation:
|
|
79
110
|
|
|
80
|
-
|
|
81
|
-
rails
|
|
111
|
+
```shell
|
|
112
|
+
bin/rails lesli:db:prepare
|
|
113
|
+
bin/rails lesli:status
|
|
82
114
|
```
|
|
83
115
|
|
|
84
|
-
|
|
116
|
+
> [!WARNING]
|
|
117
|
+
> `bin/rails lesli:db:rebuild` drops, recreates, migrates, and seeds the database. Use it only when you intentionally want to replace the current development data.
|
|
118
|
+
|
|
119
|
+
### Start the server
|
|
85
120
|
|
|
86
121
|
```shell
|
|
87
|
-
|
|
88
|
-
|
|
122
|
+
bin/rails server
|
|
123
|
+
```
|
|
89
124
|
|
|
90
|
-
|
|
91
|
-
gem "lesli", path: "engines/Lesli"
|
|
125
|
+
Then visit [http://127.0.0.1:3000/login](http://127.0.0.1:3000/login).
|
|
92
126
|
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
<br />
|
|
128
|
+
|
|
129
|
+
## Development
|
|
95
130
|
|
|
96
|
-
|
|
97
|
-
rails generate lesli:install
|
|
131
|
+
### Development template
|
|
98
132
|
|
|
99
|
-
|
|
100
|
-
rake lesli:db:reset
|
|
133
|
+
To generate a complete demo application from the current development source:
|
|
101
134
|
|
|
102
|
-
|
|
103
|
-
rails
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
rails s --environment=development
|
|
107
|
-
RAILS_SERVE_STATIC_FILES=true rails s --environment=production
|
|
135
|
+
```shell
|
|
136
|
+
rails new LesliDemo \
|
|
137
|
+
--skip-bundle \
|
|
138
|
+
-m https://raw.githubusercontent.com/LesliTech/Lesli/master/templates/template-dev.rb
|
|
108
139
|
```
|
|
109
140
|
|
|
110
|
-
|
|
141
|
+
> [!NOTE]
|
|
142
|
+
> The development template clones the mutable `master` branch of every Lesli repository. Use released gems for production applications.
|
|
143
|
+
|
|
144
|
+
### Manual local setup
|
|
145
|
+
|
|
146
|
+
To work on Lesli from a host Rails application, clone it into the application's `engines` directory:
|
|
147
|
+
|
|
148
|
+
```shell
|
|
149
|
+
cd RailsApp
|
|
150
|
+
mkdir -p engines
|
|
151
|
+
git clone https://github.com/LesliTech/Lesli.git engines/Lesli
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Reference the local engine from the host application's `Gemfile`:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
gem "lesli", path: "engines/Lesli"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Install and initialize the local engine:
|
|
161
|
+
|
|
162
|
+
```shell
|
|
163
|
+
bundle install
|
|
164
|
+
bin/rails generate lesli:install
|
|
165
|
+
bin/rails lesli:db:prepare
|
|
166
|
+
bin/rails server
|
|
167
|
+
```
|
|
111
168
|
|
|
112
|
-
|
|
169
|
+
For additional server configurations, use the standard Rails server options:
|
|
113
170
|
|
|
114
|
-
|
|
115
|
-
|
|
171
|
+
```shell
|
|
172
|
+
bin/rails server --binding=0.0.0.0
|
|
173
|
+
bin/rails server --environment=development
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
<br />
|
|
116
177
|
|
|
178
|
+
## Documentation
|
|
117
179
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
180
|
+
- [Lesli website](https://www.lesli.dev/)
|
|
181
|
+
- [Documentation](https://www.lesli.dev/lesli/)
|
|
182
|
+
- [Contribution guide](./docs/contributing/github.md)
|
|
183
|
+
- [Releases and changelog](https://github.com/LesliTech/Lesli/releases)
|
|
121
184
|
|
|
185
|
+
<br />
|
|
186
|
+
|
|
187
|
+
## Community
|
|
122
188
|
|
|
123
|
-
|
|
189
|
+
- [X: @LesliTech](https://x.com/LesliTech)
|
|
190
|
+
- [hello@lesli.tech](mailto:hello@lesli.tech)
|
|
191
|
+
- [https://www.lesli.tech](https://www.lesli.tech)
|
|
124
192
|
|
|
125
|
-
|
|
126
|
-
* [hello@lesli.tech](hello@lesli.tech)
|
|
127
|
-
* [https://www.lesli.tech](https://www.lesli.tech)
|
|
193
|
+
<br />
|
|
128
194
|
|
|
195
|
+
## License
|
|
129
196
|
|
|
130
|
-
### License
|
|
131
|
-
-------
|
|
132
197
|
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
133
198
|
|
|
134
199
|
This program is free software: you can redistribute it and/or modify
|
|
@@ -142,18 +207,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
142
207
|
GNU General Public License for more details.
|
|
143
208
|
|
|
144
209
|
You should have received a copy of the GNU General Public License
|
|
145
|
-
along with this program. If not, see
|
|
210
|
+
along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
|
|
146
211
|
|
|
147
|
-
|
|
148
|
-
<br />
|
|
149
|
-
<br />
|
|
212
|
+
The complete license text is available in the [license file](./license).
|
|
150
213
|
|
|
151
|
-
|
|
152
|
-
<img width="80" alt="Lesli logo" src="https://cdn.lesli.tech/lesli/brand/app-icon.svg" />
|
|
153
|
-
<h3 align="center" class="mt-0">
|
|
154
|
-
The Open-Source SaaS Development Framework for Ruby on Rails.
|
|
155
|
-
</h3>
|
|
156
|
-
</div>
|
|
214
|
+
---
|
|
157
215
|
|
|
158
216
|
<br />
|
|
159
217
|
<br />
|
|
218
|
+
|
|
219
|
+
<div align="center">
|
|
220
|
+
<img width="80" alt="Lesli icon" src="https://cdn.lesli.tech/lesli/brand/app-icon.svg" />
|
|
221
|
+
<h3 align="center">The Open-Source SaaS Development Framework for Ruby on Rails.</h3>
|
|
222
|
+
</div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lesli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.1.
|
|
4
|
+
version: 5.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Lesli Development Team
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '8.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rails-i18n
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '8.1'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: turbo-rails
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -161,6 +175,7 @@ files:
|
|
|
161
175
|
- Rakefile
|
|
162
176
|
- app/assets/images/lesli/lesli-logo.svg
|
|
163
177
|
- app/assets/stylesheets/lesli/application.css
|
|
178
|
+
- app/assets/stylesheets/lesli/application.tailwind.css
|
|
164
179
|
- app/controllers/lesli/abouts_controller.rb
|
|
165
180
|
- app/controllers/lesli/application_controller.rb
|
|
166
181
|
- app/controllers/lesli/application_devise_controller.rb
|