Basic search outline config UI implemented.
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
import { apiHttp } from "../services/http";
|
||||
import Alpine from "alpinejs";
|
||||
import clone from "just-clone";
|
||||
|
||||
const uploadDelay = 500;
|
||||
const startingSlide = "#quick-picks-slide";
|
||||
|
||||
function initInteractiveElements() {
|
||||
@@ -11,14 +16,251 @@ function initInteractiveElements() {
|
||||
});
|
||||
}
|
||||
|
||||
function initConfigVisuals() {
|
||||
const minRatingDisplay = document.querySelector("#configuration #min-rating-display");
|
||||
const minRatingSlider = document.querySelector("#configuration #min-rating");
|
||||
const updateDisplay = function () {
|
||||
minRatingDisplay.innerHTML = `Minimum rating: ${minRatingSlider.value}%`;
|
||||
};
|
||||
minRatingSlider.addEventListener("input", updateDisplay);
|
||||
updateDisplay();
|
||||
function initConfigData() {
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.data("search", () => ({
|
||||
loggedIn: false,
|
||||
query: "",
|
||||
searchOutline: {
|
||||
ready: false,
|
||||
filters: {
|
||||
"currency": 0,
|
||||
"minRating": 80,
|
||||
"keepUnrated": true,
|
||||
"enableUpperPrice": false,
|
||||
"upperPrice": 0,
|
||||
"lowerPrice": 0,
|
||||
"minPurchases": 0,
|
||||
"keepUnknownPurchaseCount": true,
|
||||
"minReviews": 0,
|
||||
"keepUnknownReviewCount": true,
|
||||
"enableMaxShipping": false,
|
||||
"maxShippingFee": 0,
|
||||
"keepUnknownShipping": true
|
||||
},
|
||||
shopToggles: {},
|
||||
},
|
||||
deletingSearchOutline: false,
|
||||
creatingSearchOutline: false,
|
||||
updatingLastUsed: false,
|
||||
changingName: false,
|
||||
updatingFilters: false,
|
||||
updatingDisabledShops: false,
|
||||
searchOutlines: [],
|
||||
selectedSearchOutline: 0,
|
||||
serverSearchOutlineName: null,
|
||||
resultsQuery: null,
|
||||
results: {
|
||||
bestPrice: null,
|
||||
},
|
||||
searchOutlineChangeTimeout: null,
|
||||
hasResults() {
|
||||
return this.resultsQuery !== null;
|
||||
},
|
||||
submitSearch() {
|
||||
// TODO: implement search Web API call.
|
||||
this.resultsQuery = this.query;
|
||||
console.log("Search requested.");
|
||||
},
|
||||
SearchOutlineNameChange() {
|
||||
if (this.validateSearchOutlineName()) {
|
||||
this.changeSearchOutlineName(this.serverSearchOutlineName, this.searchOutlines[this.selectedSearchOutline]);
|
||||
}
|
||||
},
|
||||
validateNumericalInputs() {
|
||||
if (!this.searchOutline.filters.lowerPrice) this.searchOutline.filters.lowerPrice = 0;
|
||||
if (!this.searchOutline.filters.upperPrice) this.searchOutline.filters.upperPrice = 0;
|
||||
if (!this.searchOutline.filters.maxShippingFee) this.searchOutline.filters.maxShippingFee = 0;
|
||||
if (!this.searchOutline.filters.minPurchases) this.searchOutline.filters.minPurchases = 0;
|
||||
if (!this.searchOutline.filters.minRating) this.searchOutline.filters.minRating = 0;
|
||||
if (!this.searchOutline.filters.minReviews) this.searchOutline.filters.minReviews = 0;
|
||||
},
|
||||
validateSearchOutlineName() {
|
||||
let clonedSearchOutlines = this.searchOutlines.slice();
|
||||
clonedSearchOutlines.splice(this.selectedSearchOutline, 1);
|
||||
if (this.searchOutlines[this.selectedSearchOutline].length < 1 || clonedSearchOutlines.includes(this.searchOutlines[this.selectedSearchOutline])) {
|
||||
this.searchOutlines[this.selectedSearchOutline] = this.serverSearchOutlineName;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
searchOutlineChanged() {
|
||||
if (!this.loggedIn) return;
|
||||
if (this.searchOutlineChangeTimeout != null) {
|
||||
clearTimeout(this.searchOutlineChangeTimeout);
|
||||
}
|
||||
this.searchOutlineChangeTimeout = setTimeout(() => {
|
||||
this.uploadAll();
|
||||
}, uploadDelay);
|
||||
},
|
||||
uploadAll() {
|
||||
let name = this.searchOutlines[this.selectedSearchOutline];
|
||||
this.uploadFilters(name, clone(this.searchOutline.filters));
|
||||
this.uploadDisabledShops(name, clone(this.searchOutline.shopToggles));
|
||||
},
|
||||
async uploadFilters(name, filters) {
|
||||
if (!this.loggedIn) return;
|
||||
this.updatingFilters = true;
|
||||
let uploadFilterResponse = await apiHttp.put(`SearchOutline/${name}/Filters`, filters);
|
||||
this.updatingFilters = false;
|
||||
if (uploadFilterResponse.status != 204) {
|
||||
throw `Error while attempting to upload filters. Response code ${uploadFilterResponse.status} (expected 204).`;
|
||||
}
|
||||
},
|
||||
async uploadDisabledShops(name, disabledShops) {
|
||||
if (!this.loggedIn) return;
|
||||
this.updatingDisabledShops = true;
|
||||
let disabledShopSet = [];
|
||||
Object.keys(disabledShops).forEach(key => {
|
||||
if (!this.searchOutline.shopToggles[key]) {
|
||||
disabledShopSet.push(key);
|
||||
}
|
||||
});
|
||||
let uploadDisabledShopsResponse = await apiHttp.put(`SearchOutline/${name}/DisabledShops`, disabledShopSet);
|
||||
this.updatingDisabledShops = false;
|
||||
if (uploadDisabledShopsResponse.status != 204) {
|
||||
throw `Error while attempting to upload disabled shops. Response code ${uploadDisabledShopsResponse.status} (expected 204).`;
|
||||
}
|
||||
},
|
||||
async createSearchOutline(name) {
|
||||
if (!this.loggedIn) return;
|
||||
this.creatingSearchOutline = true;
|
||||
let createRequest = await apiHttp.post("SearchOutline/" + name);
|
||||
this.creatingSearchOutline = false;
|
||||
if (createRequest.status != 204) {
|
||||
throw `Could not create profile. Response code ${createRequest.status} (expected 204).`;
|
||||
}
|
||||
this.searchOutlines.push(name);
|
||||
},
|
||||
async updateLastUsed(name) {
|
||||
if (!this.loggedIn) return;
|
||||
this.updatingLastUsed = true;
|
||||
let lastUsedRequest = await apiHttp.put("SearchOutline/" + name + "/LastUsed");
|
||||
this.updatingLastUsed = false;
|
||||
if (lastUsedRequest.status != 204) {
|
||||
throw `Could not update last used search outline. Received status code ${lastUsedRequest.status} (expected 204).`;
|
||||
}
|
||||
},
|
||||
async changeSearchOutlineName(old, current) {
|
||||
if (!this.loggedIn) return;
|
||||
this.changingName = true;
|
||||
let nameChangeRequest = await apiHttp.put(`SearchOutline/${old}/Name/${current}`);
|
||||
this.changingName = false;
|
||||
if (nameChangeRequest.status != 204) {
|
||||
throw `Could not update name on server side. Received ${nameChangeRequest.status} (expected 204).`;
|
||||
}
|
||||
this.serverSearchOutlineName = current;
|
||||
},
|
||||
async loadSearchOutline(name) {
|
||||
this.searchOutline.ready = false;
|
||||
if (!this.loggedIn) {
|
||||
let defaultNameRequest = await apiHttp.get("SearchOutline/DefaultName");
|
||||
if (defaultNameRequest.status != 200) {
|
||||
console.error(`Could not load default search outline name. Got response code ${defaultNameRequest.status} (Expected 200).`);
|
||||
return;
|
||||
}
|
||||
this.searchOutlines.push(defaultNameRequest.data);
|
||||
let disabledShopsResponse = await apiHttp.get("SearchOutline/DefaultDisabledShops");
|
||||
let availableShops = (await apiHttp.get("Search/AvailableShops")).data;
|
||||
if (disabledShopsResponse.status == 200) {
|
||||
availableShops.forEach(shopName => {
|
||||
this.searchOutline.shopToggles[shopName] = !disabledShopsResponse.data.includes(shopName);
|
||||
});
|
||||
} else {
|
||||
console.error(`Could not fetch default disabled shops for "${name}". Status code: ${disabledShopsResponse.status} (Expected 200)`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (this.searchOutlineChangeTimeout != null) {
|
||||
clearTimeout(this.searchOutlineChangeTimeout);
|
||||
this.uploadAll();
|
||||
}
|
||||
let filterResponse = await apiHttp.get("SearchOutline/" + name + "/Filters");
|
||||
if (filterResponse.status == 200) {
|
||||
this.searchOutline.filters = filterResponse.data;
|
||||
} else {
|
||||
console.error(`Could not fetch filter for "${name}". Status code: ${filterResponse.status} (Expected 200)`);
|
||||
return;
|
||||
}
|
||||
|
||||
let disabledShopsResponse = await apiHttp.get("SearchOutline/" + name + "/DisabledShops");
|
||||
let availableShops = (await apiHttp.get("Search/AvailableShops")).data;
|
||||
if (disabledShopsResponse.status == 200) {
|
||||
availableShops.forEach(shopName => {
|
||||
this.searchOutline.shopToggles[shopName] = !disabledShopsResponse.data.includes(shopName);
|
||||
});
|
||||
} else {
|
||||
console.error(`Could not fetch disabled shops for "${name}". Status code: ${disabledShopsResponse.status} (Expected 200)`);
|
||||
return;
|
||||
}
|
||||
await this.updateLastUsed(name);
|
||||
}
|
||||
this.serverSearchOutlineName = name;
|
||||
this.searchOutline.ready = true;
|
||||
},
|
||||
createSearchOutlineWithGeneratedName() {
|
||||
apiHttp.get("SearchOutline/DefaultName/").then((response) => {
|
||||
if (response.status != 200) {
|
||||
throw `Could not get a default name. Response code ${response.status} (expected 200).`;
|
||||
}
|
||||
this.createSearchOutline(response.data);
|
||||
});
|
||||
},
|
||||
deleteSearchOutline(name) {
|
||||
this.deletingSearchOutline = true;
|
||||
let beforeDelete = this.searchOutlines[this.selectedSearchOutline];
|
||||
if (this.selectedSearchOutline == this.searchOutlines.length - 1 && this.searchOutlines.indexOf(name) <= this.selectedSearchOutline) {
|
||||
this.selectedSearchOutline -= 1;
|
||||
}
|
||||
apiHttp.delete(`SearchOutline/${name}`).then((results) => {
|
||||
this.deletingSearchOutline = false;
|
||||
if (results.status != 204) {
|
||||
throw `Unable to delete ${name}. Received status ${results.status} (expected 204)`;
|
||||
}
|
||||
this.searchOutlines.splice(this.searchOutlines.indexOf(name), 1);
|
||||
if (beforeDelete !== this.searchOutlines[this.selectedSearchOutline]) {
|
||||
this.loadSearchOutline(this.searchOutlines[this.selectedSearchOutline]).then(() => {
|
||||
this.deletingSearchOutline = false;
|
||||
});
|
||||
} else {
|
||||
this.deletingSearchOutline = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
async init() {
|
||||
// TODO: Test logged in outline sequence and logged out outline sequence.
|
||||
this.loggedIn = (await apiHttp.get("User/LoggedIn")).data;
|
||||
if (this.loggedIn) {
|
||||
this.searchOutlines = (await apiHttp.get("SearchOutline/Names")).data;
|
||||
if (this.searchOutlines.length == 0) {
|
||||
let name = (await apiHttp.get("SearchOutline/DefaultName")).data;
|
||||
try {
|
||||
await this.createSearchOutline(name);
|
||||
await this.updateLastUsed(name);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let lastUsedRequest = await apiHttp.get("SearchOutline/LastUsed");
|
||||
if (lastUsedRequest.status == 200) {
|
||||
this.selectedSearchOutline = this.searchOutlines.indexOf(lastUsedRequest.data);
|
||||
} else {
|
||||
console.warn(`Could not load name of last used search outline. Got response code ${lastUsedRequest.status} (Expected 200). Using "${this.searchOutlines[0]}".`);
|
||||
let putlastUsedRequest = await apiHttp.put("SearchOutline/" + this.searchOutlines[this.selectedSearchOutline] + "/LastUsed");
|
||||
if (putlastUsedRequest.status != 204) {
|
||||
console.error(`Could not update last used search outline. Received status code ${putlastUsedRequest.status} (Expected 204).`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.loadSearchOutline(this.searchOutlines[this.selectedSearchOutline]);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
Alpine.start();
|
||||
}
|
||||
|
||||
function initSlides() {
|
||||
@@ -46,8 +288,8 @@ function initSlides() {
|
||||
|
||||
async function main() {
|
||||
initInteractiveElements();
|
||||
initConfigVisuals();
|
||||
initSlides();
|
||||
initConfigData();
|
||||
}
|
||||
|
||||
main();
|
||||
|
Reference in New Issue
Block a user