@bonginkan/maria-lite 6.3.0 → 6.3.1
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 +18 -2
- package/dist/cli.cjs +2170 -90
- package/dist/desktop-client.js +924 -11
- package/dist/ext.cjs +1 -1
- package/dist/ext.d.cts +1 -1
- package/origin/index.meta.json +1 -1
- package/package.json +1 -1
package/dist/desktop-client.js
CHANGED
|
@@ -359,7 +359,8 @@ var init_helpers = __esm({
|
|
|
359
359
|
"google-gmail": "mail",
|
|
360
360
|
"google-calendar": "calendar",
|
|
361
361
|
"google-drive": "hard-drive",
|
|
362
|
-
"google-meet": "video"
|
|
362
|
+
"google-meet": "video",
|
|
363
|
+
"resource-manager": "building"
|
|
363
364
|
};
|
|
364
365
|
LUCIDE_APP_ICONS = {
|
|
365
366
|
// Basic Tools
|
|
@@ -457,6 +458,7 @@ var init_helpers = __esm({
|
|
|
457
458
|
gcal: "calendar",
|
|
458
459
|
gdrive: "hard-drive",
|
|
459
460
|
gmeet: "video",
|
|
461
|
+
"resource-manager": "building",
|
|
460
462
|
// System
|
|
461
463
|
help: "life-buoy",
|
|
462
464
|
version: "info",
|
|
@@ -473,7 +475,8 @@ var init_helpers = __esm({
|
|
|
473
475
|
__ui_design: "paintbrush",
|
|
474
476
|
__file_manager: "folder-open",
|
|
475
477
|
__task_manager: "activity",
|
|
476
|
-
__log_viewer: "scroll-text"
|
|
478
|
+
__log_viewer: "scroll-text",
|
|
479
|
+
__resource_manager: "building"
|
|
477
480
|
};
|
|
478
481
|
LUCIDE_FILE_EXT_ICONS = {
|
|
479
482
|
ts: "file-code",
|
|
@@ -701,7 +704,7 @@ var init_state = __esm({
|
|
|
701
704
|
label: "Google",
|
|
702
705
|
emoji: "\u{1F310}",
|
|
703
706
|
serverCategories: ["google"],
|
|
704
|
-
commandIds: ["gmail", "gcal", "gdrive", "gmeet"],
|
|
707
|
+
commandIds: ["gmail", "gcal", "gdrive", "gmeet", "resource-manager"],
|
|
705
708
|
alwaysVisible: true
|
|
706
709
|
},
|
|
707
710
|
{
|
|
@@ -8072,6 +8075,10 @@ function openGoogleCalendar() {
|
|
|
8072
8075
|
calCalendars = [];
|
|
8073
8076
|
calEnabledCalendarIds = /* @__PURE__ */ new Set();
|
|
8074
8077
|
calShowCalendarPanel = false;
|
|
8078
|
+
calFindBuildings = [];
|
|
8079
|
+
calFindResources = [];
|
|
8080
|
+
calFindBuildingId = "";
|
|
8081
|
+
calFindResourceId = "";
|
|
8075
8082
|
renderApp2();
|
|
8076
8083
|
}
|
|
8077
8084
|
function renderApp2() {
|
|
@@ -8631,6 +8638,18 @@ async function handleEdit(container, ev) {
|
|
|
8631
8638
|
btn.textContent = "Save Changes";
|
|
8632
8639
|
}
|
|
8633
8640
|
}
|
|
8641
|
+
function updateResourceDropdown(container) {
|
|
8642
|
+
const rSelect = container.querySelector(".gc-ft-resource");
|
|
8643
|
+
if (!rSelect) return;
|
|
8644
|
+
const filtered = calFindBuildingId ? calFindResources.filter((r) => r.buildingId === calFindBuildingId) : calFindResources;
|
|
8645
|
+
rSelect.innerHTML = '<option value="">All</option>';
|
|
8646
|
+
for (const r of filtered) {
|
|
8647
|
+
const opt = document.createElement("option");
|
|
8648
|
+
opt.value = r.resourceId;
|
|
8649
|
+
opt.textContent = `${r.resourceName}${r.capacity ? ` (${r.capacity})` : ""}`;
|
|
8650
|
+
rSelect.appendChild(opt);
|
|
8651
|
+
}
|
|
8652
|
+
}
|
|
8634
8653
|
function renderFindTime(container) {
|
|
8635
8654
|
const tomorrow = /* @__PURE__ */ new Date();
|
|
8636
8655
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
@@ -8644,6 +8663,11 @@ function renderFindTime(container) {
|
|
|
8644
8663
|
html += `<label>Required Attendees<input class="gc-ft-required" type="text" placeholder="email1@example.com, email2@example.com" /><span class="gc-ft-hint">All must be available. Comma-separated emails.</span></label>`;
|
|
8645
8664
|
html += `<label>Optional Attendees<input class="gc-ft-optional" type="text" placeholder="email3@example.com, email4@example.com" /><span class="gc-ft-hint">Preferred but not required. Priority by input order.</span></label>`;
|
|
8646
8665
|
html += `<div class="gc-ft-row">`;
|
|
8666
|
+
html += `<label>Building<select class="gc-ft-building"><option value="">All</option></select></label>`;
|
|
8667
|
+
html += `<label>Resource<select class="gc-ft-resource"><option value="">All</option></select></label>`;
|
|
8668
|
+
html += `</div>`;
|
|
8669
|
+
html += `<span class="gc-ft-hint">Filter by building/resource to check room availability.</span>`;
|
|
8670
|
+
html += `<div class="gc-ft-row">`;
|
|
8647
8671
|
html += `<label>Duration<select class="gc-ft-duration">`;
|
|
8648
8672
|
for (const m of [15, 30, 45, 60, 90, 120, 180, 240]) {
|
|
8649
8673
|
const label = m < 60 ? `${m} min` : m === 60 ? "1 hour" : `${m / 60} hours`;
|
|
@@ -8677,6 +8701,42 @@ function renderFindTime(container) {
|
|
|
8677
8701
|
html += `<div class="gc-ft-results"></div>`;
|
|
8678
8702
|
html += `</div>`;
|
|
8679
8703
|
container.innerHTML = html;
|
|
8704
|
+
const bSelectEl = container.querySelector(".gc-ft-building");
|
|
8705
|
+
const rSelectEl = container.querySelector(".gc-ft-resource");
|
|
8706
|
+
void (async () => {
|
|
8707
|
+
try {
|
|
8708
|
+
const [bData, rData] = await Promise.all([
|
|
8709
|
+
apiJson("/api/calendar/buildings"),
|
|
8710
|
+
apiJson("/api/calendar/resources")
|
|
8711
|
+
]);
|
|
8712
|
+
calFindBuildings = bData.buildings || [];
|
|
8713
|
+
calFindResources = rData.resources || [];
|
|
8714
|
+
if (bSelectEl) {
|
|
8715
|
+
for (const b of calFindBuildings) {
|
|
8716
|
+
const opt = document.createElement("option");
|
|
8717
|
+
opt.value = b.buildingId;
|
|
8718
|
+
opt.textContent = b.buildingName;
|
|
8719
|
+
bSelectEl.appendChild(opt);
|
|
8720
|
+
}
|
|
8721
|
+
if (calFindBuildingId) bSelectEl.value = calFindBuildingId;
|
|
8722
|
+
}
|
|
8723
|
+
updateResourceDropdown(container);
|
|
8724
|
+
if (calFindResourceId && rSelectEl) rSelectEl.value = calFindResourceId;
|
|
8725
|
+
} catch {
|
|
8726
|
+
}
|
|
8727
|
+
})();
|
|
8728
|
+
if (bSelectEl) {
|
|
8729
|
+
bSelectEl.addEventListener("change", () => {
|
|
8730
|
+
calFindBuildingId = bSelectEl.value;
|
|
8731
|
+
calFindResourceId = "";
|
|
8732
|
+
updateResourceDropdown(container);
|
|
8733
|
+
});
|
|
8734
|
+
}
|
|
8735
|
+
if (rSelectEl) {
|
|
8736
|
+
rSelectEl.addEventListener("change", () => {
|
|
8737
|
+
calFindResourceId = rSelectEl.value;
|
|
8738
|
+
});
|
|
8739
|
+
}
|
|
8680
8740
|
container.querySelector(".gc-ft-search-btn").addEventListener("click", () => void handleFindSlots(container, 0));
|
|
8681
8741
|
}
|
|
8682
8742
|
async function handleFindSlots(container, offset = 0) {
|
|
@@ -8693,7 +8753,8 @@ async function handleFindSlots(container, offset = 0) {
|
|
|
8693
8753
|
const btn = container.querySelector(".gc-ft-search-btn");
|
|
8694
8754
|
const required = reqEmails ? reqEmails.split(",").map((e) => e.trim()).filter(Boolean) : [];
|
|
8695
8755
|
const optional = optEmails ? optEmails.split(",").map((e) => e.trim()).filter(Boolean) : [];
|
|
8696
|
-
|
|
8756
|
+
const useResourceApi = !!(calFindBuildingId || calFindResourceId);
|
|
8757
|
+
if (!useResourceApi && !required.length) {
|
|
8697
8758
|
statusEl.innerHTML = `<span class="gc-status-err">At least one required attendee is needed</span>`;
|
|
8698
8759
|
return;
|
|
8699
8760
|
}
|
|
@@ -8703,10 +8764,27 @@ async function handleFindSlots(container, offset = 0) {
|
|
|
8703
8764
|
}
|
|
8704
8765
|
btn.disabled = true;
|
|
8705
8766
|
btn.textContent = "Searching...";
|
|
8706
|
-
|
|
8767
|
+
const participantCount = required.length + optional.length;
|
|
8768
|
+
statusEl.innerHTML = useResourceApi ? `<span style="color:var(--muted);">Checking resource availability${participantCount > 0 ? ` for ${participantCount} participant(s)` : ""}...</span>` : `<span style="color:var(--muted);">Querying free/busy data for ${participantCount} participant(s)...</span>`;
|
|
8707
8769
|
resultsEl.innerHTML = "";
|
|
8708
8770
|
try {
|
|
8709
|
-
const res = await apiJson("/api/
|
|
8771
|
+
const res = useResourceApi ? await apiJson("/api/calendar/slots", {
|
|
8772
|
+
method: "POST",
|
|
8773
|
+
headers: { "Content-Type": "application/json" },
|
|
8774
|
+
body: JSON.stringify({
|
|
8775
|
+
dateFrom,
|
|
8776
|
+
dateTo,
|
|
8777
|
+
durationMinutes: duration,
|
|
8778
|
+
workStartHour: workStart,
|
|
8779
|
+
workEndHour: workEnd,
|
|
8780
|
+
buildingId: calFindBuildingId || void 0,
|
|
8781
|
+
resourceId: calFindResourceId || void 0,
|
|
8782
|
+
offset,
|
|
8783
|
+
bufferMinutes,
|
|
8784
|
+
required: required.length > 0 ? required : void 0,
|
|
8785
|
+
optional: optional.length > 0 ? optional : void 0
|
|
8786
|
+
})
|
|
8787
|
+
}) : await apiJson("/api/google/calendar/find-slots", {
|
|
8710
8788
|
method: "POST",
|
|
8711
8789
|
headers: { "Content-Type": "application/json" },
|
|
8712
8790
|
body: JSON.stringify({
|
|
@@ -8761,6 +8839,13 @@ async function handleFindSlots(container, offset = 0) {
|
|
|
8761
8839
|
}
|
|
8762
8840
|
}
|
|
8763
8841
|
rhtml += `<div class="gc-ft-slot-buffer">Buffer: ${slot.bufferBeforeMinutes} min before, ${slot.bufferAfterMinutes} min after</div>`;
|
|
8842
|
+
if (slot.resourceEmails?.length) {
|
|
8843
|
+
const names = slot.resourceEmails.map((e) => {
|
|
8844
|
+
const r = calFindResources.find((res2) => res2.resourceEmail === e);
|
|
8845
|
+
return r ? r.resourceName : e;
|
|
8846
|
+
});
|
|
8847
|
+
rhtml += `<div class="gc-ft-slot-meta" style="color:var(--accent);font-size:.82em;margin-top:2px;">\u{1F3E2} Available: ${esc(names.join(", "))}</div>`;
|
|
8848
|
+
}
|
|
8764
8849
|
rhtml += `<button class="gc-ft-slot-create">Create Event</button>`;
|
|
8765
8850
|
rhtml += `</div>`;
|
|
8766
8851
|
}
|
|
@@ -8870,7 +8955,7 @@ function formatDateTime(dateStr) {
|
|
|
8870
8955
|
return dateStr;
|
|
8871
8956
|
}
|
|
8872
8957
|
}
|
|
8873
|
-
var calView, calCurrentEventId, calCurrentCalendarId, calCurrentEvent, calDays, calWinId, calCalendars, calEnabledCalendarIds, calShowCalendarPanel, calPrefill, STYLES2;
|
|
8958
|
+
var calView, calCurrentEventId, calCurrentCalendarId, calCurrentEvent, calDays, calWinId, calCalendars, calEnabledCalendarIds, calShowCalendarPanel, calPrefill, calFindBuildings, calFindResources, calFindBuildingId, calFindResourceId, STYLES2;
|
|
8874
8959
|
var init_google_calendar = __esm({
|
|
8875
8960
|
"services/desktop/client/modules/google-calendar.ts"() {
|
|
8876
8961
|
init_helpers();
|
|
@@ -8886,6 +8971,10 @@ var init_google_calendar = __esm({
|
|
|
8886
8971
|
calEnabledCalendarIds = /* @__PURE__ */ new Set();
|
|
8887
8972
|
calShowCalendarPanel = false;
|
|
8888
8973
|
calPrefill = null;
|
|
8974
|
+
calFindBuildings = [];
|
|
8975
|
+
calFindResources = [];
|
|
8976
|
+
calFindBuildingId = "";
|
|
8977
|
+
calFindResourceId = "";
|
|
8889
8978
|
STYLES2 = `
|
|
8890
8979
|
.gc-app{font-family:var(--sans);color:var(--fg);display:flex;flex-direction:column;height:100%;overflow:hidden}
|
|
8891
8980
|
.gc-toolbar{display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--input-border);background:var(--ctx-hover);flex-shrink:0;align-items:center;flex-wrap:wrap}
|
|
@@ -10322,6 +10411,716 @@ var init_google_meet = __esm({
|
|
|
10322
10411
|
}
|
|
10323
10412
|
});
|
|
10324
10413
|
|
|
10414
|
+
// services/desktop/client/modules/resource-manager.ts
|
|
10415
|
+
function injectStyles2() {
|
|
10416
|
+
if (stylesInjected) return;
|
|
10417
|
+
const style = document.createElement("style");
|
|
10418
|
+
style.textContent = STYLES5;
|
|
10419
|
+
document.head.appendChild(style);
|
|
10420
|
+
stylesInjected = true;
|
|
10421
|
+
}
|
|
10422
|
+
function openResourceManager() {
|
|
10423
|
+
injectStyles2();
|
|
10424
|
+
const useLucide = state.iconStyle === "modern" || state.iconStyle === "minimal";
|
|
10425
|
+
const winIcon = useLucide ? lucideIcon("building", "di-lucide") : "\u{1F3E2}";
|
|
10426
|
+
const win = createWindow({
|
|
10427
|
+
appId: "__resource_manager",
|
|
10428
|
+
title: "Resource Manager",
|
|
10429
|
+
icon: winIcon,
|
|
10430
|
+
content: "app",
|
|
10431
|
+
width: 720,
|
|
10432
|
+
height: 520
|
|
10433
|
+
});
|
|
10434
|
+
rmWinId = win.id;
|
|
10435
|
+
rmView = "buildings";
|
|
10436
|
+
rmBuildings = [];
|
|
10437
|
+
rmResources = [];
|
|
10438
|
+
rmFilterBuildingId = "";
|
|
10439
|
+
rmStatus = "";
|
|
10440
|
+
rmStatusType = "";
|
|
10441
|
+
renderRmApp();
|
|
10442
|
+
}
|
|
10443
|
+
function renderRmApp() {
|
|
10444
|
+
const body = getWinBody(rmWinId);
|
|
10445
|
+
let html = '<div class="rm-app">';
|
|
10446
|
+
if (rmStatus) {
|
|
10447
|
+
const cls = rmStatusType === "ok" ? "rm-status-ok" : rmStatusType === "err" ? "rm-status-err" : "";
|
|
10448
|
+
html += `<div class="rm-status ${cls}">${esc(rmStatus)}</div>`;
|
|
10449
|
+
}
|
|
10450
|
+
html += '<div class="rm-toolbar">';
|
|
10451
|
+
if (rmView === "create-building" || rmView === "create-resource" || rmView === "edit-building" || rmView === "edit-resource") {
|
|
10452
|
+
html += `<button class="rm-back-btn" data-rm-action="back">${lucideIcon("arrow-left", "")} Back</button>`;
|
|
10453
|
+
} else {
|
|
10454
|
+
html += `<button class="${rmView === "buildings" ? "active" : ""}" data-rm-action="tab-buildings">Buildings</button>`;
|
|
10455
|
+
html += `<button class="${rmView === "resources" ? "active" : ""}" data-rm-action="tab-resources">Resources</button>`;
|
|
10456
|
+
if (rmView === "buildings") {
|
|
10457
|
+
html += `<button class="rm-add-btn" data-rm-action="new-building">+ New Building</button>`;
|
|
10458
|
+
}
|
|
10459
|
+
if (rmView === "resources") {
|
|
10460
|
+
html += `<select data-rm-action="filter-building"><option value="">All buildings</option>`;
|
|
10461
|
+
for (const b of rmBuildings) {
|
|
10462
|
+
const sel = rmFilterBuildingId === b.buildingId ? " selected" : "";
|
|
10463
|
+
html += `<option value="${esc(b.buildingId)}"${sel}>${esc(b.buildingName)}</option>`;
|
|
10464
|
+
}
|
|
10465
|
+
html += `</select>`;
|
|
10466
|
+
html += `<button class="rm-add-btn" data-rm-action="new-resource">+ New Resource</button>`;
|
|
10467
|
+
}
|
|
10468
|
+
}
|
|
10469
|
+
html += "</div>";
|
|
10470
|
+
html += '<div class="rm-content" id="rmContent"></div>';
|
|
10471
|
+
html += "</div>";
|
|
10472
|
+
body.innerHTML = html;
|
|
10473
|
+
attachToolbarListeners(body);
|
|
10474
|
+
if (rmView === "buildings") {
|
|
10475
|
+
void renderBuildingsList();
|
|
10476
|
+
} else if (rmView === "resources") {
|
|
10477
|
+
void renderResourcesList();
|
|
10478
|
+
} else if (rmView === "create-building") {
|
|
10479
|
+
renderCreateBuildingForm();
|
|
10480
|
+
} else if (rmView === "create-resource") {
|
|
10481
|
+
renderCreateResourceForm();
|
|
10482
|
+
} else if (rmView === "edit-building") {
|
|
10483
|
+
renderEditBuildingForm();
|
|
10484
|
+
} else if (rmView === "edit-resource") {
|
|
10485
|
+
renderEditResourceForm();
|
|
10486
|
+
}
|
|
10487
|
+
}
|
|
10488
|
+
function attachToolbarListeners(container) {
|
|
10489
|
+
container.querySelectorAll("[data-rm-action]").forEach((el) => {
|
|
10490
|
+
const action = el.dataset.rmAction || "";
|
|
10491
|
+
if (el.tagName === "SELECT") {
|
|
10492
|
+
el.addEventListener("change", () => {
|
|
10493
|
+
if (action === "filter-building") {
|
|
10494
|
+
rmFilterBuildingId = el.value;
|
|
10495
|
+
void renderResourcesList();
|
|
10496
|
+
}
|
|
10497
|
+
});
|
|
10498
|
+
} else {
|
|
10499
|
+
el.addEventListener("click", () => {
|
|
10500
|
+
rmStatus = "";
|
|
10501
|
+
rmStatusType = "";
|
|
10502
|
+
if (action === "tab-buildings") {
|
|
10503
|
+
rmView = "buildings";
|
|
10504
|
+
renderRmApp();
|
|
10505
|
+
} else if (action === "tab-resources") {
|
|
10506
|
+
rmView = "resources";
|
|
10507
|
+
renderRmApp();
|
|
10508
|
+
} else if (action === "new-building") {
|
|
10509
|
+
rmView = "create-building";
|
|
10510
|
+
renderRmApp();
|
|
10511
|
+
} else if (action === "new-resource") {
|
|
10512
|
+
rmView = "create-resource";
|
|
10513
|
+
renderRmApp();
|
|
10514
|
+
} else if (action === "back") {
|
|
10515
|
+
rmView = rmView === "create-building" || rmView === "edit-building" ? "buildings" : "resources";
|
|
10516
|
+
rmEditingBuilding = null;
|
|
10517
|
+
rmEditingResource = null;
|
|
10518
|
+
renderRmApp();
|
|
10519
|
+
}
|
|
10520
|
+
});
|
|
10521
|
+
}
|
|
10522
|
+
});
|
|
10523
|
+
}
|
|
10524
|
+
function getContentEl() {
|
|
10525
|
+
return document.getElementById("rmContent");
|
|
10526
|
+
}
|
|
10527
|
+
async function renderBuildingsList() {
|
|
10528
|
+
const el = getContentEl();
|
|
10529
|
+
if (!el) return;
|
|
10530
|
+
el.innerHTML = '<div class="rm-loading">Loading buildings...</div>';
|
|
10531
|
+
try {
|
|
10532
|
+
const data = await apiJson("/api/calendar/buildings");
|
|
10533
|
+
if (!data.ok) {
|
|
10534
|
+
el.innerHTML = `<div class="rm-empty">${esc(data.error || "Could not load buildings")}</div>`;
|
|
10535
|
+
return;
|
|
10536
|
+
}
|
|
10537
|
+
rmBuildings = data.buildings || [];
|
|
10538
|
+
} catch (e) {
|
|
10539
|
+
el.innerHTML = `<div class="rm-empty">Error: ${esc(e instanceof Error ? e.message : String(e))}</div>`;
|
|
10540
|
+
return;
|
|
10541
|
+
}
|
|
10542
|
+
if (rmBuildings.length === 0) {
|
|
10543
|
+
el.innerHTML = '<div class="rm-empty">No buildings found. Click "+ New Building" to create one.</div>';
|
|
10544
|
+
return;
|
|
10545
|
+
}
|
|
10546
|
+
let html = '<div class="rm-list">';
|
|
10547
|
+
html += '<div class="rm-list-header">';
|
|
10548
|
+
html += '<div class="rm-list-col" style="flex:2">Name</div>';
|
|
10549
|
+
html += '<div class="rm-list-col" style="flex:2">Description</div>';
|
|
10550
|
+
html += '<div class="rm-list-col" style="flex:1">Floors</div>';
|
|
10551
|
+
html += '<div class="rm-list-col" style="flex:0.5"></div>';
|
|
10552
|
+
html += "</div>";
|
|
10553
|
+
for (const b of rmBuildings) {
|
|
10554
|
+
const allFloors = b.floorNames && b.floorNames.length > 0 ? b.floorNames.join(", ") : "-";
|
|
10555
|
+
const floors = b.floorNames && b.floorNames.length > 5 ? `${b.floorNames.slice(0, 5).join(", ")} +${b.floorNames.length - 5}` : allFloors;
|
|
10556
|
+
html += '<div class="rm-list-row">';
|
|
10557
|
+
html += `<div class="rm-list-col" style="flex:2;font-weight:600" title="${esc(b.buildingName)}">${esc(b.buildingName)}</div>`;
|
|
10558
|
+
html += `<div class="rm-list-col" style="flex:2" title="${esc(b.description || "-")}">${esc(b.description || "-")}</div>`;
|
|
10559
|
+
html += `<div class="rm-list-col" style="flex:1" title="${esc(allFloors)}">${esc(floors)}</div>`;
|
|
10560
|
+
html += `<div class="rm-list-col" style="flex:0.5"><button class="rm-edit-btn" data-rm-edit-building="${esc(b.buildingId)}">Edit</button></div>`;
|
|
10561
|
+
html += "</div>";
|
|
10562
|
+
}
|
|
10563
|
+
html += "</div>";
|
|
10564
|
+
el.innerHTML = html;
|
|
10565
|
+
el.querySelectorAll("[data-rm-edit-building]").forEach((btn) => {
|
|
10566
|
+
btn.addEventListener("click", () => {
|
|
10567
|
+
const id = btn.dataset.rmEditBuilding || "";
|
|
10568
|
+
rmEditingBuilding = rmBuildings.find((b) => b.buildingId === id) || null;
|
|
10569
|
+
if (rmEditingBuilding) {
|
|
10570
|
+
rmView = "edit-building";
|
|
10571
|
+
renderRmApp();
|
|
10572
|
+
}
|
|
10573
|
+
});
|
|
10574
|
+
});
|
|
10575
|
+
}
|
|
10576
|
+
async function renderResourcesList() {
|
|
10577
|
+
const el = getContentEl();
|
|
10578
|
+
if (!el) return;
|
|
10579
|
+
el.innerHTML = '<div class="rm-loading">Loading resources...</div>';
|
|
10580
|
+
if (rmBuildings.length === 0) {
|
|
10581
|
+
try {
|
|
10582
|
+
const bData = await apiJson("/api/calendar/buildings");
|
|
10583
|
+
if (bData.ok) rmBuildings = bData.buildings || [];
|
|
10584
|
+
} catch {
|
|
10585
|
+
}
|
|
10586
|
+
}
|
|
10587
|
+
try {
|
|
10588
|
+
const data = await apiJson("/api/calendar/resources");
|
|
10589
|
+
if (!data.ok) {
|
|
10590
|
+
el.innerHTML = `<div class="rm-empty">${esc(data.error || "Could not load resources")}</div>`;
|
|
10591
|
+
return;
|
|
10592
|
+
}
|
|
10593
|
+
rmResources = data.resources || [];
|
|
10594
|
+
} catch (e) {
|
|
10595
|
+
el.innerHTML = `<div class="rm-empty">Error: ${esc(e instanceof Error ? e.message : String(e))}</div>`;
|
|
10596
|
+
return;
|
|
10597
|
+
}
|
|
10598
|
+
const filtered = rmFilterBuildingId ? rmResources.filter((r) => r.buildingId === rmFilterBuildingId) : rmResources;
|
|
10599
|
+
if (filtered.length === 0) {
|
|
10600
|
+
const msg = rmFilterBuildingId ? "No resources found for this building." : 'No resources found. Click "+ New Resource" to create one.';
|
|
10601
|
+
el.innerHTML = `<div class="rm-empty">${esc(msg)}</div>`;
|
|
10602
|
+
return;
|
|
10603
|
+
}
|
|
10604
|
+
let html = '<div class="rm-list">';
|
|
10605
|
+
html += '<div class="rm-list-header">';
|
|
10606
|
+
html += '<div class="rm-list-col" style="flex:2">Name</div>';
|
|
10607
|
+
html += '<div class="rm-list-col" style="flex:1.5">Building</div>';
|
|
10608
|
+
html += '<div class="rm-list-col" style="flex:1">Type</div>';
|
|
10609
|
+
html += '<div class="rm-list-col" style="flex:0.5">Capacity</div>';
|
|
10610
|
+
html += '<div class="rm-list-col" style="flex:2">Email</div>';
|
|
10611
|
+
html += '<div class="rm-list-col" style="flex:0.5"></div>';
|
|
10612
|
+
html += "</div>";
|
|
10613
|
+
const buildingMap = /* @__PURE__ */ new Map();
|
|
10614
|
+
for (const b of rmBuildings) {
|
|
10615
|
+
buildingMap.set(b.buildingId, b.buildingName);
|
|
10616
|
+
}
|
|
10617
|
+
for (const r of filtered) {
|
|
10618
|
+
const buildingName = r.buildingId ? buildingMap.get(r.buildingId) || r.buildingId : "-";
|
|
10619
|
+
const resourceType = r.resourceType || "-";
|
|
10620
|
+
const capacity = r.capacity != null ? String(r.capacity) : "-";
|
|
10621
|
+
const displayName = r.generatedResourceName || r.resourceName;
|
|
10622
|
+
html += '<div class="rm-list-row">';
|
|
10623
|
+
html += `<div class="rm-list-col" style="flex:2;font-weight:600" title="${esc(displayName)}">${esc(displayName)}</div>`;
|
|
10624
|
+
html += `<div class="rm-list-col" style="flex:1.5" title="${esc(buildingName)}">${esc(buildingName)}</div>`;
|
|
10625
|
+
html += `<div class="rm-list-col" style="flex:1">${esc(resourceType)}</div>`;
|
|
10626
|
+
html += `<div class="rm-list-col" style="flex:0.5">${esc(capacity)}</div>`;
|
|
10627
|
+
html += `<div class="rm-list-col" style="flex:2" title="${esc(r.resourceEmail)}">${esc(r.resourceEmail)}</div>`;
|
|
10628
|
+
html += `<div class="rm-list-col" style="flex:0.5"><button class="rm-edit-btn" data-rm-edit-resource="${esc(r.resourceId)}">Edit</button></div>`;
|
|
10629
|
+
html += "</div>";
|
|
10630
|
+
}
|
|
10631
|
+
html += "</div>";
|
|
10632
|
+
el.innerHTML = html;
|
|
10633
|
+
el.querySelectorAll("[data-rm-edit-resource]").forEach((btn) => {
|
|
10634
|
+
btn.addEventListener("click", () => {
|
|
10635
|
+
const id = btn.dataset.rmEditResource || "";
|
|
10636
|
+
rmEditingResource = rmResources.find((r) => r.resourceId === id) || null;
|
|
10637
|
+
if (rmEditingResource) {
|
|
10638
|
+
rmView = "edit-resource";
|
|
10639
|
+
renderRmApp();
|
|
10640
|
+
}
|
|
10641
|
+
});
|
|
10642
|
+
});
|
|
10643
|
+
}
|
|
10644
|
+
function renderCreateBuildingForm() {
|
|
10645
|
+
const el = getContentEl();
|
|
10646
|
+
if (!el) return;
|
|
10647
|
+
let html = '<div class="rm-form">';
|
|
10648
|
+
html += '<label>Building Name <span style="color:var(--danger)">*</span>';
|
|
10649
|
+
html += '<input type="text" id="rmBuildingName" placeholder="e.g. Main Office" /></label>';
|
|
10650
|
+
html += "<label>Description";
|
|
10651
|
+
html += '<textarea id="rmBuildingDesc" placeholder="Optional description"></textarea></label>';
|
|
10652
|
+
html += "<label>Floor Names";
|
|
10653
|
+
html += '<input type="text" id="rmBuildingFloors" placeholder="Comma-separated, e.g. 1F, 2F, 3F" />';
|
|
10654
|
+
html += '<span style="font-size:.75em;color:var(--muted)">Comma-separated floor names or a number of floors</span></label>';
|
|
10655
|
+
html += '<button class="rm-form-btn" id="rmCreateBuildingBtn">Create Building</button>';
|
|
10656
|
+
html += '<div id="rmFormStatus" class="rm-status"></div>';
|
|
10657
|
+
html += "</div>";
|
|
10658
|
+
el.innerHTML = html;
|
|
10659
|
+
const btn = document.getElementById("rmCreateBuildingBtn");
|
|
10660
|
+
if (btn) {
|
|
10661
|
+
btn.addEventListener("click", () => void handleCreateBuilding());
|
|
10662
|
+
}
|
|
10663
|
+
}
|
|
10664
|
+
async function handleCreateBuilding() {
|
|
10665
|
+
const nameInput = document.getElementById("rmBuildingName");
|
|
10666
|
+
const descInput = document.getElementById("rmBuildingDesc");
|
|
10667
|
+
const floorsInput = document.getElementById("rmBuildingFloors");
|
|
10668
|
+
const statusEl = document.getElementById("rmFormStatus");
|
|
10669
|
+
const btn = document.getElementById("rmCreateBuildingBtn");
|
|
10670
|
+
const buildingName = nameInput?.value.trim() || "";
|
|
10671
|
+
if (!buildingName) {
|
|
10672
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Building name is required</span>';
|
|
10673
|
+
return;
|
|
10674
|
+
}
|
|
10675
|
+
if (btn) {
|
|
10676
|
+
btn.disabled = true;
|
|
10677
|
+
btn.textContent = "Creating...";
|
|
10678
|
+
}
|
|
10679
|
+
if (statusEl) statusEl.textContent = "";
|
|
10680
|
+
const body = { buildingName };
|
|
10681
|
+
const desc = descInput?.value.trim();
|
|
10682
|
+
if (desc) body.description = desc;
|
|
10683
|
+
const floorsRaw = floorsInput?.value.trim();
|
|
10684
|
+
if (floorsRaw) {
|
|
10685
|
+
const numFloors = parseInt(floorsRaw, 10);
|
|
10686
|
+
if (!isNaN(numFloors) && String(numFloors) === floorsRaw && numFloors > 0) {
|
|
10687
|
+
const names = [];
|
|
10688
|
+
for (let i = 1; i <= numFloors; i++) names.push(String(i));
|
|
10689
|
+
body.floorNames = names;
|
|
10690
|
+
} else {
|
|
10691
|
+
body.floorNames = floorsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
try {
|
|
10695
|
+
const res = await api("/api/calendar/buildings", {
|
|
10696
|
+
method: "POST",
|
|
10697
|
+
headers: { "Content-Type": "application/json" },
|
|
10698
|
+
body: JSON.stringify(body)
|
|
10699
|
+
});
|
|
10700
|
+
if (res.status === 403) {
|
|
10701
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">This requires Google Workspace admin privileges. Please ask an administrator to manage resources.</span>';
|
|
10702
|
+
if (btn) {
|
|
10703
|
+
btn.disabled = false;
|
|
10704
|
+
btn.textContent = "Create Building";
|
|
10705
|
+
}
|
|
10706
|
+
return;
|
|
10707
|
+
}
|
|
10708
|
+
const data = await res.json();
|
|
10709
|
+
if (!res.ok || !data.ok) {
|
|
10710
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">${esc(data.error || "Failed to create building")}</span>`;
|
|
10711
|
+
if (btn) {
|
|
10712
|
+
btn.disabled = false;
|
|
10713
|
+
btn.textContent = "Create Building";
|
|
10714
|
+
}
|
|
10715
|
+
return;
|
|
10716
|
+
}
|
|
10717
|
+
rmStatus = "Building created successfully!";
|
|
10718
|
+
rmStatusType = "ok";
|
|
10719
|
+
rmView = "buildings";
|
|
10720
|
+
renderRmApp();
|
|
10721
|
+
} catch (e) {
|
|
10722
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">Error: ${esc(e instanceof Error ? e.message : String(e))}</span>`;
|
|
10723
|
+
if (btn) {
|
|
10724
|
+
btn.disabled = false;
|
|
10725
|
+
btn.textContent = "Create Building";
|
|
10726
|
+
}
|
|
10727
|
+
}
|
|
10728
|
+
}
|
|
10729
|
+
function renderCreateResourceForm() {
|
|
10730
|
+
const el = getContentEl();
|
|
10731
|
+
if (!el) return;
|
|
10732
|
+
if (rmBuildings.length === 0) {
|
|
10733
|
+
el.innerHTML = '<div class="rm-loading">Loading buildings...</div>';
|
|
10734
|
+
void apiJson("/api/calendar/buildings").then((data) => {
|
|
10735
|
+
if (data.ok) rmBuildings = data.buildings || [];
|
|
10736
|
+
renderCreateResourceFormInner(el);
|
|
10737
|
+
}).catch(() => {
|
|
10738
|
+
renderCreateResourceFormInner(el);
|
|
10739
|
+
});
|
|
10740
|
+
return;
|
|
10741
|
+
}
|
|
10742
|
+
renderCreateResourceFormInner(el);
|
|
10743
|
+
}
|
|
10744
|
+
function renderCreateResourceFormInner(el) {
|
|
10745
|
+
let html = '<div class="rm-form">';
|
|
10746
|
+
html += '<label>Resource Name <span style="color:var(--danger)">*</span>';
|
|
10747
|
+
html += '<input type="text" id="rmResName" placeholder="e.g. Conference Room A" /></label>';
|
|
10748
|
+
html += "<label>Building";
|
|
10749
|
+
html += '<select id="rmResBuilding"><option value="">-- None --</option>';
|
|
10750
|
+
for (const b of rmBuildings) {
|
|
10751
|
+
html += `<option value="${esc(b.buildingId)}">${esc(b.buildingName)}</option>`;
|
|
10752
|
+
}
|
|
10753
|
+
html += "</select></label>";
|
|
10754
|
+
html += "<label>Capacity";
|
|
10755
|
+
html += '<input type="number" id="rmResCapacity" placeholder="e.g. 10" min="0" /></label>';
|
|
10756
|
+
html += "<label>Resource Type";
|
|
10757
|
+
html += '<select id="rmResType">';
|
|
10758
|
+
html += '<option value="CONFERENCE_ROOM">Conference Room</option>';
|
|
10759
|
+
html += '<option value="OTHER">Other</option>';
|
|
10760
|
+
html += "</select></label>";
|
|
10761
|
+
html += "<label>Floor Name";
|
|
10762
|
+
html += '<input type="text" id="rmResFloor" placeholder="e.g. 2F" /></label>';
|
|
10763
|
+
html += "<label>Description";
|
|
10764
|
+
html += '<textarea id="rmResDesc" placeholder="Optional description"></textarea></label>';
|
|
10765
|
+
html += '<button class="rm-form-btn" id="rmCreateResourceBtn">Create Resource</button>';
|
|
10766
|
+
html += '<div id="rmFormStatus" class="rm-status"></div>';
|
|
10767
|
+
html += "</div>";
|
|
10768
|
+
el.innerHTML = html;
|
|
10769
|
+
const btn = document.getElementById("rmCreateResourceBtn");
|
|
10770
|
+
if (btn) {
|
|
10771
|
+
btn.addEventListener("click", () => void handleCreateResource());
|
|
10772
|
+
}
|
|
10773
|
+
}
|
|
10774
|
+
async function handleCreateResource() {
|
|
10775
|
+
const nameInput = document.getElementById("rmResName");
|
|
10776
|
+
const buildingSelect = document.getElementById("rmResBuilding");
|
|
10777
|
+
const capacityInput = document.getElementById("rmResCapacity");
|
|
10778
|
+
const typeSelect = document.getElementById("rmResType");
|
|
10779
|
+
const floorInput = document.getElementById("rmResFloor");
|
|
10780
|
+
const descInput = document.getElementById("rmResDesc");
|
|
10781
|
+
const statusEl = document.getElementById("rmFormStatus");
|
|
10782
|
+
const btn = document.getElementById("rmCreateResourceBtn");
|
|
10783
|
+
const resourceName = nameInput?.value.trim() || "";
|
|
10784
|
+
if (!resourceName) {
|
|
10785
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Resource name is required</span>';
|
|
10786
|
+
return;
|
|
10787
|
+
}
|
|
10788
|
+
if (btn) {
|
|
10789
|
+
btn.disabled = true;
|
|
10790
|
+
btn.textContent = "Creating...";
|
|
10791
|
+
}
|
|
10792
|
+
if (statusEl) statusEl.textContent = "";
|
|
10793
|
+
const reqBody = { resourceName };
|
|
10794
|
+
const buildingId = buildingSelect?.value;
|
|
10795
|
+
if (buildingId) reqBody.buildingId = buildingId;
|
|
10796
|
+
const capacityRaw = capacityInput?.value.trim();
|
|
10797
|
+
if (capacityRaw) {
|
|
10798
|
+
const cap = parseInt(capacityRaw, 10);
|
|
10799
|
+
if (!isNaN(cap) && cap >= 0) reqBody.capacity = cap;
|
|
10800
|
+
}
|
|
10801
|
+
const resourceType = typeSelect?.value;
|
|
10802
|
+
if (resourceType) reqBody.resourceType = resourceType;
|
|
10803
|
+
const floorName = floorInput?.value.trim();
|
|
10804
|
+
if (floorName) reqBody.floorName = floorName;
|
|
10805
|
+
const description = descInput?.value.trim();
|
|
10806
|
+
if (description) reqBody.resourceDescription = description;
|
|
10807
|
+
try {
|
|
10808
|
+
const res = await api("/api/calendar/resources", {
|
|
10809
|
+
method: "POST",
|
|
10810
|
+
headers: { "Content-Type": "application/json" },
|
|
10811
|
+
body: JSON.stringify(reqBody)
|
|
10812
|
+
});
|
|
10813
|
+
if (res.status === 403) {
|
|
10814
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">This requires Google Workspace admin privileges. Please ask an administrator to manage resources.</span>';
|
|
10815
|
+
if (btn) {
|
|
10816
|
+
btn.disabled = false;
|
|
10817
|
+
btn.textContent = "Create Resource";
|
|
10818
|
+
}
|
|
10819
|
+
return;
|
|
10820
|
+
}
|
|
10821
|
+
const data = await res.json();
|
|
10822
|
+
if (!res.ok || !data.ok) {
|
|
10823
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">${esc(data.error || "Failed to create resource")}</span>`;
|
|
10824
|
+
if (btn) {
|
|
10825
|
+
btn.disabled = false;
|
|
10826
|
+
btn.textContent = "Create Resource";
|
|
10827
|
+
}
|
|
10828
|
+
return;
|
|
10829
|
+
}
|
|
10830
|
+
rmStatus = "Resource created successfully!";
|
|
10831
|
+
rmStatusType = "ok";
|
|
10832
|
+
rmView = "resources";
|
|
10833
|
+
renderRmApp();
|
|
10834
|
+
} catch (e) {
|
|
10835
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">Error: ${esc(e instanceof Error ? e.message : String(e))}</span>`;
|
|
10836
|
+
if (btn) {
|
|
10837
|
+
btn.disabled = false;
|
|
10838
|
+
btn.textContent = "Create Resource";
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
function renderEditBuildingForm() {
|
|
10843
|
+
const el = getContentEl();
|
|
10844
|
+
if (!el || !rmEditingBuilding) return;
|
|
10845
|
+
const b = rmEditingBuilding;
|
|
10846
|
+
let html = '<div class="rm-form">';
|
|
10847
|
+
html += "<label>Building ID";
|
|
10848
|
+
html += `<input type="text" value="${esc(b.buildingId)}" disabled style="opacity:.6;cursor:not-allowed" /></label>`;
|
|
10849
|
+
html += '<label>Building Name <span style="color:var(--danger)">*</span>';
|
|
10850
|
+
html += `<input type="text" id="rmEditBuildingName" value="${esc(b.buildingName)}" /></label>`;
|
|
10851
|
+
html += "<label>Description";
|
|
10852
|
+
html += `<textarea id="rmEditBuildingDesc">${esc(b.description || "")}</textarea></label>`;
|
|
10853
|
+
html += "<label>Floor Names";
|
|
10854
|
+
html += `<input type="text" id="rmEditBuildingFloors" value="${esc(b.floorNames ? b.floorNames.join(", ") : "")}" />`;
|
|
10855
|
+
html += '<span style="font-size:.75em;color:var(--muted)">Comma-separated floor names or a number of floors</span></label>';
|
|
10856
|
+
html += '<button class="rm-form-btn" id="rmUpdateBuildingBtn">Update Building</button>';
|
|
10857
|
+
html += '<div id="rmFormStatus" class="rm-status"></div>';
|
|
10858
|
+
html += "</div>";
|
|
10859
|
+
el.innerHTML = html;
|
|
10860
|
+
const btn = document.getElementById("rmUpdateBuildingBtn");
|
|
10861
|
+
if (btn) {
|
|
10862
|
+
btn.addEventListener("click", () => void handleUpdateBuilding());
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
async function handleUpdateBuilding() {
|
|
10866
|
+
if (!rmEditingBuilding) return;
|
|
10867
|
+
const nameInput = document.getElementById("rmEditBuildingName");
|
|
10868
|
+
const descInput = document.getElementById("rmEditBuildingDesc");
|
|
10869
|
+
const floorsInput = document.getElementById("rmEditBuildingFloors");
|
|
10870
|
+
const statusEl = document.getElementById("rmFormStatus");
|
|
10871
|
+
const btn = document.getElementById("rmUpdateBuildingBtn");
|
|
10872
|
+
const body = {};
|
|
10873
|
+
const newName = nameInput?.value.trim() || "";
|
|
10874
|
+
if (newName && newName !== rmEditingBuilding.buildingName) body.buildingName = newName;
|
|
10875
|
+
const newDesc = descInput?.value.trim() || "";
|
|
10876
|
+
if (newDesc !== (rmEditingBuilding.description || "")) body.description = newDesc;
|
|
10877
|
+
const floorsRaw = floorsInput?.value.trim() || "";
|
|
10878
|
+
const existingFloors = rmEditingBuilding.floorNames ? rmEditingBuilding.floorNames.join(", ") : "";
|
|
10879
|
+
if (floorsRaw !== existingFloors) {
|
|
10880
|
+
if (!floorsRaw) {
|
|
10881
|
+
body.floorNames = [];
|
|
10882
|
+
} else {
|
|
10883
|
+
const numFloors = parseInt(floorsRaw, 10);
|
|
10884
|
+
if (!isNaN(numFloors) && String(numFloors) === floorsRaw && numFloors > 0) {
|
|
10885
|
+
const names = [];
|
|
10886
|
+
for (let i = 1; i <= numFloors; i++) names.push(String(i));
|
|
10887
|
+
body.floorNames = names;
|
|
10888
|
+
} else {
|
|
10889
|
+
body.floorNames = floorsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10890
|
+
}
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10893
|
+
if (Object.keys(body).length === 0) {
|
|
10894
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">No changes detected</span>';
|
|
10895
|
+
return;
|
|
10896
|
+
}
|
|
10897
|
+
if (btn) {
|
|
10898
|
+
btn.disabled = true;
|
|
10899
|
+
btn.textContent = "Updating...";
|
|
10900
|
+
}
|
|
10901
|
+
if (statusEl) statusEl.textContent = "";
|
|
10902
|
+
try {
|
|
10903
|
+
const res = await api(`/api/calendar/buildings/${encodeURIComponent(rmEditingBuilding.buildingId)}`, {
|
|
10904
|
+
method: "PATCH",
|
|
10905
|
+
headers: { "Content-Type": "application/json" },
|
|
10906
|
+
body: JSON.stringify(body)
|
|
10907
|
+
});
|
|
10908
|
+
if (res.status === 403) {
|
|
10909
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Permission denied: Google Workspace admin privileges required.</span>';
|
|
10910
|
+
if (btn) {
|
|
10911
|
+
btn.disabled = false;
|
|
10912
|
+
btn.textContent = "Update Building";
|
|
10913
|
+
}
|
|
10914
|
+
return;
|
|
10915
|
+
}
|
|
10916
|
+
if (res.status === 404) {
|
|
10917
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Building not found.</span>';
|
|
10918
|
+
if (btn) {
|
|
10919
|
+
btn.disabled = false;
|
|
10920
|
+
btn.textContent = "Update Building";
|
|
10921
|
+
}
|
|
10922
|
+
return;
|
|
10923
|
+
}
|
|
10924
|
+
const data = await res.json();
|
|
10925
|
+
if (!res.ok || !data.ok) {
|
|
10926
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">${esc(data.error || "Failed to update building")}</span>`;
|
|
10927
|
+
if (btn) {
|
|
10928
|
+
btn.disabled = false;
|
|
10929
|
+
btn.textContent = "Update Building";
|
|
10930
|
+
}
|
|
10931
|
+
return;
|
|
10932
|
+
}
|
|
10933
|
+
rmStatus = "Building updated successfully!";
|
|
10934
|
+
rmStatusType = "ok";
|
|
10935
|
+
rmEditingBuilding = null;
|
|
10936
|
+
rmView = "buildings";
|
|
10937
|
+
renderRmApp();
|
|
10938
|
+
} catch (e) {
|
|
10939
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">Error: ${esc(e instanceof Error ? e.message : String(e))}</span>`;
|
|
10940
|
+
if (btn) {
|
|
10941
|
+
btn.disabled = false;
|
|
10942
|
+
btn.textContent = "Update Building";
|
|
10943
|
+
}
|
|
10944
|
+
}
|
|
10945
|
+
}
|
|
10946
|
+
function renderEditResourceForm() {
|
|
10947
|
+
const el = getContentEl();
|
|
10948
|
+
if (!el || !rmEditingResource) return;
|
|
10949
|
+
if (rmBuildings.length === 0) {
|
|
10950
|
+
el.innerHTML = '<div class="rm-loading">Loading buildings...</div>';
|
|
10951
|
+
void apiJson("/api/calendar/buildings").then((data) => {
|
|
10952
|
+
if (data.ok) rmBuildings = data.buildings || [];
|
|
10953
|
+
renderEditResourceFormInner(el);
|
|
10954
|
+
}).catch(() => {
|
|
10955
|
+
renderEditResourceFormInner(el);
|
|
10956
|
+
});
|
|
10957
|
+
return;
|
|
10958
|
+
}
|
|
10959
|
+
renderEditResourceFormInner(el);
|
|
10960
|
+
}
|
|
10961
|
+
function renderEditResourceFormInner(el) {
|
|
10962
|
+
if (!rmEditingResource) return;
|
|
10963
|
+
const r = rmEditingResource;
|
|
10964
|
+
let html = '<div class="rm-form">';
|
|
10965
|
+
html += "<label>Resource ID";
|
|
10966
|
+
html += `<input type="text" value="${esc(r.resourceId)}" disabled style="opacity:.6;cursor:not-allowed" /></label>`;
|
|
10967
|
+
html += '<label>Resource Name <span style="color:var(--danger)">*</span>';
|
|
10968
|
+
html += `<input type="text" id="rmEditResName" value="${esc(r.resourceName)}" /></label>`;
|
|
10969
|
+
html += "<label>Building";
|
|
10970
|
+
html += '<select id="rmEditResBuilding"><option value="">-- None --</option>';
|
|
10971
|
+
for (const b of rmBuildings) {
|
|
10972
|
+
const sel = r.buildingId === b.buildingId ? " selected" : "";
|
|
10973
|
+
html += `<option value="${esc(b.buildingId)}"${sel}>${esc(b.buildingName)}</option>`;
|
|
10974
|
+
}
|
|
10975
|
+
html += "</select></label>";
|
|
10976
|
+
html += "<label>Capacity";
|
|
10977
|
+
html += `<input type="number" id="rmEditResCapacity" value="${r.capacity != null ? r.capacity : ""}" min="0" /></label>`;
|
|
10978
|
+
html += "<label>Resource Type";
|
|
10979
|
+
html += '<select id="rmEditResType">';
|
|
10980
|
+
html += `<option value="CONFERENCE_ROOM"${r.resourceType === "CONFERENCE_ROOM" ? " selected" : ""}>Conference Room</option>`;
|
|
10981
|
+
html += `<option value="OTHER"${r.resourceType === "OTHER" ? " selected" : ""}>Other</option>`;
|
|
10982
|
+
html += "</select></label>";
|
|
10983
|
+
html += "<label>Floor Name";
|
|
10984
|
+
html += `<input type="text" id="rmEditResFloor" value="${esc(r.floorName || "")}" /></label>`;
|
|
10985
|
+
html += "<label>Description";
|
|
10986
|
+
html += `<textarea id="rmEditResDesc">${esc(r.resourceDescription || "")}</textarea></label>`;
|
|
10987
|
+
html += "<label>Email";
|
|
10988
|
+
html += `<input type="text" value="${esc(r.resourceEmail)}" disabled style="opacity:.6;cursor:not-allowed" /></label>`;
|
|
10989
|
+
html += '<button class="rm-form-btn" id="rmUpdateResourceBtn">Update Resource</button>';
|
|
10990
|
+
html += '<div id="rmFormStatus" class="rm-status"></div>';
|
|
10991
|
+
html += "</div>";
|
|
10992
|
+
el.innerHTML = html;
|
|
10993
|
+
const btn = document.getElementById("rmUpdateResourceBtn");
|
|
10994
|
+
if (btn) {
|
|
10995
|
+
btn.addEventListener("click", () => void handleUpdateResource());
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
async function handleUpdateResource() {
|
|
10999
|
+
if (!rmEditingResource) return;
|
|
11000
|
+
const nameInput = document.getElementById("rmEditResName");
|
|
11001
|
+
const buildingSelect = document.getElementById("rmEditResBuilding");
|
|
11002
|
+
const capacityInput = document.getElementById("rmEditResCapacity");
|
|
11003
|
+
const typeSelect = document.getElementById("rmEditResType");
|
|
11004
|
+
const floorInput = document.getElementById("rmEditResFloor");
|
|
11005
|
+
const descInput = document.getElementById("rmEditResDesc");
|
|
11006
|
+
const statusEl = document.getElementById("rmFormStatus");
|
|
11007
|
+
const btn = document.getElementById("rmUpdateResourceBtn");
|
|
11008
|
+
const body = {};
|
|
11009
|
+
const newName = nameInput?.value.trim() || "";
|
|
11010
|
+
if (newName && newName !== rmEditingResource.resourceName) body.resourceName = newName;
|
|
11011
|
+
const newBuilding = buildingSelect?.value || "";
|
|
11012
|
+
if (newBuilding !== (rmEditingResource.buildingId || "")) body.buildingId = newBuilding;
|
|
11013
|
+
const capRaw = capacityInput?.value.trim() || "";
|
|
11014
|
+
const newCap = capRaw ? parseInt(capRaw, 10) : 0;
|
|
11015
|
+
if (newCap !== (rmEditingResource.capacity || 0)) body.capacity = newCap;
|
|
11016
|
+
const newType = typeSelect?.value || "";
|
|
11017
|
+
if (newType && newType !== (rmEditingResource.resourceType || "")) body.resourceType = newType;
|
|
11018
|
+
const newFloor = floorInput?.value.trim() || "";
|
|
11019
|
+
if (newFloor !== (rmEditingResource.floorName || "")) body.floorName = newFloor;
|
|
11020
|
+
const newDesc = descInput?.value.trim() || "";
|
|
11021
|
+
if (newDesc !== (rmEditingResource.resourceDescription || "")) body.resourceDescription = newDesc;
|
|
11022
|
+
if (Object.keys(body).length === 0) {
|
|
11023
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">No changes detected</span>';
|
|
11024
|
+
return;
|
|
11025
|
+
}
|
|
11026
|
+
if (btn) {
|
|
11027
|
+
btn.disabled = true;
|
|
11028
|
+
btn.textContent = "Updating...";
|
|
11029
|
+
}
|
|
11030
|
+
if (statusEl) statusEl.textContent = "";
|
|
11031
|
+
try {
|
|
11032
|
+
const res = await api(`/api/calendar/resources/${encodeURIComponent(rmEditingResource.resourceId)}`, {
|
|
11033
|
+
method: "PATCH",
|
|
11034
|
+
headers: { "Content-Type": "application/json" },
|
|
11035
|
+
body: JSON.stringify(body)
|
|
11036
|
+
});
|
|
11037
|
+
if (res.status === 403) {
|
|
11038
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Permission denied: Google Workspace admin privileges required.</span>';
|
|
11039
|
+
if (btn) {
|
|
11040
|
+
btn.disabled = false;
|
|
11041
|
+
btn.textContent = "Update Resource";
|
|
11042
|
+
}
|
|
11043
|
+
return;
|
|
11044
|
+
}
|
|
11045
|
+
if (res.status === 404) {
|
|
11046
|
+
if (statusEl) statusEl.innerHTML = '<span class="rm-status-err">Resource not found.</span>';
|
|
11047
|
+
if (btn) {
|
|
11048
|
+
btn.disabled = false;
|
|
11049
|
+
btn.textContent = "Update Resource";
|
|
11050
|
+
}
|
|
11051
|
+
return;
|
|
11052
|
+
}
|
|
11053
|
+
const data = await res.json();
|
|
11054
|
+
if (!res.ok || !data.ok) {
|
|
11055
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">${esc(data.error || "Failed to update resource")}</span>`;
|
|
11056
|
+
if (btn) {
|
|
11057
|
+
btn.disabled = false;
|
|
11058
|
+
btn.textContent = "Update Resource";
|
|
11059
|
+
}
|
|
11060
|
+
return;
|
|
11061
|
+
}
|
|
11062
|
+
rmStatus = "Resource updated successfully!";
|
|
11063
|
+
rmStatusType = "ok";
|
|
11064
|
+
rmEditingResource = null;
|
|
11065
|
+
rmView = "resources";
|
|
11066
|
+
renderRmApp();
|
|
11067
|
+
} catch (e) {
|
|
11068
|
+
if (statusEl) statusEl.innerHTML = `<span class="rm-status-err">Error: ${esc(e instanceof Error ? e.message : String(e))}</span>`;
|
|
11069
|
+
if (btn) {
|
|
11070
|
+
btn.disabled = false;
|
|
11071
|
+
btn.textContent = "Update Resource";
|
|
11072
|
+
}
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
var rmView, rmWinId, rmBuildings, rmResources, rmFilterBuildingId, rmStatus, rmStatusType, rmEditingBuilding, rmEditingResource, STYLES5, stylesInjected;
|
|
11076
|
+
var init_resource_manager = __esm({
|
|
11077
|
+
"services/desktop/client/modules/resource-manager.ts"() {
|
|
11078
|
+
init_helpers();
|
|
11079
|
+
init_state();
|
|
11080
|
+
init_window_manager();
|
|
11081
|
+
rmView = "buildings";
|
|
11082
|
+
rmWinId = -1;
|
|
11083
|
+
rmBuildings = [];
|
|
11084
|
+
rmResources = [];
|
|
11085
|
+
rmFilterBuildingId = "";
|
|
11086
|
+
rmStatus = "";
|
|
11087
|
+
rmStatusType = "";
|
|
11088
|
+
rmEditingBuilding = null;
|
|
11089
|
+
rmEditingResource = null;
|
|
11090
|
+
STYLES5 = `
|
|
11091
|
+
.rm-app{font-family:var(--sans);color:var(--fg);display:flex;flex-direction:column;height:100%;overflow:hidden}
|
|
11092
|
+
.rm-toolbar{display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--input-border);background:var(--ctx-hover);flex-shrink:0;align-items:center;flex-wrap:wrap}
|
|
11093
|
+
.rm-toolbar button{padding:6px 14px;border:1px solid var(--input-border);background:var(--panel);color:var(--fg);border-radius:var(--radius-sm);cursor:pointer;font-size:.82em;font-weight:600;transition:all .15s;min-width:90px;text-align:center}
|
|
11094
|
+
.rm-toolbar button:hover{background:var(--ctx-hover)}
|
|
11095
|
+
.rm-toolbar button.active{background:linear-gradient(135deg,#4285f4,#34a853);color:#fff;border-color:transparent}
|
|
11096
|
+
.rm-content{flex:1;overflow-y:auto;padding:0}
|
|
11097
|
+
.rm-loading{text-align:center;padding:40px;color:var(--muted);font-size:.9em}
|
|
11098
|
+
.rm-empty{text-align:center;padding:40px;color:var(--muted);font-size:.9em}
|
|
11099
|
+
.rm-list{width:100%}
|
|
11100
|
+
.rm-list-header{display:flex;gap:8px;padding:8px 14px;font-size:.78em;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);border-bottom:1px solid var(--input-border);background:var(--ctx-hover)}
|
|
11101
|
+
.rm-list-row{display:flex;gap:8px;padding:10px 14px;border-bottom:1px solid var(--input-border);font-size:.85em;align-items:center;transition:background .1s}
|
|
11102
|
+
.rm-list-row:hover{background:var(--ctx-hover)}
|
|
11103
|
+
.rm-list-col{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
11104
|
+
.rm-form{padding:16px;display:flex;flex-direction:column;gap:10px}
|
|
11105
|
+
.rm-form label{font-size:.82em;font-weight:600;display:flex;flex-direction:column;gap:4px}
|
|
11106
|
+
.rm-form input,.rm-form textarea,.rm-form select{padding:8px 10px;border:1px solid var(--input-border);border-radius:var(--radius-sm);font-size:.88em;background:var(--input-bg);color:var(--fg);font-family:var(--sans)}
|
|
11107
|
+
.rm-form textarea{min-height:60px;resize:vertical}
|
|
11108
|
+
.rm-form-btn{padding:10px 24px;border:none;color:#fff;background:linear-gradient(135deg,#4285f4,#34a853);border-radius:var(--radius);cursor:pointer;font-weight:700;font-size:.92em;align-self:flex-start}
|
|
11109
|
+
.rm-form-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
11110
|
+
.rm-status{padding:8px 14px;font-size:.82em;text-align:center}
|
|
11111
|
+
.rm-status-ok{color:var(--success)}
|
|
11112
|
+
.rm-status-err{color:var(--danger)}
|
|
11113
|
+
.rm-add-btn{padding:6px 14px;border:1px solid var(--input-border);background:var(--panel);color:var(--accent);border-radius:var(--radius-sm);cursor:pointer;font-size:.82em;font-weight:600;margin-left:auto}
|
|
11114
|
+
.rm-add-btn:hover{background:var(--ctx-hover)}
|
|
11115
|
+
.rm-toolbar select{padding:5px 8px;border:1px solid var(--input-border);border-radius:var(--radius-sm);font-size:.82em;background:var(--input-bg);color:var(--fg)}
|
|
11116
|
+
.rm-back-btn{padding:6px 14px;border:1px solid var(--input-border);background:var(--panel);color:var(--fg);border-radius:var(--radius-sm);cursor:pointer;font-size:.82em;font-weight:600}
|
|
11117
|
+
.rm-edit-btn{padding:3px 8px;border:1px solid var(--input-border);background:var(--panel);color:var(--accent);border-radius:var(--radius-sm);cursor:pointer;font-size:.75em;white-space:nowrap}
|
|
11118
|
+
.rm-edit-btn:hover{background:var(--ctx-hover)}
|
|
11119
|
+
`;
|
|
11120
|
+
stylesInjected = false;
|
|
11121
|
+
}
|
|
11122
|
+
});
|
|
11123
|
+
|
|
10325
11124
|
// services/desktop/client/modules/start-menu.ts
|
|
10326
11125
|
function injectStartMenuDeps(deps) {
|
|
10327
11126
|
_openApp = deps.openApp;
|
|
@@ -10403,6 +11202,13 @@ function renderStartMenu() {
|
|
|
10403
11202
|
html += `<div class="app-name">Google Connect</div>`;
|
|
10404
11203
|
html += `<div class="app-desc">Manage Google account connection</div>`;
|
|
10405
11204
|
html += `</div></div>`;
|
|
11205
|
+
const rmIcon = useLucide ? lucideIcon(LUCIDE_SYSTEM_ICONS["resource-manager"] || "building", "di-lucide") : "\u{1F3E2}";
|
|
11206
|
+
html += `<div class="start-app-item" data-app-id="__resource_manager" data-cat="google">`;
|
|
11207
|
+
html += `<span class="app-icon">${rmIcon}</span>`;
|
|
11208
|
+
html += `<div class="app-info">`;
|
|
11209
|
+
html += `<div class="app-name">Resource Manager</div>`;
|
|
11210
|
+
html += `<div class="app-desc">Manage buildings and resources</div>`;
|
|
11211
|
+
html += `</div></div>`;
|
|
10406
11212
|
}
|
|
10407
11213
|
if (folder.id === "system") {
|
|
10408
11214
|
const settingsIcon = useLucide ? lucideIcon(LUCIDE_SYSTEM_ICONS["ui-design"] || "paintbrush", "di-lucide") : "\u{1F3A8}";
|
|
@@ -10483,6 +11289,8 @@ function attachStartMenuListeners() {
|
|
|
10483
11289
|
openGoogleDrive();
|
|
10484
11290
|
} else if (appId === "__google_meet") {
|
|
10485
11291
|
openGoogleMeet();
|
|
11292
|
+
} else if (appId === "__resource_manager") {
|
|
11293
|
+
openResourceManager();
|
|
10486
11294
|
} else if (appId === "__uni_editor_picker") {
|
|
10487
11295
|
void openUniEditorPicker();
|
|
10488
11296
|
} else if (appId === "__persona_viewer") {
|
|
@@ -10507,7 +11315,7 @@ function attachStartMenuListeners() {
|
|
|
10507
11315
|
e.preventDefault();
|
|
10508
11316
|
e.stopPropagation();
|
|
10509
11317
|
const appId = item.dataset.appId || "";
|
|
10510
|
-
const pinnableInternals = ["__terminal", "__file_manager", "__ui-design", "__task_manager", "__log_viewer", "__persona_viewer", "__google_connect", "__google_gmail", "__google_calendar", "__google_drive", "__google_meet"];
|
|
11318
|
+
const pinnableInternals = ["__terminal", "__file_manager", "__ui-design", "__task_manager", "__log_viewer", "__persona_viewer", "__google_connect", "__google_gmail", "__google_calendar", "__google_drive", "__google_meet", "__resource_manager"];
|
|
10511
11319
|
if (appId.startsWith("__") && !pinnableInternals.includes(appId)) return;
|
|
10512
11320
|
showAppContextMenu(e.clientX, e.clientY, appId);
|
|
10513
11321
|
});
|
|
@@ -10601,6 +11409,7 @@ var init_start_menu = __esm({
|
|
|
10601
11409
|
init_google_calendar();
|
|
10602
11410
|
init_google_drive();
|
|
10603
11411
|
init_google_meet();
|
|
11412
|
+
init_resource_manager();
|
|
10604
11413
|
_openApp = null;
|
|
10605
11414
|
_openTaskManager = null;
|
|
10606
11415
|
_openLogViewer = null;
|
|
@@ -11277,6 +12086,7 @@ function renderDesktopIcons() {
|
|
|
11277
12086
|
if (action === "google-calendar") openGoogleCalendar();
|
|
11278
12087
|
if (action === "google-drive") openGoogleDrive();
|
|
11279
12088
|
if (action === "google-meet") openGoogleMeet();
|
|
12089
|
+
if (action === "resource-manager") openResourceManager();
|
|
11280
12090
|
});
|
|
11281
12091
|
});
|
|
11282
12092
|
container.querySelectorAll(".desktop-folder-icon").forEach((folderEl) => {
|
|
@@ -11339,7 +12149,8 @@ function openCategoryFolder(folderId) {
|
|
|
11339
12149
|
{ appId: "__google_gmail", label: "Gmail", lucide: "mail", emoji: "\u{1F4E7}", desc: "Read and send email" },
|
|
11340
12150
|
{ appId: "__google_calendar", label: "Calendar", lucide: "calendar", emoji: "\u{1F4C5}", desc: "View and manage events" },
|
|
11341
12151
|
{ appId: "__google_drive", label: "Google Drive", lucide: "hard-drive", emoji: "\u{1F4BE}", desc: "Browse and search files" },
|
|
11342
|
-
{ appId: "__google_meet", label: "Google Meet", lucide: "video", emoji: "\u{1F4F9}", desc: "Create and join meetings" }
|
|
12152
|
+
{ appId: "__google_meet", label: "Google Meet", lucide: "video", emoji: "\u{1F4F9}", desc: "Create and join meetings" },
|
|
12153
|
+
{ appId: "__resource_manager", label: "Resource Manager", lucide: "building", emoji: "\u{1F3E2}", desc: "Manage buildings and resources" }
|
|
11343
12154
|
];
|
|
11344
12155
|
for (const ga of gApps) {
|
|
11345
12156
|
let gaIcon;
|
|
@@ -11434,6 +12245,10 @@ function openCategoryFolder(folderId) {
|
|
|
11434
12245
|
openGoogleMeet();
|
|
11435
12246
|
return;
|
|
11436
12247
|
}
|
|
12248
|
+
if (appId === "__resource_manager") {
|
|
12249
|
+
openResourceManager();
|
|
12250
|
+
return;
|
|
12251
|
+
}
|
|
11437
12252
|
const app = findApp(appId);
|
|
11438
12253
|
if (app) openApp(app);
|
|
11439
12254
|
});
|
|
@@ -11557,6 +12372,7 @@ var init_desktop_icons = __esm({
|
|
|
11557
12372
|
init_google_calendar();
|
|
11558
12373
|
init_google_drive();
|
|
11559
12374
|
init_google_meet();
|
|
12375
|
+
init_resource_manager();
|
|
11560
12376
|
}
|
|
11561
12377
|
});
|
|
11562
12378
|
|
|
@@ -11629,11 +12445,35 @@ function refreshOpenFolderWindows() {
|
|
|
11629
12445
|
html += `<div class="folder-app-label">${esc(app.label)}</div>`;
|
|
11630
12446
|
html += `</div>`;
|
|
11631
12447
|
}
|
|
12448
|
+
if (folderId === "google") {
|
|
12449
|
+
const gBg = CATEGORY_COLORS["google"] || "";
|
|
12450
|
+
const gApps = [
|
|
12451
|
+
{ appId: "__google_gmail", label: "Gmail", lucide: "mail", emoji: "\u{1F4E7}", desc: "Read and send email" },
|
|
12452
|
+
{ appId: "__google_calendar", label: "Calendar", lucide: "calendar", emoji: "\u{1F4C5}", desc: "View and manage events" },
|
|
12453
|
+
{ appId: "__google_drive", label: "Google Drive", lucide: "hard-drive", emoji: "\u{1F4BE}", desc: "Browse and search files" },
|
|
12454
|
+
{ appId: "__google_meet", label: "Google Meet", lucide: "video", emoji: "\u{1F4F9}", desc: "Create and join meetings" },
|
|
12455
|
+
{ appId: "__resource_manager", label: "Resource Manager", lucide: "building", emoji: "\u{1F3E2}", desc: "Manage buildings and resources" }
|
|
12456
|
+
];
|
|
12457
|
+
for (const ga of gApps) {
|
|
12458
|
+
let gaIcon;
|
|
12459
|
+
if (state.iconStyle === "modern") {
|
|
12460
|
+
gaIcon = `<div class="di-tile di-tile-sm" style="background:${gBg}">${lucideIcon(ga.lucide, "di-lucide")}</div>`;
|
|
12461
|
+
} else if (state.iconStyle === "minimal") {
|
|
12462
|
+
gaIcon = lucideIcon(ga.lucide, "di-lucide");
|
|
12463
|
+
} else {
|
|
12464
|
+
gaIcon = ga.emoji;
|
|
12465
|
+
}
|
|
12466
|
+
html += `<div class="folder-app-item" data-app-id="${ga.appId}" title="${esc(ga.desc)}">`;
|
|
12467
|
+
html += `<div class="folder-app-icon">${gaIcon}</div>`;
|
|
12468
|
+
html += `<div class="folder-app-label">${esc(ga.label)}</div>`;
|
|
12469
|
+
html += `</div>`;
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
11632
12472
|
if (folderId === "system") {
|
|
12473
|
+
const sysBg = CATEGORY_COLORS["system"] || "";
|
|
11633
12474
|
let settingsIcon;
|
|
11634
12475
|
if (state.iconStyle === "modern") {
|
|
11635
|
-
|
|
11636
|
-
settingsIcon = `<div class="di-tile di-tile-sm" style="background:${bg}">${lucideIcon(LUCIDE_SYSTEM_ICONS["ui-design"] || "paintbrush", "di-lucide")}</div>`;
|
|
12476
|
+
settingsIcon = `<div class="di-tile di-tile-sm" style="background:${sysBg}">${lucideIcon(LUCIDE_SYSTEM_ICONS["ui-design"] || "paintbrush", "di-lucide")}</div>`;
|
|
11637
12477
|
} else if (state.iconStyle === "minimal") {
|
|
11638
12478
|
settingsIcon = lucideIcon(LUCIDE_SYSTEM_ICONS["ui-design"] || "paintbrush", "di-lucide");
|
|
11639
12479
|
} else {
|
|
@@ -11643,6 +12483,30 @@ function refreshOpenFolderWindows() {
|
|
|
11643
12483
|
html += `<div class="folder-app-icon">${settingsIcon}</div>`;
|
|
11644
12484
|
html += `<div class="folder-app-label">Settings</div>`;
|
|
11645
12485
|
html += `</div>`;
|
|
12486
|
+
let tmIcon;
|
|
12487
|
+
if (state.iconStyle === "modern") {
|
|
12488
|
+
tmIcon = `<div class="di-tile di-tile-sm" style="background:${sysBg}">${lucideIcon("activity", "di-lucide")}</div>`;
|
|
12489
|
+
} else if (state.iconStyle === "minimal") {
|
|
12490
|
+
tmIcon = lucideIcon("activity", "di-lucide");
|
|
12491
|
+
} else {
|
|
12492
|
+
tmIcon = "\u{1F4CA}";
|
|
12493
|
+
}
|
|
12494
|
+
html += `<div class="folder-app-item" data-app-id="__task_manager" title="View running jobs & concurrency">`;
|
|
12495
|
+
html += `<div class="folder-app-icon">${tmIcon}</div>`;
|
|
12496
|
+
html += `<div class="folder-app-label">Concurrency Manager</div>`;
|
|
12497
|
+
html += `</div>`;
|
|
12498
|
+
let logIcon;
|
|
12499
|
+
if (state.iconStyle === "modern") {
|
|
12500
|
+
logIcon = `<div class="di-tile di-tile-sm" style="background:${sysBg}">${lucideIcon("scroll-text", "di-lucide")}</div>`;
|
|
12501
|
+
} else if (state.iconStyle === "minimal") {
|
|
12502
|
+
logIcon = lucideIcon("scroll-text", "di-lucide");
|
|
12503
|
+
} else {
|
|
12504
|
+
logIcon = "\u{1F4DC}";
|
|
12505
|
+
}
|
|
12506
|
+
html += `<div class="folder-app-item" data-app-id="__log_viewer" title="View command history & events">`;
|
|
12507
|
+
html += `<div class="folder-app-icon">${logIcon}</div>`;
|
|
12508
|
+
html += `<div class="folder-app-label">Event Log</div>`;
|
|
12509
|
+
html += `</div>`;
|
|
11646
12510
|
}
|
|
11647
12511
|
html += "</div></div>";
|
|
11648
12512
|
body.innerHTML = html;
|
|
@@ -11655,6 +12519,34 @@ function refreshOpenFolderWindows() {
|
|
|
11655
12519
|
openUiDesign();
|
|
11656
12520
|
return;
|
|
11657
12521
|
}
|
|
12522
|
+
if (appId === "__task_manager") {
|
|
12523
|
+
_openTaskManager2?.();
|
|
12524
|
+
return;
|
|
12525
|
+
}
|
|
12526
|
+
if (appId === "__log_viewer") {
|
|
12527
|
+
_openLogViewer2?.();
|
|
12528
|
+
return;
|
|
12529
|
+
}
|
|
12530
|
+
if (appId === "__google_gmail") {
|
|
12531
|
+
openGoogleGmail();
|
|
12532
|
+
return;
|
|
12533
|
+
}
|
|
12534
|
+
if (appId === "__google_calendar") {
|
|
12535
|
+
openGoogleCalendar();
|
|
12536
|
+
return;
|
|
12537
|
+
}
|
|
12538
|
+
if (appId === "__google_drive") {
|
|
12539
|
+
openGoogleDrive();
|
|
12540
|
+
return;
|
|
12541
|
+
}
|
|
12542
|
+
if (appId === "__google_meet") {
|
|
12543
|
+
openGoogleMeet();
|
|
12544
|
+
return;
|
|
12545
|
+
}
|
|
12546
|
+
if (appId === "__resource_manager") {
|
|
12547
|
+
openResourceManager();
|
|
12548
|
+
return;
|
|
12549
|
+
}
|
|
11658
12550
|
const a = findApp(appId);
|
|
11659
12551
|
if (a && _openApp2) _openApp2(a);
|
|
11660
12552
|
});
|
|
@@ -11772,6 +12664,20 @@ function showAppContextMenu(x, y, appId) {
|
|
|
11772
12664
|
if (_openTaskManager2) _openTaskManager2();
|
|
11773
12665
|
} else if (aid === "__log_viewer") {
|
|
11774
12666
|
if (_openLogViewer2) _openLogViewer2();
|
|
12667
|
+
} else if (aid === "__google_gmail") {
|
|
12668
|
+
openGoogleGmail();
|
|
12669
|
+
} else if (aid === "__google_calendar") {
|
|
12670
|
+
openGoogleCalendar();
|
|
12671
|
+
} else if (aid === "__google_drive") {
|
|
12672
|
+
openGoogleDrive();
|
|
12673
|
+
} else if (aid === "__google_meet") {
|
|
12674
|
+
openGoogleMeet();
|
|
12675
|
+
} else if (aid === "__resource_manager") {
|
|
12676
|
+
openResourceManager();
|
|
12677
|
+
} else if (aid === "__google_connect") {
|
|
12678
|
+
openGoogleConnect();
|
|
12679
|
+
} else if (aid === "__persona_viewer") {
|
|
12680
|
+
void openPersonaViewer();
|
|
11775
12681
|
} else {
|
|
11776
12682
|
const a = findApp(aid);
|
|
11777
12683
|
if (a && _openApp2) _openApp2(a);
|
|
@@ -12107,6 +13013,13 @@ var init_context_menu = __esm({
|
|
|
12107
13013
|
init_taskbar();
|
|
12108
13014
|
init_desktop_icons();
|
|
12109
13015
|
init_ui_design();
|
|
13016
|
+
init_google_gmail();
|
|
13017
|
+
init_google_calendar();
|
|
13018
|
+
init_google_drive();
|
|
13019
|
+
init_google_meet();
|
|
13020
|
+
init_resource_manager();
|
|
13021
|
+
init_google_connect();
|
|
13022
|
+
init_persona_viewer();
|
|
12110
13023
|
init_state();
|
|
12111
13024
|
_openApp2 = null;
|
|
12112
13025
|
_toggleTheme = null;
|