escalated-django 0.4.0__tar.gz

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 (79) hide show
  1. escalated_django-0.4.0/LICENSE +21 -0
  2. escalated_django-0.4.0/MANIFEST.in +3 -0
  3. escalated_django-0.4.0/PKG-INFO +330 -0
  4. escalated_django-0.4.0/README.md +310 -0
  5. escalated_django-0.4.0/escalated/__init__.py +1 -0
  6. escalated_django-0.4.0/escalated/apps.py +10 -0
  7. escalated_django-0.4.0/escalated/conf.py +78 -0
  8. escalated_django-0.4.0/escalated/drivers/__init__.py +22 -0
  9. escalated_django-0.4.0/escalated/drivers/api_client.py +157 -0
  10. escalated_django-0.4.0/escalated/drivers/cloud.py +86 -0
  11. escalated_django-0.4.0/escalated/drivers/local.py +379 -0
  12. escalated_django-0.4.0/escalated/drivers/synced.py +127 -0
  13. escalated_django-0.4.0/escalated/handlers.py +197 -0
  14. escalated_django-0.4.0/escalated/mail/__init__.py +3 -0
  15. escalated_django-0.4.0/escalated/mail/adapters/__init__.py +38 -0
  16. escalated_django-0.4.0/escalated/mail/adapters/base.py +48 -0
  17. escalated_django-0.4.0/escalated/mail/adapters/imap.py +218 -0
  18. escalated_django-0.4.0/escalated/mail/adapters/mailgun.py +131 -0
  19. escalated_django-0.4.0/escalated/mail/adapters/postmark.py +114 -0
  20. escalated_django-0.4.0/escalated/mail/adapters/ses.py +225 -0
  21. escalated_django-0.4.0/escalated/mail/inbound_message.py +27 -0
  22. escalated_django-0.4.0/escalated/management/__init__.py +0 -0
  23. escalated_django-0.4.0/escalated/management/commands/__init__.py +0 -0
  24. escalated_django-0.4.0/escalated/management/commands/check_sla.py +27 -0
  25. escalated_django-0.4.0/escalated/management/commands/close_resolved.py +64 -0
  26. escalated_django-0.4.0/escalated/management/commands/evaluate_escalations.py +18 -0
  27. escalated_django-0.4.0/escalated/management/commands/poll_imap.py +120 -0
  28. escalated_django-0.4.0/escalated/management/commands/purge_activities.py +49 -0
  29. escalated_django-0.4.0/escalated/middleware.py +52 -0
  30. escalated_django-0.4.0/escalated/migrations/0001_initial.py +531 -0
  31. escalated_django-0.4.0/escalated/migrations/0002_settings_and_guest_tickets.py +73 -0
  32. escalated_django-0.4.0/escalated/migrations/0003_inboundemail.py +107 -0
  33. escalated_django-0.4.0/escalated/migrations/0004_v040_advanced_features.py +185 -0
  34. escalated_django-0.4.0/escalated/migrations/__init__.py +0 -0
  35. escalated_django-0.4.0/escalated/models.py +771 -0
  36. escalated_django-0.4.0/escalated/permissions.py +141 -0
  37. escalated_django-0.4.0/escalated/serializers.py +324 -0
  38. escalated_django-0.4.0/escalated/services/__init__.py +0 -0
  39. escalated_django-0.4.0/escalated/services/assignment_service.py +103 -0
  40. escalated_django-0.4.0/escalated/services/attachment_service.py +100 -0
  41. escalated_django-0.4.0/escalated/services/escalation_service.py +189 -0
  42. escalated_django-0.4.0/escalated/services/inbound_email_service.py +414 -0
  43. escalated_django-0.4.0/escalated/services/macro_service.py +84 -0
  44. escalated_django-0.4.0/escalated/services/notification_service.py +311 -0
  45. escalated_django-0.4.0/escalated/services/sla_service.py +265 -0
  46. escalated_django-0.4.0/escalated/services/ticket_service.py +99 -0
  47. escalated_django-0.4.0/escalated/signals.py +28 -0
  48. escalated_django-0.4.0/escalated/templates/escalated/emails/assigned.html +61 -0
  49. escalated_django-0.4.0/escalated/templates/escalated/emails/escalated.html +48 -0
  50. escalated_django-0.4.0/escalated/templates/escalated/emails/new_ticket.html +54 -0
  51. escalated_django-0.4.0/escalated/templates/escalated/emails/reply.html +39 -0
  52. escalated_django-0.4.0/escalated/templates/escalated/emails/resolved.html +31 -0
  53. escalated_django-0.4.0/escalated/templates/escalated/emails/sla_breach.html +55 -0
  54. escalated_django-0.4.0/escalated/templates/escalated/emails/status_changed.html +38 -0
  55. escalated_django-0.4.0/escalated/urls.py +112 -0
  56. escalated_django-0.4.0/escalated/views/__init__.py +0 -0
  57. escalated_django-0.4.0/escalated/views/admin.py +1452 -0
  58. escalated_django-0.4.0/escalated/views/agent.py +696 -0
  59. escalated_django-0.4.0/escalated/views/customer.py +267 -0
  60. escalated_django-0.4.0/escalated/views/guest.py +212 -0
  61. escalated_django-0.4.0/escalated/views/inbound.py +84 -0
  62. escalated_django-0.4.0/escalated_django.egg-info/PKG-INFO +330 -0
  63. escalated_django-0.4.0/escalated_django.egg-info/SOURCES.txt +78 -0
  64. escalated_django-0.4.0/escalated_django.egg-info/dependency_links.txt +1 -0
  65. escalated_django-0.4.0/escalated_django.egg-info/requires.txt +9 -0
  66. escalated_django-0.4.0/escalated_django.egg-info/top_level.txt +2 -0
  67. escalated_django-0.4.0/pyproject.toml +31 -0
  68. escalated_django-0.4.0/setup.cfg +8 -0
  69. escalated_django-0.4.0/tests/__init__.py +0 -0
  70. escalated_django-0.4.0/tests/conftest.py +65 -0
  71. escalated_django-0.4.0/tests/factories.py +140 -0
  72. escalated_django-0.4.0/tests/integration/__init__.py +0 -0
  73. escalated_django-0.4.0/tests/integration/test_lifecycle.py +229 -0
  74. escalated_django-0.4.0/tests/integration/test_views.py +324 -0
  75. escalated_django-0.4.0/tests/settings.py +56 -0
  76. escalated_django-0.4.0/tests/unit/__init__.py +0 -0
  77. escalated_django-0.4.0/tests/unit/test_models.py +239 -0
  78. escalated_django-0.4.0/tests/unit/test_services.py +359 -0
  79. escalated_django-0.4.0/tests/urls.py +5 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Escalated Dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ recursive-include escalated/templates *
