lesli_bell 1.0.1 → 1.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.
- checksums.yaml +4 -4
- data/Rakefile +2 -1
- data/app/assets/stylesheets/lesli_bell/application.tailwind.css +2 -0
- data/app/models/lesli_bell/announcement/user.rb +2 -2
- data/app/views/lesli_bell/notifications/index.html.erb +49 -33
- data/db/seeds.rb +1 -1
- data/lib/lesli_bell/engine.rb +0 -3
- data/lib/lesli_bell/version.rb +2 -2
- data/readme.md +165 -48
- metadata +4 -14
- data/lib/scss/application.scss +0 -0
- data/lib/vue/application.js +0 -73
- data/lib/vue/apps/announcements/components/form.vue +0 -237
- data/lib/vue/apps/announcements/index.vue +0 -145
- data/lib/vue/apps/announcements/new.vue +0 -48
- data/lib/vue/apps/notifications/components/notification-form.vue +0 -171
- data/lib/vue/apps/notifications/index.vue +0 -144
- data/lib/vue/apps/notifications/new.vue +0 -56
- data/lib/vue/stores/announcement.js +0 -147
- data/lib/vue/stores/notification.js +0 -148
- data/lib/vue/stores/translations.json +0 -232
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 230a04119c05f73f87f6fefcffc2f930301763388864f34fa6feb3bcf510598b
|
|
4
|
+
data.tar.gz: 654752028cdbe7d683f2c30a8060156f73b5e4a78de4d6dec07a5b1916cc3aa8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6d3dd2e9d2a37278dc122df57f2ab02b344f1c56d92167e4c4ae5eecef40badfda09cc39d8d4716ecccb72b144e111a7c0d54e9c95b300759f9784d630843fc
|
|
7
|
+
data.tar.gz: 77dbd210d847f72c9c923f1c010c73ee8453e83f544876a2df1a03edf3af3528530eccb881c0fb23a404fac81da316ea8b0821215818ff7f415af0422c58f8cd
|
data/Rakefile
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer utilities{.absolute{position:absolute}.relative{position:relative}.top-3{top:calc(var(--spacing) * 3)}.right-3{right:calc(var(--spacing) * 3)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.block{display:block}.inline-flex{display:inline-flex}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.items-center{align-items:center}.justify-center{justify-content:center}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-danger-300{border-color:var(--lesli-color-danger-300)}.border-info-300{border-color:var(--lesli-color-info-300)}.border-neutral-300{border-color:var(--lesli-color-neutral-300)}.border-success-300{border-color:var(--lesli-color-success-300)}.border-warning-300{border-color:var(--lesli-color-warning-300)}.bg-danger-50{background-color:var(--lesli-color-danger-50)}.bg-info-50{background-color:var(--lesli-color-info-50)}.bg-neutral-50{background-color:var(--lesli-color-neutral-50)}.bg-success-50{background-color:var(--lesli-color-success-50)}.bg-warning-50{background-color:var(--lesli-color-warning-50)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-3{padding-block:calc(var(--spacing) * 3)}.pr-14{padding-right:calc(var(--spacing) * 14)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.break-all{word-break:break-all}.text-danger-700{color:var(--lesli-color-danger-700)}.text-danger-900{color:var(--lesli-color-danger-900)}.text-info-700{color:var(--lesli-color-info-700)}.text-info-900{color:var(--lesli-color-info-900)}.text-neutral-700{color:var(--lesli-color-neutral-700)}.text-neutral-900{color:var(--lesli-color-neutral-900)}.text-success-700{color:var(--lesli-color-success-700)}.text-success-900{color:var(--lesli-color-success-900)}.text-warning-700{color:var(--lesli-color-warning-700)}.text-warning-900{color:var(--lesli-color-warning-900)}.underline{text-decoration-line:underline}.decoration-current\/40{text-decoration-color:currentColor}@supports (color:color-mix(in lab, red, red)){.decoration-current\/40{-webkit-text-decoration-color:color-mix(in oklab, currentcolor 40%, transparent);-webkit-text-decoration-color:color-mix(in oklab, currentcolor 40%, transparent);text-decoration-color:color-mix(in oklab, currentcolor 40%, transparent)}}.underline-offset-2{text-underline-offset:2px}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-danger-100:hover{background-color:var(--lesli-color-danger-100)}.hover\:bg-info-100:hover{background-color:var(--lesli-color-info-100)}.hover\:bg-neutral-100:hover{background-color:var(--lesli-color-neutral-100)}.hover\:bg-success-100:hover{background-color:var(--lesli-color-success-100)}.hover\:bg-warning-100:hover{background-color:var(--lesli-color-warning-100)}.hover\:decoration-current:hover{text-decoration-color:currentColor}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-current\/40:focus-visible{--tw-ring-color:currentcolor}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-current\/40:focus-visible{--tw-ring-color:color-mix(in oklab, currentcolor 40%, transparent)}}.focus-visible\:ring-danger-500\/40:focus-visible{--tw-ring-color:var(--lesli-color-danger-500)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-danger-500\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--lesli-color-danger-500) 40%, transparent)}}.focus-visible\:ring-info-500\/40:focus-visible{--tw-ring-color:var(--lesli-color-info-500)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-info-500\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--lesli-color-info-500) 40%, transparent)}}.focus-visible\:ring-neutral-500\/40:focus-visible{--tw-ring-color:var(--lesli-color-neutral-500)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-neutral-500\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--lesli-color-neutral-500) 40%, transparent)}}.focus-visible\:ring-success-500\/40:focus-visible{--tw-ring-color:var(--lesli-color-success-500)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-success-500\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--lesli-color-success-500) 40%, transparent)}}.focus-visible\:ring-warning-500\/40:focus-visible{--tw-ring-color:var(--lesli-color-warning-500)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-warning-500\/40:focus-visible{--tw-ring-color:color-mix(in oklab, var(--lesli-color-warning-500) 40%, transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module LesliBell
|
|
2
2
|
class Announcement::User < ApplicationRecord
|
|
3
|
-
belongs_to :announcement
|
|
4
|
-
|
|
3
|
+
belongs_to :announcement
|
|
4
|
+
belongs_to :user, class_name: "::Lesli::User"
|
|
5
5
|
end
|
|
6
6
|
end
|
|
@@ -30,24 +30,6 @@ Building a better future, one line of code at a time.
|
|
|
30
30
|
// ·
|
|
31
31
|
%>
|
|
32
32
|
|
|
33
|
-
<style>
|
|
34
|
-
.notification.is-danger.is-light {
|
|
35
|
-
border: 2px solid hsl(var(--bulma-notification-h), var(--bulma-notification-s), var(--bulma-notification-color-l))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.notification.is-warning.is-light {
|
|
39
|
-
border: 2px solid hsl(var(--bulma-notification-h), var(--bulma-notification-s), var(--bulma-notification-color-l));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.notification.is-success.is-light {
|
|
43
|
-
border: 2px solid hsl(var(--bulma-notification-h), var(--bulma-notification-s), var(--bulma-notification-color-l));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.notification.is-info.is-light {
|
|
47
|
-
border: 2px solid hsl(var(--bulma-notification-h), var(--bulma-notification-s), var(--bulma-notification-color-l));
|
|
48
|
-
}
|
|
49
|
-
</style>
|
|
50
|
-
|
|
51
33
|
<%= render(LesliView::Layout::Container.new("lesli-bell-notifications-index")) do %>
|
|
52
34
|
|
|
53
35
|
<%= render(LesliView::Components::Header.new("Notifications")) do %>
|
|
@@ -56,41 +38,75 @@ border: 2px solid hsl(var(--bulma-notification-h), var(--bulma-notification-s),
|
|
|
56
38
|
|
|
57
39
|
<%= render(LesliView::Components::Toolbar.new) %>
|
|
58
40
|
|
|
59
|
-
<% if @notifications.
|
|
41
|
+
<% if @notifications[:records].empty? %>
|
|
60
42
|
<%= render(LesliView::Elements::Empty.new) %>
|
|
61
43
|
<% else %>
|
|
62
|
-
<section class="
|
|
44
|
+
<section class="space-y-2" aria-label="Notifications">
|
|
63
45
|
<% @notifications[:records].each do |notification| %>
|
|
64
|
-
|
|
46
|
+
<%
|
|
47
|
+
notification_classes, close_button_classes = case notification[:category].to_s
|
|
48
|
+
when "danger"
|
|
49
|
+
[
|
|
50
|
+
"border-danger-300 bg-danger-50 text-danger-900",
|
|
51
|
+
"text-danger-700 hover:bg-danger-100 focus-visible:ring-danger-500/40"
|
|
52
|
+
]
|
|
53
|
+
when "warning"
|
|
54
|
+
[
|
|
55
|
+
"border-warning-300 bg-warning-50 text-warning-900",
|
|
56
|
+
"text-warning-700 hover:bg-warning-100 focus-visible:ring-warning-500/40"
|
|
57
|
+
]
|
|
58
|
+
when "success"
|
|
59
|
+
[
|
|
60
|
+
"border-success-300 bg-success-50 text-success-900",
|
|
61
|
+
"text-success-700 hover:bg-success-100 focus-visible:ring-success-500/40"
|
|
62
|
+
]
|
|
63
|
+
when "info"
|
|
64
|
+
[
|
|
65
|
+
"border-info-300 bg-info-50 text-info-900",
|
|
66
|
+
"text-info-700 hover:bg-info-100 focus-visible:ring-info-500/40"
|
|
67
|
+
]
|
|
68
|
+
else
|
|
69
|
+
[
|
|
70
|
+
"border-neutral-300 bg-neutral-50 text-neutral-900",
|
|
71
|
+
"text-neutral-700 hover:bg-neutral-100 focus-visible:ring-neutral-500/40"
|
|
72
|
+
]
|
|
73
|
+
end
|
|
74
|
+
%>
|
|
75
|
+
|
|
76
|
+
<article class="relative rounded-lg border-2 px-4 py-3 pr-14 shadow-sm <%= notification_classes %>">
|
|
65
77
|
|
|
66
78
|
<!-- Close button -->
|
|
67
79
|
<%= button_to(
|
|
68
|
-
notification_path(notification[:id]),
|
|
69
|
-
method: :put,
|
|
70
|
-
class: "
|
|
71
|
-
form: { class: "
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</span>
|
|
80
|
+
notification_path(notification[:id]),
|
|
81
|
+
method: :put,
|
|
82
|
+
class: "inline-flex size-8 items-center justify-center rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 #{close_button_classes}",
|
|
83
|
+
form: { class: "absolute right-3 top-3" },
|
|
84
|
+
aria: { label: "Mark notification as read" }) do %>
|
|
85
|
+
<span class="material-symbols text-xl" aria-hidden="true">close</span>
|
|
75
86
|
<% end %>
|
|
76
87
|
|
|
77
88
|
<!-- Notification subject -->
|
|
78
|
-
<strong><%= notification[:subject] %></strong>
|
|
89
|
+
<strong class="block text-sm font-semibold"><%= notification[:subject] %></strong>
|
|
79
90
|
|
|
80
91
|
<!-- Notification body (optional) -->
|
|
81
92
|
<% if notification[:body].present? %>
|
|
82
|
-
<p><%= notification[:body] %></p>
|
|
93
|
+
<p class="mt-1 text-sm leading-6"><%= notification[:body] %></p>
|
|
83
94
|
<% end %>
|
|
84
95
|
|
|
85
96
|
<!-- Notification URL (optional) -->
|
|
86
97
|
<% if notification[:url].present? %>
|
|
87
|
-
<p>
|
|
88
|
-
<a
|
|
98
|
+
<p class="mt-2 text-sm">
|
|
99
|
+
<a
|
|
100
|
+
class="break-all font-medium underline decoration-current/40 underline-offset-2 hover:decoration-current focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current/40"
|
|
101
|
+
href="<%= notification[:url] %>"
|
|
102
|
+
target="_blank"
|
|
103
|
+
rel="noopener noreferrer"
|
|
104
|
+
>
|
|
89
105
|
<%= notification[:url] %>
|
|
90
106
|
</a>
|
|
91
107
|
</p>
|
|
92
108
|
<% end %>
|
|
93
|
-
</
|
|
109
|
+
</article>
|
|
94
110
|
<% end %>
|
|
95
111
|
</section>
|
|
96
112
|
<% end %>
|
data/db/seeds.rb
CHANGED
|
@@ -31,7 +31,7 @@ Building a better future, one line of code at a time.
|
|
|
31
31
|
=end
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Termline.info("Loading seeds for: LesliBell #{LesliBell::VERSION} (#{LesliBell::BUILD})")
|
|
35
35
|
|
|
36
36
|
# · load specific environment seeds
|
|
37
37
|
if Rails.env.development? || Lesli.config.demo
|
data/lib/lesli_bell/engine.rb
CHANGED
|
@@ -4,9 +4,6 @@ module LesliBell
|
|
|
4
4
|
|
|
5
5
|
initializer :lesli_bell do |app|
|
|
6
6
|
|
|
7
|
-
# register assets manifest
|
|
8
|
-
config.assets.precompile += %w[lesli_bell_manifest.js]
|
|
9
|
-
|
|
10
7
|
# register engine migrations path
|
|
11
8
|
unless app.root.to_s.match root.to_s
|
|
12
9
|
config.paths["db/migrate"].expanded.each do |expanded_path|
|
data/lib/lesli_bell/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
<img width="100" alt="LesliBell" src="./app/assets/images/lesli_bell/bell-logo.svg" />
|
|
4
|
+
</h1>
|
|
5
|
+
<h3 align="center">Notifications and announcements for the Lesli Framework.</h3>
|
|
4
6
|
</div>
|
|
5
7
|
|
|
6
8
|
<br />
|
|
7
|
-
<hr/>
|
|
8
9
|
|
|
9
10
|
<div align="center">
|
|
10
|
-
<a target="_blank" href="https://github.com/LesliTech/LesliBell/actions">
|
|
11
|
-
<img
|
|
11
|
+
<a target="_blank" href="https://github.com/LesliTech/LesliBell/actions/workflows/lesli-ci-tests.yaml">
|
|
12
|
+
<img
|
|
13
|
+
alt="LesliBell test status"
|
|
14
|
+
src="https://img.shields.io/github/actions/workflow/status/LesliTech/LesliBell/lesli-ci-tests.yaml?branch=master&style=for-the-badge&logo=github&label=tests">
|
|
12
15
|
</a>
|
|
13
16
|
<a target="_blank" href="https://rubygems.org/gems/lesli_bell">
|
|
14
17
|
<img alt="Gem Version" src="https://img.shields.io/gem/v/lesli_bell?style=for-the-badge&logo=ruby">
|
|
@@ -16,83 +19,200 @@
|
|
|
16
19
|
<a target="_blank" href="https://codecov.io/github/LesliTech/LesliBell">
|
|
17
20
|
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/LesliTech/LesliBell?style=for-the-badge&logo=codecov">
|
|
18
21
|
</a>
|
|
22
|
+
<a target="_blank" href="https://sonarcloud.io/project/overview?id=LesliTech_LesliBell">
|
|
23
|
+
<img alt="Sonar Quality Gate" src="https://img.shields.io/sonar/quality_gate/LesliTech_LesliBell?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=Quality">
|
|
24
|
+
</a>
|
|
19
25
|
</div>
|
|
20
26
|
|
|
21
|
-
<hr/>
|
|
22
27
|
<br />
|
|
23
28
|
|
|
24
29
|
<div align="center">
|
|
25
30
|
<img
|
|
26
31
|
style="width:100%;max-width:800px;border-radius:6px;"
|
|
27
|
-
alt="
|
|
32
|
+
alt="LesliBell notifications and announcements"
|
|
33
|
+
src="./docs/images/screenshot.png" />
|
|
28
34
|
</div>
|
|
29
35
|
|
|
36
|
+
---
|
|
37
|
+
|
|
30
38
|
<br />
|
|
31
|
-
|
|
39
|
+
|
|
40
|
+
## Introduction
|
|
41
|
+
|
|
42
|
+
LesliBell is the official notifications and announcements engine for the [Lesli Framework](https://github.com/LesliTech/Lesli).
|
|
43
|
+
|
|
44
|
+
It provides account-scoped communication tools that integrate with Lesli users, permissions, navigation, and shared application infrastructure.
|
|
45
|
+
|
|
32
46
|
<br />
|
|
33
47
|
|
|
34
|
-
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- User notification feeds with unread counts and read status
|
|
51
|
+
- Notification categories for informational, success, warning, and danger messages
|
|
52
|
+
- Delivery-channel metadata for web, push, email, dialog, and mobile notifications
|
|
53
|
+
- Account-scoped announcements
|
|
54
|
+
- Announcement metadata for paths, date ranges, links, and dismissible presentation
|
|
55
|
+
- Pagination and integration with the standard Lesli interface
|
|
56
|
+
|
|
57
|
+
<br />
|
|
58
|
+
|
|
59
|
+
## Try LesliBell
|
|
60
|
+
|
|
61
|
+
- [Try the online demo](https://demo.lesli.dev/)
|
|
62
|
+
- [Run the Docker demo](https://github.com/LesliTech/lesli-docker-demo)
|
|
63
|
+
|
|
64
|
+
<br />
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Requirements
|
|
69
|
+
|
|
70
|
+
- A Rails application with [Lesli](https://rubygems.org/gems/lesli) 5.1 or newer
|
|
71
|
+
- SQLite by default, or PostgreSQL when preferred by the host application
|
|
72
|
+
|
|
73
|
+
### Install LesliBell
|
|
74
|
+
|
|
75
|
+
Add the engine to the host Rails application and prepare its database:
|
|
35
76
|
|
|
36
77
|
```shell
|
|
37
|
-
# Add LesliBell engine gem
|
|
38
78
|
bundle add lesli_bell
|
|
79
|
+
bin/rails lesli:db:prepare
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Mount the engine
|
|
39
83
|
|
|
40
|
-
|
|
41
|
-
|
|
84
|
+
Applications using Lesli's standard router mount LesliBell automatically at `/bell`:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# config/routes.rb
|
|
88
|
+
Rails.application.routes.draw do
|
|
89
|
+
Lesli::Router.mount(self)
|
|
90
|
+
end
|
|
42
91
|
```
|
|
43
92
|
|
|
93
|
+
If the application does not use the standard Lesli router, mount the engine directly:
|
|
94
|
+
|
|
44
95
|
```ruby
|
|
45
|
-
#
|
|
96
|
+
# config/routes.rb
|
|
46
97
|
Rails.application.routes.draw do
|
|
47
98
|
mount LesliBell::Engine => "/bell"
|
|
48
99
|
end
|
|
49
100
|
```
|
|
50
101
|
|
|
102
|
+
Start Rails and visit [http://127.0.0.1:3000/bell](http://127.0.0.1:3000/bell):
|
|
103
|
+
|
|
104
|
+
```shell
|
|
105
|
+
bin/rails server
|
|
106
|
+
```
|
|
107
|
+
|
|
51
108
|
<br />
|
|
52
|
-
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
### Create a notification for the current user
|
|
113
|
+
|
|
114
|
+
LesliBell creates notifications through an account-scoped service. Pass a signed-in Lesli user to the service:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
LesliBell::NotificationService.new(current_user).create_for_me(
|
|
118
|
+
subject: "Your report is ready",
|
|
119
|
+
body: "Open the report to review the latest results.",
|
|
120
|
+
category: :success,
|
|
121
|
+
channel: :web,
|
|
122
|
+
url: "/reports/weekly"
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Notify multiple users
|
|
127
|
+
|
|
128
|
+
Use user IDs that belong to the current account:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
user_ids = current_user.account.users.pluck(:id)
|
|
132
|
+
|
|
133
|
+
LesliBell::NotificationService.new(current_user).create(
|
|
134
|
+
{
|
|
135
|
+
subject: "Scheduled maintenance",
|
|
136
|
+
category: :warning,
|
|
137
|
+
channel: :web
|
|
138
|
+
},
|
|
139
|
+
send_to_user_ids: user_ids
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Unknown categories fall back to `info`; unknown channels fall back to `web`. The notification feed displays `created`, `sent`, and legacy records without a status. Updating `/bell/notifications/:id` marks that notification as read for the current user.
|
|
144
|
+
|
|
145
|
+
See [Notifications](https://www.lesli.dev/engines/bell/notifications/overview/) and [Announcements](https://www.lesli.dev/engines/bell/announcements/overview/) for the data contracts, service behavior, and current route surface.
|
|
146
|
+
|
|
53
147
|
<br />
|
|
54
148
|
|
|
55
|
-
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
Clone LesliBell into the host application's `engines` directory:
|
|
56
152
|
|
|
57
153
|
```shell
|
|
58
|
-
|
|
59
|
-
|
|
154
|
+
cd RailsApp
|
|
155
|
+
mkdir -p engines
|
|
156
|
+
git clone https://github.com/LesliTech/LesliBell.git engines/LesliBell
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Reference the local engine from the host application's `Gemfile`:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
gem "lesli_bell", path: "engines/LesliBell"
|
|
163
|
+
```
|
|
60
164
|
|
|
61
|
-
|
|
62
|
-
gem "lesli", path: "engines/LesliBell"
|
|
165
|
+
Install dependencies, prepare the host database, and start Rails:
|
|
63
166
|
|
|
64
|
-
|
|
167
|
+
```shell
|
|
65
168
|
bundle install
|
|
169
|
+
bin/rails lesli:db:prepare
|
|
170
|
+
bin/rails server
|
|
171
|
+
```
|
|
66
172
|
|
|
67
|
-
|
|
68
|
-
rake lesli:db:setup
|
|
173
|
+
### Tests
|
|
69
174
|
|
|
175
|
+
From a complete Lesli development workspace, run the engine test suite from the LesliBell directory:
|
|
176
|
+
|
|
177
|
+
```shell
|
|
178
|
+
cd engines/LesliBell
|
|
179
|
+
bin/rails test
|
|
70
180
|
```
|
|
71
181
|
|
|
72
|
-
|
|
73
|
-
<hr/>
|
|
74
|
-
<br />
|
|
182
|
+
Run the same suite with the engine coverage profile enabled:
|
|
75
183
|
|
|
76
|
-
|
|
184
|
+
```shell
|
|
185
|
+
COVERAGE=true bin/rails test
|
|
186
|
+
```
|
|
77
187
|
|
|
78
|
-
|
|
79
|
-
* [docker demo](https://github.com/LesliTech/lesli-docker-demo)
|
|
188
|
+
<br />
|
|
80
189
|
|
|
190
|
+
## Documentation
|
|
81
191
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
192
|
+
- [Lesli website](https://www.lesli.dev/)
|
|
193
|
+
- [LesliBell documentation](https://www.lesli.dev/engines/bell/)
|
|
194
|
+
- [Installation](https://www.lesli.dev/engines/bell/about/installation/)
|
|
195
|
+
- [Notifications](https://www.lesli.dev/engines/bell/notifications/overview/)
|
|
196
|
+
- [Announcements](https://www.lesli.dev/engines/bell/announcements/overview/)
|
|
197
|
+
- [Database](https://www.lesli.dev/engines/bell/about/database/)
|
|
198
|
+
- [Translations](https://www.lesli.dev/engines/bell/about/translations/)
|
|
199
|
+
- [Frontend](https://www.lesli.dev/engines/bell/about/frontend/)
|
|
200
|
+
- [Release notes](https://github.com/LesliTech/LesliBell/releases)
|
|
201
|
+
- [Issue tracker](https://github.com/LesliTech/LesliBell/issues)
|
|
202
|
+
- [Source code](https://github.com/LesliTech/LesliBell)
|
|
85
203
|
|
|
204
|
+
<br />
|
|
86
205
|
|
|
87
|
-
|
|
206
|
+
## Community
|
|
88
207
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
208
|
+
- [X: @LesliTech](https://x.com/LesliTech)
|
|
209
|
+
- [hello@lesli.tech](mailto:hello@lesli.tech)
|
|
210
|
+
- [https://www.lesli.tech](https://www.lesli.tech)
|
|
92
211
|
|
|
212
|
+
<br />
|
|
213
|
+
|
|
214
|
+
## License
|
|
93
215
|
|
|
94
|
-
### License
|
|
95
|
-
-------
|
|
96
216
|
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
97
217
|
|
|
98
218
|
This program is free software: you can redistribute it and/or modify
|
|
@@ -106,19 +226,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
106
226
|
GNU General Public License for more details.
|
|
107
227
|
|
|
108
228
|
You should have received a copy of the GNU General Public License
|
|
109
|
-
along with this program. If not, see
|
|
229
|
+
along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
|
|
110
230
|
|
|
111
|
-
|
|
112
|
-
<hr />
|
|
113
|
-
<br />
|
|
114
|
-
<br />
|
|
231
|
+
The complete license text is available in the [license file](./license).
|
|
115
232
|
|
|
116
|
-
|
|
117
|
-
<img width="200" alt="Lesli logo" src="https://cdn.lesli.tech/lesli/brand/app-logo.svg" />
|
|
118
|
-
<h3 align="center" class="mt-0">
|
|
119
|
-
The Open-Source SaaS Development Framework for Ruby on Rails.
|
|
120
|
-
</h3>
|
|
121
|
-
</div>
|
|
233
|
+
---
|
|
122
234
|
|
|
123
235
|
<br />
|
|
124
236
|
<br />
|
|
237
|
+
|
|
238
|
+
<div align="center">
|
|
239
|
+
<img width="80" alt="Lesli icon" src="https://cdn.lesli.tech/lesli/brand/app-icon.svg" />
|
|
240
|
+
<h3 align="center">The Open-Source SaaS Development Framework for Ruby on Rails.</h3>
|
|
241
|
+
</div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lesli_bell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Lesli Development Team
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 5.1.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 5.1.0
|
|
26
26
|
description: Notifications & Announcements System for the Lesli Framework.
|
|
27
27
|
email:
|
|
28
28
|
- hello@lesli.tech
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- app/assets/javascripts/lesli_bell/application.js
|
|
37
37
|
- app/assets/javascripts/lesli_bell/application.js.LICENSE.txt
|
|
38
38
|
- app/assets/stylesheets/lesli_bell/application.css
|
|
39
|
+
- app/assets/stylesheets/lesli_bell/application.tailwind.css
|
|
39
40
|
- app/controllers/lesli_bell/accounts_controller.rb
|
|
40
41
|
- app/controllers/lesli_bell/announcements_controller.rb
|
|
41
42
|
- app/controllers/lesli_bell/application_controller.rb
|
|
@@ -91,18 +92,7 @@ files:
|
|
|
91
92
|
- lib/lesli_bell.rb
|
|
92
93
|
- lib/lesli_bell/engine.rb
|
|
93
94
|
- lib/lesli_bell/version.rb
|
|
94
|
-
- lib/scss/application.scss
|
|
95
95
|
- lib/tasks/lesli_bell_tasks.rake
|
|
96
|
-
- lib/vue/application.js
|
|
97
|
-
- lib/vue/apps/announcements/components/form.vue
|
|
98
|
-
- lib/vue/apps/announcements/index.vue
|
|
99
|
-
- lib/vue/apps/announcements/new.vue
|
|
100
|
-
- lib/vue/apps/notifications/components/notification-form.vue
|
|
101
|
-
- lib/vue/apps/notifications/index.vue
|
|
102
|
-
- lib/vue/apps/notifications/new.vue
|
|
103
|
-
- lib/vue/stores/announcement.js
|
|
104
|
-
- lib/vue/stores/notification.js
|
|
105
|
-
- lib/vue/stores/translations.json
|
|
106
96
|
- license
|
|
107
97
|
- readme.md
|
|
108
98
|
homepage: https://www.lesli.dev/
|
data/lib/scss/application.scss
DELETED
|
File without changes
|
data/lib/vue/application.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
Lesli
|
|
4
|
-
|
|
5
|
-
Copyright (c) 2023, Lesli Technologies, S. A.
|
|
6
|
-
|
|
7
|
-
This program is free software: you can redistribute it and/or modify
|
|
8
|
-
it under the terms of the GNU General Public License as published by
|
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
-
(at your option) any later version.
|
|
11
|
-
|
|
12
|
-
This program is distributed in the hope that it will be useful,
|
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
-
GNU General Public License for more details.
|
|
16
|
-
|
|
17
|
-
You should have received a copy of the GNU General Public License
|
|
18
|
-
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
19
|
-
|
|
20
|
-
Lesli · Ruby on Rails SaaS Development Framework.
|
|
21
|
-
|
|
22
|
-
Made with ♥ by LesliTech
|
|
23
|
-
Building a better future, one line of code at a time.
|
|
24
|
-
|
|
25
|
-
@contact hello@lesli.tech
|
|
26
|
-
@website https://www.lesli.tech
|
|
27
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
28
|
-
|
|
29
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
|
-
// ·
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// · Import Lesli builders
|
|
35
|
-
import application from "Lesli/vue/application"
|
|
36
|
-
import translation from "Lesli/vue/translation"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// · Import engine translations
|
|
40
|
-
import translations from "LesliBell/vue/stores/translations.json"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// · Import apps and components
|
|
44
|
-
// import appDashboard from "Lesli/cloud-objects/dashboards/apps/show.vue"
|
|
45
|
-
|
|
46
|
-
import appNotificationsIndex from "LesliBell/vue/apps/notifications/index.vue"
|
|
47
|
-
import appNotificationsNew from "LesliBell/vue/apps/notifications/new.vue"
|
|
48
|
-
|
|
49
|
-
import appAnnouncementsIndex from "LesliBell/vue/apps/announcements/index.vue"
|
|
50
|
-
// import appAnnouncementsNew from "./apps/announcements/new.vue"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// · Buil Lesli translations
|
|
54
|
-
translation(translations)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// · Build a new Lesli application
|
|
58
|
-
application("LesliBell", [{
|
|
59
|
-
path: "/notifications",
|
|
60
|
-
component: appNotificationsIndex
|
|
61
|
-
}, {
|
|
62
|
-
path: "/notifications/new",
|
|
63
|
-
component: appNotificationsNew
|
|
64
|
-
}, {
|
|
65
|
-
path: "/announcements",
|
|
66
|
-
component: appAnnouncementsIndex
|
|
67
|
-
}/*{
|
|
68
|
-
path: "/",
|
|
69
|
-
component: appDashboard
|
|
70
|
-
}, {
|
|
71
|
-
path: "/announcements/new",
|
|
72
|
-
component: appAnnouncementsNew
|
|
73
|
-
}*/])
|