Skip to content

Commit a8f3029

Browse files
committed
fix: Use selector on move page for tracking code
1 parent 269664b commit a8f3029

File tree

4 files changed

+218
-52
lines changed

4 files changed

+218
-52
lines changed

website/app-templates/smarty/js/moves/geokret_move.inventory.tpl.js

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,112 +5,130 @@ $("#nrSearchButton").bind("click", function() {
55
$("#nr").parsley().validate();
66
});
77

8+
//
9+
function toggleAlertMaxGKReached(el) {
10+
var count = $("#geokretyListTable [name*=\"geokretySelected\"]:checkbox:checked").length;
11+
if (count >= {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
12+
$("#geokretySelectAll").prop("checked", false);
13+
showAlertMaxGKReached(true);
14+
} else {
15+
showAlertMaxGKReached(false);
16+
}
17+
$("#modalInventorySelectButton span.badge").text(count);
18+
}
19+
820
// change header checkbox
9-
$("body").on('change', "#geokretySelectAll", function() {
21+
$("body").on("change", "#geokretySelectAll", function() {
1022
var checked = $(this).is(":checked");
1123
if (checked) {
12-
var inventory = $("#geokretyListTable tr:not(.hidden) [name*='geokretySelected']").slice(0, {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS});
24+
var inventory = $("#geokretyListTable tr:not(.hidden) [name*=\"geokretySelected\"]").slice(0, {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS});
1325
inventory.each(function() {
14-
if ($("#geokretyListTable [name*='geokretySelected']:checkbox:checked").length >= {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
15-
$(this).prop("#geokretyListTable", false);
26+
if ($("#geokretyListTable [name*=\"geokretySelected\"]:checkbox:checked").length >= {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
27+
$(this).prop("checked", false);
1628
return false;
1729
}
1830
this.checked = checked;
1931
})
2032
} else {
21-
$("#geokretyListTable tr [name*='geokretySelected']").each(function() {
33+
$("#geokretyListTable tr [name*=\"geokretySelected\"]").each(function() {
2234
$(this).prop("checked", false);
2335
})
2436
}
2537
toggleAlertMaxGKReached();
2638

2739
// change one GeoKret checkbox
28-
}).on('change', "#geokretyListTable [name*='geokretySelected']", function() {
29-
var inventory = $("#geokretyListTable [name*='geokretySelected']:checkbox:checked");
40+
}).on("change", "#geokretyListTable [name*=\"geokretySelected\"]", function() {
41+
var inventory = $("#geokretyListTable [name*=\"geokretySelected\"]:checkbox:checked");
3042
if (inventory.length > {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
3143
$("#geokretySelectAll").prop("checked", false);
3244
$(this).prop("checked", false);
3345
}
3446
toggleAlertMaxGKReached();
3547

3648
// GK specific choose button
37-
}).on('click', "#geokretyListTable [name*='btnChooseGK']", function(event) {
49+
}).on("click", "#geokretyListTable [name*=\"btnChooseGK\"]", function(event) {
3850
event.preventDefault();
39-
var inventory = $("#geokretyListTable [name*='geokretySelected']:checkbox:checked");
51+
var inventory = $("#geokretyListTable [name*=\"geokretySelected\"]:checkbox:checked");
4052
if (inventory.length <= {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
41-
var trackingCode = $(this).data('trackingcode');
42-
fillTrackingCode(trackingCode);
53+
var trackingCode = $(this).data("trackingcode");
54+
fillTrackingCode([trackingCode]);
4355
}
4456

4557
// Select button from the modal
46-
}).on('click', "#modalInventorySelectButton", function(event) {
58+
}).on("click", "#modalInventorySelectButton", function() {
4759
var trackingCodes = Array();
48-
$("#geokretyListTable [name*='geokretySelected']:checkbox:checked").each(function() {
49-
trackingCodes.push($(this).data('trackingcode'));
60+
$("#geokretyListTable [name*=\"geokretySelected\"]:checkbox:checked").each(function() {
61+
trackingCodes.push($(this).data("trackingcode"));
5062
})
5163
fillTrackingCode(trackingCodes);
5264

53-
// Status icon binding on GeoKrety result list (action: remove from selection)
54-
}).on('click', "#nrResult [name*='gkStatusIcon']", function(event) {
55-
event.preventDefault();
56-
var trackingCode = $(this).data('trackingcode');
57-
removeTrackingCode(trackingCode);
58-
5965
// Filter the inventory
60-
}).on('keyup', "#gk-filter", function(event) {
66+
}).on("keyup", "#gk-filter", function() {
6167
var filter = $("#gk-filter").val().toLowerCase();
6268
$("#geokretyListTable .gk-name").each(function() {
63-
var title = $(this).attr('title');
69+
var title = $(this).attr("title");
6470
var gkid = $(this).text();
65-
var tr = $(this).closest('tr');
71+
var tr = $(this).closest("tr");
6672
if (~latinize(title.toLowerCase()).indexOf(latinize(filter)) || ~latinize(gkid.toLowerCase()).indexOf(latinize(filter))) {
67-
tr.removeClass('hidden');
73+
tr.removeClass("hidden");
6874
} else {
69-
tr.addClass('hidden');
75+
tr.addClass("hidden");
7076
}
7177
});
72-
// var inventory = $("#geokretyListTable [name*='geokretySelected']");
78+
// var inventory = $("#geokretyListTable [name*=\"geokretySelected\"]");
7379
if ($("#geokretyListTable tr").length > {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
7480
$("#geokretySelectAll").prop("checked", false);
7581
}
7682
})
7783

7884
// Check by already added GK
7985
function checkAlreadyAddedTrackingCode() {
80-
var codes = $("#nr").val().split(',');
81-
codes.forEach(function(item) {
82-
$("#geokretyListTable input[data-trackingcode='"+item+"']").each(function() {
83-
$(this).prop("checked", true);
84-
})
85-
});
86+
const ts = getTS();
87+
let codes = [];
88+
89+
if (ts) {
90+
const v = ts.getValue(); // array for multi, string for single
91+
codes = Array.isArray(v) ? v : (v ? [v] : []);
92+
} else {
93+
codes = ($("#nr").val() || "").split(",").map(norm).filter(Boolean);
94+
}
95+
96+
codes.forEach(function (item) {
97+
$("#geokretyListTable input[data-trackingcode]").filter(function () {
98+
return String(this.getAttribute("data-trackingcode")).toUpperCase() === item;
99+
}).prop("checked", true);
100+
});
101+
102+
if (typeof toggleAlertMaxGKReached === "function") {
86103
toggleAlertMaxGKReached();
104+
}
87105
}
88106

89107
// Add a tracking code to the list
90108
function fillTrackingCode(trackingCodes) {
91-
var codes = [$("#nr").val(), trackingCodes].filter(function (el) { return el }).join(',');
92-
$("#nr").parsley().reset();
93-
$("#nr").val(codes).trigger("focusout");
94-
$('#modal').modal('hide');
95-
}
109+
const ts = getTS();
96110

97-
// Remove a tracking code from the list
98-
function removeTrackingCode(trackingCode) {
99-
var foundTrackingCodes = $("#nr").val().split(',').filter(function (el) { return el.toUpperCase() != trackingCode.toUpperCase() }).join(',');
111+
const raw = trackingCodes || [];
112+
const codes = raw.map(norm).filter(Boolean);
113+
114+
if (ts) {
115+
codes.forEach((code) => {
116+
if (!ts.options[code]) {
117+
ts.addOption({ tracking_code: code, label: code });
118+
}
119+
ts.addItem(code, true);
120+
});
100121
$("#nr").parsley().reset();
101-
$("#nr").val(foundTrackingCodes).trigger("focusout");
102-
}
122+
$("#modal").modal("hide");
123+
return;
124+
}
103125

104-
//
105-
function toggleAlertMaxGKReached(el) {
106-
var count = $("#geokretyListTable [name*='geokretySelected']:checkbox:checked").length;
107-
if (count >= {GK_CHECK_TRACKING_CODE_MAX_PROCESSED_ITEMS}) {
108-
$("#geokretySelectAll").prop("checked", false);
109-
showAlertMaxGKReached(true);
110-
} else {
111-
showAlertMaxGKReached(false);
112-
}
113-
$("#modalInventorySelectButton span.badge").text(count);
126+
// Fallback: plain input as CSV
127+
const current = $("#nr").val();
128+
const merged = [current, codes.join(",")].filter(Boolean).join(",");
129+
$("#nr").parsley().reset();
130+
$("#nr").val(merged).trigger("focusout");
131+
$("#modal").modal("hide");
114132
}
115133

116134
// Show/hide warning message
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
function getTS() {
2+
const el = document.getElementById("nr");
3+
return el && el.tomselect ? el.tomselect : null;
4+
}
5+
6+
const el = document.getElementById("nr");
7+
const isLoggedIn = JSON.parse(
8+
"{if $f3->get("SESSION.CURRENT_USER")}true{else}false{/if}"
9+
);
10+
11+
// const MIN_LEN = parseInt(
12+
// el.dataset.minlen || "{GK_SITE_TRACKING_CODE_MIN_LENGTH}",
13+
// 10
14+
// );
15+
// const MAX_LEN = parseInt(
16+
// el.dataset.maxlen || "{GK_SITE_TRACKING_CODE_MAX_LENGTH}",
17+
// 10
18+
// );
19+
20+
// helper: trim & normalize
21+
const norm = (s) => (s || "").toString().trim().toUpperCase();
22+
23+
const plugins = {
24+
remove_button: {
25+
title: "{t}Remove selected item{/t}",
26+
},
27+
clear_button: {
28+
title: "{t}Remove all selected items{/t}",
29+
},
30+
};
31+
32+
const options = {
33+
maxItems: isLoggedIn ? parseInt("{GK_CHECK_WAYPOINT_NAME_COUNT}", 10) : 1,
34+
persist: false,
35+
addPrecedence: true,
36+
hidePlaceholder: true,
37+
closeAfterSelect: true,
38+
createOnBlur: true,
39+
duplicates: false,
40+
preload: isLoggedIn,
41+
valueField: "tracking_code",
42+
labelField: "label",
43+
// searchField: ["name", "tracking_code", "gkid"],
44+
searchField: [],
45+
plugins: plugins,
46+
47+
// TODO this is bugged, it prevents adding new items
48+
// // min/max
49+
// createFilter: function (input) {
50+
// const v = norm(input);
51+
// if (v.length < MIN_LEN || v.length > MAX_LEN) return false;
52+
// },
53+
54+
create(input) {
55+
const v = norm(input);
56+
return {
57+
tracking_code: v,
58+
label: v,
59+
name: "",
60+
gkid: "",
61+
};
62+
},
63+
render: {
64+
option_create(data, escape) {
65+
return (
66+
"<div class=\"create\">" +
67+
"{t}Add tracking code{/t}: <strong>" +
68+
escape(data.input).toUpperCase() +
69+
"</strong>" +
70+
"</div>"
71+
);
72+
},
73+
},
74+
};
75+
76+
// add dropdown header plugin only if logged in
77+
if (isLoggedIn) {
78+
options.plugins.dropdown_header = {
79+
title: "{t}Your inventory{/t}",
80+
};
81+
82+
options.load = function (query, callback) {
83+
fetch("{"geokrety_move_select_from_inventory"|alias}", {
84+
headers: {
85+
Accept: "application/json",
86+
},
87+
})
88+
.then((r) => r.json())
89+
.then((data) => callback(data))
90+
.catch(() => callback());
91+
};
92+
}
93+
94+
if (!isLoggedIn) {
95+
options.render = options.render || {};
96+
options.render.no_results = function () {
97+
return "";
98+
};
99+
}
100+
101+
const ts = new TomSelect($("#nr"), options);
102+
103+
// TODO temporarily disabled
104+
// // Guardrail for paste/enter
105+
// ts.on("item_add", (value) => {
106+
// const v = norm(value);
107+
// console.log(value);
108+
// if (v.length < MIN_LEN || v.length > MAX_LEN) {
109+
// ts.removeItem(value);
110+
// ts.removeOption(value);
111+
// }
112+
// });

website/app-templates/smarty/pages/geokret_move.tpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
{\GeoKrety\Assets::instance()->addJs(GK_CDN_BOOTSTRAP_DATETIMEPICKER_JS) && ''}
1111
{\GeoKrety\Assets::instance()->addJs(GK_CDN_LIBRARIES_INSCRYBMDE_JS_URL) && ''}
1212
{\GeoKrety\Assets::instance()->addJs(GK_CDN_BOOTSTRAP_3_TYPEAHEAD_JS) && ''}
13+
{\GeoKrety\Assets::instance()->addCss(GK_CDN_TOM_SELECT_CSS) && ''}
14+
{\GeoKrety\Assets::instance()->addJs(GK_CDN_TOM_SELECT_JS) && ''}
1315
{if !$f3->get('SESSION.CURRENT_USER')}
1416
{include file='macros/recaptcha.tpl'}
1517
{/if}
@@ -21,7 +23,10 @@
2123

2224
{block name=javascript}
2325
{include file="js/moves/geokret_move.tpl.js"}
26+
{if $f3->get('SESSION.CURRENT_USER')}
2427
{include file="js/dialogs/dialog_geokret_move_select_from_inventory.tpl.js"}
28+
{/if}
29+
{include file="js/moves/tomselect-inventory.tpl.js"}
2530
{if GK_DEVEL}
2631
{* used by Tests-qa in Robot Framework *}
2732
$("#mapid").data({ map: map });

website/app/GeoKrety/Controller/Pages/GeokretSelectFromInventory.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88
class GeokretSelectFromInventory extends Base {
99
public function get($f3) {
10+
$accept = strtolower((string) ($f3->get('HEADERS.Accept') ?? ''));
11+
$wants_html = (strpos($accept, 'application/json') !== false) || ($f3->get('GET.format') === 'json');
12+
if ($wants_html) {
13+
return $this->get_json($f3);
14+
}
15+
1016
// Load owned GeoKrety
1117
$geokret = new Geokret();
1218
$filter = ['holder = ? AND parked = ?', $f3->get('SESSION.CURRENT_USER'), null];
@@ -16,4 +22,29 @@ public function get($f3) {
1622

1723
Smarty::render('extends:base_modal.tpl|dialog/geokret_move_select_from_inventory.tpl');
1824
}
25+
26+
public function get_json($f3) {
27+
$geokret = new Geokret();
28+
$filter = ['holder = ? AND parked = ?', $f3->get('SESSION.CURRENT_USER'), null];
29+
$option = ['order' => 'name ASC'];
30+
$rows = $geokret->find($filter, $option);
31+
32+
// Build a clean array for TomSelect (and any client)
33+
$out = [];
34+
foreach ($rows ?: [] as $gk) {
35+
$out[] = [
36+
'gkid' => (string) $gk->gkid,
37+
'name' => $gk->name,
38+
'tracking_code' => $gk->tracking_code,
39+
'type' => $gk->type->getTypeId(),
40+
'collectible' => (bool) $gk->isCollectible(),
41+
'parked' => (bool) $gk->isParked(),
42+
'label' => sprintf('%s - %s (%s)', $gk->gkid, $gk->name, $gk->tracking_code),
43+
];
44+
}
45+
46+
// Always return a JSON array (no numeric object keys)
47+
header('Content-Type: application/json; charset=utf-8');
48+
echo json_encode(array_values($out), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
49+
}
1950
}

0 commit comments

Comments
 (0)