spine-framework-portal 0.1.1 → 0.1.4
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.
- package/README.md +26 -0
- package/functions/custom_portal-community-escalation.ts +202 -0
- package/functions/custom_portal-signals.ts +122 -14
- package/manifest.json +1 -0
- package/package.json +2 -3
- package/seed/roles.json +12 -0
- package/seed/triggers.json +2 -1
- package/seed/types.json +147 -70
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# spine-framework-portal
|
|
2
|
+
|
|
3
|
+
Customer self-service portal app for Spine Framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install spine-framework-portal
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx spine-framework install-app customer-portal
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Community discussion and Q&A
|
|
20
|
+
- Course and lesson content delivery
|
|
21
|
+
- Support ticket submission and tracking
|
|
22
|
+
- Knowledge base article access
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
MIT
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Portal Community Escalation Handler
|
|
2
|
+
// Converts unanswered community posts to support tickets.
|
|
3
|
+
// Standalone: no dependency on cortex functions.
|
|
4
|
+
// Triggered by cron — see portal seed/triggers.json.
|
|
5
|
+
|
|
6
|
+
import { createHandler } from './_shared/middleware'
|
|
7
|
+
import { adminDb } from './_shared/db'
|
|
8
|
+
|
|
9
|
+
interface CommunityPost {
|
|
10
|
+
id: string
|
|
11
|
+
title: string
|
|
12
|
+
description?: string
|
|
13
|
+
account_id: string
|
|
14
|
+
person_id: string
|
|
15
|
+
created_at: string
|
|
16
|
+
data?: {
|
|
17
|
+
category?: string
|
|
18
|
+
tags?: string[]
|
|
19
|
+
status?: string
|
|
20
|
+
escalation?: {
|
|
21
|
+
escalated_to_ticket_id?: string
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function escalatePostToTicket(post: CommunityPost): Promise<string | null> {
|
|
27
|
+
try {
|
|
28
|
+
const { data: existingTicket } = await adminDb
|
|
29
|
+
.from('items')
|
|
30
|
+
.select('id')
|
|
31
|
+
.eq('type_slug', 'support_ticket')
|
|
32
|
+
.eq('data->>source_post_id', post.id)
|
|
33
|
+
.limit(1)
|
|
34
|
+
.maybeSingle()
|
|
35
|
+
|
|
36
|
+
if (existingTicket) {
|
|
37
|
+
console.log(`Post ${post.id} already escalated to ticket ${existingTicket.id}`)
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { data: newTicket, error: insertError } = await adminDb
|
|
42
|
+
.from('items')
|
|
43
|
+
.insert({
|
|
44
|
+
type_slug: 'support_ticket',
|
|
45
|
+
title: `Escalated: ${post.title}`,
|
|
46
|
+
description: post.description || 'No description provided',
|
|
47
|
+
account_id: post.account_id,
|
|
48
|
+
person_id: post.person_id,
|
|
49
|
+
status: 'open',
|
|
50
|
+
data: {
|
|
51
|
+
source_post_id: post.id,
|
|
52
|
+
source: 'community_escalation',
|
|
53
|
+
escalated_at: new Date().toISOString(),
|
|
54
|
+
original_category: post.data?.category || 'general',
|
|
55
|
+
original_tags: post.data?.tags || [],
|
|
56
|
+
community_status: 'unanswered_24h',
|
|
57
|
+
ai_metadata: {
|
|
58
|
+
confidence_threshold: 0.75,
|
|
59
|
+
escalation_reason: 'community_unanswered',
|
|
60
|
+
problem_statement: post.title,
|
|
61
|
+
source_content: post.description?.slice(0, 1000),
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
.select('id')
|
|
66
|
+
.single()
|
|
67
|
+
|
|
68
|
+
if (insertError || !newTicket) {
|
|
69
|
+
throw new Error(`Failed to create ticket: ${insertError?.message}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ticketId = newTicket.id
|
|
73
|
+
|
|
74
|
+
await adminDb.from('threads').insert({
|
|
75
|
+
target_type: 'items',
|
|
76
|
+
target_id: ticketId,
|
|
77
|
+
visibility: 'external',
|
|
78
|
+
status: 'active',
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
await adminDb
|
|
82
|
+
.from('items')
|
|
83
|
+
.update({
|
|
84
|
+
data: {
|
|
85
|
+
...post.data,
|
|
86
|
+
status: 'escalated',
|
|
87
|
+
escalation: {
|
|
88
|
+
escalated_to_ticket_id: ticketId,
|
|
89
|
+
escalated_at: new Date().toISOString(),
|
|
90
|
+
reason: 'unanswered_24h',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
updated_at: new Date().toISOString(),
|
|
94
|
+
})
|
|
95
|
+
.eq('id', post.id)
|
|
96
|
+
|
|
97
|
+
// Attempt to trigger support triage pipeline if present
|
|
98
|
+
try {
|
|
99
|
+
const { data: pipeline } = await adminDb
|
|
100
|
+
.from('pipelines')
|
|
101
|
+
.select('id')
|
|
102
|
+
.ilike('name', '%support%triage%')
|
|
103
|
+
.limit(1)
|
|
104
|
+
.maybeSingle()
|
|
105
|
+
|
|
106
|
+
if (pipeline) {
|
|
107
|
+
await adminDb.from('pipeline_executions').insert({
|
|
108
|
+
pipeline_id: pipeline.id,
|
|
109
|
+
target_type: 'items',
|
|
110
|
+
target_id: ticketId,
|
|
111
|
+
status: 'pending',
|
|
112
|
+
input_context: {
|
|
113
|
+
ticket_id: ticketId,
|
|
114
|
+
account_id: post.account_id,
|
|
115
|
+
title: post.title,
|
|
116
|
+
description: post.description || '',
|
|
117
|
+
source: 'community_escalation',
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.error('Failed to trigger triage pipeline:', err)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(`Escalated post ${post.id} to ticket ${ticketId}`)
|
|
126
|
+
return ticketId
|
|
127
|
+
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error(`Failed to escalate post ${post.id}:`, err)
|
|
130
|
+
throw err
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const checkUnanswered = createHandler(async (_ctx, _body) => {
|
|
135
|
+
console.log('[PortalEscalation] Starting community escalation check...')
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const cutoffTime = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
|
|
139
|
+
|
|
140
|
+
const { data: unansweredPosts, error: postsError } = await adminDb
|
|
141
|
+
.from('items')
|
|
142
|
+
.select('id, title, description, account_id, person_id, created_at, data')
|
|
143
|
+
.eq('type_slug', 'community_post')
|
|
144
|
+
.not('data->>status', 'eq', 'escalated')
|
|
145
|
+
.lt('created_at', cutoffTime)
|
|
146
|
+
.order('created_at', { ascending: true })
|
|
147
|
+
.limit(50)
|
|
148
|
+
|
|
149
|
+
if (postsError) {
|
|
150
|
+
throw new Error(`Failed to fetch posts: ${postsError.message}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!unansweredPosts || unansweredPosts.length === 0) {
|
|
154
|
+
return { status: 'ok', processed: 0, escalated: 0, failed: 0, skipped: 0 }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const postsToEscalate: CommunityPost[] = []
|
|
158
|
+
for (const post of unansweredPosts) {
|
|
159
|
+
const { data: replies } = await adminDb
|
|
160
|
+
.from('items')
|
|
161
|
+
.select('id')
|
|
162
|
+
.eq('type_slug', 'community_reply')
|
|
163
|
+
.eq('data->>post_id', post.id)
|
|
164
|
+
.gt('created_at', post.created_at)
|
|
165
|
+
.limit(1)
|
|
166
|
+
|
|
167
|
+
if (!replies || replies.length === 0) {
|
|
168
|
+
postsToEscalate.push(post as CommunityPost)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`[PortalEscalation] Found ${postsToEscalate.length} unanswered posts`)
|
|
173
|
+
|
|
174
|
+
const results = { escalated: [] as string[], failed: [] as string[], skipped: [] as string[] }
|
|
175
|
+
|
|
176
|
+
for (const post of postsToEscalate) {
|
|
177
|
+
try {
|
|
178
|
+
const ticketId = await escalatePostToTicket(post)
|
|
179
|
+
if (ticketId) results.escalated.push(post.id)
|
|
180
|
+
else results.skipped.push(post.id)
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(`Failed to escalate post ${post.id}:`, err)
|
|
183
|
+
results.failed.push(post.id)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
status: 'ok',
|
|
189
|
+
processed: postsToEscalate.length,
|
|
190
|
+
escalated: results.escalated.length,
|
|
191
|
+
failed: results.failed.length,
|
|
192
|
+
skipped: results.skipped.length,
|
|
193
|
+
details: results,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error('[PortalEscalation] Failed:', err)
|
|
198
|
+
const error: any = new Error('Failed to process community escalation')
|
|
199
|
+
error.statusCode = 500
|
|
200
|
+
throw error
|
|
201
|
+
}
|
|
202
|
+
})
|
|
@@ -1,11 +1,45 @@
|
|
|
1
|
+
// Portal Signal Handler
|
|
2
|
+
// Records portal user actions as funnel signals in the items table.
|
|
3
|
+
// Portal users are always identified — no anonymous session handling.
|
|
4
|
+
// Standalone: no dependency on cortex functions.
|
|
5
|
+
|
|
1
6
|
import { createHandler } from './_shared/middleware'
|
|
2
|
-
import {
|
|
7
|
+
import { adminDb } from './_shared/db'
|
|
8
|
+
import { resolveTypeIds, resolveAccountId } from './_shared/resolve-ids'
|
|
9
|
+
|
|
10
|
+
async function resolveIds() {
|
|
11
|
+
const [types, unidentifiedVisitorsAccountId] = await Promise.all([
|
|
12
|
+
resolveTypeIds([{ kind: 'item', slug: 'funnel_signal' }]),
|
|
13
|
+
resolveAccountId('unidentified-visitors'),
|
|
14
|
+
])
|
|
15
|
+
return {
|
|
16
|
+
FUNNEL_SIGNAL_TYPE_ID: types['item/funnel_signal'],
|
|
17
|
+
UNIDENTIFIED_VISITORS_ACCOUNT_ID: unidentifiedVisitorsAccountId,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function ratingToTemperature(rating: number): 'cold' | 'warm' | 'hot' {
|
|
22
|
+
if (rating <= 2) return 'cold'
|
|
23
|
+
if (rating <= 3) return 'warm'
|
|
24
|
+
return 'hot'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function calculateSimpleScore(actionValue: number): { calculated: number; rating: 1 | 2 | 3 | 4 | 5 } {
|
|
28
|
+
const calculated = actionValue
|
|
29
|
+
let rating: 1 | 2 | 3 | 4 | 5
|
|
30
|
+
if (calculated <= 1) rating = 1
|
|
31
|
+
else if (calculated <= 4) rating = 2
|
|
32
|
+
else if (calculated <= 8) rating = 3
|
|
33
|
+
else if (calculated <= 15) rating = 4
|
|
34
|
+
else rating = 5
|
|
35
|
+
return { calculated, rating }
|
|
36
|
+
}
|
|
3
37
|
|
|
4
38
|
export const handler = createHandler(async (ctx, body) => {
|
|
5
39
|
const { action_type, action_value, action_description, session_id } = body || {}
|
|
6
40
|
|
|
7
|
-
if (!action_type ||
|
|
8
|
-
const err: any = new Error('action_type and action_value are required')
|
|
41
|
+
if (!action_type || ![1, 2, 5].includes(action_value)) {
|
|
42
|
+
const err: any = new Error('action_type and action_value (1, 2, or 5) are required')
|
|
9
43
|
err.statusCode = 400
|
|
10
44
|
throw err
|
|
11
45
|
}
|
|
@@ -16,18 +50,92 @@ export const handler = createHandler(async (ctx, body) => {
|
|
|
16
50
|
throw err
|
|
17
51
|
}
|
|
18
52
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
53
|
+
const ids = await resolveIds()
|
|
54
|
+
const now = new Date().toISOString()
|
|
55
|
+
const scoring = calculateSimpleScore(action_value)
|
|
56
|
+
const resolvedSessionId = session_id || `portal_${ctx.principal.id}_${Date.now()}`
|
|
57
|
+
|
|
58
|
+
const signalData = {
|
|
59
|
+
identity: {
|
|
60
|
+
anonymous_id: null,
|
|
61
|
+
person_id: ctx.principal.id,
|
|
62
|
+
account_id: ctx.accountId,
|
|
63
|
+
session_id: resolvedSessionId,
|
|
64
|
+
},
|
|
65
|
+
classification: {
|
|
66
|
+
stage: 'identified',
|
|
67
|
+
source: 'int',
|
|
68
|
+
},
|
|
69
|
+
action: {
|
|
70
|
+
action_type,
|
|
71
|
+
action_value,
|
|
72
|
+
action_description: action_description || null,
|
|
73
|
+
},
|
|
74
|
+
scoring_components: {
|
|
75
|
+
raw_score: {
|
|
76
|
+
calculated: scoring.calculated,
|
|
77
|
+
max_possible: 25,
|
|
78
|
+
rating: scoring.rating,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
processing: {
|
|
82
|
+
received_at: now,
|
|
83
|
+
enriched_at: now,
|
|
84
|
+
scored_at: now,
|
|
85
|
+
stitched_at: null,
|
|
86
|
+
stitched_to_account_id: null,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { data, error } = await adminDb
|
|
91
|
+
.from('items')
|
|
92
|
+
.insert({
|
|
93
|
+
type_id: ids.FUNNEL_SIGNAL_TYPE_ID,
|
|
94
|
+
title: `${action_type} - ${action_value}`,
|
|
95
|
+
account_id: ctx.accountId,
|
|
96
|
+
data: signalData,
|
|
97
|
+
})
|
|
98
|
+
.select('id')
|
|
99
|
+
.single()
|
|
100
|
+
|
|
101
|
+
if (error) {
|
|
102
|
+
throw new Error(`Failed to record portal signal: ${error.message}`)
|
|
28
103
|
}
|
|
29
104
|
|
|
30
|
-
|
|
105
|
+
// Update account funnel data
|
|
106
|
+
const { data: account } = await adminDb
|
|
107
|
+
.from('accounts')
|
|
108
|
+
.select('data')
|
|
109
|
+
.eq('id', ctx.accountId)
|
|
110
|
+
.single()
|
|
111
|
+
|
|
112
|
+
if (account) {
|
|
113
|
+
const currentRating = account.data?.ratings?.identified?.rating || 0
|
|
114
|
+
const shouldUpdate = scoring.rating > currentRating
|
|
115
|
+
|
|
116
|
+
await adminDb
|
|
117
|
+
.from('accounts')
|
|
118
|
+
.update({
|
|
119
|
+
data: {
|
|
120
|
+
...account.data,
|
|
121
|
+
...(shouldUpdate && {
|
|
122
|
+
lead_score: scoring.calculated,
|
|
123
|
+
temperature: ratingToTemperature(scoring.rating),
|
|
124
|
+
lifecycle_stage: 'identified',
|
|
125
|
+
ratings: {
|
|
126
|
+
...(account.data?.ratings || {}),
|
|
127
|
+
identified: {
|
|
128
|
+
rating: scoring.rating,
|
|
129
|
+
raw_score: scoring.calculated,
|
|
130
|
+
calculated_at: now,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
last_signal_at: now,
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
.eq('id', ctx.accountId)
|
|
138
|
+
}
|
|
31
139
|
|
|
32
|
-
return { status: 'ok' }
|
|
140
|
+
return { status: 'ok', signal_id: data.id, rating: scoring.rating }
|
|
33
141
|
})
|
package/manifest.json
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"features": ["tickets", "kb", "courses", "community", "marketplace"],
|
|
55
55
|
"dependencies": ["items", "threads", "messages"],
|
|
56
56
|
"entry_point": "./index.tsx",
|
|
57
|
+
"directories": ["pages", "components", "hooks", "config", "seed", "functions", "migrations", "tests"],
|
|
57
58
|
"is_public": true,
|
|
58
59
|
"auth_required": true
|
|
59
60
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-framework-portal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Customer Portal — self-service portal app for Spine Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"directory": "custom/apps/customer-portal"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"spine-framework": ">=0.1.0"
|
|
14
|
-
"spine-framework-cortex": ">=0.1.0"
|
|
13
|
+
"spine-framework": ">=0.1.0"
|
|
15
14
|
},
|
|
16
15
|
"files": [
|
|
17
16
|
"index.tsx",
|
package/seed/roles.json
ADDED
package/seed/triggers.json
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
"description": "Check for community posts >24h without answers and escalate to support tickets",
|
|
5
5
|
"trigger_type": "cron",
|
|
6
6
|
"event_type": null,
|
|
7
|
+
"scope": "account",
|
|
7
8
|
"config": {
|
|
8
|
-
"function": "
|
|
9
|
+
"function": "custom_portal-community-escalation.checkUnanswered",
|
|
9
10
|
"schedule": "0 */4 * * *",
|
|
10
11
|
"timezone": "UTC",
|
|
11
12
|
"description": "Check for community posts >24h without answers, create tickets"
|
package/seed/types.json
CHANGED
|
@@ -10,20 +10,38 @@
|
|
|
10
10
|
"is_active": true,
|
|
11
11
|
"design_schema": {
|
|
12
12
|
"scope": "platform",
|
|
13
|
+
"views": {
|
|
14
|
+
"default_list": {
|
|
15
|
+
"type": "list",
|
|
16
|
+
"label": "Community Posts",
|
|
17
|
+
"fields": {
|
|
18
|
+
"title": { "sortable": true, "display_type": "text" },
|
|
19
|
+
"channel": { "sortable": true, "display_type": "badge" },
|
|
20
|
+
"moderation_status": { "sortable": true, "display_type": "badge" }
|
|
21
|
+
},
|
|
22
|
+
"display": "table"
|
|
23
|
+
},
|
|
24
|
+
"default_detail": {
|
|
25
|
+
"type": "detail",
|
|
26
|
+
"label": "Community Post",
|
|
27
|
+
"sections": [
|
|
28
|
+
{ "title": "Content", "fields": ["title", "content", "channel", "context"] },
|
|
29
|
+
{ "title": "Moderation", "fields": ["moderation_status", "helpful_count", "not_helpful_count", "accepted_answer_id"] }
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
13
33
|
"fields": {
|
|
14
|
-
"title": { "label": "Title", "required": true, "data_type": "text" },
|
|
15
|
-
"content": { "label": "Content", "required": true, "data_type": "text" },
|
|
16
|
-
"context": { "label": "Context", "required": true, "data_type": "
|
|
17
|
-
"channel": { "label": "Channel", "required": true, "data_type": "
|
|
18
|
-
"moderation_status": { "label": "Moderation Status", "data_type": "
|
|
19
|
-
"helpful_count": { "label": "Helpful Votes", "data_type": "
|
|
20
|
-
"not_helpful_count": { "label": "Not Helpful Votes", "data_type": "
|
|
21
|
-
"accepted_answer_id": { "label": "Accepted Answer", "data_type": "
|
|
34
|
+
"title": { "label": "Title", "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
35
|
+
"content": { "label": "Content", "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
36
|
+
"context": { "label": "Context", "required": true, "data_type": "select", "options": [{"label":"Support","value":"support"},{"label":"Community","value":"community"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
37
|
+
"channel": { "label": "Channel", "required": true, "data_type": "select", "default": "general", "options": [{"label":"General","value":"general"},{"label":"Announcements","value":"announcements"},{"label":"Help","value":"help"},{"label":"Show and Tell","value":"show-and-tell"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
38
|
+
"moderation_status": { "label": "Moderation Status", "data_type": "select", "default": "pending", "options": [{"label":"Pending","value":"pending"},{"label":"Approved","value":"approved"},{"label":"Flagged","value":"flagged"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
39
|
+
"helpful_count": { "label": "Helpful Votes", "data_type": "number", "default": 0, "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
40
|
+
"not_helpful_count": { "label": "Not Helpful Votes", "data_type": "number", "default": 0, "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
41
|
+
"accepted_answer_id": { "label": "Accepted Answer", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } }
|
|
22
42
|
},
|
|
23
|
-
"record_permissions": {
|
|
24
|
-
|
|
25
|
-
"system_admin": ["create", "read", "update", "delete"]
|
|
26
|
-
}
|
|
43
|
+
"record_permissions": { "member": ["create", "read", "update"], "support": ["create", "read", "update"], "system_admin": ["create", "read", "update", "delete"] },
|
|
44
|
+
"functionality": null
|
|
27
45
|
},
|
|
28
46
|
"validation_schema": {}
|
|
29
47
|
},
|
|
@@ -38,20 +56,38 @@
|
|
|
38
56
|
"is_active": true,
|
|
39
57
|
"design_schema": {
|
|
40
58
|
"scope": "platform",
|
|
59
|
+
"views": {
|
|
60
|
+
"default_list": {
|
|
61
|
+
"type": "list",
|
|
62
|
+
"label": "Course Lessons",
|
|
63
|
+
"fields": {
|
|
64
|
+
"title": { "sortable": true, "display_type": "text" },
|
|
65
|
+
"sequence": { "sortable": true, "display_type": "number" },
|
|
66
|
+
"estimated_duration": { "sortable": true, "display_type": "number" }
|
|
67
|
+
},
|
|
68
|
+
"display": "table"
|
|
69
|
+
},
|
|
70
|
+
"default_detail": {
|
|
71
|
+
"type": "detail",
|
|
72
|
+
"label": "Course Lesson",
|
|
73
|
+
"sections": [
|
|
74
|
+
{ "title": "Content", "fields": ["title", "content", "video_url", "estimated_duration"] },
|
|
75
|
+
{ "title": "Settings", "fields": ["sequence", "progress_required", "prerequisites"] }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
41
79
|
"fields": {
|
|
42
|
-
"title": { "label": "Title", "required": true, "data_type": "text" },
|
|
43
|
-
"content": { "label": "Content", "required": true, "data_type": "
|
|
44
|
-
"context": { "label": "Context", "required": true, "data_type": "
|
|
45
|
-
"sequence": { "label": "Lesson Sequence", "data_type": "
|
|
46
|
-
"video_url": { "label": "Video URL", "data_type": "
|
|
47
|
-
"estimated_duration": { "label": "Estimated Duration (minutes)", "data_type": "
|
|
48
|
-
"progress_required": { "label": "Progress Required", "data_type": "boolean", "default": true },
|
|
49
|
-
"prerequisites": { "label": "Prerequisite Lessons", "data_type": "
|
|
80
|
+
"title": { "label": "Title", "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
81
|
+
"content": { "label": "Content", "required": true, "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
82
|
+
"context": { "label": "Context", "required": true, "data_type": "select", "options": [{"label":"Knowledge Base","value":"kb"},{"label":"Course","value":"course"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
83
|
+
"sequence": { "label": "Lesson Sequence", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
84
|
+
"video_url": { "label": "Video URL", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
85
|
+
"estimated_duration": { "label": "Estimated Duration (minutes)", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
86
|
+
"progress_required": { "label": "Progress Required", "data_type": "boolean", "default": true, "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
87
|
+
"prerequisites": { "label": "Prerequisite Lessons", "data_type": "json", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } }
|
|
50
88
|
},
|
|
51
|
-
"record_permissions": {
|
|
52
|
-
|
|
53
|
-
"support": ["read", "create", "update"]
|
|
54
|
-
}
|
|
89
|
+
"record_permissions": { "member": ["read"], "support": ["read", "create", "update"], "system_admin": ["create", "read", "update", "delete"] },
|
|
90
|
+
"functionality": null
|
|
55
91
|
},
|
|
56
92
|
"validation_schema": {}
|
|
57
93
|
},
|
|
@@ -66,18 +102,39 @@
|
|
|
66
102
|
"is_active": true,
|
|
67
103
|
"design_schema": {
|
|
68
104
|
"scope": "account",
|
|
105
|
+
"views": {
|
|
106
|
+
"default_list": {
|
|
107
|
+
"type": "list",
|
|
108
|
+
"label": "Integrity Reports",
|
|
109
|
+
"fields": {
|
|
110
|
+
"title": { "sortable": true, "display_type": "text" },
|
|
111
|
+
"is_active": { "sortable": true, "display_type": "badge" },
|
|
112
|
+
"created_at": { "sortable": true, "display_type": "timestamp" }
|
|
113
|
+
},
|
|
114
|
+
"display": "table"
|
|
115
|
+
},
|
|
116
|
+
"default_detail": {
|
|
117
|
+
"type": "detail",
|
|
118
|
+
"label": "Integrity Report",
|
|
119
|
+
"sections": [
|
|
120
|
+
{ "title": "Hashes", "fields": ["core_hash", "manifest_hash", "deploy_id", "deploy_url"] },
|
|
121
|
+
{ "title": "Metadata", "fields": ["is_active", "created_at", "updated_at"] }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
},
|
|
69
125
|
"fields": {
|
|
70
|
-
"title": { "label": "Title", "system": true, "required": true, "data_type": "text" },
|
|
71
|
-
"status": { "label": "Status", "system": true, "required": false, "data_type": "text" },
|
|
72
|
-
"is_active": { "label": "Active", "system": true, "required": true, "data_type": "boolean" },
|
|
73
|
-
"core_hash": { "label": "Core Hash", "system": false, "required": true, "data_type": "text" },
|
|
74
|
-
"manifest_hash": { "label": "Manifest Hash", "system": false, "required": true, "data_type": "text" },
|
|
75
|
-
"deploy_id": { "label": "Deploy ID", "system": false, "required": false, "data_type": "text" },
|
|
76
|
-
"deploy_url": { "label": "Deploy URL", "system": false, "required": false, "data_type": "text" },
|
|
77
|
-
"created_at": { "label": "Created", "system": true, "readonly": true, "required": false, "data_type": "datetime" },
|
|
78
|
-
"updated_at": { "label": "Updated", "system": true, "readonly": true, "required": false, "data_type": "datetime" }
|
|
126
|
+
"title": { "label": "Title", "system": true, "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
127
|
+
"status": { "label": "Status", "system": true, "required": false, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
128
|
+
"is_active": { "label": "Active", "system": true, "required": true, "data_type": "boolean", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
129
|
+
"core_hash": { "label": "Core Hash", "system": false, "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
130
|
+
"manifest_hash": { "label": "Manifest Hash", "system": false, "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
131
|
+
"deploy_id": { "label": "Deploy ID", "system": false, "required": false, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
132
|
+
"deploy_url": { "label": "Deploy URL", "system": false, "required": false, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
133
|
+
"created_at": { "label": "Created", "system": true, "readonly": true, "required": false, "data_type": "datetime", "permissions": { "system_admin": ["read"], "support": ["read"], "member": ["read"] } },
|
|
134
|
+
"updated_at": { "label": "Updated", "system": true, "readonly": true, "required": false, "data_type": "datetime", "permissions": { "system_admin": ["read"], "support": ["read"], "member": ["read"] } }
|
|
79
135
|
},
|
|
80
|
-
"record_permissions": { "system_admin": ["create", "read", "update", "delete"] }
|
|
136
|
+
"record_permissions": { "member": ["read"], "support": ["read", "create", "update"], "system_admin": ["create", "read", "update", "delete"] },
|
|
137
|
+
"functionality": null
|
|
81
138
|
},
|
|
82
139
|
"validation_schema": {}
|
|
83
140
|
},
|
|
@@ -92,44 +149,64 @@
|
|
|
92
149
|
"is_active": true,
|
|
93
150
|
"design_schema": {
|
|
94
151
|
"scope": "customer",
|
|
152
|
+
"views": {
|
|
153
|
+
"default_list": {
|
|
154
|
+
"type": "list",
|
|
155
|
+
"label": "Support Tickets",
|
|
156
|
+
"fields": {
|
|
157
|
+
"title": { "sortable": true, "display_type": "text" },
|
|
158
|
+
"status": { "sortable": true, "display_type": "badge" },
|
|
159
|
+
"priority": { "sortable": true, "display_type": "badge" },
|
|
160
|
+
"escalated": { "sortable": true, "display_type": "boolean" }
|
|
161
|
+
},
|
|
162
|
+
"display": "table"
|
|
163
|
+
},
|
|
164
|
+
"default_detail": {
|
|
165
|
+
"type": "detail",
|
|
166
|
+
"label": "Support Ticket",
|
|
167
|
+
"sections": [
|
|
168
|
+
{ "title": "Request", "fields": ["title", "description", "status", "priority", "context"] },
|
|
169
|
+
{ "title": "AI Handling", "fields": ["ai_response", "ai_confidence", "escalated", "aim_triage_agent_id", "aim_escalation_reason", "aim_human_assignee_id"] },
|
|
170
|
+
{ "title": "Analysis", "fields": ["ca_true_problem", "ca_reported_issue", "ca_final_solution", "ca_solution_steps", "ca_customer_temperature", "ca_time_to_resolution", "ca_automation_potential"] },
|
|
171
|
+
{ "title": "KB Generation", "fields": ["ca_kb_candidate", "kb_proposed_kb_id", "kb_redacted_draft", "kb_approved_at", "kb_approved_by", "kb_human_edits"] }
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
},
|
|
95
175
|
"fields": {
|
|
96
|
-
"title": { "label": "Title", "system": true, "required": true, "data_type": "text" },
|
|
97
|
-
"description": { "label": "Description", "system": true, "required": true, "data_type": "
|
|
98
|
-
"status": { "label": "Status", "system": true, "data_type": "
|
|
99
|
-
"context": { "label": "Context", "required": true, "data_type": "
|
|
100
|
-
"priority": { "label": "Priority", "data_type": "
|
|
101
|
-
"escalated": { "label": "Escalated to Human", "data_type": "boolean", "default": false },
|
|
102
|
-
"ai_response": { "label": "AI Response", "data_type": "
|
|
103
|
-
"ai_confidence": { "label": "AI Confidence", "data_type": "
|
|
104
|
-
"aim_triage_agent_id": { "label": "Triage Agent ID", "data_type": "text" },
|
|
105
|
-
"aim_escalation_reason": { "label": "Escalation Reason", "data_type": "
|
|
106
|
-
"aim_human_assignee_id": { "label": "Human Assignee", "data_type": "text" },
|
|
107
|
-
"aim_confidence_threshold": { "label": "Confidence Threshold", "data_type": "number", "default": 0.75 },
|
|
108
|
-
"aim_confidence_at_response": { "label": "Confidence at Response", "data_type": "number" },
|
|
109
|
-
"ca_true_problem": { "label": "True Problem Identified", "data_type": "
|
|
110
|
-
"ca_reported_issue": { "label": "Reported Issue", "data_type": "
|
|
111
|
-
"ca_final_solution": { "label": "Final Solution Summary", "data_type": "
|
|
112
|
-
"ca_solution_steps": { "label": "Steps to Solve", "data_type": "json" },
|
|
113
|
-
"ca_diagnostic_steps": { "label": "Steps to Diagnose", "data_type": "json" },
|
|
114
|
-
"ca_analysis_tags": { "label": "Analysis Tags", "data_type": "json" },
|
|
115
|
-
"ca_escalation_required": { "label": "Was Escalated to Human Agent", "data_type": "boolean" },
|
|
116
|
-
"ca_customer_temperature": { "label": "Customer Temperature", "data_type": "
|
|
117
|
-
"ca_time_to_resolution": { "label": "Time to Resolution (minutes)", "data_type": "number" },
|
|
118
|
-
"ca_back_and_forth_count": { "label": "Number of Back and Forths", "data_type": "number" },
|
|
119
|
-
"ca_automation_potential": { "label": "Automation Potential", "data_type": "
|
|
120
|
-
"ca_sentiment_progression": { "label": "Customer Sentiment Progression", "data_type": "json" },
|
|
121
|
-
"ca_kb_candidate": { "label": "KB Candidate", "data_type": "boolean" },
|
|
122
|
-
"kb_proposed_kb_id": { "label": "Proposed KB Article ID", "data_type": "text" },
|
|
123
|
-
"kb_redacted_draft": { "label": "Redacted Draft", "data_type": "
|
|
124
|
-
"kb_approved_at": { "label": "Approved At", "data_type": "datetime" },
|
|
125
|
-
"kb_approved_by": { "label": "Approved By", "data_type": "text" },
|
|
126
|
-
"kb_human_edits": { "label": "Human Edits", "data_type": "
|
|
176
|
+
"title": { "label": "Title", "system": true, "required": true, "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
177
|
+
"description": { "label": "Description", "system": true, "required": true, "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
178
|
+
"status": { "label": "Status", "system": true, "data_type": "select", "default": "open", "options": [{"label":"Open","value":"open"},{"label":"AI Responding","value":"ai_responding"},{"label":"Human Assigned","value":"human_assigned"},{"label":"In Progress","value":"in_progress"},{"label":"Resolved","value":"resolved"},{"label":"Closed","value":"closed"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
179
|
+
"context": { "label": "Context", "required": true, "data_type": "select", "options": [{"label":"Support","value":"support"},{"label":"Community","value":"community"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read", "write"] } },
|
|
180
|
+
"priority": { "label": "Priority", "data_type": "select", "default": "medium", "options": [{"label":"Low","value":"low"},{"label":"Medium","value":"medium"},{"label":"High","value":"high"},{"label":"Urgent","value":"urgent"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
181
|
+
"escalated": { "label": "Escalated to Human", "data_type": "boolean", "default": false, "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
182
|
+
"ai_response": { "label": "AI Response", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
183
|
+
"ai_confidence": { "label": "AI Confidence", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
184
|
+
"aim_triage_agent_id": { "label": "Triage Agent ID", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
185
|
+
"aim_escalation_reason": { "label": "Escalation Reason", "data_type": "select", "options": [{"label":"Low Confidence","value":"low_confidence"},{"label":"Thumbs Down","value":"thumbs_down"},{"label":"Customer Request","value":"customer_request"},{"label":"None","value":"none"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
186
|
+
"aim_human_assignee_id": { "label": "Human Assignee", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
187
|
+
"aim_confidence_threshold": { "label": "Confidence Threshold", "data_type": "number", "default": 0.75, "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
188
|
+
"aim_confidence_at_response": { "label": "Confidence at Response", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
189
|
+
"ca_true_problem": { "label": "True Problem Identified", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
190
|
+
"ca_reported_issue": { "label": "Reported Issue", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
191
|
+
"ca_final_solution": { "label": "Final Solution Summary", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
192
|
+
"ca_solution_steps": { "label": "Steps to Solve", "data_type": "json", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
193
|
+
"ca_diagnostic_steps": { "label": "Steps to Diagnose", "data_type": "json", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
194
|
+
"ca_analysis_tags": { "label": "Analysis Tags", "data_type": "json", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
195
|
+
"ca_escalation_required": { "label": "Was Escalated to Human Agent", "data_type": "boolean", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
196
|
+
"ca_customer_temperature": { "label": "Customer Temperature", "data_type": "select", "options": [{"label":"Positive","value":"positive"},{"label":"Neutral","value":"neutral"},{"label":"Negative","value":"negative"},{"label":"Frustrated","value":"frustrated"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
197
|
+
"ca_time_to_resolution": { "label": "Time to Resolution (minutes)", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
198
|
+
"ca_back_and_forth_count": { "label": "Number of Back and Forths", "data_type": "number", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
199
|
+
"ca_automation_potential": { "label": "Automation Potential", "data_type": "select", "options": [{"label":"High","value":"high"},{"label":"Medium","value":"medium"},{"label":"Low","value":"low"}], "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
200
|
+
"ca_sentiment_progression": { "label": "Customer Sentiment Progression", "data_type": "json", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
201
|
+
"ca_kb_candidate": { "label": "KB Candidate", "data_type": "boolean", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
202
|
+
"kb_proposed_kb_id": { "label": "Proposed KB Article ID", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
203
|
+
"kb_redacted_draft": { "label": "Redacted Draft", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } },
|
|
204
|
+
"kb_approved_at": { "label": "Approved At", "data_type": "datetime", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
205
|
+
"kb_approved_by": { "label": "Approved By", "data_type": "text", "permissions": { "system_admin": ["read", "write"], "support": ["read"], "member": ["read"] } },
|
|
206
|
+
"kb_human_edits": { "label": "Human Edits", "data_type": "textarea", "permissions": { "system_admin": ["read", "write"], "support": ["read", "write"], "member": ["read"] } }
|
|
127
207
|
},
|
|
128
|
-
"record_permissions": {
|
|
129
|
-
|
|
130
|
-
"support": ["create", "read", "update"],
|
|
131
|
-
"system_admin": ["create", "read", "update", "delete"]
|
|
132
|
-
}
|
|
208
|
+
"record_permissions": { "member": ["create", "read", "update"], "support": ["create", "read", "update"], "system_admin": ["create", "read", "update", "delete"] },
|
|
209
|
+
"functionality": null
|
|
133
210
|
},
|
|
134
211
|
"validation_schema": {}
|
|
135
212
|
}
|