-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathController.js
More file actions
227 lines (202 loc) Β· 9.69 KB
/
Copy pathController.js
File metadata and controls
227 lines (202 loc) Β· 9.69 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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/**
* Controller.gs
* Interface layer between the Spreadsheet UI/Web App and the Service logic
*/
/**
* Main Web App Entry Point
*/
function doGet(e) {
const clientId = e.parameter.clientId || '';
const editOrderId = e.parameter.orderId || '';
let prefillData = null;
if (editOrderId) {
prefillData = getOrderById(editOrderId);
}
const template = HtmlService.createTemplateFromFile('index');
template.clientId = clientId;
template.editOrderId = editOrderId;
template.prefillData = prefillData;
template.categorySettings = getCategorySettings();
template.appStyles = getAppStyles();
template.appConfig = getAppConfig();
template.version = CURRENT_VERSION;
return template.evaluate()
.setTitle(APP_TITLE)
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
/**
* Spreadsheet Menu Trigger
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('Order System');
menu.addItem('π Open Order Form (Web App)', 'showOrderFormDialog')
.addItem('β Add New Product', 'showAddProductSidebar')
.addSeparator()
.addItem('π Generate Invoice PDF for Selection', 'generateSelectedOrderPdf')
.addItem('π Generate Order Form PDF for Selection', 'generateSelectedOrderFormPdf')
.addItem('π Open PDF Exports Folder', 'openExportFolder')
.addSeparator()
.addSubMenu(ui.createMenu('π‘οΈ Header Protection')
.addItem('πΈ Backup Sheet Headers', 'backupSheetHeaders')
.addItem('π Compare Headers to Backup', 'compareSheetHeaders')
.addItem('β»οΈ Reset Headers from Backup', 'resetSheetHeaders')
)
.addSubMenu(ui.createMenu('π Deployment')
.addItem('π Get Copy Link', 'showCopyLink')
.addItem('π¦ Create New Copy for Colleague', 'createCleanCopy')
.addItem('π Check for Updates', 'checkForUpdates')
.addItem('π‘ Register as Master', 'registerAsMaster')
.addSeparator()
.addItem('β¬οΈ Pull Updates from Master', 'pullUpdatesFromMaster')
)
.addSubMenu(ui.createMenu('π§ System')
.addItem('π Factory Reset', 'factoryResetSpreadsheet')
)
.addToUi();
// Auto-backup headers on first open (initialization lifecycle)
initializeOnFirstOpen_();
}
/**
* UI Component Launchers
*/
function showAddProductSidebar() {
const template = HtmlService.createTemplateFromFile('addProductSidebar');
const html = template.evaluate()
.setTitle('Add/Edit Product');
SpreadsheetApp.getUi().showSidebar(html);
}
function showOrderFormDialog() {
const ui = SpreadsheetApp.getUi();
const sheet = SpreadsheetApp.getActiveSheet();
const activeCell = sheet.getActiveCell();
const activeVal = String(activeCell.getValue()).trim();
let editOrderId = '';
let clientId = '';
if (activeVal && (activeVal.startsWith('ORD-') || /^\d+$/.test(activeVal))) {
try {
const data = getOrderById(activeVal);
if (data) {
editOrderId = data.id;
clientId = data.clientId;
}
} catch (e) { }
}
// Read WEB_APP_URL from SETTINGS
let webAppUrl = '';
try {
const r = SpreadsheetApp.getActiveSpreadsheet().getRangeByName('WEB_APP_URL');
if (r) webAppUrl = String(r.getValue() || '').trim();
} catch (e) { }
if (!webAppUrl) {
ui.alert('WEB_APP_URL is not set in SETTINGS.\n\nPlease deploy the web app and paste the URL into SETTINGS \u2192 WEB_APP_URL.');
return;
}
// Append query params for client/order pre-fill
const parts = [];
if (clientId) parts.push('clientId=' + encodeURIComponent(clientId));
if (editOrderId) parts.push('orderId=' + encodeURIComponent(editOrderId));
const finalUrl = webAppUrl + (parts.length ? '?' + parts.join('&') : '');
// A tiny launcher dialog opens a proper movable + resizable popup then closes itself.
// NOTE: we do NOT call google.script.host.close() here β doing so kills the popup
// because the popup is a child of the dialog's window context in Chrome.
// The user closes this small launcher manually after the popup is open.
const launcher = HtmlService.createHtmlOutput(
'<html><head><script>' +
'window.onload=function(){' +
' var sw=window.screen.availWidth, sh=window.screen.availHeight;' +
' var pw=Math.min(1200,Math.round(sw*0.85));' +
' var ph=Math.min(900, Math.round(sh*0.85));' +
' var lft=Math.round((sw-pw)/2), top=Math.round((sh-ph)/4);' +
' var popup=window.open("' + finalUrl + '","OrderForm",' +
' "width="+pw+",height="+ph+",left="+lft+",top="+top+' +
' ",resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,status=no");' +
' if(popup){' +
' document.getElementById("msg").textContent="\u2713 Order Form opened in a new window.";' +
' document.getElementById("msg").style.color="#188038";' +
' } else {' +
' document.getElementById("msg").innerHTML="Popup blocked. <a href=\\"' + finalUrl + '\\" target=\\"_blank\\">Click here</a> to open.";' +
' document.getElementById("msg").style.color="#c5221f";' +
' }' +
'};' +
'<\/script><\/head>' +
'<body style="font-family:Roboto,sans-serif;padding:16px;text-align:center;">' +
'<p id="msg" style="font-size:13px;margin:0 0 12px;">Opening Order Form\u2026<\/p>' +
'<button onclick="google.script.host.close()" ' +
' style="background:#1a73e8;color:#fff;border:none;padding:8px 20px;' +
' border-radius:6px;cursor:pointer;font-size:13px;">Close<\/button>' +
'<\/body><\/html>'
).setWidth(320).setHeight(110);
ui.showModalDialog(launcher, 'Order System');
}
/**
* Install Dashboard Click Trigger
* Run this once to enable dashboard button clicks
*/
function installDashboardTrigger() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Remove existing onSelectionChange triggers to avoid duplicates
const triggers = ScriptApp.getUserTriggers(ss);
triggers.forEach(trigger => {
if (trigger.getHandlerFunction() === 'onSelectionChange') {
ScriptApp.deleteTrigger(trigger);
}
});
// Install new trigger
ScriptApp.newTrigger('onSelectionChange')
.forSpreadsheet(ss)
.onSelectionChange()
.create();
SpreadsheetApp.getActiveSpreadsheet().toast('Dashboard buttons are now active! Click any action in Column B.', 'Trigger Installed', 5);
}
/**
* Client-Side API Wrappers (Called by google.script.run)
*/
function updateConfigSetting(key, value) {
return Operations.updateConfigSetting(key, value);
}
function createExternalTemplates() {
return Setup.createExternalTemplates();
}
/**
* Show the "Copy this Spreadsheet" link in a dialog.
* The link uses Google Sheets' built-in /copy URL pattern.
* Anyone with access who clicks it will be prompted to make their own copy.
*/
function showCopyLink() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ssId = ss.getId();
const copyUrl = 'https://docs.google.com/spreadsheets/d/' + ssId + '/copy';
const qrUrl = 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=' + encodeURIComponent(copyUrl);
const html = HtmlService.createHtmlOutput(
'<div style="font-family: Roboto, sans-serif; padding: 20px;">' +
'<h3 style="color: #006c4c; margin-top: 0;">π Copy Link</h3>' +
'<p style="font-size: 13px; color: #666;">Share this link with a colleague. When they open it, Google will prompt them to make their own copy of this spreadsheet.</p>' +
'<div style="background: #f5f5f5; padding: 12px; border-radius: 8px; border: 1px solid #e0e0e0; margin: 16px 0; word-break: break-all; font-family: Consolas, monospace; font-size: 12px;" id="linkBox">' +
copyUrl +
'</div>' +
'<div style="display: flex; gap: 8px; margin-bottom: 20px;">' +
'<button onclick="copyLink()" style="background: #006c4c; color: white; border: none; padding: 10px 20px; border-radius: 8px; cursor: pointer; font-size: 14px;" id="copyBtn">π Copy Link</button>' +
'<a href="' + copyUrl + '" target="_blank" style="background: #1a73e8; color: white; padding: 10px 20px; border-radius: 8px; text-decoration: none; font-size: 14px;">π Open</a>' +
'</div>' +
'<hr style="margin: 16px 0; border: none; border-top: 1px solid #e0e0e0;">' +
'<p style="font-size: 12px; color: #666; margin-bottom: 8px;">QR Code (scan to copy):</p>' +
'<img src="' + qrUrl + '" width="200" height="200" style="border: 1px solid #eee; border-radius: 8px;">' +
'<script>' +
'function copyLink() {' +
' var text = "' + copyUrl + '";' +
' navigator.clipboard.writeText(text).then(function() {' +
' document.getElementById("copyBtn").textContent = "β
Copied!";' +
' setTimeout(function() { document.getElementById("copyBtn").textContent = "π Copy Link"; }, 2000);' +
' }).catch(function() {' +
' var ta = document.createElement("textarea");' +
' ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta);' +
' document.getElementById("copyBtn").textContent = "β
Copied!";' +
' setTimeout(function() { document.getElementById("copyBtn").textContent = "π Copy Link"; }, 2000);' +
' });' +
'}' +
'</script>' +
'</div>'
).setWidth(480).setHeight(480);
SpreadsheetApp.getUi().showModalDialog(html, 'Share Spreadsheet Copy Link');
}