support_desk 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 953cadd4b7e15695dd5d17092647e38c5595e4b81bccfc2917b491c428f4d7d6
4
- data.tar.gz: 077ca8e707b0789140bd484414645ad1217b210d92d02557d7174fd32617b90b
3
+ metadata.gz: c258a259ca701d1c8850126370d587f1297eba3429ec0649912258596ac3789d
4
+ data.tar.gz: 48684ab7f4b23e2d76e2c17839bf602900e4e22123ef41400595546caedcdcf4
5
5
  SHA512:
6
- metadata.gz: f442d5f55ab69f61941fcd0b401e0be726d7ca1b228d68d683b52cc14e795d1f136fadf1915faf0153dca9d33a5f3720f764cacd06f3bab3567af20a36393ba6
7
- data.tar.gz: 83cc58116727f3f2aebf62f8b61f4a2617d443b58f7cee8179620e5889532e2f281a6515b6c058f7baed4512a259f06592ff6d8b420b2afcbd12e2ddc1241ad6
6
+ metadata.gz: 70aa54f8c81942b81f10e00b7259825ca003b71bafb127fac498cb8325562207abd1a5a76628d41163c78e04ee3ca5bd5833d1bdd40b4b8756bd7a136225fec2
7
+ data.tar.gz: 65cddb6b49effbb1898a91c06cc44ce7944215c2139ce885580ec79faabd7c682693b171b589b756f7cab9fb1cb0f4b07df8532872e68b9ab3ddcfdeb2f9a9e2
data/CHANGELOG.md CHANGED
@@ -4,6 +4,42 @@ All notable changes to this project are documented here.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.1.2] - 2026-09-16
8
+
9
+ ### Fixed
10
+ - **0.1.1 shipped without the thing it was released for.** The gem published
11
+ to rubygems as 0.1.1 was built from a tree that predated the merge, so
12
+ `SupportDesk::Desk` never declared `verified: true` and no desk was ever
13
+ badged — while the release notes said it was. The repository was correct
14
+ the whole time; only the package was wrong, which is the worst shape for
15
+ this kind of mistake because nothing in git looks off.
16
+
17
+ `SupportDesk::Desk.chat_verified?` now returns true, and a test asserts it
18
+ against the loaded class rather than the source file, so a package built
19
+ from the wrong tree fails instead of shipping quietly.
20
+
21
+ ## [0.1.1] - 2026-09-16
22
+
23
+ ### Changed
24
+
25
+ - **The desk is an official account.** `SupportDesk::Desk` now declares
26
+ `acts_as_messager verified: true` (chats 0.3.0), so chats badges it
27
+ wherever it names a messager: the grouped inbox row and the case thread's
28
+ header. A requester can tell the real desk from anyone who simply called
29
+ themselves "Soporte" without reading the name carefully — which matters
30
+ more here than anywhere else in a product, because the desk is the one
31
+ counterpart that legitimately asks people for account details. The badge
32
+ is chats' own; recolour it with the `--chats-verified` CSS variable or
33
+ replace it with `Chats.configure { |c| c.verified_badge = … }`.
34
+ - **The inbox door is badged too.** The door that stands in for the desk's
35
+ inbox row before a requester has written now carries the same mark, so the
36
+ badge reads as a property of the account rather than of having already
37
+ written to us. It still never INSERTs a desk (`support_desk_record` is a
38
+ find-or-stand-in, never `SupportDesk.desk`, whose first call in a process
39
+ creates the row), and it costs no query of its own: the door's avatar and
40
+ its badge share one memoised lookup per render, pinned by a query-count
41
+ test.
42
+
7
43
  ## [0.1.0] - 2026-09-16
8
44
 
9
45
  First release: the whole core of a support desk, on top of `chats` 0.2.
data/README.md CHANGED
@@ -7,6 +7,16 @@
7
7
 
8
8
  `support_desk` gives your Rails app a **support desk**: tickets that are real conversations. Somebody asks for help about something in your app (a ride, an order, a withdrawal) or about nothing in particular, your desk answers, humans sign the answers, and your team works a queue.
9
9
 
