@equilateral_ai/mindmeld 3.3.1 → 3.5.0
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 +1 -10
- package/hooks/pre-compact.js +213 -25
- package/hooks/session-end.js +112 -3
- package/hooks/session-start.js +635 -41
- package/hooks/subagent-start.js +150 -0
- package/hooks/subagent-stop.js +184 -0
- package/package.json +8 -7
- package/scripts/init-project.js +74 -33
- package/scripts/mcp-bridge.js +220 -0
- package/src/core/CorrelationAnalyzer.js +157 -0
- package/src/core/LLMPatternDetector.js +198 -0
- package/src/core/RelevanceDetector.js +123 -36
- package/src/core/StandardsIngestion.js +119 -18
- package/src/handlers/activity/activityGetMe.js +1 -1
- package/src/handlers/activity/activityGetTeam.js +100 -55
- package/src/handlers/admin/adminSetup.js +216 -0
- package/src/handlers/alerts/alertsAcknowledge.js +6 -6
- package/src/handlers/alerts/alertsGet.js +11 -11
- package/src/handlers/analytics/activitySummaryGet.js +34 -35
- package/src/handlers/analytics/coachingGet.js +11 -11
- package/src/handlers/analytics/convergenceGet.js +236 -0
- package/src/handlers/analytics/developerScoreGet.js +41 -111
- package/src/handlers/collaborators/collaboratorInvite.js +1 -1
- package/src/handlers/company/companyUsersDelete.js +141 -0
- package/src/handlers/company/companyUsersGet.js +90 -0
- package/src/handlers/company/companyUsersPost.js +267 -0
- package/src/handlers/company/companyUsersPut.js +76 -0
- package/src/handlers/correlations/correlationsDeveloperGet.js +12 -12
- package/src/handlers/correlations/correlationsGet.js +8 -8
- package/src/handlers/correlations/correlationsProjectGet.js +5 -5
- package/src/handlers/enterprise/controlTowerGet.js +224 -0
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +48 -9
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +1 -3
- package/src/handlers/github/githubConnectionStatus.js +1 -1
- package/src/handlers/github/githubDiscoverPatterns.js +4 -2
- package/src/handlers/github/githubPatternsReview.js +7 -36
- package/src/handlers/health/healthGet.js +55 -0
- package/src/handlers/helpers/checkSuperAdmin.js +13 -14
- package/src/handlers/helpers/mindmeldMcpCore.js +594 -0
- package/src/handlers/helpers/subscriptionTiers.js +27 -27
- package/src/handlers/mcp/mcpHandler.js +569 -0
- package/src/handlers/mcp/mindmeldMcpHandler.js +124 -0
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +243 -0
- package/src/handlers/notifications/sendNotification.js +18 -18
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +173 -0
- package/src/handlers/projects/projectCreate.js +124 -10
- package/src/handlers/projects/projectDelete.js +4 -4
- package/src/handlers/projects/projectGet.js +8 -8
- package/src/handlers/projects/projectUpdate.js +4 -4
- package/src/handlers/reports/aiLeverage.js +34 -30
- package/src/handlers/reports/engineeringInvestment.js +16 -16
- package/src/handlers/reports/riskForecast.js +41 -21
- package/src/handlers/reports/standardsRoi.js +101 -9
- package/src/handlers/scheduled/maturityUpdateJob.js +166 -0
- package/src/handlers/sessions/sessionStandardsPost.js +43 -7
- package/src/handlers/standards/discoveriesGet.js +93 -0
- package/src/handlers/standards/projectStandardsGet.js +2 -2
- package/src/handlers/standards/projectStandardsPut.js +2 -2
- package/src/handlers/standards/standardsRelevantPost.js +107 -12
- package/src/handlers/standards/standardsTransition.js +112 -15
- package/src/handlers/stripe/billingPortalPost.js +1 -1
- package/src/handlers/stripe/enterpriseCheckoutPost.js +2 -2
- package/src/handlers/stripe/subscriptionCreatePost.js +2 -2
- package/src/handlers/stripe/webhookPost.js +42 -14
- package/src/handlers/user/apiTokenCreate.js +71 -0
- package/src/handlers/user/apiTokenList.js +64 -0
- package/src/handlers/user/userSplashGet.js +90 -73
- package/src/handlers/users/cognitoPostConfirmation.js +37 -1
- package/src/handlers/users/cognitoPreSignUp.js +114 -0
- package/src/handlers/users/userGet.js +15 -11
- package/src/handlers/webhooks/githubWebhook.js +117 -125
- package/src/index.js +8 -5
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Setup Handler
|
|
3
|
+
* One-time setup for enterprise clients via Lambda invocation
|
|
4
|
+
*
|
|
5
|
+
* Invoke via AWS CLI:
|
|
6
|
+
* aws lambda invoke --function-name rapport-api-prod-AdminSetup \
|
|
7
|
+
* --payload '{"action":"setup-equilateral"}' \
|
|
8
|
+
* --profile production-sso response.json
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('../helpers');
|
|
12
|
+
|
|
13
|
+
const SETUP_CONFIGS = {
|
|
14
|
+
'setup-equilateral': {
|
|
15
|
+
client: {
|
|
16
|
+
client_id: 'EQUILATERAL_MAIN',
|
|
17
|
+
client_name: 'Equilateral AI',
|
|
18
|
+
client_type: 'ENTERPRISE',
|
|
19
|
+
subscription_tier: 'enterprise',
|
|
20
|
+
seat_count: 25,
|
|
21
|
+
billing_type: 'internal'
|
|
22
|
+
},
|
|
23
|
+
company: {
|
|
24
|
+
company_id: 'EQUILATERAL_MAIN',
|
|
25
|
+
company_name: 'Equilateral AI'
|
|
26
|
+
},
|
|
27
|
+
admin_email: 'james.ford@happyhippo.ai',
|
|
28
|
+
invitations: [
|
|
29
|
+
{ email: 'adeel@equilateral.ai', name: 'Adeel', role: 'member' },
|
|
30
|
+
{ email: 'satish@equilateral.ai', name: 'Satish', role: 'member' }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function adminSetup({ body }) {
|
|
36
|
+
try {
|
|
37
|
+
const action = body?.action;
|
|
38
|
+
|
|
39
|
+
// Special action: list-tables
|
|
40
|
+
if (action === 'list-tables') {
|
|
41
|
+
const result = await executeQuery(`
|
|
42
|
+
SELECT table_name
|
|
43
|
+
FROM information_schema.tables
|
|
44
|
+
WHERE table_schema = 'rapport'
|
|
45
|
+
ORDER BY table_name
|
|
46
|
+
`);
|
|
47
|
+
return createSuccessResponse({
|
|
48
|
+
tables: result.rows.map(r => r.table_name)
|
|
49
|
+
}, `Found ${result.rows.length} tables`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Special action: run-sql (for migrations)
|
|
53
|
+
if (action === 'run-sql') {
|
|
54
|
+
const sql = body?.sql;
|
|
55
|
+
if (!sql) {
|
|
56
|
+
return createErrorResponse(400, 'sql parameter required');
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
await executeQuery(sql);
|
|
60
|
+
return createSuccessResponse({ executed: true }, 'SQL executed successfully');
|
|
61
|
+
} catch (err) {
|
|
62
|
+
return createErrorResponse(500, `SQL error: ${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Special action: query (returns results)
|
|
67
|
+
if (action === 'query') {
|
|
68
|
+
const sql = body?.sql;
|
|
69
|
+
if (!sql) {
|
|
70
|
+
return createErrorResponse(400, 'sql parameter required');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const result = await executeQuery(sql);
|
|
74
|
+
return createSuccessResponse({ rows: result.rows, rowCount: result.rowCount }, `Query returned ${result.rowCount} rows`);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
return createErrorResponse(500, `Query error: ${err.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!action || !SETUP_CONFIGS[action]) {
|
|
81
|
+
return createErrorResponse(400, `Invalid action. Available: ${Object.keys(SETUP_CONFIGS).join(', ')}, list-tables, run-sql`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const config = SETUP_CONFIGS[action];
|
|
85
|
+
const results = [];
|
|
86
|
+
|
|
87
|
+
// 1. Create client
|
|
88
|
+
const existingClient = await executeQuery(
|
|
89
|
+
'SELECT client_id FROM rapport.clients WHERE client_id = $1',
|
|
90
|
+
[config.client.client_id]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (existingClient.rows.length > 0) {
|
|
94
|
+
results.push({ step: 'client', status: 'exists', client_id: config.client.client_id });
|
|
95
|
+
} else {
|
|
96
|
+
await executeQuery(`
|
|
97
|
+
INSERT INTO rapport.clients (
|
|
98
|
+
client_id, client_name, client_type, client_status, active,
|
|
99
|
+
subscription_tier, subscription_status, billing_type, seat_count
|
|
100
|
+
)
|
|
101
|
+
VALUES ($1, $2, $3, 'Active', true, $4, 'active', $5, $6)
|
|
102
|
+
`, [
|
|
103
|
+
config.client.client_id,
|
|
104
|
+
config.client.client_name,
|
|
105
|
+
config.client.client_type,
|
|
106
|
+
config.client.subscription_tier,
|
|
107
|
+
config.client.billing_type,
|
|
108
|
+
config.client.seat_count
|
|
109
|
+
]);
|
|
110
|
+
results.push({ step: 'client', status: 'created', client_id: config.client.client_id });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 2. Create company
|
|
114
|
+
const existingCompany = await executeQuery(
|
|
115
|
+
'SELECT company_id FROM rapport.companies WHERE company_id = $1',
|
|
116
|
+
[config.company.company_id]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (existingCompany.rows.length > 0) {
|
|
120
|
+
results.push({ step: 'company', status: 'exists', company_id: config.company.company_id });
|
|
121
|
+
} else {
|
|
122
|
+
await executeQuery(`
|
|
123
|
+
INSERT INTO rapport.companies (
|
|
124
|
+
company_id, client_id, company_name, company_status
|
|
125
|
+
)
|
|
126
|
+
VALUES ($1, $2, $3, 'Active')
|
|
127
|
+
`, [config.company.company_id, config.client.client_id, config.company.company_name]);
|
|
128
|
+
results.push({ step: 'company', status: 'created', company_id: config.company.company_id });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 3. Create admin entitlement
|
|
132
|
+
const existingEntitlement = await executeQuery(
|
|
133
|
+
'SELECT * FROM rapport.user_entitlements WHERE email_address = $1 AND company_id = $2',
|
|
134
|
+
[config.admin_email, config.company.company_id]
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (existingEntitlement.rows.length > 0) {
|
|
138
|
+
results.push({ step: 'admin_entitlement', status: 'exists', email: config.admin_email });
|
|
139
|
+
} else {
|
|
140
|
+
await executeQuery(`
|
|
141
|
+
INSERT INTO rapport.user_entitlements (
|
|
142
|
+
email_address, client_id, company_id, admin, member
|
|
143
|
+
)
|
|
144
|
+
VALUES ($1, $2, $3, true, true)
|
|
145
|
+
`, [config.admin_email, config.client.client_id, config.company.company_id]);
|
|
146
|
+
results.push({ step: 'admin_entitlement', status: 'created', email: config.admin_email });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 4. Create invitations (skip if table doesn't exist)
|
|
150
|
+
let invitationsSkipped = false;
|
|
151
|
+
for (const inv of config.invitations) {
|
|
152
|
+
if (invitationsSkipped) {
|
|
153
|
+
results.push({ step: 'invitation', status: 'skipped', email: inv.email, reason: 'table not found' });
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await executeQuery(`
|
|
158
|
+
INSERT INTO rapport.enterprise_invitations
|
|
159
|
+
(client_id, company_id, email, role, invited_by, status, created_at)
|
|
160
|
+
VALUES ($1, $2, $3, $4, $5, 'pending', NOW())
|
|
161
|
+
ON CONFLICT (company_id, email) DO UPDATE SET
|
|
162
|
+
role = EXCLUDED.role,
|
|
163
|
+
invited_by = EXCLUDED.invited_by,
|
|
164
|
+
status = 'pending',
|
|
165
|
+
created_at = NOW()
|
|
166
|
+
`, [config.client.client_id, config.company.company_id, inv.email.toLowerCase(), inv.role, config.admin_email]);
|
|
167
|
+
results.push({ step: 'invitation', status: 'created', email: inv.email, role: inv.role });
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err.code === '42P01') { // relation does not exist
|
|
170
|
+
invitationsSkipped = true;
|
|
171
|
+
results.push({ step: 'invitation', status: 'skipped', email: inv.email, reason: 'enterprise_invitations table not found' });
|
|
172
|
+
} else {
|
|
173
|
+
results.push({ step: 'invitation', status: 'error', email: inv.email, error: err.message });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 5. Get final state
|
|
179
|
+
const clientInfo = await executeQuery(
|
|
180
|
+
'SELECT client_id, client_name, subscription_tier, seat_count FROM rapport.clients WHERE client_id = $1',
|
|
181
|
+
[config.client.client_id]
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const entitlements = await executeQuery(
|
|
185
|
+
'SELECT email_address, admin, member FROM rapport.user_entitlements WHERE company_id = $1',
|
|
186
|
+
[config.company.company_id]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
let invitationsList = [];
|
|
190
|
+
try {
|
|
191
|
+
const invitations = await executeQuery(
|
|
192
|
+
'SELECT email, role, status FROM rapport.enterprise_invitations WHERE company_id = $1',
|
|
193
|
+
[config.company.company_id]
|
|
194
|
+
);
|
|
195
|
+
invitationsList = invitations.rows;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
if (err.code !== '42P01') throw err; // rethrow if not "relation does not exist"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return createSuccessResponse({
|
|
201
|
+
action,
|
|
202
|
+
results,
|
|
203
|
+
final_state: {
|
|
204
|
+
client: clientInfo.rows[0],
|
|
205
|
+
entitlements: entitlements.rows,
|
|
206
|
+
invitations: invitationsList
|
|
207
|
+
}
|
|
208
|
+
}, 'Setup complete');
|
|
209
|
+
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error('Admin setup error:', error);
|
|
212
|
+
return handleError(error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
exports.handler = wrapHandler(adminSetup);
|
|
@@ -24,10 +24,10 @@ exports.handler = wrapHandler(async ({ requestContext, body }) => {
|
|
|
24
24
|
|
|
25
25
|
// Check user role
|
|
26
26
|
const userCheck = await executeQuery(`
|
|
27
|
-
SELECT ue.
|
|
28
|
-
FROM
|
|
29
|
-
JOIN
|
|
30
|
-
WHERE ue.
|
|
27
|
+
SELECT ue.company_id, ue.manager, ue.admin, u.super_admin
|
|
28
|
+
FROM rapport.user_entitlements ue
|
|
29
|
+
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
30
|
+
WHERE ue.email_address = $1
|
|
31
31
|
`, [email]);
|
|
32
32
|
|
|
33
33
|
if (userCheck.rowCount === 0) {
|
|
@@ -35,7 +35,7 @@ exports.handler = wrapHandler(async ({ requestContext, body }) => {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const userRole = userCheck.rows[0];
|
|
38
|
-
const isManager = userRole.
|
|
38
|
+
const isManager = userRole.manager || userRole.admin || userRole.super_admin;
|
|
39
39
|
|
|
40
40
|
if (!isManager) {
|
|
41
41
|
return createErrorResponse(403, 'Manager or Admin access required');
|
|
@@ -55,7 +55,7 @@ exports.handler = wrapHandler(async ({ requestContext, body }) => {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const alert = alertCheck.rows[0];
|
|
58
|
-
if (alert.company_id !== userRole.
|
|
58
|
+
if (alert.company_id !== userRole.company_id && !userRole.super_admin) {
|
|
59
59
|
return createErrorResponse(403, 'Access denied to this alert');
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -24,13 +24,13 @@ exports.handler = wrapHandler(async ({ requestContext, queryStringParameters })
|
|
|
24
24
|
// Check user role
|
|
25
25
|
const userCheck = await executeQuery(`
|
|
26
26
|
SELECT
|
|
27
|
-
ue.
|
|
28
|
-
ue.
|
|
29
|
-
ue.
|
|
30
|
-
u.
|
|
31
|
-
FROM
|
|
32
|
-
JOIN
|
|
33
|
-
WHERE ue.
|
|
27
|
+
ue.company_id,
|
|
28
|
+
ue.manager,
|
|
29
|
+
ue.admin,
|
|
30
|
+
u.super_admin
|
|
31
|
+
FROM rapport.user_entitlements ue
|
|
32
|
+
JOIN rapport.users u ON ue.email_address = u.email_address
|
|
33
|
+
WHERE ue.email_address = $1
|
|
34
34
|
`, [email]);
|
|
35
35
|
|
|
36
36
|
if (userCheck.rowCount === 0) {
|
|
@@ -38,8 +38,8 @@ exports.handler = wrapHandler(async ({ requestContext, queryStringParameters })
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const userRole = userCheck.rows[0];
|
|
41
|
-
const isManager = userRole.
|
|
42
|
-
const companyId = userRole.
|
|
41
|
+
const isManager = userRole.manager || userRole.admin || userRole.super_admin;
|
|
42
|
+
const companyId = userRole.company_id;
|
|
43
43
|
|
|
44
44
|
// Parse query parameters
|
|
45
45
|
const status = queryStringParameters?.status || 'active';
|
|
@@ -54,7 +54,7 @@ exports.handler = wrapHandler(async ({ requestContext, queryStringParameters })
|
|
|
54
54
|
SELECT
|
|
55
55
|
aa.alert_id,
|
|
56
56
|
aa.email_address,
|
|
57
|
-
u.
|
|
57
|
+
CONCAT(u.first_name, ' ', u.last_name) as user_name,
|
|
58
58
|
aa.alert_type,
|
|
59
59
|
aa.severity,
|
|
60
60
|
aa.details,
|
|
@@ -64,7 +64,7 @@ exports.handler = wrapHandler(async ({ requestContext, queryStringParameters })
|
|
|
64
64
|
aa.expires_at,
|
|
65
65
|
aa.created_at
|
|
66
66
|
FROM rapport.attention_alerts aa
|
|
67
|
-
JOIN
|
|
67
|
+
JOIN rapport.users u ON aa.email_address = u.email_address
|
|
68
68
|
WHERE aa.company_id = $1
|
|
69
69
|
`;
|
|
70
70
|
|
|
@@ -25,9 +25,9 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
25
25
|
|
|
26
26
|
// Verify user access
|
|
27
27
|
const userCheck = await executeQuery(`
|
|
28
|
-
SELECT ue.
|
|
29
|
-
FROM
|
|
30
|
-
WHERE ue.
|
|
28
|
+
SELECT ue.company_id
|
|
29
|
+
FROM rapport.user_entitlements ue
|
|
30
|
+
WHERE ue.email_address = $1
|
|
31
31
|
LIMIT 1
|
|
32
32
|
`, [email]);
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
35
35
|
return createErrorResponse(403, 'Access denied');
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const companyId = userCheck.rows[0].
|
|
38
|
+
const companyId = userCheck.rows[0].company_id;
|
|
39
39
|
const params = queryStringParameters || {};
|
|
40
40
|
const projectId = params.project_id || null;
|
|
41
41
|
const period = params.period || '7d';
|
|
@@ -58,8 +58,8 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
58
58
|
DATE(p.created_at) as date,
|
|
59
59
|
COUNT(*) as count
|
|
60
60
|
FROM rapport.patterns p
|
|
61
|
-
JOIN
|
|
62
|
-
WHERE ue.
|
|
61
|
+
JOIN rapport.user_entitlements ue ON p.created_by = ue.email_address
|
|
62
|
+
WHERE ue.company_id = $1
|
|
63
63
|
AND p.created_at >= $2
|
|
64
64
|
${projectId ? 'AND p.project_id = $3' : ''}
|
|
65
65
|
GROUP BY DATE(p.created_at)
|
|
@@ -76,20 +76,20 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
76
76
|
console.log('[ActivitySummary] Harvests query failed:', err.message);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Injections:
|
|
79
|
+
// Injections: count from session_standards joined through sessions
|
|
80
80
|
let injections = { total: 0, by_day: [] };
|
|
81
81
|
try {
|
|
82
82
|
const injectionResult = await executeQuery(`
|
|
83
83
|
SELECT
|
|
84
|
-
DATE(
|
|
84
|
+
DATE(ss.created_at) as date,
|
|
85
85
|
COUNT(*) as count
|
|
86
|
-
FROM rapport.
|
|
87
|
-
JOIN
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
AND
|
|
91
|
-
${
|
|
92
|
-
GROUP BY DATE(
|
|
86
|
+
FROM rapport.session_standards ss
|
|
87
|
+
JOIN rapport.sessions s ON s.session_id = ss.session_id
|
|
88
|
+
JOIN rapport.user_entitlements ue ON s.email_address = ue.email_address
|
|
89
|
+
WHERE ue.company_id = $1
|
|
90
|
+
AND ss.created_at >= $2
|
|
91
|
+
${projectId ? 'AND s.project_id = $3' : ''}
|
|
92
|
+
GROUP BY DATE(ss.created_at)
|
|
93
93
|
ORDER BY date
|
|
94
94
|
`, baseParams);
|
|
95
95
|
|
|
@@ -109,8 +109,8 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
109
109
|
const promotionResult = await executeQuery(`
|
|
110
110
|
SELECT COUNT(*) as count
|
|
111
111
|
FROM rapport.audit_trail at
|
|
112
|
-
JOIN
|
|
113
|
-
WHERE ue.
|
|
112
|
+
JOIN rapport.user_entitlements ue ON at.email_address = ue.email_address
|
|
113
|
+
WHERE ue.company_id = $1
|
|
114
114
|
AND at.action = 'standard_promoted'
|
|
115
115
|
AND at.created_at >= $2
|
|
116
116
|
${projectFilter}
|
|
@@ -124,8 +124,8 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
124
124
|
p.status,
|
|
125
125
|
COUNT(*) as count
|
|
126
126
|
FROM rapport.patterns p
|
|
127
|
-
JOIN
|
|
128
|
-
WHERE ue.
|
|
127
|
+
JOIN rapport.user_entitlements ue ON p.created_by = ue.email_address
|
|
128
|
+
WHERE ue.company_id = $1
|
|
129
129
|
AND p.status IN ('proposed', 'active')
|
|
130
130
|
${projectId ? 'AND p.project_id = $3' : ''}
|
|
131
131
|
GROUP BY p.status
|
|
@@ -150,8 +150,8 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
150
150
|
COALESCE(at.details->>'category', 'uncategorized') as category,
|
|
151
151
|
COUNT(*) as count
|
|
152
152
|
FROM rapport.audit_trail at
|
|
153
|
-
JOIN
|
|
154
|
-
WHERE ue.
|
|
153
|
+
JOIN rapport.user_entitlements ue ON at.email_address = ue.email_address
|
|
154
|
+
WHERE ue.company_id = $1
|
|
155
155
|
AND at.action = 'violation_detected'
|
|
156
156
|
AND at.created_at >= $2
|
|
157
157
|
${projectFilter}
|
|
@@ -169,28 +169,27 @@ async function getActivitySummary({ requestContext, queryStringParameters }) {
|
|
|
169
169
|
console.log('[ActivitySummary] Violations query failed:', err.message);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
// Top standards by usage count
|
|
172
|
+
// Top standards by usage count from session_standards
|
|
173
173
|
let topStandards = [];
|
|
174
174
|
try {
|
|
175
175
|
const standardsResult = await executeQuery(`
|
|
176
176
|
SELECT
|
|
177
|
-
|
|
178
|
-
COUNT(*) as
|
|
179
|
-
FROM rapport.
|
|
180
|
-
JOIN
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
AND
|
|
184
|
-
AND
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
ORDER BY usage_count DESC
|
|
177
|
+
ss.standard_name,
|
|
178
|
+
COUNT(*) as count
|
|
179
|
+
FROM rapport.session_standards ss
|
|
180
|
+
JOIN rapport.sessions s ON s.session_id = ss.session_id
|
|
181
|
+
JOIN rapport.user_entitlements ue ON s.email_address = ue.email_address
|
|
182
|
+
WHERE ue.company_id = $1
|
|
183
|
+
AND ss.created_at >= $2
|
|
184
|
+
${projectId ? 'AND s.project_id = $3' : ''}
|
|
185
|
+
GROUP BY ss.standard_name
|
|
186
|
+
ORDER BY count DESC
|
|
188
187
|
LIMIT 10
|
|
189
188
|
`, baseParams);
|
|
190
189
|
|
|
191
190
|
topStandards = standardsResult.rows.map(row => ({
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
standard_name: row.standard_name,
|
|
192
|
+
count: parseInt(row.count, 10)
|
|
194
193
|
}));
|
|
195
194
|
} catch (err) {
|
|
196
195
|
console.log('[ActivitySummary] Top standards query failed:', err.message);
|
|
@@ -43,12 +43,12 @@ async function getCoachingRecommendations({ requestContext, queryStringParameter
|
|
|
43
43
|
|
|
44
44
|
if (!isSelf) {
|
|
45
45
|
const accessCheck = await executeQuery(`
|
|
46
|
-
SELECT ue.
|
|
47
|
-
FROM
|
|
48
|
-
JOIN rapport.projects p ON p.
|
|
49
|
-
WHERE ue.
|
|
46
|
+
SELECT ue.company_id
|
|
47
|
+
FROM rapport.user_entitlements ue
|
|
48
|
+
JOIN rapport.projects p ON p.company_id = ue.company_id
|
|
49
|
+
WHERE ue.email_address = $1
|
|
50
50
|
AND p.project_id = $2
|
|
51
|
-
AND (ue.
|
|
51
|
+
AND (ue.admin = true OR ue.manager = true)
|
|
52
52
|
LIMIT 1
|
|
53
53
|
`, [email, projectId]);
|
|
54
54
|
|
|
@@ -60,12 +60,12 @@ async function getCoachingRecommendations({ requestContext, queryStringParameter
|
|
|
60
60
|
// Verify target user exists and belongs to the project's company
|
|
61
61
|
const userResult = await executeQuery(`
|
|
62
62
|
SELECT
|
|
63
|
-
u.
|
|
64
|
-
u.
|
|
65
|
-
FROM
|
|
66
|
-
JOIN
|
|
67
|
-
JOIN rapport.projects p ON p.
|
|
68
|
-
WHERE u.
|
|
63
|
+
u.email_address as user_email,
|
|
64
|
+
CONCAT(u.first_name, ' ', u.last_name) as display_name
|
|
65
|
+
FROM rapport.users u
|
|
66
|
+
JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
|
|
67
|
+
JOIN rapport.projects p ON p.company_id = ue.company_id
|
|
68
|
+
WHERE u.email_address = $1
|
|
69
69
|
AND p.project_id = $2
|
|
70
70
|
LIMIT 1
|
|
71
71
|
`, [targetUserId, projectId]);
|