Implemented search filters for random restaurant
parent
1ebbe3579e
commit
b05fb94547
|
|
@ -3,8 +3,10 @@ import { RouterView } from "vue-router";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import NavigationBar from "./components/NavigationBar.vue";
|
import NavigationBar from "./components/NavigationBar.vue";
|
||||||
import { useRestaurantsStore } from "./stores/restaurants";
|
import { useRestaurantsStore } from "./stores/restaurants";
|
||||||
|
import { usePreferencesStore } from "./stores/preferences";
|
||||||
|
|
||||||
useRestaurantsStore().init();
|
useRestaurantsStore().init();
|
||||||
|
usePreferencesStore().init();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,9 @@ import { storeToRefs } from "pinia";
|
||||||
import { useRestaurantsStore } from "../stores/restaurants";
|
import { useRestaurantsStore } from "../stores/restaurants";
|
||||||
import { useForm } from "vee-validate";
|
import { useForm } from "vee-validate";
|
||||||
import { toTypedSchema } from "@vee-validate/yup";
|
import { toTypedSchema } from "@vee-validate/yup";
|
||||||
import Restaurant from "../models/restaurant";
|
import { makeFilters } from "../utils/filters";
|
||||||
import { filterValues } from "../utils/filters";
|
import { SearchPreferences } from "../models/search";
|
||||||
|
import { usePreferencesStore } from "../stores/preferences";
|
||||||
|
|
||||||
const priceTicks = {
|
const priceTicks = {
|
||||||
1: "€",
|
1: "€",
|
||||||
|
|
@ -20,6 +21,7 @@ const priceTicks = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { restaurants } = storeToRefs(useRestaurantsStore());
|
const { restaurants } = storeToRefs(useRestaurantsStore());
|
||||||
|
const { searchPreferences } = storeToRefs(usePreferencesStore());
|
||||||
|
|
||||||
const restaurantOptions = computed(() => {
|
const restaurantOptions = computed(() => {
|
||||||
return restaurants.value.map((restaurant) => restaurant.id);
|
return restaurants.value.map((restaurant) => restaurant.id);
|
||||||
|
|
@ -35,11 +37,35 @@ const restaurantLabels = computed(() => {
|
||||||
|
|
||||||
const availableTags = ref<Tag[]>([]);
|
const availableTags = ref<Tag[]>([]);
|
||||||
|
|
||||||
const tagsNames = computed(() => {
|
const tagsOptions = computed(() => {
|
||||||
return availableTags.value.map((tag) => tag.name);
|
return availableTags.value.map((tag) => tag.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
const { values, defineField } = useForm({
|
const tagsLabels = computed(() => {
|
||||||
|
const labels: Map<string, string> = new Map();
|
||||||
|
availableTags.value.forEach((tag) => {
|
||||||
|
labels.set(tag.id, tag.name);
|
||||||
|
});
|
||||||
|
return labels;
|
||||||
|
});
|
||||||
|
|
||||||
|
function mapValuesToModel(values: Object): SearchPreferences {
|
||||||
|
return {
|
||||||
|
priceRangeEnabled: values["priceRangeEnabled"],
|
||||||
|
includeTagsEnabled: values["includeTagsEnabled"],
|
||||||
|
excludeTagsEnabled: values["excludeTagsEnabled"],
|
||||||
|
includeRestaurantsEnabled: values["includeRestaurantsEnabled"],
|
||||||
|
excludeRestaurantsEnabled: values["excludeRestaurantsEnabled"],
|
||||||
|
lowerPriceBound: values["priceRange"] ? priceTicks[values["priceRange"][0]!] : undefined,
|
||||||
|
upperPriceBound: values["priceRange"] ? priceTicks[values["priceRange"][1]!] : undefined,
|
||||||
|
includedTags: values["includedTags"],
|
||||||
|
excludedTags: values["excludedTags"],
|
||||||
|
includedRestaurants: values["includedRestaurants"],
|
||||||
|
excludedRestaurants: values["excludedRestaurants"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { values, handleSubmit, defineField } = useForm({
|
||||||
validationSchema: toTypedSchema(
|
validationSchema: toTypedSchema(
|
||||||
yup.object({
|
yup.object({
|
||||||
priceRangeEnabled: yup.boolean(),
|
priceRangeEnabled: yup.boolean(),
|
||||||
|
|
@ -54,19 +80,33 @@ const { values, defineField } = useForm({
|
||||||
excludedRestaurants: yup.array().of(yup.string()),
|
excludedRestaurants: yup.array().of(yup.string()),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
initialValues: {
|
||||||
|
priceRangeEnabled: searchPreferences.value?.priceRangeEnabled,
|
||||||
|
includeTagsEnabled: searchPreferences.value?.includeTagsEnabled,
|
||||||
|
excludeTagsEnabled: searchPreferences.value?.excludeTagsEnabled,
|
||||||
|
includeRestaurantsEnabled: searchPreferences.value?.includeRestaurantsEnabled,
|
||||||
|
excludeRestaurantsEnabled: searchPreferences.value?.excludeRestaurantsEnabled,
|
||||||
|
priceRange: [
|
||||||
|
Number(Object.keys(priceTicks).find((key) => priceTicks[key] == searchPreferences.value?.lowerPriceBound)),
|
||||||
|
Number(Object.keys(priceTicks).find((key) => priceTicks[key] == searchPreferences.value?.upperPriceBound)),
|
||||||
|
],
|
||||||
|
includedTags: searchPreferences.value?.includedTags,
|
||||||
|
excludedTags: searchPreferences.value?.excludedTags,
|
||||||
|
includedRestaurants: searchPreferences.value?.includedRestaurants,
|
||||||
|
excludedRestaurants: searchPreferences.value?.excludedRestaurants,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredRestaurants = ref<number>(restaurants.value.length);
|
const filteredRestaurants = ref<number>(restaurants.value.length);
|
||||||
|
|
||||||
watch(values, async (newval, oldval) => {
|
watch(
|
||||||
const filters: ((_: Restaurant) => boolean)[] = [];
|
values,
|
||||||
|
async (newval, oldval) => {
|
||||||
if (values.excludeRestaurantsEnabled && values.excludedRestaurants) {
|
const filters = makeFilters(mapValuesToModel(newval));
|
||||||
filters.push(filterValues.bind(undefined, "id", values.excludedRestaurants, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredRestaurants.value = useRestaurantsStore().getFilteredRestaurants(filters).length;
|
filteredRestaurants.value = useRestaurantsStore().getFilteredRestaurants(filters).length;
|
||||||
});
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const [priceRangeEnabled, priceRangeEnabledAttrs] = defineField("priceRangeEnabled");
|
const [priceRangeEnabled, priceRangeEnabledAttrs] = defineField("priceRangeEnabled");
|
||||||
const [includeTagsEnabled, includeTagsEnabledAttrs] = defineField("includeTagsEnabled");
|
const [includeTagsEnabled, includeTagsEnabledAttrs] = defineField("includeTagsEnabled");
|
||||||
|
|
@ -91,9 +131,17 @@ const emit = defineEmits({
|
||||||
preferencesSaved: () => true,
|
preferencesSaved: () => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSavePreferences() {
|
const onSubmit = handleSubmit(async (values) => {
|
||||||
|
try {
|
||||||
|
const data = mapValuesToModel(values);
|
||||||
|
|
||||||
|
await pb.collection("search_preferences").update(searchPreferences.value!.id!, data);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
emit("preferencesSaved");
|
emit("preferencesSaved");
|
||||||
}
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -123,7 +171,13 @@ function onSavePreferences() {
|
||||||
<input type="checkbox" v-model="includeTagsEnabled" class="checkbox border-solid" />
|
<input type="checkbox" v-model="includeTagsEnabled" class="checkbox border-solid" />
|
||||||
Include tags
|
Include tags
|
||||||
</label>
|
</label>
|
||||||
<TagsField v-model="includedTags" :create="false" :options="tagsNames" :disabled="!includeTagsEnabled" />
|
<TagsField
|
||||||
|
v-model="includedTags"
|
||||||
|
:create="false"
|
||||||
|
:options="tagsOptions"
|
||||||
|
:labels="tagsLabels"
|
||||||
|
:disabled="!includeTagsEnabled"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap w-full">
|
<div class="flex flex-row flex-nowrap w-full">
|
||||||
|
|
@ -132,7 +186,13 @@ function onSavePreferences() {
|
||||||
<input type="checkbox" v-model="excludeTagsEnabled" class="checkbox border-solid" />
|
<input type="checkbox" v-model="excludeTagsEnabled" class="checkbox border-solid" />
|
||||||
Exclude tags
|
Exclude tags
|
||||||
</label>
|
</label>
|
||||||
<TagsField v-model="excludedTags" :create="false" :options="tagsNames" :disabled="!excludeTagsEnabled" />
|
<TagsField
|
||||||
|
v-model="excludedTags"
|
||||||
|
:create="false"
|
||||||
|
:options="tagsOptions"
|
||||||
|
:labels="tagsLabels"
|
||||||
|
:disabled="!excludeTagsEnabled"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap w-full">
|
<div class="flex flex-row flex-nowrap w-full">
|
||||||
|
|
@ -167,6 +227,6 @@ function onSavePreferences() {
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<div>
|
<div>
|
||||||
{{ filteredRestaurants }} filtered restaurants
|
{{ filteredRestaurants }} filtered restaurants
|
||||||
<button @click="onSavePreferences" class="btn btn-outline w-full">Save preferences</button>
|
<button @click="onSubmit" class="btn btn-outline w-full">Save preferences</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ const columns = [
|
||||||
data: "tags",
|
data: "tags",
|
||||||
title: "Tags",
|
title: "Tags",
|
||||||
render: {
|
render: {
|
||||||
_: "[, ]",
|
_: "[, ].name",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ data: "price", title: "Price" },
|
{ data: "price", title: "Price" },
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import { createPinia } from "pinia";
|
||||||
|
|
||||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
import { faBars, faCheck, faUser, faXmark, faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
import { faBars, faCheck, faUser, faXmark, faExclamationCircle, faGear } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faGitAlt } from "@fortawesome/free-brands-svg-icons";
|
import { faGitAlt } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
|
||||||
library.add(faBars, faUser, faXmark, faCheck, faGitAlt, faExclamationCircle);
|
library.add(faBars, faUser, faXmark, faCheck, faGitAlt, faExclamationCircle, faGear);
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import Tag from "./tag";
|
||||||
|
|
||||||
export default interface Restaurant {
|
export default interface Restaurant {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
tags: string[];
|
tags: Tag[];
|
||||||
price: string;
|
price: string;
|
||||||
averageRating: number;
|
averageRating: number;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
export interface SearchPreferences {
|
||||||
|
id?: string;
|
||||||
|
priceRangeEnabled?: boolean;
|
||||||
|
includeTagsEnabled?: boolean;
|
||||||
|
excludeTagsEnabled?: boolean;
|
||||||
|
includeRestaurantsEnabled?: boolean;
|
||||||
|
excludeRestaurantsEnabled?: boolean;
|
||||||
|
lowerPriceBound?: string;
|
||||||
|
upperPriceBound?: string;
|
||||||
|
includedTags?: string[];
|
||||||
|
excludedTags?: string[];
|
||||||
|
includedRestaurants?: string[];
|
||||||
|
excludedRestaurants?: string[];
|
||||||
|
}
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useCounterStore = defineStore('counter', () => {
|
|
||||||
const count = ref(0)
|
|
||||||
const doubleCount = computed(() => count.value * 2)
|
|
||||||
function increment() {
|
|
||||||
count.value++
|
|
||||||
}
|
|
||||||
|
|
||||||
return { count, doubleCount, increment }
|
|
||||||
})
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
import { SearchPreferences } from "../models/search";
|
||||||
|
import { pb } from "../pocketbase";
|
||||||
|
|
||||||
|
export const usePreferencesStore = defineStore("preferences", {
|
||||||
|
state: () => ({
|
||||||
|
searchPreferences: undefined as SearchPreferences | undefined,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async load() {
|
||||||
|
if (!pb.authStore.model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchPreferences = await pb
|
||||||
|
.collection("search_preferences")
|
||||||
|
.getFirstListItem(pb.filter("user ~ {:id}", { id: pb.authStore.model.id }));
|
||||||
|
},
|
||||||
|
|
||||||
|
async reset() {
|
||||||
|
pb.collection("search_preferences").unsubscribe();
|
||||||
|
|
||||||
|
await this.load();
|
||||||
|
|
||||||
|
pb.collection("search_preferences").subscribe(this.searchPreferences.id, async (e) => {
|
||||||
|
await this.load();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
pb.authStore.onChange(async () => {
|
||||||
|
await this.reset();
|
||||||
|
}, true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -12,7 +12,10 @@ export const useRestaurantsStore = defineStore("restaurants", {
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getRandomRestaurant(state) {
|
getRandomRestaurant(state) {
|
||||||
return (filters: ((_: Restaurant) => boolean)[]) => {
|
return (filters?: ((_: Restaurant) => boolean)[]) => {
|
||||||
|
if (filters === undefined) {
|
||||||
|
return _.sample(state.restaurants);
|
||||||
|
}
|
||||||
const filtered = state.restaurants.filter((restaurant) => {
|
const filtered = state.restaurants.filter((restaurant) => {
|
||||||
for (const filter of filters) {
|
for (const filter of filters) {
|
||||||
if (!filter(restaurant)) {
|
if (!filter(restaurant)) {
|
||||||
|
|
@ -25,9 +28,11 @@ export const useRestaurantsStore = defineStore("restaurants", {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getFilteredRestaurants(state) {
|
getFilteredRestaurants(state) {
|
||||||
return (filters: ((_: Restaurant) => boolean)[]) => {
|
return (filters?: ((_: Restaurant) => boolean)[]) => {
|
||||||
|
if (filters === undefined) {
|
||||||
|
return state.restaurants;
|
||||||
|
}
|
||||||
const filtered = state.restaurants.filter((restaurant) => {
|
const filtered = state.restaurants.filter((restaurant) => {
|
||||||
console.log(restaurant);
|
|
||||||
for (const filter of filters) {
|
for (const filter of filters) {
|
||||||
if (!filter(restaurant)) {
|
if (!filter(restaurant)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -56,7 +61,7 @@ export const useRestaurantsStore = defineStore("restaurants", {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restaurant.expand?.tags !== undefined) {
|
if (restaurant.expand?.tags !== undefined) {
|
||||||
restaurant.tags = restaurant.expand.tags.map((tag) => tag.name);
|
restaurant.tags = restaurant.expand.tags;
|
||||||
} else {
|
} else {
|
||||||
restaurant.tags = [];
|
restaurant.tags = [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,70 @@
|
||||||
export function filterValues(field: string, values: any[], negative: boolean = false, obj: Object) {
|
import Restaurant from "../models/restaurant";
|
||||||
console.log("fielf", field, "values", values, "negative", negative, "obj", obj);
|
import { SearchPreferences } from "../models/search";
|
||||||
|
|
||||||
|
function _filterValue(options: any[], value: any, negative: boolean = false) {
|
||||||
if (negative) {
|
if (negative) {
|
||||||
return !values.includes(obj[field]);
|
return !options.includes(value);
|
||||||
} else {
|
} else {
|
||||||
return values.includes(obj[field]);
|
return options.includes(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function filterValues(
|
||||||
|
field: string,
|
||||||
|
options: any[],
|
||||||
|
negative: boolean = false,
|
||||||
|
subfield: string | undefined = undefined,
|
||||||
|
obj: Object
|
||||||
|
) {
|
||||||
|
if (Array.isArray(obj[field])) {
|
||||||
|
for (let element of obj[field]) {
|
||||||
|
if (subfield) {
|
||||||
|
element = element[subfield];
|
||||||
|
}
|
||||||
|
if (negative) {
|
||||||
|
if (!_filterValue(options, element, negative)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_filterValue(options, element, negative)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return negative;
|
||||||
|
} else {
|
||||||
|
return _filterValue(options, obj[field], negative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeFilters(config: SearchPreferences) {
|
||||||
|
const filters: ((_: Restaurant) => boolean)[] = [];
|
||||||
|
|
||||||
|
if (config.excludeRestaurantsEnabled && config.excludedRestaurants) {
|
||||||
|
filters.push(filterValues.bind(undefined, "id", config.excludedRestaurants, true, undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.includeRestaurantsEnabled && config.includedRestaurants) {
|
||||||
|
filters.push(filterValues.bind(undefined, "id", config.includedRestaurants, false, undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.excludeTagsEnabled && config.excludedTags) {
|
||||||
|
filters.push(filterValues.bind(undefined, "tags", config.excludedTags, true, "id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.includeTagsEnabled && config.includedTags) {
|
||||||
|
filters.push(filterValues.bind(undefined, "tags", config.includedTags, false, "id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.priceRangeEnabled) {
|
||||||
|
const prices = ["€", "€€", "€€€", "€€€€"];
|
||||||
|
|
||||||
|
const lowerIndex = prices.findIndex((element) => element == config.lowerPriceBound);
|
||||||
|
const upperIndex = prices.findIndex((element) => element == config.upperPriceBound);
|
||||||
|
|
||||||
|
const values = prices.slice(lowerIndex, upperIndex);
|
||||||
|
filters.push(filterValues.bind(undefined, "price", values, false, undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,39 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { onMounted, ref, watch } from "vue";
|
||||||
import "@/assets/form.css";
|
import "@/assets/form.css";
|
||||||
|
|
||||||
import RandomConfigurationDrawer from "../components/RandomConfigurationDrawer.vue";
|
import RandomConfigurationDrawer from "../components/RandomConfigurationDrawer.vue";
|
||||||
|
|
||||||
import { useRestaurantsStore } from "../stores/restaurants.ts";
|
import { useRestaurantsStore } from "../stores/restaurants.ts";
|
||||||
import Restaurant from "../models/restaurant.ts";
|
import Restaurant from "../models/restaurant.ts";
|
||||||
|
import { makeFilters } from "../utils/filters.ts";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { usePreferencesStore } from "../stores/preferences.ts";
|
||||||
|
|
||||||
const restaurant = ref<Restaurant>();
|
const restaurant = ref<Restaurant>();
|
||||||
|
|
||||||
const store = useRestaurantsStore();
|
const store = useRestaurantsStore();
|
||||||
|
const { searchPreferences } = storeToRefs(usePreferencesStore());
|
||||||
|
|
||||||
function getRandomRestaurant() {
|
function getRandomRestaurant() {
|
||||||
restaurant.value = store.getRandomRestaurant();
|
restaurant.value = store.getRandomRestaurant(filters.value);
|
||||||
|
console.log(restaurant.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filters = ref<((_: Restaurant) => boolean)[]>();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => searchPreferences.value,
|
||||||
|
(newval, oldval) => {
|
||||||
|
if (newval) {
|
||||||
|
filters.value = makeFilters(newval);
|
||||||
|
} else {
|
||||||
|
filters.value = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const drawer = ref<typeof RandomConfigurationDrawer>();
|
const drawer = ref<typeof RandomConfigurationDrawer>();
|
||||||
|
|
||||||
function onConfigureClicked() {
|
function onConfigureClicked() {
|
||||||
|
|
@ -27,10 +46,12 @@ function onConfigureClicked() {
|
||||||
<h2 class="mb-10" v-if="restaurant !== undefined">How about going to...</h2>
|
<h2 class="mb-10" v-if="restaurant !== undefined">How about going to...</h2>
|
||||||
<p class="">{{ restaurant?.name }}</p>
|
<p class="">{{ restaurant?.name }}</p>
|
||||||
<button class="btn btn-outline btn-primary mt-10" @click="getRandomRestaurant">Get random restaurant</button>
|
<button class="btn btn-outline btn-primary mt-10" @click="getRandomRestaurant">Get random restaurant</button>
|
||||||
|
<button class="btn btn-ghost mt-5" @click="onConfigureClicked">
|
||||||
|
<font-awesome-icon icon="gear" />
|
||||||
|
Configure
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn" @click="onConfigureClicked">Configure</button>
|
|
||||||
|
|
||||||
<RandomConfigurationDrawer ref="drawer" />
|
<RandomConfigurationDrawer ref="drawer" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,8 @@ const onSubmit = handleSubmit(async (values) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
await pb.collection("users").create(data);
|
await pb.collection("users").create(data);
|
||||||
await pb
|
await pb.collection("users").authWithPassword(values["username"], values["password"]);
|
||||||
.collection("users")
|
await pb.collection("search_preferences").create({ user: currentUser.value.id });
|
||||||
.authWithPassword(values["username"], values["password"]);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err;
|
error.value = err;
|
||||||
}
|
}
|
||||||
|
|
@ -49,44 +48,25 @@ const [passwordConfirm, passwordConfirmAttrs] = defineField("passwordConfirm");
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<p v-if="currentUser">Signed in as {{ currentUser.username }}</p>
|
<p v-if="currentUser">Signed in as {{ currentUser.username }}</p>
|
||||||
<form
|
<form v-else class="flex flex-col place-content-center items-center h-full" @submit.prevent="onSubmit">
|
||||||
v-else
|
|
||||||
class="flex flex-col place-content-center items-center h-full"
|
|
||||||
@submit.prevent="onSubmit"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
<div class="mx-5 my-4 w-full">
|
<div class="mx-5 my-4 w-full">
|
||||||
<label class="form-label"> Username </label>
|
<label class="form-label"> Username </label>
|
||||||
<input
|
<input class="form-field" type="text" v-model="username" v-bind="usernameAttrs" />
|
||||||
class="form-field"
|
|
||||||
type="text"
|
|
||||||
v-model="username"
|
|
||||||
v-bind="usernameAttrs"
|
|
||||||
/>
|
|
||||||
<label class="text-red-500">{{ errors.username }}</label>
|
<label class="text-red-500">{{ errors.username }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
<div class="mx-5 my-4 w-full">
|
<div class="mx-5 my-4 w-full">
|
||||||
<label class="form-label"> Password </label>
|
<label class="form-label"> Password </label>
|
||||||
<input
|
<input class="form-field" type="password" v-model="password" v-bind="passwordAttrs" />
|
||||||
class="form-field"
|
|
||||||
type="password"
|
|
||||||
v-model="password"
|
|
||||||
v-bind="passwordAttrs"
|
|
||||||
/>
|
|
||||||
<label class="text-red-500">{{ errors.password }}</label>
|
<label class="text-red-500">{{ errors.password }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
<div class="mx-5 my-4 w-full">
|
<div class="mx-5 my-4 w-full">
|
||||||
<label class="form-label"> Confirm password </label>
|
<label class="form-label"> Confirm password </label>
|
||||||
<input
|
<input class="form-field" type="password" v-model="passwordConfirm" v-bind="passwordConfirmAttrs" />
|
||||||
class="form-field"
|
|
||||||
type="password"
|
|
||||||
v-model="passwordConfirm"
|
|
||||||
v-bind="passwordConfirmAttrs"
|
|
||||||
/>
|
|
||||||
<label class="text-red-500">{{ errors.passwordConfirm }}</label>
|
<label class="text-red-500">{{ errors.passwordConfirm }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue