Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/pages/tasking/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ ResizeDividers(map)
esri.basemapLayer('Topographic', { ignoreDeprecationWarning: true }).addTo(map);

function VM() {
const self = this;

const self = this;

self.mapVM = new MapVM(map, self);

Expand Down Expand Up @@ -1506,6 +1506,48 @@ function VM() {
});
};



self.openBlankOpsLogModal = function() {
const modalEl = document.getElementById('CreateOpsLogModal');
const modal = new bootstrap.Modal(modalEl);

const vm = self.CreateOpsLogModalVM;

vm.modalInstance = modal;

vm.openForNewJobLog({}); // pass empty object for blank log
modal.show();

modalEl.addEventListener('shown.bs.modal', function () {
document.getElementById('OpsLogTextInput')?.focus();
}, { once: true });
};

self.openBlankRadioOpsLogModal = function() {
const modalEl = document.getElementById('CreateOpsLogModal');
const modal = new bootstrap.Modal(modalEl);

const vm = self.CreateOpsLogModalVM;

vm.modalInstance = modal;

vm.openForRadioLog({}); // blank radio log with Radio tag auto-selected
modal.show();

modalEl.addEventListener('shown.bs.modal', function () {
document.getElementById('OpsLogTextInput')?.focus();
}, { once: true });

installModalHotkeys({
modalEl,
onSave: () => vm.submit?.(),
onClose: () => modal.hide(),
allowInInputs: true
});
};