10
+ Here is the whole thing — the kind of support desk a DoorDash, an Uber Eats or a Grab needs — running on a made-up delivery app called Pepperbox. These are the **bundled views**, unmodified, themed by the host with a handful of CSS variables:
11
+
12
+ | One row, every case | Pick a topic | Which order? |
13
+ |:---:|:---:|:---:|
14
+ | ![The chats inbox: every support conversation folded into one official, verified row](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/01-inbox.png) | ![The wizard's first step: a list of support topics](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/02-topics.png) | ![The wizard's second step: the requester's own orders](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/03-subject.png) |
15
+ | **Say what happened** | **Signed by a human** | **The agent queue** |
16
+ | ![The composer, with a card naming the order the case is about](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/04-compose.png) | ![The conversation, with the desk's verified badge and an answer signed by the agent who wrote it](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/05-thread.png) | ![The agent queue, with tabs, counts and waiting chips](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/06-queue.png) |
17
+ | **The whole case** | **Notes stay inside** | **Hand it over** |
18
+ | ![One case: what it is about, and the transcript the requester sees](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/07-case.png) | ![The internal note composer, which never reaches the customer](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/08-note.png) | ![Handing a case to a colleague, above the history of every hand-off before it](https://raw.githubusercontent.com/rameerez/support_desk/main/screenshots/09-handoff.png) |
19
+
10
20
  It is a product gem on the [`chats`](https://github.com/rameerez/chats) kernel: chats owns the transcript, realtime, attachments, read state and moderation; `support_desk` owns the case — topics, assignment, SLA clocks, events and the console API.
11
21
 
12
22
  Every app eventually needs a support inbox, and everyone rebuilds the same ticket table, the same "assigned to me" tab, the same "which order is this about?" picker and the same email bridge. `support_desk` is that whole rebuild, done once, done right, on top of the messaging you already have.
@@ -458,7 +468,7 @@ with_support_config(reply_policy: :assignee_only) { … }
458
468
 
459
469
  After checking out the repo, run `bundle install`, then `bundle exec rake ci`. The dummy app lives in `test/dummy` and mounts all three surfaces the way a real host does: the requester engine at `/messages/support`, `chats` at `/messages`, and the turnkey console at `/admin/support` — plus the same console again inside a host-owned `madmin` namespace, because "the console uses only the public API" is a claim that needs a second implementation to be worth anything.
460
470
 
461
- `chats` is the kernel this gem is a product on and the two are developed in lockstep, so the Gemfile points at a sibling checkout (`../chats`) until `chats` 0.2.0 is on rubygems.
471
+ `chats` is the kernel this gem is a product on and the two are developed in lockstep, so the Gemfile points at a sibling checkout (`../chats`) whenever this gem needs a `chats` that is not on rubygems yet. It currently requires `chats` ~> 0.3 (the `verified:` messager option).
462
472
 
463
473
  ## Contributing
464
474
 
@@ -105,21 +105,57 @@ module SupportDesk
105
105
  !viewer.support_tickets.exists?
106
106
  end
107
107
 
108
+ # The desk RECORD for +desk_key+, resolved WITHOUT ever creating one.
109
+ #
110
+ # `SupportDesk.desk` is memoised for the life of the process, but its
111
+ # first call in that process INSERTs (`Desk.for` is find-or-create), and
112
+ # this renders on an inbox that may have nothing to do with support: a
113
+ # page view is not a reason to write a row.
114
+ #
115
+ # Before anybody has ever written there is no row, so this hands back an
116
+ # UNSAVED stand-in carrying the key. `name`, `display_name` and `avatar`
117
+ # all answer from configuration, so it renders exactly like the real
118
+ # thing — the same "null object that still renders" the topic tree uses.
119
+ # That keeps the door's badge a property of the ACCOUNT rather than of
120
+ # whether the table happens to have a row yet, and it means
121
+ # `Chats.config.verified_badge` is always handed a SupportDesk::Desk and
122
+ # never a Class.
123
+ #
124
+ # Memoised per view instance, so the door's avatar and its badge cost ONE
125
+ # query between them rather than one each.
126
+ def support_desk_record(desk_key = :default)
127
+ key = desk_key.to_s
128
+ @support_desk_records ||= {}
129
+ @support_desk_records[key] ||= SupportDesk::Desk.find_by(key: key) || SupportDesk::Desk.new(key: key)
130
+ end
131
+
108
132
  # The desk's face. Once the desk has a row it is just another chats
109
133
  # messager, so chats draws it; before that (nobody has ever written to
110
134
  # it) an initials disc from `config.name`.
111
135
  #
112
- # It never CREATES the desk: this renders on an inbox that may have
113
- # nothing to do with support, and a page view is not a reason to INSERT.
136
+ # It never CREATES the desk see support_desk_record.
114
137
  def support_desk_avatar(desk_key = :default, css_class: "chats-avatar")
115
- desk = SupportDesk::Desk.find_by(key: desk_key.to_s)
116
- return chats_messager_avatar(desk, css_class: css_class) if desk
138
+ desk = support_desk_record(desk_key)
139
+ return chats_messager_avatar(desk, css_class: css_class) if desk.persisted?
117
140
 
118
141
  name = SupportDesk.config.desk(desk_key).name
119
142
  initials = name.to_s.split.first(2).filter_map { |word| word[0] }.join.upcase
120
143
  tag.span(initials.presence || "?", class: "#{css_class} chats-avatar--initials", "aria-hidden": true)
121
144
  end
122
145
 
146
+ # The desk's official-account badge (chats' `verified:` option), for the
147
+ # door — the entry that stands in for the desk's own inbox row before the
148
+ # requester has written.
149
+ #
150
+ # chats badges that real row from the moment it exists, so without this
151
+ # somebody would see an unbadged "Soporte" before their first case and a
152
+ # badged one after, which makes the mark read as a property of having
153
+ # written to us rather than of the account. Renders nothing if a host
154
+ # ever declares its desk unverified.
155
+ def support_desk_badge(desk_key = :default)
156
+ chats_verified_badge(support_desk_record(desk_key))
157
+ end
158
+
123
159
  # Where a case is read and answered: its chats conversation. Falls back
124
160
  # to the engine's own ticket URL (which redirects to the same place) for
125
161
  # the one case that has no conversation yet, so a link is never dead.
@@ -9,16 +9,22 @@
9
9
  Locals come from chats' inbox (viewer:, inbox:). Renders nothing under
10
10
  :when_tickets or :never, and nothing once there is a real row to show. %>
11
11
  <% if support_inbox_door?(viewer) %>
12
+ <% desk_key = viewer.class.try(:support_desk_key) || :default %>
12
13
  <%= support_desk_styles %>
13
14
  <ul class="chats-inbox__list support-desk-inbox-door">
14
15
  <li class="chats-row">
15
16
  <%= link_to support_desk_routes.root_path, class: "chats-row__link" do %>
16
- <%= support_desk_avatar(viewer.class.try(:support_desk_key) || :default) %>
17
+ <%= support_desk_avatar(desk_key) %>
17
18
 
18
19
  <span class="chats-row__body">
19
20
  <span class="chats-row__top">
20
- <span class="chats-row__title">
21
- <%= SupportDesk.config.desk(viewer.class.try(:support_desk_key) || :default).name %>
21
+ <%# Same shape chats gives its own rows: the name ellipsizes, the
22
+ official-account badge beside it always shows. %>
23
+ <span class="chats-row__name">
24
+ <span class="chats-row__title">
25
+ <%= SupportDesk.config.desk(desk_key).name %>
26
+ </span>
27
+ <%= support_desk_badge(desk_key) %>
22
28
  </span>
23
29
  </span>
24
30
  <span class="chats-row__bottom">
@@ -9,6 +9,11 @@ module SupportDesk
9
9
  # one. Its conversations collapse into a single grouped inbox row, so
10
10
  # somebody with four open tickets sees "Soporte" once, not four times.
11
11
  #
12
+ # It is also a VERIFIED messager: a desk is the official voice of the
13
+ # product, and the person writing to it should be able to tell that from
14
+ # an impostor without reading the name carefully. chats badges it wherever
15
+ # it shows a messager's name.
16
+ #
12
17
  # SupportDesk.desk # the :default desk, memoised
13
18
  # SupportDesk.desk(:billing) # another one
14
19
  #
@@ -20,6 +25,7 @@ module SupportDesk
20
25
  acts_as_messager notifications: false,
21
26
  blockable: false,
22
27
  inbox: :grouped,
28
+ verified: true,
23
29
  group_path: ->(_viewer) { SupportDesk.root_path }
24
30
 
25
31
  has_many :tickets,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SupportDesk
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: support_desk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez
@@ -55,14 +55,14 @@ dependencies:
55
55
  requirements:
56
56
  - - "~>"
57
57
  - !ruby/object:Gem::Version
58
- version: '0.2'
58
+ version: '0.3'
59
59
  type: :runtime
60
60
  prerelease: false
61
61
  version_requirements: !ruby/object:Gem::Requirement
62
62
  requirements:
63
63
  - - "~>"
64
64
  - !ruby/object:Gem::Version
65
- version: '0.2'
65
+ version: '0.3'
66
66
  - !ruby/object:Gem::Dependency
67
67
  name: globalid
68
68
  requirement: !ruby/object:Gem::Requirement