lesli_admin 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/lesli_admin/admin-logo.svg +1 -0
  3. data/app/assets/javascripts/lesli_admin/application.js +2698 -219
  4. data/app/assets/stylesheets/lesli_admin/application.scss +33 -0
  5. data/app/assets/stylesheets/lesli_admin/users.scss +67 -0
  6. data/app/controllers/lesli_admin/profiles_controller.rb +56 -0
  7. data/app/controllers/lesli_admin/users_controller.rb +13 -19
  8. data/app/helpers/lesli_admin/profiles_helper.rb +4 -0
  9. data/app/models/lesli_admin/account.rb +3 -31
  10. data/app/services/lesli_admin/user_service.rb +17 -52
  11. data/app/views/lesli_admin/profiles/show.html.erb +1 -0
  12. data/app/views/lesli_admin/users/show.html.erb +1 -10
  13. data/config/locales/translations.en.yml +5 -0
  14. data/config/locales/translations.es.yml +5 -0
  15. data/config/routes.rb +4 -7
  16. data/db/migrate/0101000110_create_lesli_admin_accounts.rb +42 -0
  17. data/lib/lesli_admin/engine.rb +31 -0
  18. data/lib/lesli_admin/version.rb +2 -1
  19. data/lib/vue/application.js +15 -98
  20. data/lib/vue/apps/account/components/form-information.vue +1 -1
  21. data/lib/vue/apps/account/show.vue +3 -3
  22. data/lib/vue/apps/dashboard/show.vue +6 -9
  23. data/lib/vue/apps/profile/show.vue +29 -18
  24. data/lib/vue/apps/users/components/information-card.vue +4 -7
  25. data/lib/vue/apps/users/components/information-form.vue +4 -7
  26. data/lib/vue/apps/users/index.vue +9 -4
  27. data/lib/vue/apps/users/show.vue +25 -10
  28. data/lib/vue/stores/translations.json +38 -0
  29. data/lib/vue/stores/user.js +17 -5
  30. data/lib/vue/stores/users.js +25 -0
  31. data/readme.md +61 -18
  32. metadata +27 -12
  33. data/app/assets/stylesheets/lesli_admin/application.css +0 -15
  34. data/app/models/lesli_admin/dashboard.rb +0 -4
  35. data/app/models/lesli_admin/user.rb +0 -4
  36. data/lib/vue/stores/account.js +0 -113
  37. data/lib/vue/stores/descriptor.js +0 -116
  38. data/lib/vue/stores/descriptors.js +0 -167
  39. data/lib/vue/stores/integration.js +0 -103
  40. data/lib/vue/stores/role.js +0 -243
  41. data/lib/vue/stores/systemController.js +0 -67
  42. /data/lib/vue/stores/{accountSettings.js → account_settings.js} +0 -0
@@ -36,7 +36,7 @@ import { computed, onMounted } from "vue"
36
36
 
37
37
 
38
38
  // · import lesli stores
39
- import { useAccount } from "LesliAdmin/stores/account"
39
+ import { useAccount } from "Lesli/stores/account"
40
40
 
41
41
 
42
42
  // · import account components
