Skip to content

Commit ad1237a

Browse files
authored
Merge pull request #7 from escalated-dev/feature/platform-parity
Feature/platform parity
2 parents 8f3ccae + 1ad7df9 commit ad1237a

100 files changed

Lines changed: 14162 additions & 1814 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/screenshots.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: component-screenshots
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
screenshots:
15+
runs-on: ubuntu-latest
16+
name: Component Screenshots
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: 22
26+
27+
- name: Install dependencies
28+
run: npm install
29+
30+
- name: Install Playwright Chromium
31+
run: npx playwright install chromium --with-deps
32+
33+
- name: Build Storybook
34+
run: npx storybook build --output-dir storybook-static
35+
36+
- name: Take screenshots
37+
run: |
38+
npx http-server storybook-static -p 6006 -s &
39+
sleep 3
40+
npx playwright test --config=playwright-screenshots.config.js --reporter=list || true
41+
kill %1 2>/dev/null || true
42+
43+
- name: Upload screenshots
44+
uses: actions/upload-artifact@v4
45+
if: always()
46+
with:
47+
name: component-screenshots
48+
path: screenshot-results/*.png
49+
retention-days: 30
50+
51+
- name: Comment on PR with screenshot summary
52+
if: github.event_name == 'pull_request' && always()
53+
uses: actions/github-script@v7
54+
with:
55+
script: |
56+
const fs = require('fs');
57+
const path = require('path');
58+
const dir = 'screenshot-results';
59+
60+
if (!fs.existsSync(dir)) {
61+
console.log('No screenshot-results directory found');
62+
return;
63+
}
64+
65+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.png'));
66+
if (files.length === 0) {
67+
console.log('No screenshots captured');
68+
return;
69+
}
70+
71+
const { data: { artifacts } } = await github.rest.actions.listArtifactsForRepo({
72+
owner: context.repo.owner,
73+
repo: context.repo.repo,
74+
});
75+
76+
const body = [
77+
'## Component Screenshots',
78+
'',
79+
`Captured **${files.length}** component screenshots from Storybook.`,
80+
'',
81+
'Download the full set from the **component-screenshots** artifact in the workflow run.',
82+
'',
83+
'<details>',
84+
'<summary>Screenshot list</summary>',
85+
'',
86+
...files.map(f => `- \`${f.replace('.png', '')}\``),
87+
'',
88+
'</details>',
89+
].join('\n');
90+
91+
// Find existing comment to update
92+
const { data: comments } = await github.rest.issues.listComments({
93+
owner: context.repo.owner,
94+
repo: context.repo.repo,
95+
issue_number: context.issue.number,
96+
});
97+
98+
const existing = comments.find(c =>
99+
c.user.type === 'Bot' && c.body.includes('## Component Screenshots')
100+
);
101+
102+
if (existing) {
103+
await github.rest.issues.updateComment({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
comment_id: existing.id,
107+
body,
108+
});
109+
} else {
110+
await github.rest.issues.createComment({
111+
owner: context.repo.owner,
112+
repo: context.repo.repo,
113+
issue_number: context.issue.number,
114+
body,
115+
});
116+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ node_modules/
22
dist/
33
.DS_Store
44
*.log
5+
storybook-static/
6+
screenshot-results/

.storybook/main.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import vue from '@vitejs/plugin-vue';
2+
3+
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
4+
const config = {
5+
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
6+
addons: ['@storybook/addon-docs', '@storybook/addon-a11y'],
7+
framework: '@storybook/vue3-vite',
8+
async viteFinal(config) {
9+
const hasVue = config.plugins?.some(
10+
(p) => p && (p.name === 'vite:vue' || (Array.isArray(p) && p[0]?.name === 'vite:vue')),
11+
);
12+
if (!hasVue) {
13+
config.plugins = config.plugins || [];
14+
config.plugins.push(vue());
15+
}
16+
return config;
17+
},
18+
};
19+
export default config;

.storybook/preview.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { computed } from 'vue';
2+
import './storybook.css';
3+
4+
/** @type { import('@storybook/vue3-vite').Preview } */
5+
const preview = {
6+
parameters: {
7+
controls: {
8+
matchers: {
9+
color: /(background|color)$/i,
10+
date: /Date$/i,
11+
},
12+
},
13+
a11y: {
14+
test: 'todo',
15+
},
16+
},
17+
globalTypes: {
18+
theme: {
19+
name: 'Theme',
20+
description: 'Dark or light mode',
21+
defaultValue: 'dark',
22+
toolbar: {
23+
icon: 'paintbrush',
24+
items: [
25+
{ value: 'dark', title: 'Dark' },
26+
{ value: 'light', title: 'Light' },
27+
{ value: 'side-by-side', title: 'Side by Side' },
28+
],
29+
dynamicTitle: true,
30+
},
31+
},
32+
},
33+
decorators: [
34+
(story, context) => {
35+
const theme = context.globals.theme || 'dark';
36+
37+
if (theme === 'side-by-side') {
38+
return {
39+
components: { story },
40+
provide: {
41+
'esc-dark': computed(() => false),
42+
},
43+
setup() {
44+
const darkProvide = { 'esc-dark': computed(() => true) };
45+
return { darkProvide };
46+
},
47+
template: `
48+
<div style="display: flex; gap: 24px; align-items: flex-start;">
49+
<div style="flex: 1; padding: 24px; background: #fff; border-radius: 12px;">
50+
<div style="font-size: 11px; color: #999; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.05em;">Light</div>
51+
<story />
52+
</div>
53+
<div style="flex: 1; padding: 24px; background: #171717; border-radius: 12px;">
54+
<div style="font-size: 11px; color: #666; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.05em;">Dark</div>
55+
<story v-bind="darkProvide" />
56+
</div>
57+
</div>
58+
`,
59+
};
60+
}
61+
62+
const isDark = theme === 'dark';
63+
return {
64+
components: { story },
65+
provide: {
66+
'esc-dark': computed(() => isDark),
67+
},
68+
template: `
69+
<div style="padding: 24px; min-height: 100px; border-radius: 12px;"
70+
:style="{ background: ${isDark} ? '#171717' : '#fff' }">
71+
<story />
72+
</div>
73+
`,
74+
};
75+
},
76+
],
77+
};
78+
79+
export default preview;

