-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeco.cpo-webhooks.user.js
More file actions
121 lines (107 loc) · 4.67 KB
/
geco.cpo-webhooks.user.js
File metadata and controls
121 lines (107 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// ==UserScript==
// @name GECO2CPO Webhook
// @namespace https://geco.reply.com/
// @version 1.2.1
// @description Sync changes to CPO
// @author Roman Allenstein <r.allenstein@reply.de>
// @match https://geco.reply.com/
// @match https://geco.reply.com/GeCoO/Project/ManagePlanning.aspx*
// @run-at document-start
// @grant GM_xmlhttpRequest
// @connect localhost
// @connect cpo.lab.roman-allenstein.de
// @downloadURL https://github.com/vanilla-reply/geco-toolbox/raw/refs/heads/main/geco.cpo-webhooks.user.js
// @updateURL https://github.com/vanilla-reply/geco-toolbox/raw/refs/heads/main/geco.cpo-webhooks.user.js
// ==/UserScript==
// == Changelog ========================================================================================================
// 1.0 Initial release
// 1.0.1 Added CPO_BASE and WEBHOOK_URL constants for easier configuration
// 1.1.0 Add timesheet webhook (sync-timesheet), refactor postWebhook to generic endpoint+payload
// 1.1.1 Downgrade missing project ID warning to log on timesheet pages
// 1.1.2 Restrict @match to root URL to avoid loading on RefreshSession pages
// 1.2.0 Replace DOM btn-save handler with XHR interceptor for SavePlanning_1_0
// 1.2.1 Add @match for ManagePlanning.aspx iframe so XHR interceptor runs there
(function () {
'use strict';
const DEBUG = false; // ← auf false setzen für "silent mode"
const CPO_BASE = DEBUG ? 'http://localhost:8080' : 'https://cpo.lab.roman-allenstein.de';
const ENDPOINTS = {
syncPlanning: '/webhook/sync-planning',
syncTimesheet: '/webhook/sync-timesheet',
};
function log(...args) {
if (DEBUG) console.log('[GECO2CPO]', ...args);
}
function warn(...args) {
if (DEBUG) console.warn('[GECO2CPO]', ...args);
}
function error(...args) {
if (DEBUG) console.error('[GECO2CPO]', ...args);
}
log('userscript loaded on', location.href);
// --- XHR interceptor ---
const origOpen = XMLHttpRequest.prototype.open;
const origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this._geco2cpo = { method, url };
return origOpen.call(this, method, url, ...rest);
};
XMLHttpRequest.prototype.send = function (body) {
if (
this._geco2cpo &&
this._geco2cpo.method === 'POST' &&
typeof this._geco2cpo.url === 'string'
) {
const url = this._geco2cpo.url;
if (url.includes('SavePlanning_1_0')) {
try {
const data = JSON.parse(body);
log('intercepted SavePlanning_1_0', data);
const gecoProjectId = data?.plc?.[0]?.ProjectSubId;
if (gecoProjectId) {
postWebhook(ENDPOINTS.syncPlanning, { gecoProjectId });
} else {
warn('SavePlanning_1_0: missing ProjectSubId in payload');
}
} catch (e) {
error('SavePlanning_1_0: failed to parse body', e);
}
}
if (url.includes('SaveProjectTimesheet_1_1')) {
try {
const data = JSON.parse(body);
log('intercepted SaveProjectTimesheet_1_1', data);
const gecoEmployeeId = data?.userId;
const monthRaw = data?.month; // format "M/dd/yyyy"
if (gecoEmployeeId && monthRaw) {
const parts = monthRaw.split('/');
const month = Number(parts[0]);
const year = Number(parts[2]);
postWebhook(ENDPOINTS.syncTimesheet, { gecoEmployeeId, year, month });
} else {
warn('SaveProjectTimesheet_1_1: missing userId or month', { gecoEmployeeId, monthRaw });
}
} catch (e) {
error('SaveProjectTimesheet_1_1: failed to parse body', e);
}
}
}
return origSend.call(this, body);
};
function postWebhook(endpoint, payload) {
const url = `${CPO_BASE}${endpoint}`;
log('POST', url, payload);
GM_xmlhttpRequest({
method: 'POST',
url,
headers: { 'Content-Type': 'application/json' },
data: JSON.stringify(payload),
onload: (resp) => {
log('webhook response', resp.status, resp.responseText);
},
onerror: (err) => {
error('webhook error', err);
}
});
}
})();