@@ -69,7 +69,7 @@ onMounted(() => {
69
69
 
70
70
  </script>
71
71
  <template>
72
- <lesli-content>
72
+ <lesli-application-container>
73
73
  <lesli-header title="Account information">
74
74
  </lesli-header>
75
75
  <lesli-tabs v-model="tab">
@@ -77,7 +77,7 @@ onMounted(() => {
77
77
  <form-information></form-information>
78
78
  </lesli-tab-item>
79
79
  </lesli-tabs>
80
- </lesli-content>
80
+ </lesli-application-container>
81
81
  <!--
82
82
  <lesli-tab-item title="Address" icon="location_on">
83
83
  <address-form></address-form>
@@ -1,33 +1,30 @@
1
1
  <script setup>
2
2
 
3
-
4
- import dashboardComponent from "Lesli/layouts/dashboard-component.vue"
5
3
  import { lesliChartLine } from "lesli-vue/components"
6
4
 
7
5
  </script>
8
6
  <template>
9
- <lesli-application>
7
+ <lesli-application-container>
10
8
  <lesli-header title="Dashboard"></lesli-header>
11
-
12
9
  <div class="columns">
13
10
  <div class="column">
14
- <lesli-content>
11
+ <lesli-application-component>
15
12
  <lesli-chart-line
16
13
  title="My daily activity graph"
17
14
  :series="[{ data:[4, 1, 4, 2, 5] }]"
18
15
  :labels="['Monday','Tuesday','Wednesday', 'Thursday', 'Friday']">
19
16
  </lesli-chart-line>
20
- </lesli-content>
17
+ </lesli-application-component>
21
18
  </div>
22
19
  <div class="column">
23
- <lesli-content>
20
+ <lesli-application-component>
24
21
  <lesli-chart-line
25
22
  title="My daily activity graph"
26
23
  :series="[{ name: 'Last week', data:[4, 1, 4, 2, 5] }, { name: 'Current week', data:[3, 2, 5, 4, 2] }]"
27
24
  :labels="['Monday','Tuesday','Wednesday', 'Thursday', 'Friday']">
28
25
  </lesli-chart-line>
29
- </lesli-content>
26
+ </lesli-application-component>
30
27
  </div>
31
28
  </div>
32
- </lesli-application>
29
+ </lesli-application-container>
33
30
  </template>
@@ -18,18 +18,17 @@ GNU General Public License for more details.
18
18
  You should have received a copy of the GNU General Public License
19
19
  along with this program. If not, see http://www.gnu.org/licenses/.
20
20
 
21
- Lesli · Your Smart Business Assistant.
21
+ Lesli · Ruby on Rails SaaS Development Framework.
22
22
 
23
23
  Made with ♥ by https://www.lesli.tech
24
24
  Building a better future, one line of code at a time.
25
25
 
26
26
  @contact hello@lesli.tech
27
- @website https://lesli.tech
27
+ @website https://www.lesli.tech
28
28
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
29
29
 
30
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
31
31
  // ·
32
-
33
32
  */
34
33
 
35
34
 
@@ -40,12 +39,19 @@ import { useRouter, useRoute } from 'vue-router'
40
39
 
41
40
 
42
41
  // · import lesli stores
43
- import { useUser } from "CloudAdmin/stores/user"
44
- import { useProfile } from "Lesli/shared/stores/profile"
42
+ import { useUser } from "LesliAdmin/stores/user"
43
+
44
+
45
+ // · implement stores
46
+ const storeUser = useUser()
47
+ const router = useRouter()
48
+ const route = useRoute()
45
49
 
46
50
 
47
51
  // · import profile components
48
- import informationCard from "CloudAdmin/apps/users/components/information-card.vue"
52
+ import informationCard from "LesliAdmin/apps/users/components/information-card.vue"
53
+ import informationForm from "LesliAdmin/apps/users/components/information-form.vue"
54
+ /*
49
55
  import informationForm from "CloudAdmin/apps/users/components/information-form.vue"
50
56
 
51
57
  import managementSession from "CloudAdmin/apps/users/components/management-sessions.vue"
@@ -53,6 +59,8 @@ import managementSecurity from "CloudAdmin/apps/users/components/management-secu
53
59
 
54
60
  import integrationsInformation from "CloudAdmin/apps/users/components/integrations-information.vue"
55
61
  import settings from "CloudAdmin/apps/users/components/management-settings.vue"
62
+ */
63
+
56
64
  /*
57
65
  import cardInformation from "../users/components/information-card.vue"
58
66
  import formInformation from "../users/components/information-form.vue"
@@ -64,12 +72,6 @@ import changeEmail from "./components/change-email.vue"
64
72
  */
65
73
 
66
74
 
67
- // · implement stores
68
- const storeUser = useUser()
69
- const router = useRouter()
70
- const route = useRoute()
71
-
72
-
73
75
  // · translations
74
76
  const translations = {
75
77
  core: {
@@ -87,19 +89,28 @@ const tab = ref(0)
87
89
  // ·
88
90
  onMounted(() => {
89
91
  storeUser.getUser()
90
- storeUser.getOptions()
92
+ //storeUser.getOptions()
91
93
  })
92
94
 
93
95
  </script>
94
96
  <template>
95
- <application-component>
97
+ <lesli-application-container>
96
98
  <information-card></information-card>
99
+ <lesli-tabs v-model="tab">
100
+ <lesli-tab-item icon="info_outline" title="Information">
101
+ <information-form></information-form>
102
+ </lesli-tab-item>
103
+ </lesli-tabs>
104
+ </lesli-application-container>
105
+ <!--
106
+ <application-component>
107
+
97
108
  <lesli-tabs v-model="tab" v-if="storeUser.user.id">
98
109
  <lesli-tab-item icon="info_outline" :title="translations.core.users.view_tab_title_information">
99
110
  <information-form></information-form>
100
111
  </lesli-tab-item>
101
112
  <lesli-tab-item icon="security" :title="translations.core.users.view_tab_title_roles_and_privileges">
102
- <!--form-roles></form-roles -->
113
+ < ! - -form-roles></form-roles - - >
103
114
  </lesli-tab-item>
104
115
  <lesli-tab-item icon="lock_outline" :title="translations.core.users.view_tab_title_security || 'Security'">
105
116
  <management-security></management-security>
@@ -108,11 +119,11 @@ onMounted(() => {
108
119
  <management-session></management-session>
109
120
  </lesli-tab-item>
110
121
  <lesli-tab-item icon="settings" :title="translations.core.users.view_tab_title_settings">
111
- <!--settings></settings-->
122
+ < ! - - settings></settings - - >
112
123
  </lesli-tab-item>
113
124
  </lesli-tabs>
114
125
  </application-component>
115
- <!--
126
+
116
127
  <section class="application-component">
117
128
  <information-card></information-card>
118
129
  <lesli-tabs v-if="storeUser.user.id">
@@ -17,7 +17,7 @@ GNU General Public License for more details.
17
17
  You should have received a copy of the GNU General Public License
18
18
  along with this program. If not, see http://www.gnu.org/licenses/.
19
19
 
20
- Lesli · Ruby on Rails Development Platform.
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
21
 
22
22
  Made with ♥ by https://www.lesli.tech
23
23
  Building a better future, one line of code at a time.
@@ -26,16 +26,13 @@ Building a better future, one line of code at a time.
26
26
  @website https://www.lesli.tech
27
27
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
28
 
29
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
30
  // ·
31
31
  */
32
32
 
33
33
 
34
- // · import vue tools
35
-
36
-
37
34
  // · import lesli stores
38
- import { useUser } from "LesliApp/administration/stores/user"
35
+ import { useUser } from "Lesli/stores/user"
39
36
 
40
37
 
41
38
  // · implement stores
@@ -52,7 +49,7 @@ const translations = {
52
49
 
53
50
  </script>
54
51
  <template>
55
- <div class="information-card mb-5">
52
+ <div class="user-information-card mb-5">
56
53
  <div class="media is-align-items-center">
57
54
  <div class="media-left">
58
55
  <figure class="image is-128x128">
@@ -1,6 +1,5 @@
1
1
  <script setup>
2
2
  /*
3
-
4
3
  Lesli
5
4
 
6
5
  Copyright (c) 2023, Lesli Technologies, S. A.
@@ -18,28 +17,26 @@ GNU General Public License for more details.
18
17
  You should have received a copy of the GNU General Public License
19
18
  along with this program. If not, see http://www.gnu.org/licenses/.
20
19
 
21
- Lesli · Your Smart Business Assistant.
20
+ Lesli · Ruby on Rails SaaS Development Framework.
22
21
 
23
22
  Made with ♥ by https://www.lesli.tech
24
23
  Building a better future, one line of code at a time.
25
24
 
26
25
  @contact hello@lesli.tech
27
- @website https://lesli.tech
26
+ @website https://www.lesli.tech
28
27
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
29
28
 
30
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
31
30
  // ·
32
-
33
31
  */
34
32
 
35
33
 
36
-
37
34
  // · import vue tools
38
35
  import { onMounted, inject } from "vue"
39
36
 
40
37
 
41
38
  // · import lesli stores
42
- import { useUser } from "LesliApp/administration/stores/user"
39
+ import { useUser } from "Lesli/stores/user"
43
40
 
44
41
 
45
42
  // · import vue router composable
@@ -54,7 +54,9 @@ const storeUsers = useUsers()
54
54
  const translations = {
55
55
  core: {
56
56
  roles: I18n.t("core.roles"),
57
- users: I18n.t("core.users"),
57
+ users: {
58
+ view_text_title_users: i18n.t('lesli_admin.users.title_users')
59
+ },
58
60
  shared: I18n.t("core.shared")
59
61
  }
60
62
  }
@@ -96,6 +98,7 @@ const columns = [{
96
98
  // ·
97
99
  const selection = ref()
98
100
 
101
+
99
102
  // · defining props
100
103
  const props = defineProps({
101
104
  appMountPath: {
@@ -123,7 +126,7 @@ function showUser(user) {
123
126
  }
124
127
  </script>
125
128
  <template>
126
- <lesli-application>
129
+ <lesli-application-container>
127
130
  <lesli-header :title="translations.core.users.view_text_title_users + ' (' +storeUsers.index.pagination.total+ ')' ">
128
131
  <lesli-button icon="add" :to="url.root(props.appMountPath+`/new`)">
129
132
  {{ translations.core.users.view_text_add_user }}
@@ -136,6 +139,8 @@ function showUser(user) {
136
139
  </lesli-button>
137
140
  </lesli-header>
138
141
 
142
+ <lesli-toolbar>
143
+ </lesli-toolbar>
139
144
  <!--
140
145
  <lesli-toolbar
141
146
  @search="storeUsers.search"
@@ -165,7 +170,7 @@ function showUser(user) {
165
170
  :columns="columns"
166
171
  :records="storeUsers.index.records"
167
172
  :pagination="storeUsers.index.pagination"
168
- :link="(user) => url.root(props.appMountPath+`/${user.id}`).s"
173
+ :link="(user) => url.admin('users/:id', user.id)"
169
174
  @paginate="storeUsers.paginateIndex"
170
175
  @sort="storeUsers.sortIndex">
171
176
 
@@ -197,5 +202,5 @@ function showUser(user) {
197
202
  </a>
198
203
  </template>
199
204
  </lesli-table>
200
- </lesli-application>
205
+ </lesli-application-container>
201
206
  </template>
@@ -17,7 +17,7 @@ GNU General Public License for more details.
17
17
  You should have received a copy of the GNU General Public License
18
18
  along with this program. If not, see http://www.gnu.org/licenses/.
19
19
 
20
- Lesli · Ruby on Rails Development Platform.
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
21
 
22
22
  Made with ♥ by https://www.lesli.tech
23
23
  Building a better future, one line of code at a time.
@@ -26,7 +26,7 @@ Building a better future, one line of code at a time.
26
26
  @website https://www.lesli.tech
27
27
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
28
 
29
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
30
  // ·
31
31
  */
32
32
 
@@ -37,23 +37,28 @@ import { useRouter, useRoute } from 'vue-router'
37
37
 
38
38
 
39
39
  // · import lesli stores
40
- import { useUser } from "CloudAdmin/stores/user"
40
+ import { useUser } from "LesliAdmin/stores/user"
41
+
42
+
43
+ // · implement stores
44
+ const storeUser = useUser()
45
+ const router = useRouter()
46
+ const route = useRoute()
41
47
 
42
48
 
43
49
  // · import profile components
44
- import informationCard from "CloudAdmin/apps/users/components/information-card.vue"
50
+ import informationCard from "LesliAdmin/apps/users/components/information-card.vue"
51
+ import informationForm from "LesliAdmin/apps/users/components/information-form.vue"
52
+ /*
45
53
  import informationForm from "CloudAdmin/apps/users/components/information-form.vue"
46
54
 
47
55
  import managementRoles from "CloudAdmin/apps/users/components/management-roles.vue"
48
56
  import managementSession from "CloudAdmin/apps/users/components/management-sessions.vue"
49
57
  import managementSecurity from "CloudAdmin/apps/users/components/management-security.vue"
50
58
  import managementSettings from "CloudAdmin/apps/users/components/management-settings.vue"
59
+ */
51
60
 
52
61
 
53
- // · implement stores
54
- const storeUser = useUser()
55
- const router = useRouter()
56
- const route = useRoute()
57
62
 
58
63
 
59
64
  // · translations
@@ -72,12 +77,21 @@ const tab = ref(0)
72
77
 
73
78
  // · initializing
74
79
  onMounted(() => {
75
- storeUser.$reset()
76
- storeUser.getOptions()
80
+ // storeUser.$reset()
81
+ // storeUser.getOptions()
77
82
  storeUser.getUser(route.params?.id)
78
83
  })
79
84
  </script>
80
85
  <template>
86
+ <lesli-application-container>
87
+ <information-card></information-card>
88
+ <lesli-tabs v-model="tab">
89
+ <lesli-tab-item icon="info_outline" title="Information">
90
+ <information-form></information-form>
91
+ </lesli-tab-item>
92
+ </lesli-tabs>
93
+ </lesli-application-container>
94
+ <!--
81
95
  <application-component>
82
96
  <information-card></information-card>
83
97
  <lesli-tabs v-model="tab" v-if="storeUser.user.id">
@@ -98,4 +112,5 @@ onMounted(() => {
98
112
  </lesli-tab-item>
99
113
  </lesli-tabs>
100
114
  </application-component>
115
+ -->
101
116
  </template>
@@ -0,0 +1,38 @@
1
+ {
2
+ "en": {
3
+ "lesli": {
4
+ "shared": {
5
+ "title_lesli": ":lesli.shared.title_lesli:"
6
+ },
7
+ "users": {
8
+ "title_users": "Users"
9
+ }
10
+ },
11
+ "lesli_admin": {
12
+ "shared": {
13
+ "title_lesli": ":lesli.shared.title_lesli:"
14
+ },
15
+ "users": {
16
+ "title_users": "Users"
17
+ }
18
+ }
19
+ },
20
+ "es": {
21
+ "lesli": {
22
+ "shared": {
23
+ "title_lesli": "Lesli en español "
24
+ },
25
+ "users": {
26
+ "title_users": "Usuarios"
27
+ }
28
+ },
29
+ "lesli_admin": {
30
+ "shared": {
31
+ "title_lesli": "Lesli en español "
32
+ },
33
+ "users": {
34
+ "title_users": "Usuarios"
35
+ }
36
+ }
37
+ }
38
+ }
@@ -85,12 +85,14 @@ export const useUser = defineStore("lesli.user", {
85
85
 
86
86
  getUser(id=null) {
87
87
 
88
- // get the profile by default
89
- let url = this.url.lesli("profile")
88
+ let url = this.url.admin("profile")
90
89
 
91
- // get an specifick user if id is provided
92
- if (id) { url = this.url.lesli("users/:id", id) }
90
+ if (id !== null) {
91
+ console.log("aqui")
92
+ url = this.url.admin("users/:id", id)
93
+ }
93
94
 
95
+ // get an specifick user if id is provided
94
96
  this.http.get(url).then(result => {
95
97
  this.user = result
96
98
  this.user.password = ""
@@ -110,7 +112,17 @@ export const useUser = defineStore("lesli.user", {
110
112
 
111
113
  putUser() {
112
114
  this.http.put(this.url.lesli("users/:id", this.user.id), {
113
- user: this.user
115
+ user: {
116
+ active: this.user.active,
117
+ alias: this.user.alias,
118
+ first_name: this.user.first_name,
119
+ last_name: this.user.last_name,
120
+ telephone: this.user.telephone,
121
+ detail_attributes: {
122
+ title: this.user.detail_attributes.title,
123
+ salutation: this.user.detail_attributes.salutation
124
+ }
125
+ }
114
126
  }).then(result => {
115
127
  this.msg.success(I18n.t("core.users.messages_success_operation"))
116
128
  }).catch(error => {
@@ -128,6 +128,31 @@ export const useUsers = defineStore("administration.users", {
128
128
  })
129
129
  },
130
130
 
131
+ getUser(id=null) {
132
+
133
+ // get the profile by default
134
+ let url = this.url.lesli("profile")
135
+
136
+ // get an specifick user if id is provided
137
+ if (id) { url = this.url.lesli("users/:id", id) }
138
+
139
+ this.http.get(url).then(result => {
140
+ this.user = result
141
+ this.user.password = ""
142
+ this.user.password_confirmation = ""
143
+
144
+ this.language = result.locale ? result.locale.value : this.language
145
+
146
+ // Backend should return the list of roles ordered by object level permission
147
+ this.role_names = result.roles[0].name
148
+
149
+ }).catch(error => {
150
+ this.msg.danger(I18n.t("core.shared.messages_danger_internal_error"))
151
+ }).finally(() => {
152
+ this.loading = false
153
+ })
154
+ },
155
+
131
156
  postUsers() {
132
157
  return this.http.post(this.url.admin("users"), {
133
158
  user: this.user
data/readme.md CHANGED
@@ -1,28 +1,71 @@
1
- # LesliAdmin
2
- Short description and motivation.
1
+ <p align="center">
2
+ <img width="90" alt="LesliCloud logo" src="./app/assets/images/lesli_admin/admin-logo.svg" />
3
+ <h3 align="center">Administration area for the Lesli Framework.</h3>
4
+ </p>
3
5
 
4
- ## Usage
5
- How to use my plugin.
6
+ <hr/>
7
+ <p align="center">
8
+ <a target="blank" href="https://rubygems.org/gems/lesli_admin">
9
+ <img src="https://badge.fury.io/rb/lesli_admin.svg" alt="Gem Version" height="24">
10
+ </a>
11
+ </p>
12
+ <hr/>
6
13
 
7
- ## Installation
8
- Add this line to your application's Gemfile:
14
+ ### Quick start
9
15
 
10
- ```ruby
11
- gem "lesli_admin"
16
+ ```shell
17
+ # Add LesliAdmin engine
18
+ bundle add lesli_admin
12
19
  ```
13
20
 
14
- And then execute:
15
- ```bash
16
- $ bundle
21
+ ```shell
22
+ # Setup database
23
+ rake lesli:db:setup
17
24
  ```
18
25
 
19
- Or install it yourself as:
20
- ```bash
21
- $ gem install lesli_admin
26
+ ```ruby
27
+ # Load LesliAdmin
28
+ Rails.application.routes.draw do
29
+ mount LesliAdmin::Engine => "/admin"
30
+ end
22
31
  ```
23
32
 
24
- ## Contributing
25
- Contribution directions go here.
26
33
 
27
- ## License
28
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
34
+ ### Documentation
35
+ * [website](https://www.lesli.dev/admin/)
36
+ * [database](./docs/database.md)
37
+ * [documentation](https://www.lesli.dev/documentation/)
38
+
39
+
40
+ ### Get in touch with Lesli
41
+
42
+ * [Website: https://www.lesli.tech](https://www.lesli.tech)
43
+ * [Email: hello@lesli.tech](hello@lesli.tech)
44
+ * [Twitter: @LesliTech](https://twitter.com/LesliTech)
45
+
46
+
47
+ ### License
48
+ -------
49
+ Copyright (c) 2023, Lesli Technologies, S. A.
50
+
51
+ This program is free software: you can redistribute it and/or modify
52
+ it under the terms of the GNU General Public License as published by
53
+ the Free Software Foundation, either version 3 of the License, or
54
+ (at your option) any later version.
55
+
56
+ This program is distributed in the hope that it will be useful,
57
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
58
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59
+ GNU General Public License for more details.
60
+
61
+ You should have received a copy of the GNU General Public License
62
+ along with this program. If not, see http://www.gnu.org/licenses/.
63
+
64
+ <hr />
65
+ <br />
66
+
67
+ <p align="center">
68
+ <img width="200" alt="Lesli logo" src="https://cdn.lesli.tech/lesli/brand/app-logo.svg" />
69
+ <h4 align="center">Ruby on Rails SaaS Development Framework.</h4>
70
+ </p>
71
+