feat: basic routing and fav nav
This commit is contained in:
@@ -4,5 +4,9 @@ const path = require('path');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
entry: path.resolve(__dirname, 'src/index.mjs'),
|
entry: path.resolve(__dirname, 'src/index.mjs'),
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
plugins: [require('@poi/plugin-vue-static')()],
|
plugins: [
|
||||||
|
require('@poi/plugin-vue-static')({
|
||||||
|
routes: ['/', '/home', '/favorites'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
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,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="container">
|
|
||||||
<h1 class="title">
|
|
||||||
Strain Search
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<!-- Name Search Input -->
|
<!-- Name Search Input -->
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Search By Name</label>
|
<label class="label">Search By Name</label>
|
||||||
@@ -77,7 +72,6 @@
|
|||||||
<button type="button" class="button is-text" @click.prevent="resetForm">Clear</button>
|
<button type="button" class="button is-text" @click.prevent="resetForm">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div>
|
||||||
<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">No Matching Strains :(</h3>
|
||||||
<h3 v-if="strains.length > 0" class="title is-3">Found {{strains.length}} Strains</h3>
|
<h3 v-if="strains.length > 0" class="title is-3">Found {{strains.length}} Strains</h3>
|
||||||
|
|||||||
@@ -19,8 +19,19 @@ const router = new Router({
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
component: () => import(/* webpackChunkName: "Home" */ './pages/Home.vue'),
|
||||||
component: () => import(/* webpackChunkName: "homeapp" */ './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'),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
15
packages/search-site/src/pages/Favorites.vue
Normal file
15
packages/search-site/src/pages/Favorites.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div id="favorites-page">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
Favorites
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FavoritesPage',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,27 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div id="home-page">
|
||||||
<section class="section">
|
<div id="app-nav" class="tabs is-centered">
|
||||||
<SearchForm :effects="effects" :uses="uses" :conditions="conditions" :flavors="flavors" />
|
<ul>
|
||||||
</section>
|
<li :class="{'is-active': activeTab === 'search'}" @click="setActive('search')">
|
||||||
<section class="section">
|
<router-link :to="{name: 'search'}">Search</router-link>
|
||||||
<StrainList :strains="matches" />
|
</li>
|
||||||
</section>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<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 {
|
export default {
|
||||||
name: 'Home',
|
name: 'HomePage',
|
||||||
components: {
|
data() {
|
||||||
SearchForm,
|
return {
|
||||||
StrainList,
|
activeTab: 'search',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setActive(name) {
|
||||||
|
this.activeTab = name;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
head: {
|
head: {
|
||||||
title: 'Strain Search',
|
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>
|
</script>
|
||||||
|
|||||||
151
packages/search-site/src/pages/Search.vue
Normal file
151
packages/search-site/src/pages/Search.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<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" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<StrainList :strains="matches" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</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: 'SearchPage',
|
||||||
|
components: {
|
||||||
|
SearchForm,
|
||||||
|
StrainList,
|
||||||
|
},
|
||||||
|
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>
|
||||||
Reference in New Issue
Block a user