2021-08-17 07:59:01 +00:00
import { apiHttp } from "../services/http" ;
import Alpine from "alpinejs" ;
import clone from "just-clone" ;
2021-08-29 02:59:56 +00:00
const UPLOAD _DELAY = 1500 ;
const START _SLIDE = "#quick-picks-slide" ;
2021-07-22 21:12:27 +00:00
2021-08-05 06:22:19 +00:00
function initInteractiveElements ( ) {
2021-07-23 02:24:39 +00:00
let configurationToggle = document . getElementById ( "configuration-toggle" ) ;
2021-08-05 06:22:19 +00:00
let configurationElem = document . getElementById ( "configuration" ) ;
2021-07-23 02:24:39 +00:00
configurationElem . addEventListener ( "show.bs.collapse" , function ( ) {
configurationToggle . classList . add ( "active" ) ;
2021-07-22 21:12:27 +00:00
} ) ;
2021-07-23 02:24:39 +00:00
configurationElem . addEventListener ( "hidden.bs.collapse" , function ( ) {
configurationToggle . classList . remove ( "active" ) ;
} ) ;
2021-08-05 06:22:19 +00:00
}
2021-07-23 02:24:39 +00:00
2021-08-17 07:59:01 +00:00
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 ,
2021-08-29 02:59:56 +00:00
timeoutInProgress : false ,
2021-08-17 07:59:01 +00:00
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 ] ) ;
}
} ,
2021-08-29 02:59:56 +00:00
validateAllNumericalInputs ( ) {
2021-08-17 07:59:01 +00:00
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 ) ;
}
2021-08-29 02:59:56 +00:00
this . timeoutInProgress = true ;
2021-08-17 07:59:01 +00:00
this . searchOutlineChangeTimeout = setTimeout ( ( ) => {
this . uploadAll ( ) ;
2021-08-29 02:59:56 +00:00
this . timeoutInProgress = false ;
} , UPLOAD _DELAY ) ;
2021-08-17 07:59:01 +00:00
} ,
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 ;
2021-08-29 02:59:56 +00:00
this . validateAllNumericalInputs ( ) ;
2021-08-17 07:59:01 +00:00
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 ( ) ;
2021-07-22 21:12:27 +00:00
}
2021-08-05 06:22:19 +00:00
function initSlides ( ) {
document . querySelectorAll ( "#content-pages > .selectors > .nav-item > button" ) . forEach ( tabElem => {
tabElem . addEventListener ( "click" , ( ) => {
const destUrl = new URL ( tabElem . getAttribute ( "data-bs-target" ) , window . location . href ) ;
if ( location . href === destUrl . href ) return ;
history . pushState ( { } , document . title , destUrl ) ;
} ) ;
} ) ;
const goTo = ( ) => {
const match = location . href . match ( "(#[\\w-]+)" ) ;
2021-08-29 02:59:56 +00:00
const idAnchor = match && match [ 1 ] ? match [ 1 ] : START _SLIDE ;
2021-08-05 06:22:19 +00:00
document . querySelector ( "#content-pages > .selectors > .nav-item > .active" ) ? . classList . remove ( "active" ) ;
document . querySelector ( "#content-pages > .multipage-slides > .active.show" ) ? . classList . remove ( "active" , "show" ) ;
document . querySelector ( ` #content-pages > .selectors > .nav-item > [data-bs-target=" ${ idAnchor } "] ` ) . classList . add ( "active" ) ;
document . querySelector ( ` #content-pages > .multipage-slides > ${ idAnchor } ` ) . classList . add ( "active" , "show" ) ;
} ;
window . addEventListener ( "popstate" , goTo ) ;
goTo ( ) ;
require ( "bootstrap/js/dist/tab.js" ) ;
document . querySelector ( "#content-pages" ) . classList . remove ( "invisible" ) ;
2021-07-22 21:12:27 +00:00
}
2021-08-05 06:22:19 +00:00
async function main ( ) {
initInteractiveElements ( ) ;
initSlides ( ) ;
2021-08-17 07:59:01 +00:00
initConfigData ( ) ;
2021-07-22 21:12:27 +00:00
}
main ( ) ;