Messaging System – User Manual

Messaging System

Internal messaging for your veterinary clinic — send direct messages or broadcast to your whole team, attach files, track read receipts, and manage your inbox all from within the clinic portal.

📣 Broadcast to Everyone 📎 File Attachments ✓✓ Read Receipts 🗑️ WhatsApp-style Delete 📥 Inbox Widget 🔒 Clinic-scoped
📋

Overview

The Messaging System provides a private, internal communication channel for all staff within a veterinary clinic. Messages are scoped to a clinic's director group — staff can only communicate with others under the same director and cannot see messages from other clinics using the same platform.

Every logged-in user (regardless of role) can send and receive messages. Broadcasts let you send one message to your entire team at once. Attachments support images, PDFs, Word, Excel, ZIP, and more. Sent messages include read receipts so you can see whether each recipient has opened the message.

🔒

Clinic-scoped

Messages are isolated per clinic. Staff only ever see colleagues under the same director — no cross-clinic visibility.

📣

Broadcast

One checkbox sends a message to every staff member in your group at once, without selecting individuals.

📎

File attachments

Attach any file type — images, PDF, Word, Excel, PowerPoint, ZIP, video, audio, CSV and more.

✓✓

Read receipts

Opening a sent message's detail view shows each recipient with a ✓✓ Read or ✓ Sent status.

🗑️

WhatsApp-style delete

Delete a message for yourself only, or delete it for everyone — shown as "⊘ This message was deleted" to all parties.

📥

Inbox widget

A compact unread-messages widget that can be placed on any page — shows only unread messages with one-click read/delete.

👥

Who Can Use It

All logged-in users can send and receive messages. There are no role restrictions on access to the messaging pages. However, the recipient list excludes clinic_pet_owner accounts — pet owners do not appear as selectable message recipients.

Clinic Director

Can message any individual staff member or broadcast to the entire clinic group. Sees messages sent and received between all interactions with the system.

Veterinarian

Can message any colleague in the same clinic group — Director, other Vets, Para Vets, Receptionists. Can send broadcasts.

Para Vet

Full messaging access — can compose, receive, and reply. Para Vets appear as selectable recipients for others in the group.

Receptionist

Full messaging access — can compose, receive, and reply. Receptionists appear as selectable recipients for others in the group.

ℹ️

Pet owners are excluded from the recipient picker. Even if a pet owner account belongs to the same director group, they will not appear in the "To" user list and cannot be messaged through this system.

🗂️

The Two Pages

The Messaging System provides two shortcodes that can be placed on any page.

ShortcodeNamePurpose
Please log in to access messages.
Message CenterFull messaging hub — Sent and Received tabs, Compose button, search, filters, message cards, detail popups, and delete. This is the primary page for all messaging activity.
Please log in to access messages.
Inbox WidgetA compact widget that shows only unread messages. Designed to be placed on a dashboard or home page as a quick-glance notification panel.
💡

The Inbox widget and the Message Center are fully independent — each can be placed on a separate page. The unread badge count shown in the Inbox widget updates live when messages are marked as read.

🔒

Clinic Scoping

Every message is stamped with a director_id at send time. This ID is the sender's own user ID if they are a Director, or the clinic_director_id stored in their user meta if they are a staff member.

When fetching received messages, the system returns messages where:

  • The recipient_id exactly matches the current user, OR
  • The recipient_id is 0 (broadcast) AND the message's director_id matches the user's own director group

This means a broadcast from one clinic will never appear in another clinic's received list, even if both clinics are on the same installation. And a direct message sent to one user will only ever appear in that user's received list.

⚠️

If a staff member has no clinic_director_id in their user meta, they will not be correctly scoped to a clinic group and may not be able to send or receive messages properly. Ensure all staff are assigned a director via the clinic setup process.

📬

Message Center

The Message Center (

Please log in to access messages.
) is the main messaging interface. It displays your name and avatar in a welcome bar at the top, with a Compose button on the right. Below that, two tabs — Sent and Received — let you switch between your outgoing and incoming messages.

