support_desk 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +134 -0
  3. data/README.md +163 -8
  4. data/app/assets/stylesheets/support_desk.css +77 -0
  5. data/app/controllers/support_desk/application_controller.rb +21 -2
  6. data/app/controllers/support_desk/tickets_controller.rb +98 -9
  7. data/app/helpers/support_desk/engine_helper.rb +75 -10
  8. data/app/helpers/support_desk/thread_helper.rb +29 -0
  9. data/app/views/layouts/support_desk/panel.html.erb +6 -0
  10. data/app/views/support_desk/launcher/_badge.html.erb +6 -0
  11. data/app/views/support_desk/launcher/_icon.html.erb +6 -0
  12. data/app/views/support_desk/launcher/_launcher.html.erb +40 -0
  13. data/app/views/support_desk/launcher/_signed_out.html.erb +25 -0
  14. data/app/views/support_desk/panel/case.html.erb +36 -0
  15. data/app/views/support_desk/panel/home.html.erb +36 -0
  16. data/app/views/support_desk/panel/new.html.erb +8 -0
  17. data/app/views/support_desk/panel/rate_limited.html.erb +5 -0
  18. data/app/views/support_desk/tickets/_limit_wall.html.erb +33 -0
  19. data/app/views/support_desk/tickets/_pick_thing.html.erb +1 -1
  20. data/app/views/support_desk/tickets/_pick_topic.html.erb +1 -1
  21. data/app/views/support_desk/tickets/_wizard_header.html.erb +1 -1
  22. data/app/views/support_desk/tickets/_write.html.erb +4 -3
  23. data/app/views/support_desk/tickets/rate_limited.html.erb +1 -31
  24. data/app/views/support_desk/tickets/show.html.erb +8 -0
  25. data/config/locales/support_desk.en.yml +38 -0
  26. data/config/locales/support_desk.es.yml +38 -0
  27. data/config/routes.rb +4 -0
  28. data/lib/generators/support_desk/templates/initializer.rb +21 -1
  29. data/lib/generators/support_desk/views_generator.rb +9 -3
  30. data/lib/support_desk/brief.rb +1 -1
  31. data/lib/support_desk/configuration.rb +100 -19
  32. data/lib/support_desk/conversation_access.rb +42 -0
  33. data/lib/support_desk/engine.rb +7 -0
  34. data/lib/support_desk/launcher.rb +64 -0
  35. data/lib/support_desk/models/concerns/requester.rb +6 -2
  36. data/lib/support_desk/models/ticket.rb +55 -1
  37. data/lib/support_desk/version.rb +1 -1
  38. data/lib/support_desk/wizard.rb +1 -5
  39. data/lib/support_desk.rb +41 -6
  40. metadata +28 -11
data/lib/support_desk.rb CHANGED
@@ -330,9 +330,11 @@ module SupportDesk
330
330
 
331
331
  # --- The chats seam -------------------------------------------------------------
332
332
 
333
- # Subscribe the gem's own `:message_created` listener, which is what
333
+ # Subscribe the gem's own chats listeners: `:message_created`, which is what
334
334
  # keeps `awaiting`, the SLA clocks and reopen-on-reply true without
335
- # anybody remembering to call anything.
335
+ # anybody remembering to call anything, and `:unread_changed`, which
336
+ # keeps the launcher's badge live.
337
+
336
338
  #
337
339
  # Safe to call as often as you like: chats replaces a subscriber
338
340
  # registered under the same `key:` rather than stacking another one.
@@ -346,6 +348,11 @@ module SupportDesk
346
348
  # the Ticket class is a new object after each reload.
347
349
  SupportDesk::Ticket.for_conversation(message.conversation)&.register!(message)
348
350
  end
351
+ # The launcher's badge (chats >= 0.7.0): wherever an unread count in a
352
+ # case's conversation may have moved, the case's own notices included.
353
+ Chats.on(:unread_changed, key: :support_desk_launcher) do |messager:, conversation:|
354
+ SupportDesk::Launcher.unread_changed(messager, conversation)
355
+ end
349
356
  self
350
357
  end
351
358
 
