Compare commits
13 Commits
1c2df7733d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e31b239af | |||
| cb2b7827f1 | |||
| 6949aa7780 | |||
| 2b9e76e475 | |||
| b4ce9557f3 | |||
| 3ab623fea1 | |||
| 1f2e307f05 | |||
| b73957e7df | |||
| 2c60ec4afc | |||
| 8c45a533b0 | |||
| 3154b1e014 | |||
| 85efbd84d1 | |||
| 3b39345fa6 |
@@ -1,5 +1,13 @@
|
||||
### Changelog
|
||||
|
||||
#### [v1.1.1](https://git.w33ble.com/w33ble/strain-tools/compare/v1.1.0...v1.1.1) (2 August 2019)
|
||||
- fix: only pre-render the main page [`cb2b782`](https://git.w33ble.com/w33ble/strain-tools/commit/cb2b7827f11bb9db029d7d9fd7fbe9a6a27930f2)
|
||||
|
||||
#### [v1.1.0](https://git.w33ble.com/w33ble/strain-tools/compare/v1.0.2...v1.1.0) (18 September 2018)
|
||||
- feat: basic routing and fav nav [`3b39345`](https://git.w33ble.com/w33ble/strain-tools/commit/3b39345fa66ac4748a400e9e80e032ec9ae9697b)
|
||||
- feat: show favorite strain cards [`85efbd8`](https://git.w33ble.com/w33ble/strain-tools/commit/85efbd84d1704c55a3eefae7fa2d36e7fd7f3cc2)
|
||||
- fix: weird bug in StrainList [`2c60ec4`](https://git.w33ble.com/w33ble/strain-tools/commit/2c60ec4afc16f81fd6b610443091d26d278d3f81)
|
||||
|
||||
#### [v1.0.2](https://git.w33ble.com/w33ble/strain-tools/compare/v1.0.1...v1.0.2) (13 September 2018)
|
||||
|
||||
#### [v1.0.1](https://git.w33ble.com/w33ble/strain-tools/compare/v1.0.0...v1.0.1) (13 September 2018)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "strain-tools",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.1",
|
||||
"description": "strain tools",
|
||||
"main": "index",
|
||||
"module": "index.mjs",
|
||||
@@ -33,10 +33,10 @@
|
||||
"commitLimit": false
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,mjs}": [
|
||||
"*.{js,mjs,vue}": [
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.{js,mjs,json,css}": [
|
||||
"*.{js,mjs,vue,json,css}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scraper",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.1",
|
||||
"private": true,
|
||||
"description": "scrapes strain info, stores for later reference",
|
||||
"main": "index",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "search-site",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.1",
|
||||
"private": true,
|
||||
"main": "index",
|
||||
"module": "index.mjs",
|
||||
@@ -18,7 +18,6 @@
|
||||
"dependencies": {
|
||||
"esm": "^3.0.82",
|
||||
"lunr": "^2.3.3",
|
||||
"mitt": "^1.1.3",
|
||||
"vue": "^2.5.17",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-template-compiler": "^2.5.17"
|
||||
|
||||
@@ -4,5 +4,9 @@ const path = require('path');
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, 'src/index.mjs'),
|
||||
outDir: 'dist',
|
||||
plugins: [require('@poi/plugin-vue-static')()],
|
||||
plugins: [
|
||||
require('@poi/plugin-vue-static')({
|
||||
routes: ['/'],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
14
packages/search-site/src/components/AppNav.vue
Normal file
14
packages/search-site/src/components/AppNav.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div id="app-nav" class="tabs">
|
||||
<ul>
|
||||
<li class="is-active"><a>Search</a></li>
|
||||
<li><a>Favorites</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppNav',
|
||||
};
|
||||
</script>
|
||||
@@ -1,89 +1,81 @@
|
||||
<template>
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Strain Search
|
||||
</h1>
|
||||
<!-- Name Search Input -->
|
||||
<div class="field">
|
||||
<label class="label">Search By Name</label>
|
||||
<div class="control">
|
||||
<input ref="name" class="input" type="text" placeholder="Text input">
|
||||
<p v-if="error.length" class="help is-danger">{{error}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Name Search Input -->
|
||||
<div class="field">
|
||||
<label class="label">Search By Name</label>
|
||||
<div class="control">
|
||||
<input ref="name" class="input" type="text" placeholder="Text input">
|
||||
<p v-if="error.length" class="help is-danger">{{error}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Multi-Selects -->
|
||||
<div class="columns filters">
|
||||
<!-- Multi-Selects -->
|
||||
<div class="columns filters">
|
||||
<div class="column is-half">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Desired Effects</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="effects" multiple size="6">
|
||||
<option v-for="effect in effects" :key="effect" :value="effect">{{effect | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Medical Use</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="uses" multiple size="6">
|
||||
<option v-for="use in uses" :key="use" :value="use">{{use | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Desired Effects</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="effects" multiple size="6">
|
||||
<option v-for="effect in effects" :key="effect" :value="effect">{{effect | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-half">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Condition</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="conditions" multiple size="6">
|
||||
<option v-for="condition in conditions" :key="condition" :value="condition">{{condition | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Flavor</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="flavors" multiple size="6">
|
||||
<option v-for="flavor in flavors" :key="flavor" :value="flavor">{{flavor | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Medical Use</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="uses" multiple size="6">
|
||||
<option v-for="use in uses" :key="use" :value="use">{{use | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Submits -->
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-link">Submit</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="button" class="button is-text" @click.prevent="resetForm">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="column is-half">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Condition</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="conditions" multiple size="6">
|
||||
<option v-for="condition in conditions" :key="condition" :value="condition">{{condition | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-half">
|
||||
<div class="field">
|
||||
<label class="label">Flavor</label>
|
||||
<div class="select is-multiple">
|
||||
<select ref="flavors" multiple size="6">
|
||||
<option v-for="flavor in flavors" :key="flavor" :value="flavor">{{flavor | capitalize}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Submits -->
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-link">Submit</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="button" class="button is-text" @click.prevent="resetForm">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import emitter from '../lib/emitter.mjs';
|
||||
|
||||
const getMultiValues = node => Array.from(node.selectedOptions).map(o => o.value);
|
||||
|
||||
export default {
|
||||
@@ -92,17 +84,10 @@ export default {
|
||||
uses: Array,
|
||||
conditions: Array,
|
||||
flavors: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
emitter.on('error', this.setError);
|
||||
},
|
||||
beforeDestroy() {
|
||||
emitter.off('error', this.setError);
|
||||
error: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSubmit() {
|
||||
@@ -114,15 +99,11 @@ export default {
|
||||
flavors: getMultiValues(this.$refs.flavors),
|
||||
};
|
||||
|
||||
this.error = '';
|
||||
emitter.emit('search', requirements);
|
||||
this.$emit('search', requirements);
|
||||
},
|
||||
resetForm() {
|
||||
this.$el.reset();
|
||||
},
|
||||
setError(msg) {
|
||||
this.error = msg;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
<script>
|
||||
import store from '../lib/store.mjs';
|
||||
import emitter from '../lib/emitter.mjs';
|
||||
import TagList from './TagList.vue';
|
||||
|
||||
export default {
|
||||
@@ -50,8 +49,22 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
toggleFavorite() {
|
||||
this.favorite = !this.favorite;
|
||||
emitter.emit('favorite', { id: this.strain.id, isFav: this.favorite });
|
||||
const isFav = !this.favorite;
|
||||
const { id } = this.strain;
|
||||
const favs = store.get('favorites') || [];
|
||||
const idx = favs.indexOf(id);
|
||||
|
||||
// update component state
|
||||
this.favorite = isFav;
|
||||
|
||||
// update the store
|
||||
if (idx >= 0 && !isFav) {
|
||||
// remove previously favorited strain
|
||||
store.set('favorites', favs.filter(f => f !== id));
|
||||
} else if (idx === -1 && isFav) {
|
||||
// add new favorited strain
|
||||
store.set('favorites', favs.concat(id));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<div>
|
||||
<h3 v-if="strains.length === 0" class="title is-3">No Matching Strains :(</h3>
|
||||
<h3 v-if="strains.length > 0" class="title is-3">Found {{strains.length}} Strains</h3>
|
||||
<h3 v-show="strains.length === 0" class="title is-3">No Matching Strains :(</h3>
|
||||
<h3 v-show="strains.length > 0" class="title is-3">Found {{strains.length}} Strains</h3>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
|
||||
12
packages/search-site/src/filters.mjs
Normal file
12
packages/search-site/src/filters.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
Vue.filter('capitalize', value => {
|
||||
if (!value) return '';
|
||||
const v = value.toString();
|
||||
return v.charAt(0).toUpperCase() + v.slice(1);
|
||||
});
|
||||
|
||||
Vue.filter('round', (value, digits = 2) => {
|
||||
const v = parseFloat(value, 10);
|
||||
return Math.round(v * (10 * digits)) / (10 * digits);
|
||||
});
|
||||
@@ -1,26 +1,44 @@
|
||||
import Vue from 'vue';
|
||||
import Router from 'vue-router';
|
||||
import './filters.mjs';
|
||||
import strainData from './plugins/strainData.mjs';
|
||||
import lunr from './plugins/lunr.mjs';
|
||||
|
||||
Vue.use(strainData);
|
||||
|
||||
Vue.use(
|
||||
lunr(function lunrSetup() {
|
||||
// lunr search index setup
|
||||
this.ref('id');
|
||||
this.field('name');
|
||||
this.field('effects');
|
||||
this.field('uses');
|
||||
this.field('conditions');
|
||||
this.field('flavors');
|
||||
Vue.prototype.$strainData.strains.forEach(doc => this.add(doc));
|
||||
})
|
||||
);
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
Vue.filter('capitalize', value => {
|
||||
if (!value) return '';
|
||||
const v = value.toString();
|
||||
return v.charAt(0).toUpperCase() + v.slice(1);
|
||||
});
|
||||
|
||||
Vue.filter('round', (value, digits = 2) => {
|
||||
const v = parseFloat(value, 10);
|
||||
return Math.round(v * (10 * digits)) / (10 * digits);
|
||||
});
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import(/* webpackChunkName: "homeapp" */ './pages/Home.vue'),
|
||||
component: () => import(/* webpackChunkName: "Home" */ './pages/Home.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'search',
|
||||
component: () => import(/* webpackChunkName: "Search" */ './pages/Search.vue'),
|
||||
},
|
||||
{
|
||||
path: 'favorites',
|
||||
name: 'favorites',
|
||||
component: () => import(/* webpackChunkName: "Favorites" */ './pages/Favorites.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import mitt from 'mitt';
|
||||
|
||||
const emitter = mitt();
|
||||
|
||||
export default emitter;
|
||||
43
packages/search-site/src/pages/Favorites.vue
Normal file
43
packages/search-site/src/pages/Favorites.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div id="favorites-page">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Favorites
|
||||
</h1>
|
||||
|
||||
<h3 class="title is-5">
|
||||
Strains you have saved
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<StrainList :strains="favoriteStrains" no-save-control />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '../lib/store.mjs';
|
||||
import StrainList from '../components/StrainList.vue';
|
||||
|
||||
export default {
|
||||
name: 'FavoritesPage',
|
||||
components: {
|
||||
StrainList,
|
||||
},
|
||||
data() {
|
||||
const favorites = store.get('favorites') || [];
|
||||
|
||||
return {
|
||||
favorites,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
favoriteStrains() {
|
||||
return this.$strainData.strains.filter(strain => this.favorites.indexOf(strain.id) !== -1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,27 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="section">
|
||||
<SearchForm :effects="effects" :uses="uses" :conditions="conditions" :flavors="flavors" />
|
||||
</section>
|
||||
<section class="section">
|
||||
<StrainList :strains="matches" />
|
||||
</section>
|
||||
<div id="home-page">
|
||||
<div id="app-nav" class="tabs is-centered">
|
||||
<ul>
|
||||
<li :class="{'is-active': activeTab === 'search'}" @click="setActive('search')">
|
||||
<router-link :to="{name: 'search'}">Search</router-link>
|
||||
</li>
|
||||
<li :class="{'is-active': activeTab === 'favorites'}" @click="setActive('favorites')">
|
||||
<router-link :to="{name: 'favorites'}">Favorites</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import lunr from 'lunr';
|
||||
import SearchForm from '../components/SearchForm.vue';
|
||||
import StrainList from '../components/StrainList.vue';
|
||||
import data from '../../../scraper/db.json';
|
||||
import emitter from '../lib/emitter.mjs';
|
||||
import store from '../lib/store.mjs';
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
SearchForm,
|
||||
StrainList,
|
||||
name: 'HomePage',
|
||||
data() {
|
||||
return {
|
||||
activeTab: this.$route.name,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
setActive(name) {
|
||||
this.activeTab = name;
|
||||
},
|
||||
},
|
||||
head: {
|
||||
title: 'Strain Search',
|
||||
@@ -32,118 +36,5 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
...data,
|
||||
matches: [],
|
||||
requirements: {
|
||||
name: '',
|
||||
effects: [],
|
||||
uses: [],
|
||||
conditions: [],
|
||||
flavors: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasName() {
|
||||
return this.requirements.name.length > 0;
|
||||
},
|
||||
hasFilters() {
|
||||
return (
|
||||
this.requirements.effects.length > 0 ||
|
||||
this.requirements.uses.length > 0 ||
|
||||
this.requirements.conditions.length > 0 ||
|
||||
this.requirements.flavors.length > 0
|
||||
);
|
||||
},
|
||||
searchParams() {
|
||||
const searchParts = [this.requirements.name];
|
||||
this.requirements.effects.forEach(t => searchParts.push(`+effects:${t}`));
|
||||
this.requirements.uses.forEach(t => searchParts.push(`+uses:${t}`));
|
||||
this.requirements.conditions.forEach(t => searchParts.push(`+conditions:${t}`));
|
||||
this.requirements.flavors.forEach(t => searchParts.push(`+flavors:${t}`));
|
||||
|
||||
return searchParts.join(' ');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showDefaults(limit = 40) {
|
||||
// const favs = store.get('favorites') || [];
|
||||
// const favStrains = this.strains.filter(({ id }) => favs.indexOf(id) !== -1);
|
||||
// this.matches = favStrains.concat(this.strains.slice(0, limit - favStrains.length));
|
||||
|
||||
this.matches = this.strains.slice(0, limit);
|
||||
},
|
||||
updateMatches(limit = 40) {
|
||||
if (!this.hasName && !this.hasFilters) {
|
||||
this.showDefaults(limit);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const hits = this.idx.search(this.searchParams);
|
||||
// .slice(0, limit);
|
||||
const refs = hits.map(({ ref }) => parseInt(ref, 10));
|
||||
|
||||
this.matches = this.strains
|
||||
.map(strain => {
|
||||
const idx = refs.indexOf(strain.id);
|
||||
if (idx < 0) return null;
|
||||
return Object.assign({ score: hits[idx].score }, strain);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => {
|
||||
if (a.score === b.score) {
|
||||
// sort matching search score by adjusted rating
|
||||
if (a.rating_adjusted === b.rating_adjusted) return 0;
|
||||
return a.rating_adjusted > b.rating_adjusted ? -1 : 1;
|
||||
}
|
||||
return a.score > b.score ? -1 : 1;
|
||||
});
|
||||
} catch (err) {
|
||||
this.matches = [];
|
||||
emitter.emit('error', err.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// lunr setup
|
||||
this.idx = lunr(function lunrSetup() {
|
||||
this.ref('id');
|
||||
this.field('name');
|
||||
this.field('effects');
|
||||
this.field('uses');
|
||||
this.field('conditions');
|
||||
this.field('flavors');
|
||||
data.strains.forEach(doc => this.add(doc));
|
||||
});
|
||||
|
||||
// function to handle search form submissions
|
||||
this.searchListener = reqs => {
|
||||
this.requirements = reqs;
|
||||
this.updateMatches();
|
||||
};
|
||||
|
||||
// listen for search form submissions
|
||||
emitter.on('search', r => this.searchListener(r));
|
||||
|
||||
// listen for favorite changes
|
||||
emitter.on('favorite', ({ id, isFav }) => {
|
||||
const favs = store.get('favorites') || [];
|
||||
const idx = favs.indexOf(id);
|
||||
|
||||
// remove previously favorited strain
|
||||
if (idx >= 0 && !isFav) {
|
||||
store.set('favorites', favs.filter(f => f !== id));
|
||||
} else if (idx === -1 && isFav) {
|
||||
store.set('favorites', favs.concat(id));
|
||||
}
|
||||
});
|
||||
this.updateMatches(); // set initial match list
|
||||
},
|
||||
beforeDestroy() {
|
||||
emitter.off('search', r => this.searchListener(r));
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
140
packages/search-site/src/pages/Search.vue
Normal file
140
packages/search-site/src/pages/Search.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div id="search-page">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Strain Search
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<SearchForm
|
||||
:effects="effects"
|
||||
:uses="uses"
|
||||
:conditions="conditions"
|
||||
:flavors="flavors"
|
||||
:error="error"
|
||||
@search="onSearch"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<StrainList :strains="matches" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SearchForm from '../components/SearchForm.vue';
|
||||
import StrainList from '../components/StrainList.vue';
|
||||
|
||||
export default {
|
||||
name: 'SearchPage',
|
||||
components: {
|
||||
SearchForm,
|
||||
StrainList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: '',
|
||||
matches: [],
|
||||
requirements: {
|
||||
name: '',
|
||||
effects: [],
|
||||
uses: [],
|
||||
conditions: [],
|
||||
flavors: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
strains() {
|
||||
return this.$strainData.strains;
|
||||
},
|
||||
effects() {
|
||||
return this.$strainData.effects;
|
||||
},
|
||||
uses() {
|
||||
return this.$strainData.uses;
|
||||
},
|
||||
conditions() {
|
||||
return this.$strainData.conditions;
|
||||
},
|
||||
flavors() {
|
||||
return this.$strainData.flavors;
|
||||
},
|
||||
hasName() {
|
||||
return this.requirements.name.length > 0;
|
||||
},
|
||||
hasFilters() {
|
||||
return (
|
||||
this.requirements.effects.length > 0 ||
|
||||
this.requirements.uses.length > 0 ||
|
||||
this.requirements.conditions.length > 0 ||
|
||||
this.requirements.flavors.length > 0
|
||||
);
|
||||
},
|
||||
searchParams() {
|
||||
const searchParts = [this.requirements.name];
|
||||
this.requirements.effects.forEach(t => searchParts.push(`+effects:${t}`));
|
||||
this.requirements.uses.forEach(t => searchParts.push(`+uses:${t}`));
|
||||
this.requirements.conditions.forEach(t => searchParts.push(`+conditions:${t}`));
|
||||
this.requirements.flavors.forEach(t => searchParts.push(`+flavors:${t}`));
|
||||
|
||||
return searchParts.join(' ');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSearch(reqs) {
|
||||
this.requirements = reqs;
|
||||
this.updateMatches();
|
||||
},
|
||||
showDefaults(limit = 40) {
|
||||
// const favs = store.get('favorites') || [];
|
||||
// const favStrains = this.strains.filter(({ id }) => favs.indexOf(id) !== -1);
|
||||
// this.matches = favStrains.concat(this.strains.slice(0, limit - favStrains.length));
|
||||
|
||||
this.matches = this.strains.slice(0, limit);
|
||||
},
|
||||
updateMatches(limit = 40) {
|
||||
this.error = '';
|
||||
|
||||
if (!this.hasName && !this.hasFilters) {
|
||||
this.showDefaults(limit);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const hits = this.$lunr.search(this.searchParams);
|
||||
// .slice(0, limit);
|
||||
const refs = hits.map(({ ref }) => parseInt(ref, 10));
|
||||
|
||||
this.matches = this.strains
|
||||
.map(strain => {
|
||||
const idx = refs.indexOf(strain.id);
|
||||
if (idx < 0) return null;
|
||||
return Object.assign({ score: hits[idx].score }, strain);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => {
|
||||
if (a.score === b.score) {
|
||||
// sort matching search score by adjusted rating
|
||||
if (a.rating_adjusted === b.rating_adjusted) return 0;
|
||||
return a.rating_adjusted > b.rating_adjusted ? -1 : 1;
|
||||
}
|
||||
return a.score > b.score ? -1 : 1;
|
||||
});
|
||||
} catch (err) {
|
||||
this.matches = [];
|
||||
this.error = err.message;
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.updateMatches(); // set initial match list
|
||||
},
|
||||
};
|
||||
</script>
|
||||
10
packages/search-site/src/plugins/lunr.mjs
Normal file
10
packages/search-site/src/plugins/lunr.mjs
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
import lunr from 'lunr';
|
||||
|
||||
export default function(lunrSetup) {
|
||||
return {
|
||||
install(Vue) {
|
||||
Vue.prototype.$lunr = lunr(lunrSetup);
|
||||
},
|
||||
};
|
||||
}
|
||||
8
packages/search-site/src/plugins/strainData.mjs
Normal file
8
packages/search-site/src/plugins/strainData.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
import data from '../../../scraper/db.json';
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.prototype.$strainData = data;
|
||||
},
|
||||
};
|
||||
@@ -5692,10 +5692,6 @@ mississippi@^2.0.0:
|
||||
stream-each "^1.1.0"
|
||||
through2 "^2.0.0"
|
||||
|
||||
mitt@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.1.3.tgz#528c506238a05dce11cd914a741ea2cc332da9b8"
|
||||
|
||||
mixin-deep@^1.2.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
||||
|
||||
Reference in New Issue
Block a user