Sent & Received Tabs

TabShowsExcludes
✈️ SentAll messages you have sent (direct or broadcast), excluding ones you have deleted for yourself.Messages deleted by sender (deleted_by_sender = 1)
🏠 ReceivedAll messages sent to you directly, plus any broadcasts from your clinic group — excluding ones you have deleted.Messages deleted by recipient (deleted_by_recipient = 1), and messages you sent yourself

Both tabs load automatically when the page opens. Switching tabs reloads the list for that tab. All active search terms and filter settings apply to whichever tab is currently selected.

Message Cards

Each message in the list is shown as a card with the following information:

1

Sender avatar & initials

The sender's Gravatar or profile image is shown on the left. If no image is available, a circle with the sender's first initial is used instead.

2

Subject line

The message subject is shown prominently. If a message has been deleted for everyone, the subject is replaced with "⊘ This message was deleted" in a muted style.

3

Time, From/To label, and action buttons

The timestamp is shown in your local timezone. In the Sent tab, it shows "To: [recipient name]" or "To: Everyone (Broadcast)". In the Received tab, it shows "From: [sender name]". Unread messages show a ✓ Read button; sent messages show a 🗑 delete button.

4

Message preview & tags

The first 90 characters of the message body are shown as a preview. Tags appear on the right side of the bottom row (see Card Tags below).

Clicking anywhere on a card (except on a button) opens the full message in a Detail Modal. Unread cards are visually highlighted.

Card Tags & Badges

Small coloured tags appear on message cards to convey status at a glance:

NewUnread message (Received tab only)
AllBroadcast sent to everyone in the group
→ NameDirect message — shows recipient name (Sent tab)
📎Message has a file attachment

Tags are not shown on deleted messages (⊘ deleted cards).

Search & Filters

The search bar and filter button sit at the top right of the Message Center, alongside the tabs.

Search — type to find messages by subject, body text, or sender name. Results update automatically after a 350 ms debounce (or immediately on pressing Enter). Search applies to the currently active tab.

Filter — click the funnel icon to open the Filter modal. A blue dot on the button indicates active filters. The filter modal contains:

FilterOptionsApplies to
StatusAll Messages, Unread Only, Read OnlyReceived tab only
Sort OrderNewest First (default), Oldest FirstBoth tabs
From DateDate picker — filters messages on or after this dateBoth tabs
To DateDate picker — filters messages on or before this dateBoth tabs

Click Apply Filters to reload the current tab with the new settings. Click Reset All to clear all filters and reload. Pressing Esc closes the filter popup without applying changes.

ℹ️

The Status filter (Unread/Read Only) only has an effect on the Received tab. Applying it while on the Sent tab has no visible impact because sent messages do not have a read/unread state from the sender's perspective.

✏️

Composing a Message

Click the Compose button in the top-right of the Message Center to open the compose modal. Fill in the form and click Send Message →.

Selecting Recipients

The "To" section shows a search box and a scrollable list of all users in your clinic group (excluding pet owners and yourself). Each user is shown with their name and email. Click a user's row to toggle their checkbox — selected users are highlighted. You can select multiple recipients to send the same message to several people at once; a separate message row is created in the database for each recipient.

💡

Type in the recipient search box to filter the user list by name or email. The filter is instant and case-insensitive.

Broadcast to Everyone

Check the "Send to everyone in my group" checkbox at the top of the "To" section. The individual user picker hides automatically. When sent, one message row is inserted with recipient_id = 0. This single row is visible to every member of the clinic group in their Received tab.

⚠️

A broadcast is stored as a single database row, not one row per user. This means the read receipt for a broadcast shows the overall is_read status of the row — not individual per-person read tracking. It shows all group members but their "read" status reflects the most recent update to that single row.

Message Fields

FieldRequired?Notes
To (recipient selection)RequiredAt least one checkbox ticked, OR "Send to everyone" checked
SubjectRequiredPlain text, up to 255 characters
MessageRequiredFree text, any length
Attach FileOptionalAny file type — see Attachments section