@@ -402,8 +409,10 @@ module SupportDesk
402
409
  #
403
410
  # Both surfaces are ordinary PUSHED screens (`context: "default"`), never
404
411
  # modals: the wizard is three real URLs and a modal would break the back
405
- # gesture between them. The thread itself is deliberately absent — it is
406
- # a chats conversation, and it stays under the host's chats rule.
412
+ # gesture between them. The thread itself is absent under the default
413
+ # `thread_page = :chats` — it is a chats conversation, and it stays under
414
+ # the host's chats rule. Under `:support` it is this engine's ticket page,
415
+ # and a third rule pushes it like the list.
407
416
  #
408
417
  # +mount+ defaults to wherever the engine is mounted and +title+ to the
409
418
  # desk's name.
@@ -429,8 +438,16 @@ module SupportDesk
429
438
  properties: { context: "default", title: title, pull_to_refresh_enabled: false },
430
439
  comment: "The wizard: every step is a real URL, so it pushes and the back gesture works. " \
431
440
  "Pull to refresh is off — it would throw away what the requester has typed."
432
- }
433
- ]
441
+ },
442
+ (if config.support_thread_page?
443
+ {
444
+ patterns: [ "^#{prefix}/tickets/[^/?]+(?:\\?.*)?$" ],
445
+ properties: { context: "default", title: title, pull_to_refresh_enabled: false },
446
+ comment: "A case, read on the support page (thread_page = :support). Pull to refresh is off " \
447
+ "for the same reason as the wizard: the composer at its foot holds what is typed."
448
+ }
449
+ end)
450
+ ].compact
434
451
  end
435
452
 
436
453
  # A duration in the reader's own language ("1 día", "4 hours") — the
@@ -449,6 +466,24 @@ module SupportDesk
449
466
  words.start_with?("Translation missing") ? duration.inspect : words
450
467
  end
451
468
 
469
+ # The answer promise, in the unit the desk made it in: `reply_within =
470
+ # 72.hours` reads "72 hours", `3.days` reads "3 days", `24.hours` reads
471
+ # "24 hours". A wait is approximate and `humanize_duration` rounds it
472
+ # ("about 8 hours", "3 days"); a promise is a number somebody chose, and
473
+ # rounding it says something they did not.
474
+ #
475
+ # The words are `support_desk.durations.<unit>`, pluralized by count. A
476
+ # duration of more than one unit (1.day + 2.hours), a fractional one, or
477
+ # a unit with no words in this locale is said the way a wait is.
478
+ def humanize_promise(duration)
479
+ parts = duration.respond_to?(:parts) ? duration.parts : {}
480
+ unit, count = parts.first if parts.size == 1
481
+ key = :"support_desk.durations.#{unit}"
482
+ return humanize_duration(duration) unless count.is_a?(Integer) && I18n.exists?(key)
483
+
484
+ I18n.t(key, count: count)
485
+ end
486
+
452
487
  # A stable, URL-safe key for an actor (agent, requester, desk), used in
453
488
  # cache keys and event payloads. GlobalID params already encode class +
454
489
  # id, so two classes can never collide.
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: support_desk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 2026-09-24 00:00:00.000000000 Z
11
+ date: 2026-10-19 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: activerecord
@@ -53,22 +54,22 @@ dependencies:
53
54
  name: chats
54
55
  requirement: !ruby/object:Gem::Requirement
55
56
  requirements:
56
- - - "~>"
57
- - !ruby/object:Gem::Version
58
- version: '0.3'
59
57
  - - ">="
60
58
  - !ruby/object:Gem::Version
61
- version: 0.3.2
59
+ version: 0.7.1
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.0'
62
63
  type: :runtime
63
64
  prerelease: false
64
65
  version_requirements: !ruby/object:Gem::Requirement
65
66
  requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '0.3'
69
67
  - - ">="
70
68
  - !ruby/object:Gem::Version
71
- version: 0.3.2
69
+ version: 0.7.1
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '1.0'
72
73
  - !ruby/object:Gem::Dependency
73
74
  name: globalid
74
75
  requirement: !ruby/object:Gem::Requirement
