import { createSignal, createEffect, createState, createMemo, createComputed } from "solid-js";
import { req } from '../websocket';
import { pgRange2String, fmt, create_deferred, c } from '../util';
import { state as glo } from '../store';
export default function(props) {
let original = {
search: '',
species: [],
types: [],
strains: [],
effects: [],
producers: {},
min_thc2tc: '0',
max_thc2tc: '1',
sort: 'unsorted',
inventory: null
};
let america;
if(history.state.america) {
if(typeof history.state.america.producers === 'object') {
america = JSON.parse(JSON.stringify(history.state.america));
for(const key in america.producers) {
if(america.producers[key].checked === undefined) {
america.producers[key].checked = false;
}
if(america.producers[key].brands === undefined) {
america.producers[key].brands = [];
}
}
} else {
america = history.state.america;
}
} else {
let s = new URLSearchParams(location.search);
america = {producers: {}};
if(s.has('search')) {
america['search'] = s.get('search');
}
if(s.has('min_thc2tc')) {
america['min_thc2tc'] = s.get('min_thc2tc');
}
if(s.has('max_thc2tc')) {
america['max_thc2tc'] = s.get('max_thc2tc');
}
if(s.has('sort')) {
america['sort'] = s.get('sort');
}
if(s.has('inventory')) {
america['inventory'] = s.get('inventory') === 'true';
}
if(s.has('species')) {
america['species'] = s.get('species').split('_');
}
if(s.has('types')) {
america['types'] = s.get('types').split('_');
}
if(s.has('strains')) {
america['strains'] = s.get('strains').split('_');
}
if(s.has('effects')) {
america['effects'] = s.get('effects').split('_');
}
for(const [key, value] of s) {
if(key.startsWith('p_')) {
let actual = key.split('_');
if(actual[1]) {
america.producers[actual[1]] = {checked: false, brands: value.split('_')}
}
}
}
if(s.has('producers')) {
let p = s.get('producers').split('_');
for(let i = 0; i < p.length; ++i) {
if(america.producers[p[i]] === undefined) {
america.producers[p[i]] = {checked: true, brands: []}
} else {
america.producers[p[i]].checked = true;
}
}
}
}
const [products, setProducts] = createSignal(history.state.products);
const [state, setState] = createState(Object.assign(JSON.parse(JSON.stringify(original)), america));
const [f, setF] = createSignal(history.state.f);
createComputed(() => {
let o = {};
for(let i = 0; i < f()?.idk.length; ++i) {
o[f().idk[i].producer] = {checked: false, brands: []};
if(state.producers[f().idk[i].producer] === undefined) {
setState('producers', f().idk[i].producer, o[f().idk[i].producer]);
}
}
original.producers = o;
});
let poop = create_deferred();
if(!products()) {
lol();
}
req('productsq').then(x => {setTimeout(poop.resolve); setF(x);});
createEffect(() => history.replaceState({...history.state, america: diffState()}, ""));
createEffect(() => history.replaceState({...history.state, products: products()}, ""));
createEffect(() => history.replaceState({...history.state, f: f()}, ""));
const inhalation = createMemo(() => state.types.includes('flower') || state.types.includes('preroll') || state.types.includes('cartridge') || state.types.includes('disposable'));
const sublingual = createMemo(() => state.types.includes('oil') || state.types.includes('spray'));
const ingestion = createMemo(() => state.types.includes('capsule') || state.types.includes('tea') || state.types.includes('gummy') || state.types.includes('mint') || state.types.includes('chocolate') || state.types.includes('baked'));
const canSortByConcentration = createMemo(() => {
let r = 0;
if(inhalation()) {++r}
if(sublingual()) {++r}
if(ingestion()) {++r}
return r === 1;
});
const canSortByPrice_g = createMemo(() => inhalation() && !sublingual() && !ingestion());
createEffect(() => {
if(!canSortByConcentration() && (state.sort.startsWith('THC') || state.sort.startsWith('CBD')) || !canSortByPrice_g() && state.sort.startsWith('price_g')) {
setState('sort', 'unsorted');
}
})
function inventoryRadio(e) {
let v = null;
switch(e.target.value) {
case 't':
v = true;
break;
case 'f':
v = false;
break;
}
setState('inventory', v)
}
async function lol() {
let conditions = [];
for(let key in state.producers) {
let a = new Set(state.producers[key].brands);
if(a.size) {
await poop;
}
let b;
//checking every brand under a producer does not imply checking the producer
//the following conditional is ok because productsq is fresh
if(state.producers[key].checked || a.size === (b = new Set(f().idk.find(e => e.producer === key || (e.producer === null && key === 'null')).brands)).size && state.producers[key].brands.every(value => b.has(value))) {
conditions.push({producer: key === 'null' ? null : key});
} else if(a.size) {
conditions.push({producer: key === 'null' ? null : key, brands: state.producers[key].brands});
}
}
let special = {};
if(state.min_thc2tc !== '0' || state.max_thc2tc !== '1') {
special.thc2tc = [state.min_thc2tc, state.max_thc2tc];
}
if(state.sort !== 'unsorted') {
let [what, how] = state.sort.split(/\s+/);
special[what] = how === 'ascending' || how === 'a-z';
}
req('products', {...special,
stores: [props.sid],
species: state.species.length ? state.species : undefined,
types: state.types.length ? state.types : undefined,
strain_ids: state.strains.length ? state.strains : undefined,
producers: conditions.length ? conditions : undefined,
search: state.search.length ? state.search : undefined,
in_stock: state.inventory === null ? undefined : state.inventory
}).then(setProducts);
}
function diffState() {
let diff = {};
for(const key in state) {
if(Array.isArray(state[key])) {
if(state[key].length) {
diff[key] = [...state[key]];
}
} else if(typeof state[key] === 'object') {
//
} else if(state[key] !== original[key]) {
diff[key] = state[key];
}
}
let producers = {};
let yes = false;
for(const p in state.producers) {
let yesyes = false;
let obj = {};
if(state.producers[p].checked) {
obj.checked = true;
yesyes = true;
}
if(state.producers[p].brands.length) {
obj.brands = [...state.producers[p].brands];
yesyes = true;
}
if(yesyes) {
producers[p] = obj;
yes = true;
}
}
if(yes) {
diff.producers = producers;
}
return diff;
}
return <>
<Show when={glo.user?.god && props.sid === undefined}>
<a onClick={c} href={'edit'}>⚙</a>
</Show>
<Show when={f()}>
<form onsubmit={e => {
e.preventDefault();
lol()
let diff = JSON.parse(JSON.stringify(history.state.america));
if(diff.producers) {
let a = [];
for(const key in diff.producers) {
if(diff.producers[key].checked) {
a.push(key);
}
if(diff.producers[key].brands?.length) {
diff['p_' + key] = diff.producers[key].brands.join('_');
}
}
diff.producers = a.join('_');
if(!diff.producers) {
delete diff.producers;
}
}
for(const key in diff) {
if(Array.isArray(diff[key])) {
diff[key] = diff[key].join('_');
}
}
diff = new URLSearchParams(diff).toString();
if(diff.length) {
diff = '?' + diff;
}
history.replaceState(history.state, "", location.href.split('?')[0] + diff);
}}>
<label>
search
<input type="search" value={state.search} onInput={e => setState('search', e.target.value)} />
</label><br />
<label>
sort by
<select value={state.sort} onChange={e => setState('sort', e.target.value)}>
<option>unsorted</option>
<option>price ascending</option>
<option>price descending</option>
<option disabled={!canSortByPrice_g()} title={canSortByPrice_g() ? '' : 'sorting by price / g only makes sense for inhalation products'} value="price_g ascending">minimum price / g ascending</option>
<option disabled={!canSortByPrice_g()} title={canSortByPrice_g() ? '' : 'sorting by price / g only makes sense for inhalation products'} value="price_g descending">minimum price / g descending</option>
<option disabled={!canSortByConcentration()} title={canSortByConcentration() ? '' : 'sorting by concentration only makes sense for products within a consumption method'}>THC ascending</option>
<option disabled={!canSortByConcentration()} title={canSortByConcentration() ? '' : 'sorting by concentration only makes sense for products within a consumption method'}>THC descending</option>
<option disabled={!canSortByConcentration()} title={canSortByConcentration() ? '' : 'sorting by concentration only makes sense for products within a consumption method'}>CBD ascending</option>
<option disabled={!canSortByConcentration()} title={canSortByConcentration() ? '' : 'sorting by concentration only makes sense for products within a consumption method'}>CBD descending</option>
<option>producer a-z</option>
<option>producer z-a</option>
<option>brand a-z</option>
<option>brand z-a</option>
<option>name a-z</option>
<option>name z-a</option>
</select>
</label><br />
<fieldset>
<legend>inventory</legend>
<label><input type="radio" name="inventory" checked={state.inventory === null} onChange={inventoryRadio} value="e" />everything</label><br />
<label><input type="radio" name="inventory" checked={state.inventory} onChange={inventoryRadio} value="t" />in stock</label><br />
<label><input type="radio" name="inventory" checked={state.inventory === false} onChange={inventoryRadio} value="f" />out of stock</label><br />
</fieldset>
<fieldset>
<legend>species</legend>
<For each={f().species}>{s => <>
<label><input type="checkbox" checked={state.species.includes(s)} onChange={e => {
if(e.target.checked) {
setState('species', [s, ...state.species]);
} else {
setState('species', t => t.filter(sp => sp !== s));
}
}} />{s}</label><br />
</>}</For>
</fieldset>
<fieldset>
<legend>formats</legend>
<For each={f().product_types}>{s => <>
<Switch>
<Match when={s === 'flower'}>Inhalation<br/></Match>
<Match when={s === 'oil'}>Subligual<br/></Match>
<Match when={s === 'capsule'}>Edible<br/></Match>
</Switch>
<label><input type="checkbox" checked={state.types.includes(s)} onChange={e => {
if(e.target.checked) {
setState('types', [s, ...state.types]);
} else {
setState('types', t => t.filter(typ => typ !== s));
}
}} />{s}</label><br />
</>}</For>
</fieldset>
<fieldset>
<legend>strains</legend>
<div style="height: 300px; overflow-y: auto">
<For each={f().strains}>{s => <>
<label><input type="checkbox" checked={state.strains.includes(s.id)} onChange={e => {
if(e.target.checked) {
setState('strains', [s.id, ...state.strains]);
} else {
setState('strains', t => t.filter(typ => typ !== s.id));
}
}} />{s.name}</label><br />
</>}</For>
</div>
</fieldset>
<fieldset>
<legend>producers and brands</legend>
<ul style="padding: 0; margin: 0; max-height: 300px; overflow-y: auto"><For each={f().idk}>{s => <li>
<label><input type="checkbox" checked={state.producers[s.producer].checked} onChange={e => setState('producers', s.producer, 'checked', e.target.checked)} />{s.producer === null ? 'unknown' : s.producer}</label>
<Show when={s.brands.length > 1 || s.brands[0] !== ''}><ul>
<For each={s.brands}>{ss => <li>
<label><input disabled={state.producers[s.producer].checked} type="checkbox" checked={state.producers[s.producer].brands.includes(ss)} onChange={e => {
if(e.target.checked) {
setState('producers', s.producer, 'brands', [ss, ...state.producers[s.producer].brands]);
} else {
setState('producers', s.producer, 'brands', b => b.filter(br => br !== ss));
}
}} />{ss === '' ? s.producer : ss}</label>
</li>}</For>
</ul></Show>
</li>}</For></ul>
</fieldset>
<fieldset>
<legend>THC : total cannabinoids</legend>
<label>min <input type="number" step="any" min="0" max={state.max_thc2tc} value={state.min_thc2tc} onInput={e => setState('min_thc2tc', e.target.value)} /></label>
<label>max <input type="number" step="any" min={state.min_thc2tc} max="1" value={state.max_thc2tc} onInput={e => setState('max_thc2tc', e.target.value)} /></label>
</fieldset>
<br />
<button>search</button>
</form>
</Show>
<div>{products() && products().length}</div>
<div class="grid">
<For each={products()}>{p => <div class="c card">
<div class="front">
<a onClick={c} href={p.product_id + (p.name ? '-' : '') + p.name.replace(/\s+/g, '-') + (p.variant_ids === null ? '' : '?recommended_variants=' + p.variant_ids.join('_'))}>
{fmt(p.producer, p.brand, p.name)}
</a>
<img style="width: 100%; height: auto;" width={p.x} height={p.y} src={p.image} loading="lazy" />
{p.type}<br />
{p.species}<br />
{p.strain}<br />
THC: {pgRange2String(p.thc, p.type, p.min_portions, p.max_portions)}<br />
CBD: {pgRange2String(p.cbd, p.type, p.min_portions, p.max_portions)}<br />
starting from ${p.min_price}{p.min_price_g ? `, ${p.min_price_g} / g` : ''}<br />
<Switch fallback={p.terpenes.slice(1, -1).split(',').join(', ')}>
<Match when={p.terpenes === null}>unknown terpenes</Match>
<Match when={p.terpenes.length === 0}>terpenes removed</Match>
</Switch><br />
</div>
<div class="back">
shit can go here
eventually need https://github.com/ryansolid/solid/tree/master/packages/solid-rx if we want click instead of hover
</div>
</div>}</For>
</div>
</>
}