The Send Message → button shows a spinner while the message is being sent. A green toast notification confirms success and the compose modal closes automatically. The Sent tab reloads immediately to include the new message.

File Attachments

The attachment area in the compose form accepts any file type. You can attach a file in two ways:

  • Click the attachment area to open a file browser
  • Drag and drop a file directly onto the attachment area

The file is uploaded to the server immediately after selection (before you send the message). A spinner shows while uploading. On success, a preview appears in the attachment area showing either a thumbnail (for images) or a file type icon with the filename and MIME type. Click ✕ Remove to detach the file before sending.

Supported file types include (but are not limited to):

🖼️ Images (JPG, PNG, GIF, WebP…)
📕 PDF documents
📝 Word (.doc, .docx)
📊 Excel (.xls, .xlsx)
📽️ PowerPoint (.ppt, .pptx)
🗜️ ZIP archives
📃 Text / CSV
🎬 MP4 video
🎵 MP3 audio
ℹ️

Only one file can be attached per message. If you need to send multiple files, consider zipping them first or sending multiple messages.

👁

Message Detail View

Clicking a message card opens the Detail Modal — a full-screen overlay showing the complete message. The modal displays:

  • The sender's avatar and name
  • "To: [recipient]" and the sent timestamp (in your local timezone)
  • A divider, then the full message body
  • If a file was attached — an inline image preview for images, or a download link panel for other file types
  • For sent messages — a Read Receipts section

Close the modal by clicking , clicking outside the modal panel, or pressing Esc.

Marking as Read

Unread messages (in the Received tab) show a ✓ Mark as Read button both on the card and in the detail modal footer. Clicking it sends a read request to the server and immediately:

  • Removes the "New" tag and the unread highlight from the card
  • Removes the Mark as Read button from the card and the modal
  • Replaces it with a small "Read ✓" timestamp in the modal footer
  • Refreshes the Inbox widget count (if the Inbox shortcode is on the same page)

Read status is tracked per message in the database (is_read = 1). Once marked as read, a message cannot be manually marked unread.

Read Receipts

When you open a message in the Sent tab, the detail modal includes a "Read Receipts" section below the message body. This section lists each recipient with their avatar, name, email, and a status indicator:

✓✓ ReadRecipient has opened the message
✓ SentMessage delivered, not yet read

For a direct message, the receipts section shows the one recipient. For a broadcast, it lists all members of your clinic group (excluding yourself) with the shared read status of the broadcast row.

⚠️

Broadcast read receipts show all group members, but because broadcasts are stored as a single row, the read status shown is the same for all listed members — it reflects when any member last marked it read, not each individual's read time.

🗑️

Deleting Messages

Messages are deleted in a WhatsApp-style soft-delete system — the database row is never physically removed. Instead, flags are set to hide the message from the relevant party's view. Click the 🗑 button on a sent message card (or inside the detail modal for sent messages) to open the Delete confirmation modal.

Delete Options

OptionWhat happensVisible to sender?Visible to recipient?
🗑️ Delete for MeSets deleted_by_sender = 1 (if you are the sender). The card fades out and disappears from your Sent list. The recipient is unaffected.❌ Hidden immediately✅ Still visible
Delete for EveryoneSets both deleted_by_sender = 1 and deleted_by_recipient = 1. The card stays visible but shows "⊘ This message was deleted" with the subject, preview, and tags removed.⊘ Shown as deleted⊘ Shown as deleted
⚠️

Only the sender sees the 🗑 delete button. Recipients do not have a delete button in the current version — they can only delete a message from their own view through the "Delete for Me" flow if a delete button were exposed. Currently, deletion is only exposed to the message sender.

💡

Deleted messages (for everyone) remain in the database for auditing. The "⊘ This message was deleted" state is a display-level flag — the underlying data is preserved.

📥

Inbox Widget

The Inbox (

Please log in to access messages.
) is a lightweight, standalone widget designed to be embedded on a dashboard page. It shows only unread messages from your clinic group.