@@ -143,8 +144,10 @@ files:
143
144
  - app/controllers/support_desk/console/tickets_controller.rb
144
145
  - app/controllers/support_desk/tickets_controller.rb
145
146
  - app/helpers/support_desk/engine_helper.rb
147
+ - app/helpers/support_desk/thread_helper.rb
146
148
  - app/views/chats/slots/_inbox_top.html.erb
147
149
  - app/views/chats/slots/_locked_composer.html.erb
150
+ - app/views/layouts/support_desk/panel.html.erb
148
151
  - app/views/support_desk/console/tickets/_actions.html.erb
149
152
  - app/views/support_desk/console/tickets/_assignment.html.erb
150
153
  - app/views/support_desk/console/tickets/_composer.html.erb
@@ -160,9 +163,18 @@ files:
160
163
  - app/views/support_desk/console/tickets/index.html.erb
161
164
  - app/views/support_desk/console/tickets/new.html.erb
162
165
  - app/views/support_desk/console/tickets/show.html.erb
166
+ - app/views/support_desk/launcher/_badge.html.erb
167
+ - app/views/support_desk/launcher/_icon.html.erb
168
+ - app/views/support_desk/launcher/_launcher.html.erb
169
+ - app/views/support_desk/launcher/_signed_out.html.erb
170
+ - app/views/support_desk/panel/case.html.erb
171
+ - app/views/support_desk/panel/home.html.erb
172
+ - app/views/support_desk/panel/new.html.erb
173
+ - app/views/support_desk/panel/rate_limited.html.erb
163
174
  - app/views/support_desk/tickets/_context_card.html.erb
164
175
  - app/views/support_desk/tickets/_door.html.erb
165
176
  - app/views/support_desk/tickets/_human_door.html.erb
177
+ - app/views/support_desk/tickets/_limit_wall.html.erb
166
178
  - app/views/support_desk/tickets/_pick_thing.html.erb
167
179
  - app/views/support_desk/tickets/_pick_topic.html.erb
168
180
  - app/views/support_desk/tickets/_ticket_row.html.erb
@@ -171,6 +183,7 @@ files:
171
183
  - app/views/support_desk/tickets/index.html.erb
172
184
  - app/views/support_desk/tickets/new.html.erb
173
185
  - app/views/support_desk/tickets/rate_limited.html.erb
186
+ - app/views/support_desk/tickets/show.html.erb
174
187
  - config/console_routes.rb
175
188
  - config/locales/support_desk.console.en.yml
176
189
  - config/locales/support_desk.console.es.yml
@@ -204,11 +217,13 @@ files:
204
217
  - lib/support_desk/console_engine.rb
205
218
  - lib/support_desk/console_routes.rb
206
219
  - lib/support_desk/context_card.rb
220
+ - lib/support_desk/conversation_access.rb
207
221
  - lib/support_desk/current.rb
208
222
  - lib/support_desk/doctor.rb
209
223
  - lib/support_desk/engine.rb
210
224
  - lib/support_desk/errors.rb
211
225
  - lib/support_desk/events.rb
226
+ - lib/support_desk/launcher.rb
212
227
  - lib/support_desk/macros.rb
213
228
  - lib/support_desk/message_signatures.rb
214
229
  - lib/support_desk/models/application_record.rb
@@ -244,6 +259,7 @@ metadata:
244
259
  bug_tracker_uri: https://github.com/rameerez/support_desk/issues
245
260
  documentation_uri: https://github.com/rameerez/support_desk#readme
246
261
  rubygems_mfa_required: 'true'
262
+ post_install_message:
247
263
  rdoc_options: []
248
264
  require_paths:
249
265
  - lib
@@ -258,7 +274,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
274
  - !ruby/object:Gem::Version
259
275
  version: '0'
260
276
  requirements: []
261
- rubygems_version: 3.6.2
277
+ rubygems_version: 3.4.10
278
+ signing_key:
262
279
  specification_version: 4
263
280
  summary: 'Customer support for your Rails app: tickets that are conversations, worked
264
281
  by your team and by AI agents with real guardrails'