self.openTrackableAssetsModal = function (data, event) {
// If called from a dropdown menu, close the dropdown
if (event && event.target) {
Expand Down
143 changes: 115 additions & 28 deletions src/pages/tasking/viewmodels/OpsLogModalVM.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export function CreateOpsLogModalVM(parentVM) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;

// Store parentVM reference for use in computeds
self.parentVM = parentVM;

self.entityId = ko.observable(null);
self.jobId = ko.observable(null);
self.eventId = ko.observable(null);
Expand Down Expand Up @@ -60,11 +63,42 @@ export function CreateOpsLogModalVM(parentVM) {
self.actionRequired(anyActionTagSelected);
});

// When opening, prefill job/team fields if present
// (removed unused origOpenForNewJobLog)
self.openForNewJobLog = async (job) => {
self.resetFields();
self.jobId(job.id() || "");
if (job && typeof job.id === 'function') {
self.jobId(job.id() || "");
self.jobIdInput(job.identifier ? job.identifier() : '');
self.headerLabel(`New Ops Log for ${job.identifier() || ""}`);
} else {
self.jobId("");
self.jobIdInput("");
self.headerLabel("New Ops Log");
}
self.initTags();
}

// Open for a new radio log — auto-selects the "Radio" contact method tag
self.openForRadioLog = async (job) => {
self.resetFields();
if (job && typeof job.id === 'function') {
self.jobId(job.id() || "");
self.jobIdInput(job.identifier ? job.identifier() : '');
self.headerLabel(`New Radio Log for ${job.identifier() || ""}`);
} else {
self.jobId("");
self.jobIdInput("");
self.headerLabel("New Radio Log");
}
self.initTags();
self.headerLabel(`New Ops Log for ${job.identifier() || ""}`);

// Auto-select the "Radio" tag in Contact Methods (group 3)
self.uiTags().forEach(t => {
if (t.tagGroupId() === 3 && t.name && t.name().toLowerCase().includes('radio')) {
t.selected(true);
}
});
}

self.toPayload = function () {
Expand Down Expand Up @@ -112,50 +146,57 @@ export function CreateOpsLogModalVM(parentVM) {
return true;
}

self.submitting = ko.observable(false);

self.submit = function () {
if (!validate()) {
return;
}
self.submitting(true);
const payload = self.toPayload();

parentVM.createOpsLogEntry(payload, function (result) {

if (!result) {
console.error("Ops Log submit failed");
return;
}

if (self.modalInstance) {
self.modalInstance.hide();
}
self.submitting(false);
});
};

self.resetFields = function () {
self.entityId(null);
self.jobId(null);
self.eventId(null);
self.talkgroupId(null);
self.talkgroupRequestId(null);

self.subject("");
self.text("");
self.position(null);
self.personFromId(null);
self.personTold(null);

self.important(false);
self.restricted(false);
self.actionRequired(false);
self.actionReminder(null);

self.timeLogged(null);

self.uiTags([]);
self.headerLabel("");

self.showError(false);
self.errorMessage("");
self.entityId(null);
self.jobId(null);
self.eventId(null);
self.talkgroupId(null);
self.talkgroupRequestId(null);

self.subject("");
self.text("");
self.position(null);
self.personFromId(null);
self.personTold(null);

self.important(false);
self.restricted(false);
self.actionRequired(false);
self.actionReminder(null);

self.timeLogged(null);

self.uiTags([]);
self.headerLabel("");

self.showError(false);
self.errorMessage("");

self.jobIdInput("");
self.jobIdInputHasFocus(false);
self.teamInput("");
self.teamInputHasFocus(false);
};

function uiTag(tag) {
Expand All @@ -181,4 +222,50 @@ export function CreateOpsLogModalVM(parentVM) {
});
}

// Job and Team Autocomplete
self.jobIdInput = ko.observable("");
self.jobIdInputHasFocus = ko.observable(false);
self.jobIdSuggestions = ko.pureComputed(() => {
const input = self.jobIdInput().toLowerCase();
if (!input) return [];
return ko.unwrap(self.parentVM.jobs)
.filter(j => (j.identifier && j.identifier().toLowerCase().includes(input)) || (j.id && String(j.id()).includes(input)))
.slice(0, 10)
.map(j => {
const id = j.identifier ? j.identifier() : '';
const type = j.typeName ? j.typeName() : '';
const addr = j.address && j.address.prettyAddress ? j.address.prettyAddress() : '';
const detail = [type, addr].filter(Boolean).join(' — ');
return { id: id, detail: detail, label: id, job: j };
});
});
self.showJobDropdown = ko.pureComputed(() => self.jobIdInputHasFocus() && self.jobIdSuggestions().length > 0);
self.pickJobSuggestion = function(suggestion, event) {
if (event && event.preventDefault) event.preventDefault();
if (suggestion && suggestion.job) {
self.jobIdInput(suggestion.label);
self.jobId(suggestion.job.id());
}
self.jobIdInputHasFocus(false);
};

self.teamInput = ko.observable("");
self.teamInputHasFocus = ko.observable(false);
self.teamSuggestions = ko.pureComputed(() => {
const input = self.teamInput().toLowerCase();
if (!input) return [];
return ko.unwrap(self.parentVM.trackableAssets)
.filter(a => (a.name && a.name().toLowerCase().includes(input)))
.slice(0, 10)
.map(a => ({ label: a.name ? a.name() : '', asset: a }));
});
self.showTeamDropdown = ko.pureComputed(() => self.teamInputHasFocus() && self.teamSuggestions().length > 0);
self.pickTeamSuggestion = function(suggestion, event) {
if (event && event.preventDefault) event.preventDefault();
if (suggestion && suggestion.asset) {
self.teamInput(suggestion.label);
self.subject(`${suggestion.label} - ${self.subject()}`);
}
self.teamInputHasFocus(false);
};
}
9 changes: 6 additions & 3 deletions src/pages/tasking/viewmodels/RadioLogModalVM.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export function CreateRadioLogModalVM(parentVM) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;

// Submitting state for UI feedback
self.submitting = ko.observable(false);


self.entityId = ko.observable(null);
self.jobId = ko.observable(null);
self.eventId = ko.observable(null);
Expand Down Expand Up @@ -150,18 +154,17 @@ export function CreateRadioLogModalVM(parentVM) {
if (!validate()) {
return;
}
self.submitting(true);
const payload = self.toPayload();

parentVM.createOpsLogEntry(payload, function (result) {

if (!result) {
console.error("Ops Log submit failed");
return;
}

if (self.modalInstance) {
self.modalInstance.hide();
}
self.submitting(false);
});
};

Expand Down
Loading
Loading