The widget displays:

  • A header with an envelope icon, "Inbox" title, and an unread count badge (hidden when zero)
  • A sub-heading: "Unread messages awaiting your attention"
  • The list of unread message cards, built identically to the Message Center cards

Each card in the inbox includes a ✓ Read button and a 🗑 delete button. Clicking Read marks the message and removes it from the inbox card list, updating the badge count. Clicking a card (not a button) opens the full Detail Modal directly in the inbox view.

💡

The inbox is loaded at page load and does not auto-refresh. After marking messages as read, the badge updates live in the same page session. A full page refresh will re-query from the database.

ℹ️

The Inbox widget and the Message Center can be placed on the same page or separate pages. If both shortcodes are present together, the inbox count badge updates live when messages are marked read via either interface.

FAQ

Can I send a message to someone in a different clinic?

No. The system is fully clinic-scoped. You can only message users who share the same director group as you. Users from other clinics will never appear in your recipient list and will never receive your messages.

Can I message pet owners?

No. The recipient picker explicitly excludes accounts with the clinic_pet_owner role. Pet owners will not appear as selectable recipients regardless of whether they belong to the same director group.

What happens if I send a message to multiple people at once?

A separate message row is inserted into the database for each recipient. Each recipient sees their own individual copy — they cannot see who else received the same message. Each copy has its own independent read/delete state.

What is the difference between a broadcast and a multi-recipient message?

A broadcast (checked "Send to everyone") inserts a single database row with recipient_id = 0. All group members see this one row. A multi-recipient message inserts one row per selected recipient, and each recipient sees their own private copy. Broadcasts are more efficient for true all-staff announcements; individual rows give each recipient private control over their copy.

Can I attach more than one file to a message?

No. Only one file can be attached per message. If you need to share multiple files, zip them into one archive first, then attach the zip file. Alternatively, send multiple messages each with a different attachment.

Can a recipient delete a message from their view?

Currently the delete button is only shown to the message sender. The soft-delete system does technically support recipient-side deletion (deleted_by_recipient flag), but the UI only exposes this action to senders in the current version.

Does "Delete for Everyone" actually remove the message from the database?

No. It sets both deleted_by_sender and deleted_by_recipient to 1 in the database row. The record is preserved for auditing. The visual presentation changes to "⊘ This message was deleted" for all parties, and the subject, preview, and action buttons are hidden.

How do timestamps work — what timezone are they shown in?

Messages are stored with UTC timestamps. When displayed in the browser, they are automatically converted to the user's local timezone using the browser's Date.toLocaleString() function. This means two users in different timezones will see the same message with a different local time, both of which are correct for their region.

Does the inbox widget auto-refresh?

No. The inbox loads once when the page loads. Within the same page session, marking messages as read removes them from the inbox list and updates the badge count live. But new messages arriving from other users will only appear after a manual page refresh. There is no polling or websocket live-update in the current version.

The read receipt for a broadcast shows all group members — does that mean they all received it?

Yes, all group members receive the broadcast because it is scoped by director group. The receipt list shows all members as a convenience, but the "Read/Sent" status shown is the same for everyone (it reflects the single row's is_read flag). True per-user read tracking for broadcasts would require one row per recipient, which is how individual multi-recipient messages work.

What file types are blocked from being attached?

No file types are blocked. The attachment handler uses a broad MIME allow-list that covers all common types, and a fallback that assigns application/octet-stream for any type not explicitly listed. Any file can be uploaded and attached to a message.

Can I mark a message as unread again?

No. Once a message is marked as read (is_read = 1), there is no way to revert it to unread status in the current version. The system only supports one-way read tracking.

What if I have no colleagues in my group — who can I message?

If no other users are found in your director group (or you are the only staff member), the compose modal will show "No users in your group." in the recipient list. You can still send a broadcast, but there will be no individual recipients to select. Ensure all staff are properly linked to the director via their user meta clinic_director_id.