.storybook/storybook.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
/* Storybook canvas overrides */
6+
body {
7+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
8+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# platform Feature Parity — Design Document
2+
3+
## Goal
4+
5+
Implement all 36 core gaps and scaffold all 24 plugin gaps identified in the platform comparison. Core features get full frontend + backend implementations across all frameworks. Plugins get their own repos under `escalated/plugins/` with frontend components and backend hooks. Each feature includes a TODO section documenting remaining work.
6+
7+
## Architecture
8+
9+
### Core Features
10+
11+
Core features live in the existing repos:
12+
- `escalated/` — Vue 3 frontend (pages, components, composables)
13+
- `escalated-laravel/` — Laravel backend (models, controllers, migrations, routes)
14+
- `escalated-adonis/` — AdonisJS backend
15+
- `escalated-django/` — Django backend
16+
- `escalated-rails/` — Rails backend
17+
- `escalated-wordpress/` — WordPress adapter
18+
- `escalated-filament/` — Filament admin panel
19+
20+
### Plugins
21+
22+
Each plugin is a standalone package under `escalated/plugins/`:
23+
```
24+
plugins/
25+
├── escalated-plugin-livechat/
26+
├── escalated-plugin-slack/
27+
├── escalated-plugin-jira/
28+
├── escalated-plugin-ai-copilot/
29+
├── escalated-plugin-web-widget/
30+
├── escalated-plugin-whatsapp/
31+
├── escalated-plugin-sms/
32+
├── escalated-plugin-social/
33+
├── escalated-plugin-phone/
34+
├── escalated-plugin-community/
35+
├── escalated-plugin-nps/
36+
├── escalated-plugin-kb-ai/
37+
├── escalated-plugin-approvals/
38+
├── escalated-plugin-custom-objects/
39+
├── escalated-plugin-ip-restriction/
40+
├── escalated-plugin-compliance/
41+
├── escalated-plugin-marketplace/
42+
├── escalated-plugin-scheduled-reports/
43+
├── escalated-plugin-custom-layouts/
44+
├── escalated-plugin-proactive-messages/
45+
├── escalated-plugin-omnichannel-routing/
46+
├── escalated-plugin-mobile-sdk/
47+
├── escalated-plugin-unified-status/
48+
└── escalated-plugin-ticket-sharing/
49+
```
50+
51+
Each plugin follows a standard structure:
52+
```
53+
escalated-plugin-<name>/
54+
├── package.json
55+
├── src/
56+
│ ├── index.js # defineEscalatedPlugin() entry
57+
│ └── components/ # Vue components
58+
├── TODO.md # What remains to implement
59+
└── README.md # Plugin documentation
60+
```
61+
62+
## Implementation Layers
63+
64+
### Layer 1: Structural Foundations
65+
66+
| Feature | Frontend | Backend | Priority |
67+
|---------|----------|---------|----------|
68+
| Custom Fields & Forms | Field renderer component, form builder UI | Migration, model, field types API | P0 |
69+
| Custom Statuses | Status config UI, updated StatusBadge | Migration, status categories model | P0 |
70+
| Business Hours | Schedule editor UI, holiday calendar | Schedule model, SLA time calculator | P0 |
71+
| RBAC / Custom Roles | Role editor, permission matrix UI | Roles/permissions models, middleware | P0 |
72+
| Audit Log | Audit log viewer, filters | Auditable trait, log storage | P0 |
73+
74+
### Layer 2: Core Workflows
75+
76+
| Feature | Frontend | Backend |
77+
|---------|----------|---------|
78+
| Ticket Merging | Merge dialog, merged ticket indicator | Merge service, reply consolidation |
79+
| Problem/Incident Linking | Link type selector, linked tickets panel | Ticket types, cascade resolution |
80+
| Parent/Child Tickets | Child ticket creator, relationship view | Parent-child model relationship |
81+
| Side Conversations | Side thread UI, channel selector | Side conversation model, notifications |
82+
| Agent Collision Detection | Typing indicator, collision warning banner | Real-time presence channel |
83+
| Light Agents | Permission-restricted agent view | Light agent role, limited permissions |
84+
| Skills-Based Routing | Skills tag manager, routing config UI | Skills model, routing algorithm |
85+
| Agent Capacity | Capacity settings UI, load indicator | Capacity tracking, assignment limiter |
86+
| Webhooks (Outbound) | Webhook config UI, delivery log viewer | Webhook model, dispatcher, retry queue |
87+
| Time-Based Automations | Time-condition builder UI | Automation scheduler, condition evaluator |
88+
| Trigger Categories | Category organizer for triggers | Category model for automation rules |
89+
90+
### Layer 3: Content & Analytics
91+
92+
| Feature | Frontend | Backend |
93+
|---------|----------|---------|
94+
| Knowledge Base | Article editor, category tree, search | Article/Category models, full-text search |
95+
| Pre-built Dashboards | Dashboard widgets, chart components | Aggregation queries, data endpoints |
96+
| Custom Report Builder | Drag-drop report editor | Report definition model, query engine |
97+
| Real-time Dashboards | Live-updating stats, queue depth | WebSocket/SSE data stream |
98+
| Agent Metrics | Per-agent performance cards | Metric collection, aggregation |
99+
| SLA Reporting | SLA compliance charts, breach analysis | SLA achievement calculator |
100+
| CSAT Customization | Survey config UI, conditional delivery | Survey model, delivery rules |
101+
| CSAT Reporting | CSAT analytics dashboard | CSAT aggregation queries |
102+
103+
### Layer 4: Platform & Security
104+
105+
| Feature | Frontend | Backend |
106+
|---------|----------|---------|
107+
| SSO (SAML/JWT) | SSO config UI, login redirect | SAML/JWT auth drivers |
108+
| Two-Factor Auth | 2FA setup wizard, TOTP input | TOTP generation, verification |
109+
| Data Retention | Retention policy config UI | Scheduled deletion jobs |
110+
| Custom Objects | Object/field definition UI | Dynamic schema, lookup relationships |
111+
| Sandbox | Sandbox creation UI | Environment cloning |
112+
| Email Channel (Advanced) | Multi-address config, DKIM setup | Email piping, authentication |
113+
| Conditional Fields | Show/hide rules builder | Condition evaluation engine |
114+
| Custom Statuses per Category | Sub-status config within categories | Status category model |
115+
116+
## Branching Strategy
117+
118+
- `escalated/` frontend: `feature/platform-parity` branch
119+
- Each backend: `feature/platform-parity` branch
120+
- Plugins: new repos initialized in `plugins/` directory
121+
122+
## Success Criteria
123+
124+
- All 36 core gaps have frontend components + at least Laravel backend implementation
125+
- All 24 plugins have scaffold with entry point, components, and TODO.md
126+
- All tests pass
127+
- Each feature has a TODO.md documenting remaining work

0 commit comments

Comments
 (0)