2
+ recursive-include escalated/static *
3
+ recursive-include resources *
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: escalated-django
3
+ Version: 0.4.0
4
+ Summary: Embeddable support ticket system for Django
5
+ Author-email: Escalated Dev <hello@escalated.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/escalated-dev/escalated-django
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: django>=4.2
12
+ Requires-Dist: inertia-django>=1.0
13
+ Requires-Dist: requests>=2.28
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=7.0; extra == "dev"
16
+ Requires-Dist: pytest-django>=4.5; extra == "dev"
17
+ Requires-Dist: factory-boy>=3.2; extra == "dev"
18
+ Requires-Dist: faker>=18.0; extra == "dev"
19
+ Dynamic: license-file
20
+
21
+ # Escalated for Django
22
+
23
+ A full-featured, embeddable support ticket system for Django. Drop it into any app — get a complete helpdesk with SLA tracking, escalation rules, agent workflows, and a customer portal. No external services required.
24
+
25
+ **Three hosting modes.** Run entirely self-hosted, sync to a central cloud for multi-app visibility, or proxy everything to the cloud. Switch modes with a single config change.
26
+
27
+ ## Features
28
+
29
+ - **Ticket lifecycle** — Create, assign, reply, resolve, close, reopen with configurable status transitions
30
+ - **SLA engine** — Per-priority response and resolution targets, business hours calculation, automatic breach detection
31
+ - **Escalation rules** — Condition-based rules that auto-escalate, reprioritize, reassign, or notify
32
+ - **Agent dashboard** — Ticket queue with filters, bulk actions, internal notes, canned responses
33
+ - **Customer portal** — Self-service ticket creation, replies, and status tracking
34
+ - **Admin panel** — Manage departments, SLA policies, escalation rules, tags, and view reports
35
+ - **File attachments** — Drag-and-drop uploads with configurable storage and size limits
36
+ - **Activity timeline** — Full audit log of every action on every ticket
37
+ - **Email notifications** — Configurable per-event notifications with webhook support
38
+ - **Department routing** — Organize agents into departments with auto-assignment (round-robin)
39
+ - **Tagging system** — Categorize tickets with colored tags
40
+ - **Inertia.js + Vue 3 UI** — Shared frontend via [`@escalated-dev/escalated`](https://github.com/escalated-dev/escalated)
41
+
42
+ ### v0.4.0 — Advanced Features
43
+
44
+ - **Bulk actions** — Assign, change status/priority, add tags, close, or delete multiple tickets at once
45
+ - **Macros** — Reusable multi-step automations (set status + assign + add note in one click)
46
+ - **Ticket followers** — Agents follow tickets and receive the same notifications as the assignee
47
+ - **Satisfaction ratings** — 1-5 star CSAT ratings with optional comments after resolution
48
+ - **Pinned notes** — Pin important internal notes to the top of the ticket thread
49
+ - **Keyboard shortcuts** — Full keyboard navigation for power users
50
+ - **Quick filters** — One-click filter chips (My Tickets, Unassigned, Urgent, SLA Breaching)
51
+ - **Presence indicators** — See who else is viewing a ticket in real-time
52
+ - **Enhanced dashboard** — CSAT metrics, resolution times, SLA breach tracking
53
+
54
+ ## Requirements
55
+
56
+ - Python 3.10+
57
+ - Django 4.2+
58
+ - Node.js 18+ (for frontend assets)
59
+
60
+ ## Quick Start
61
+
62
+ ```bash
63
+ pip install escalated-django
64
+ npm install @escalated-dev/escalated
65
+ ```
66
+
67
+ ### 1. Add to INSTALLED_APPS
68
+
69
+ ```python
70
+ INSTALLED_APPS = [
71
+ # ...
72
+ 'django.contrib.contenttypes',
73
+ 'inertia',
74
+ 'escalated',
75
+ ]
76
+ ```
77
+
78
+ ### 2. Include URLs
79
+
80
+ ```python
81
+ from django.urls import path, include
82
+
83
+ urlpatterns = [
84
+ # ...
85
+ path("support/", include("escalated.urls")),
86
+ ]
87
+ ```
88
+
89
+ ### 3. Run migrations
90
+
91
+ ```bash
92
+ python manage.py migrate escalated
93
+ ```
94
+
95
+ Visit `/support` — you're live.
96
+
97
+ ## Frontend Setup
98
+
99
+ Escalated uses Inertia.js with Vue 3. The frontend components are provided by the [`@escalated-dev/escalated`](https://github.com/escalated-dev/escalated) npm package.
100
+
101
+ ### Tailwind Content
102
+
103
+ Add the Escalated package to your Tailwind `content` config so its classes aren't purged:
104
+
105
+ ```js
106
+ // tailwind.config.js
107
+ content: [
108
+ // ... your existing paths
109
+ './node_modules/@escalated-dev/escalated/src/**/*.vue',
110
+ ],
111
+ ```
112
+
113
+ ### Page Resolver
114
+
115
+ Add the Escalated pages to your Inertia page resolver:
116
+
117
+ ```javascript
118
+ // frontend/main.js
119
+ import { createApp, h } from 'vue'
120
+ import { createInertiaApp } from '@inertiajs/vue3'
121
+
122
+ createInertiaApp({
123
+ resolve: name => {
124
+ if (name.startsWith('Escalated/')) {
125
+ const escalatedPages = import.meta.glob(
126
+ '../node_modules/@escalated-dev/escalated/src/pages/**/*.vue',
127
+ { eager: true }
128
+ )
129
+ const pageName = name.replace('Escalated/', '')
130
+ return escalatedPages[`../node_modules/@escalated-dev/escalated/src/pages/${pageName}.vue`]
131
+ }
132
+
133
+ const pages = import.meta.glob('./pages/**/*.vue', { eager: true })
134
+ return pages[`./pages/${name}.vue`]
135
+ },
136
+ setup({ el, App, props, plugin }) {
137
+ createApp({ render: () => h(App, props) })
138
+ .use(plugin)
139
+ .mount(el)
140
+ },
141
+ })
142
+ ```
143
+
144
+ ### Theming (Optional)
145
+
146
+ Register the `EscalatedPlugin` to render Escalated pages inside your app's layout — no page duplication needed:
147
+
148
+ ```javascript
149
+ import { EscalatedPlugin } from '@escalated-dev/escalated'
150
+ import BaseLayout from '@/layouts/BaseLayout.vue'
151
+
152
+ createInertiaApp({
153
+ setup({ el, App, props, plugin }) {
154
+ createApp({ render: () => h(App, props) })
155
+ .use(plugin)
156
+ .use(EscalatedPlugin, {
157
+ layout: BaseLayout,
158
+ theme: {
159
+ primary: '#3b82f6',
160
+ radius: '0.75rem',
161
+ }
162
+ })
163
+ .mount(el)
164
+ },
165
+ })
166
+ ```
167
+
168
+ Your layout component must accept a `#header` slot and a default slot. Escalated will render its sub-navigation in the header and page content in the default slot. Without the plugin, Escalated uses its own standalone layout.
169
+
170
+ See the [`@escalated-dev/escalated` README](https://github.com/escalated-dev/escalated) for full theming documentation and CSS custom properties.
171
+
172
+ ## Hosting Modes
173
+
174
+ ### Self-Hosted (default)
175
+
176
+ Everything stays in your database. No external calls. Full autonomy.
177
+
178
+ ```python
179
+ ESCALATED = {
180
+ "MODE": "self_hosted",
181
+ }
182
+ ```
183
+
184
+ ### Synced
185
+
186
+ Local database + automatic sync to `cloud.escalated.dev` for unified inbox across multiple apps. If the cloud is unreachable, your app keeps working — events queue and retry.
187
+
188
+ ```python
189
+ ESCALATED = {
190
+ "MODE": "synced",
191
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
192
+ "HOSTED_API_KEY": "your-api-key",
193
+ }
194
+ ```
195
+
196
+ ### Cloud
197
+
198
+ All ticket data proxied to the cloud API. Your app handles auth and renders UI, but storage lives in the cloud.
199
+
200
+ ```python
201
+ ESCALATED = {
202
+ "MODE": "cloud",
203
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
204
+ "HOSTED_API_KEY": "your-api-key",
205
+ }
206
+ ```
207
+
208
+ All three modes share the same views, UI, and business logic. The driver pattern handles the rest.
209
+
210
+ ## Configuration
211
+
212
+ Add to your `settings.py`:
213
+
214
+ ```python
215
+ ESCALATED = {
216
+ "MODE": "self_hosted", # self_hosted | synced | cloud
217
+ "TABLE_PREFIX": "escalated_",
218
+ "ROUTE_PREFIX": "support",
219
+ "DEFAULT_PRIORITY": "medium",
220
+
221
+ # Tickets
222
+ "ALLOW_CUSTOMER_CLOSE": True,
223
+ "AUTO_CLOSE_RESOLVED_AFTER_DAYS": 7,
224
+ "MAX_ATTACHMENTS": 5,
225
+ "MAX_ATTACHMENT_SIZE_KB": 10240,
226
+
227
+ # SLA
228
+ "SLA": {
229
+ "ENABLED": True,
230
+ "BUSINESS_HOURS_ONLY": False,
231
+ "BUSINESS_HOURS": {
232
+ "START": "09:00",
233
+ "END": "17:00",
234
+ "TIMEZONE": "UTC",
235
+ "DAYS": [1, 2, 3, 4, 5],
236
+ },
237
+ },
238
+
239
+ # Notifications
240
+ "NOTIFICATION_CHANNELS": ["email"],
241
+ "WEBHOOK_URL": None,
242
+
243
+ # Cloud/Synced mode
244
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
245
+ "HOSTED_API_KEY": None,
246
+ }
247
+ ```
248
+
249
+ ## Management Commands
250
+
251
+ ```bash
252
+ # Check SLA deadlines and fire breach notifications
253
+ python manage.py check_sla
254
+
255
+ # Evaluate escalation rules against open tickets
256
+ python manage.py evaluate_escalations
257
+
258
+ # Auto-close tickets resolved more than N days ago
259
+ python manage.py close_resolved --days 7
260
+
261
+ # Purge old activity logs
262
+ python manage.py purge_activities --days 90
263
+ ```
264
+
265
+ Schedule these with cron, Celery Beat, or django-crontab for automated enforcement.
266
+
267
+ ## Routes
268
+
269
+ All routes use the configurable prefix (default: `support`).
270
+
271
+ | Route | Method | Description |
272
+ |-------|--------|-------------|
273
+ | `/support/tickets/` | GET | Customer ticket list |
274
+ | `/support/tickets/create/` | GET | New ticket form |
275
+ | `/support/tickets/<id>/` | GET | Ticket detail |
276
+ | `/support/agent/` | GET | Agent dashboard |
277
+ | `/support/agent/tickets/` | GET | Agent ticket queue |
278
+ | `/support/agent/tickets/<id>/` | GET | Agent ticket view |
279
+ | `/support/admin/reports/` | GET | Admin reports |
280
+ | `/support/admin/departments/` | GET | Department management |
281
+ | `/support/admin/sla-policies/` | GET | SLA policy management |
282
+ | `/support/admin/escalation-rules/` | GET | Escalation rule management |
283
+ | `/support/admin/tags/` | GET | Tag management |
284
+ | `/support/admin/canned-responses/` | GET | Canned response management |
285
+ | `/support/agent/tickets/bulk/` | POST | Bulk actions on multiple tickets |
286
+ | `/support/agent/tickets/<id>/follow/` | POST | Follow/unfollow a ticket |
287
+ | `/support/agent/tickets/<id>/macro/` | POST | Apply a macro to a ticket |
288
+ | `/support/agent/tickets/<id>/presence/` | POST | Update presence on a ticket |
289
+ | `/support/agent/tickets/<id>/pin/<reply_id>/` | POST | Pin/unpin an internal note |
290
+ | `/support/tickets/<id>/rate/` | POST | Submit satisfaction rating |
291
+
292
+ ## Signals
293
+
294
+ Connect to ticket lifecycle events:
295
+
296
+ ```python
297
+ from escalated.signals import ticket_created, ticket_resolved
298
+
299
+ @receiver(ticket_created)
300
+ def on_ticket_created(sender, ticket, user, **kwargs):
301
+ print(f"New ticket: {ticket.reference}")
302
+
303
+ @receiver(ticket_resolved)
304
+ def on_ticket_resolved(sender, ticket, user, **kwargs):
305
+ print(f"Resolved: {ticket.reference}")
306
+ ```
307
+
308
+ Available signals: `ticket_created`, `ticket_updated`, `ticket_status_changed`, `ticket_assigned`, `ticket_unassigned`, `ticket_priority_changed`, `ticket_escalated`, `ticket_resolved`, `ticket_closed`, `ticket_reopened`, `reply_created`, `internal_note_added`, `sla_breached`, `sla_warning`, `tag_added`, `tag_removed`, `department_changed`.
309
+
310
+ ## Also Available For
311
+
312
+ - **[Escalated for Laravel](https://github.com/escalated-dev/escalated-laravel)** — Laravel Composer package
313
+ - **[Escalated for Rails](https://github.com/escalated-dev/escalated-rails)** — Ruby on Rails engine
314
+ - **[Escalated for Django](https://github.com/escalated-dev/escalated-django)** — Django reusable app (you are here)
315
+ - **[Escalated for AdonisJS](https://github.com/escalated-dev/escalated-adonis)** — AdonisJS v6 package
316
+ - **[Escalated for Filament](https://github.com/escalated-dev/escalated-filament)** — Filament v3 admin panel plugin
317
+ - **[Shared Frontend](https://github.com/escalated-dev/escalated)** — Vue 3 + Inertia.js UI components
318
+
319
+ Same architecture, same Vue UI, same three hosting modes — for every major backend framework.
320
+
321
+ ## Development
322
+
323
+ ```bash
324
+ pip install -e ".[dev]"
325
+ pytest
326
+ ```
327
+
328
+ ## License
329
+
330
+ MIT
@@ -0,0 +1,310 @@
1
+ # Escalated for Django
2
+
3
+ A full-featured, embeddable support ticket system for Django. Drop it into any app — get a complete helpdesk with SLA tracking, escalation rules, agent workflows, and a customer portal. No external services required.
4
+
5
+ **Three hosting modes.** Run entirely self-hosted, sync to a central cloud for multi-app visibility, or proxy everything to the cloud. Switch modes with a single config change.
6
+
7
+ ## Features
8
+
9
+ - **Ticket lifecycle** — Create, assign, reply, resolve, close, reopen with configurable status transitions
10
+ - **SLA engine** — Per-priority response and resolution targets, business hours calculation, automatic breach detection
11
+ - **Escalation rules** — Condition-based rules that auto-escalate, reprioritize, reassign, or notify
12
+ - **Agent dashboard** — Ticket queue with filters, bulk actions, internal notes, canned responses
13
+ - **Customer portal** — Self-service ticket creation, replies, and status tracking
14
+ - **Admin panel** — Manage departments, SLA policies, escalation rules, tags, and view reports
15
+ - **File attachments** — Drag-and-drop uploads with configurable storage and size limits
16
+ - **Activity timeline** — Full audit log of every action on every ticket
17
+ - **Email notifications** — Configurable per-event notifications with webhook support
18
+ - **Department routing** — Organize agents into departments with auto-assignment (round-robin)
19
+ - **Tagging system** — Categorize tickets with colored tags
20
+ - **Inertia.js + Vue 3 UI** — Shared frontend via [`@escalated-dev/escalated`](https://github.com/escalated-dev/escalated)
21
+
22
+ ### v0.4.0 — Advanced Features
23
+
24
+ - **Bulk actions** — Assign, change status/priority, add tags, close, or delete multiple tickets at once
25
+ - **Macros** — Reusable multi-step automations (set status + assign + add note in one click)
26
+ - **Ticket followers** — Agents follow tickets and receive the same notifications as the assignee
27
+ - **Satisfaction ratings** — 1-5 star CSAT ratings with optional comments after resolution
28
+ - **Pinned notes** — Pin important internal notes to the top of the ticket thread
29
+ - **Keyboard shortcuts** — Full keyboard navigation for power users
30
+ - **Quick filters** — One-click filter chips (My Tickets, Unassigned, Urgent, SLA Breaching)
31
+ - **Presence indicators** — See who else is viewing a ticket in real-time
32
+ - **Enhanced dashboard** — CSAT metrics, resolution times, SLA breach tracking
33
+
34
+ ## Requirements
35
+
36
+ - Python 3.10+
37
+ - Django 4.2+
38
+ - Node.js 18+ (for frontend assets)
39
+
40
+ ## Quick Start
41
+
42
+ ```bash
43
+ pip install escalated-django
44
+ npm install @escalated-dev/escalated
45
+ ```
46
+
47
+ ### 1. Add to INSTALLED_APPS
48
+
49
+ ```python
50
+ INSTALLED_APPS = [
51
+ # ...
52
+ 'django.contrib.contenttypes',
53
+ 'inertia',
54
+ 'escalated',
55
+ ]
56
+ ```
57
+
58
+ ### 2. Include URLs
59
+
60
+ ```python
61
+ from django.urls import path, include
62
+
63
+ urlpatterns = [
64
+ # ...
65
+ path("support/", include("escalated.urls")),
66
+ ]
67
+ ```
68
+
69
+ ### 3. Run migrations
70
+
71
+ ```bash
72
+ python manage.py migrate escalated
73
+ ```
74
+
75
+ Visit `/support` — you're live.
76
+
77
+ ## Frontend Setup
78
+
79
+ Escalated uses Inertia.js with Vue 3. The frontend components are provided by the [`@escalated-dev/escalated`](https://github.com/escalated-dev/escalated) npm package.
80
+
81
+ ### Tailwind Content
82
+
83
+ Add the Escalated package to your Tailwind `content` config so its classes aren't purged:
84
+
85
+ ```js
86
+ // tailwind.config.js
87
+ content: [
88
+ // ... your existing paths
89
+ './node_modules/@escalated-dev/escalated/src/**/*.vue',
90
+ ],
91
+ ```
92
+
93
+ ### Page Resolver
94
+
95
+ Add the Escalated pages to your Inertia page resolver:
96
+
97
+ ```javascript
98
+ // frontend/main.js
99
+ import { createApp, h } from 'vue'
100
+ import { createInertiaApp } from '@inertiajs/vue3'
101
+
102
+ createInertiaApp({
103
+ resolve: name => {
104
+ if (name.startsWith('Escalated/')) {
105
+ const escalatedPages = import.meta.glob(
106
+ '../node_modules/@escalated-dev/escalated/src/pages/**/*.vue',
107
+ { eager: true }
108
+ )
109
+ const pageName = name.replace('Escalated/', '')
110
+ return escalatedPages[`../node_modules/@escalated-dev/escalated/src/pages/${pageName}.vue`]
111
+ }
112
+
113
+ const pages = import.meta.glob('./pages/**/*.vue', { eager: true })
114
+ return pages[`./pages/${name}.vue`]
115
+ },
116
+ setup({ el, App, props, plugin }) {
117
+ createApp({ render: () => h(App, props) })
118
+ .use(plugin)
119
+ .mount(el)
120
+ },
121
+ })
122
+ ```
123
+
124
+ ### Theming (Optional)
125
+
126
+ Register the `EscalatedPlugin` to render Escalated pages inside your app's layout — no page duplication needed:
127
+
128
+ ```javascript
129
+ import { EscalatedPlugin } from '@escalated-dev/escalated'
130
+ import BaseLayout from '@/layouts/BaseLayout.vue'
131
+
132
+ createInertiaApp({
133
+ setup({ el, App, props, plugin }) {
134
+ createApp({ render: () => h(App, props) })
135
+ .use(plugin)
136
+ .use(EscalatedPlugin, {
137
+ layout: BaseLayout,
138
+ theme: {
139
+ primary: '#3b82f6',
140
+ radius: '0.75rem',
141
+ }
142
+ })
143
+ .mount(el)
144
+ },
145
+ })
146
+ ```
147
+
148
+ Your layout component must accept a `#header` slot and a default slot. Escalated will render its sub-navigation in the header and page content in the default slot. Without the plugin, Escalated uses its own standalone layout.
149
+
150
+ See the [`@escalated-dev/escalated` README](https://github.com/escalated-dev/escalated) for full theming documentation and CSS custom properties.
151
+
152
+ ## Hosting Modes
153
+
154
+ ### Self-Hosted (default)
155
+
156
+ Everything stays in your database. No external calls. Full autonomy.
157
+
158
+ ```python
159
+ ESCALATED = {
160
+ "MODE": "self_hosted",
161
+ }
162
+ ```
163
+
164
+ ### Synced
165
+
166
+ Local database + automatic sync to `cloud.escalated.dev` for unified inbox across multiple apps. If the cloud is unreachable, your app keeps working — events queue and retry.
167
+
168
+ ```python
169
+ ESCALATED = {
170
+ "MODE": "synced",
171
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
172
+ "HOSTED_API_KEY": "your-api-key",
173
+ }
174
+ ```
175
+
176
+ ### Cloud
177
+
178
+ All ticket data proxied to the cloud API. Your app handles auth and renders UI, but storage lives in the cloud.
179
+
180
+ ```python
181
+ ESCALATED = {
182
+ "MODE": "cloud",
183
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
184
+ "HOSTED_API_KEY": "your-api-key",
185
+ }
186
+ ```
187
+
188
+ All three modes share the same views, UI, and business logic. The driver pattern handles the rest.
189
+
190
+ ## Configuration
191
+
192
+ Add to your `settings.py`:
193
+
194
+ ```python
195
+ ESCALATED = {
196
+ "MODE": "self_hosted", # self_hosted | synced | cloud
197
+ "TABLE_PREFIX": "escalated_",
198
+ "ROUTE_PREFIX": "support",
199
+ "DEFAULT_PRIORITY": "medium",
200
+
201
+ # Tickets
202
+ "ALLOW_CUSTOMER_CLOSE": True,
203
+ "AUTO_CLOSE_RESOLVED_AFTER_DAYS": 7,
204
+ "MAX_ATTACHMENTS": 5,
205
+ "MAX_ATTACHMENT_SIZE_KB": 10240,
206
+
207
+ # SLA
208
+ "SLA": {
209
+ "ENABLED": True,
210
+ "BUSINESS_HOURS_ONLY": False,
211
+ "BUSINESS_HOURS": {
212
+ "START": "09:00",
213
+ "END": "17:00",
214
+ "TIMEZONE": "UTC",
215
+ "DAYS": [1, 2, 3, 4, 5],
216
+ },
217
+ },
218
+
219
+ # Notifications
220
+ "NOTIFICATION_CHANNELS": ["email"],
221
+ "WEBHOOK_URL": None,
222
+
223
+ # Cloud/Synced mode
224
+ "HOSTED_API_URL": "https://cloud.escalated.dev/api/v1",
225
+ "HOSTED_API_KEY": None,
226
+ }
227
+ ```
228
+
229
+ ## Management Commands
230
+
231
+ ```bash
232
+ # Check SLA deadlines and fire breach notifications
233
+ python manage.py check_sla
234
+
235
+ # Evaluate escalation rules against open tickets
236
+ python manage.py evaluate_escalations
237
+
238
+ # Auto-close tickets resolved more than N days ago
239
+ python manage.py close_resolved --days 7
240
+
241
+ # Purge old activity logs
242
+ python manage.py purge_activities --days 90
243
+ ```
244
+
245
+ Schedule these with cron, Celery Beat, or django-crontab for automated enforcement.
246
+
247
+ ## Routes
248
+
249
+ All routes use the configurable prefix (default: `support`).
250
+
251
+ | Route | Method | Description |
252
+ |-------|--------|-------------|
253
+ | `/support/tickets/` | GET | Customer ticket list |
254
+ | `/support/tickets/create/` | GET | New ticket form |
255
+ | `/support/tickets/<id>/` | GET | Ticket detail |
256
+ | `/support/agent/` | GET | Agent dashboard |
257
+ | `/support/agent/tickets/` | GET | Agent ticket queue |
258
+ | `/support/agent/tickets/<id>/` | GET | Agent ticket view |
259
+ | `/support/admin/reports/` | GET | Admin reports |
260
+ | `/support/admin/departments/` | GET | Department management |
261
+ | `/support/admin/sla-policies/` | GET | SLA policy management |
262
+ | `/support/admin/escalation-rules/` | GET | Escalation rule management |
263
+ | `/support/admin/tags/` | GET | Tag management |
264
+ | `/support/admin/canned-responses/` | GET | Canned response management |
265
+ | `/support/agent/tickets/bulk/` | POST | Bulk actions on multiple tickets |
266
+ | `/support/agent/tickets/<id>/follow/` | POST | Follow/unfollow a ticket |
267
+ | `/support/agent/tickets/<id>/macro/` | POST | Apply a macro to a ticket |
268
+ | `/support/agent/tickets/<id>/presence/` | POST | Update presence on a ticket |
269
+ | `/support/agent/tickets/<id>/pin/<reply_id>/` | POST | Pin/unpin an internal note |
270
+ | `/support/tickets/<id>/rate/` | POST | Submit satisfaction rating |
271
+
272
+ ## Signals
273
+
274
+ Connect to ticket lifecycle events:
275
+
276
+ ```python
277
+ from escalated.signals import ticket_created, ticket_resolved
278
+
279
+ @receiver(ticket_created)
280
+ def on_ticket_created(sender, ticket, user, **kwargs):
281
+ print(f"New ticket: {ticket.reference}")
282
+
283
+ @receiver(ticket_resolved)
284
+ def on_ticket_resolved(sender, ticket, user, **kwargs):
285
+ print(f"Resolved: {ticket.reference}")
286
+ ```
287
+
288
+ Available signals: `ticket_created`, `ticket_updated`, `ticket_status_changed`, `ticket_assigned`, `ticket_unassigned`, `ticket_priority_changed`, `ticket_escalated`, `ticket_resolved`, `ticket_closed`, `ticket_reopened`, `reply_created`, `internal_note_added`, `sla_breached`, `sla_warning`, `tag_added`, `tag_removed`, `department_changed`.
289
+
290
+ ## Also Available For
291
+
292
+ - **[Escalated for Laravel](https://github.com/escalated-dev/escalated-laravel)** — Laravel Composer package
293
+ - **[Escalated for Rails](https://github.com/escalated-dev/escalated-rails)** — Ruby on Rails engine
294
+ - **[Escalated for Django](https://github.com/escalated-dev/escalated-django)** — Django reusable app (you are here)
295
+ - **[Escalated for AdonisJS](https://github.com/escalated-dev/escalated-adonis)** — AdonisJS v6 package
296
+ - **[Escalated for Filament](https://github.com/escalated-dev/escalated-filament)** — Filament v3 admin panel plugin
297
+ - **[Shared Frontend](https://github.com/escalated-dev/escalated)** — Vue 3 + Inertia.js UI components
298
+
299
+ Same architecture, same Vue UI, same three hosting modes — for every major backend framework.
300
+
301
+ ## Development
302
+
303
+ ```bash
304
+ pip install -e ".[dev]"
305
+ pytest
306
+ ```
307
+
308
+ ## License
309
+
310
+ MIT
@@ -0,0 +1 @@
1
+ default_app_config = "escalated.apps.EscalatedConfig"
@@ -0,0 +1,10 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class EscalatedConfig(AppConfig):
5
+ name = "escalated"
6
+ verbose_name = "Escalated Support"
7
+ default_auto_field = "django.db.models.BigAutoField"
8
+
9
+ def ready(self):
10
+ import escalated.handlers # noqa: F401 - connects signal handlers