Added connection error message, added max height for tags field, added optional google maps link, added price options.
parent
04fb307b7a
commit
d73cb4dbb8
|
|
@ -18,6 +18,10 @@ const { errors, handleSubmit, defineField, resetForm } = useForm({
|
||||||
name: yup.string().required("This field is required."),
|
name: yup.string().required("This field is required."),
|
||||||
tags: yup.array().of(yup.string()),
|
tags: yup.array().of(yup.string()),
|
||||||
price: yup.string().required("This field is required."),
|
price: yup.string().required("This field is required."),
|
||||||
|
googleMapsLink: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.matches(/^$|https:\/\/maps\.app\.goo\.gl\/[a-zA-Z0-9]+/, "Not a valid Google Maps link."),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
@ -39,6 +43,7 @@ const submit = handleSubmit((values) => {
|
||||||
tags: tags,
|
tags: tags,
|
||||||
latitude: position.value?.lat,
|
latitude: position.value?.lat,
|
||||||
longitude: position.value?.lng,
|
longitude: position.value?.lng,
|
||||||
|
googleMapsLink: values.googleMapsLink,
|
||||||
});
|
});
|
||||||
|
|
||||||
emit("succeeded", `Restaurant ${values.name} created successfully!`);
|
emit("succeeded", `Restaurant ${values.name} created successfully!`);
|
||||||
|
|
@ -65,6 +70,7 @@ onMounted(async () => {
|
||||||
const [name, nameAttrs] = defineField("name");
|
const [name, nameAttrs] = defineField("name");
|
||||||
const [tags, tagsAttrs] = defineField("tags");
|
const [tags, tagsAttrs] = defineField("tags");
|
||||||
const [price, priceAttrs] = defineField("price");
|
const [price, priceAttrs] = defineField("price");
|
||||||
|
const [googleMapsLink, googleMapsLinkAttrs] = defineField("googleMapsLink");
|
||||||
|
|
||||||
const emit = defineEmits({
|
const emit = defineEmits({
|
||||||
succeeded: (message: string) => true,
|
succeeded: (message: string) => true,
|
||||||
|
|
@ -138,12 +144,25 @@ defineExpose({ show, hide, setRestaurantPosition });
|
||||||
v-bind="priceAttrs"
|
v-bind="priceAttrs"
|
||||||
>
|
>
|
||||||
<option value="€">1-10€</option>
|
<option value="€">1-10€</option>
|
||||||
<option value="€€">10-20€</option>
|
<option value="€€">10-15€</option>
|
||||||
<option value="€€€">20-30€</option>
|
<option value="€€€">15-20€</option>
|
||||||
|
<option value="€€€€">>20€</option>
|
||||||
</select>
|
</select>
|
||||||
<label class="text-red-500">{{ errors.price }}</label>
|
<label class="text-red-500">{{ errors.price }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-row flex-nowrap w-96">
|
||||||
|
<div class="mx-5 my-4 w-full">
|
||||||
|
<label class="form-label"> Google Maps Link (Optional) </label>
|
||||||
|
<input
|
||||||
|
:class="errors.googleMapsLink === undefined ? 'form-field' : 'form-field-error'"
|
||||||
|
v-model="googleMapsLink"
|
||||||
|
v-bind="googleMapsLinkAttrs"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<label class="text-red-500">{{ errors.googleMapsLink }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ModalComponent>
|
</ModalComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ const columns = [
|
||||||
_: "[, ]",
|
_: "[, ]",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{ data: "price", title: "Price" },
|
||||||
{ data: "average_rating", title: "" },
|
{ data: "average_rating", title: "" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -39,7 +40,7 @@ function deselectAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits({
|
const emit = defineEmits({
|
||||||
restaurantSelected: (restaurant: Object) => true,
|
restaurantSelected: (restaurant: Restaurant) => true,
|
||||||
restaurantHovered: (restaurant: Object | null) => true,
|
restaurantHovered: (restaurant: Object | null) => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -92,7 +93,7 @@ defineExpose({ deselectAll });
|
||||||
}"
|
}"
|
||||||
@select="onRowSelected"
|
@select="onRowSelected"
|
||||||
>
|
>
|
||||||
<template #column-2="props">
|
<template #column-3="props">
|
||||||
<RatingField
|
<RatingField
|
||||||
v-if="props.cellData !== null"
|
v-if="props.cellData !== null"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ import { ref } from "vue";
|
||||||
import ReviewsList from "./ReviewsList.vue";
|
import ReviewsList from "./ReviewsList.vue";
|
||||||
import ReviewField from "./ReviewField.vue";
|
import ReviewField from "./ReviewField.vue";
|
||||||
import { currentUser, pb } from "../pocketbase";
|
import { currentUser, pb } from "../pocketbase";
|
||||||
|
import Restaurant from "../models/restaurant";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
restaurant: { type: Object },
|
restaurant: { type: Object as () => Restaurant },
|
||||||
});
|
});
|
||||||
|
|
||||||
const opened = ref<boolean>(false);
|
const opened = ref<boolean>(false);
|
||||||
|
|
@ -49,11 +50,22 @@ defineExpose({ open, close });
|
||||||
<div class="drawer-side w-full">
|
<div class="drawer-side w-full">
|
||||||
<label @click.prevent="close" for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
<label @click.prevent="close" for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
<div class="menu p-4 space-y-4 w-full lg:w-2/6 min-h-full bg-base-200 text-base-content">
|
<div class="menu p-4 space-y-4 w-full lg:w-2/6 min-h-full bg-base-200 text-base-content">
|
||||||
<font-awesome-icon
|
<div class="flex flex-row w-full">
|
||||||
@click="close"
|
<font-awesome-icon
|
||||||
class="btn btn-circle btn-sm text-gray-500 bg-transparent"
|
@click="close"
|
||||||
icon="fa-solid fa-xmark"
|
class="btn btn-circle btn-sm text-gray-500 bg-transparent"
|
||||||
/>
|
icon="fa-solid fa-xmark"
|
||||||
|
/>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<a
|
||||||
|
v-if="props.restaurant?.googleMapsLink"
|
||||||
|
:href="props.restaurant.googleMapsLink"
|
||||||
|
class="btn btn-ghost btn-link btn-sm"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>View on Google Maps</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ReviewField ref="reviewField" @publish="onPublishReview" />
|
<ReviewField ref="reviewField" @publish="onPublishReview" />
|
||||||
<ReviewsList ref="reviewsList" :restaurant="props.restaurant" />
|
<ReviewsList ref="reviewsList" :restaurant="props.restaurant" />
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,9 @@ function filteredOptions() {
|
||||||
<font-awesome-icon icon="xmark" />
|
<font-awesome-icon icon="xmark" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul class="menu dropdown-content z-10 w-full rounded-lg border border-neutral-content bg-base-100 p-0 shadow">
|
<ul
|
||||||
|
class="menu flex-nowrap dropdown-content overflow-y-auto z-10 max-h-96 w-full rounded-lg border border-neutral-content bg-base-100 p-0 shadow"
|
||||||
|
>
|
||||||
<li><button @click="onCreateNewTag">Create new tag...</button></li>
|
<li><button @click="onCreateNewTag">Create new tag...</button></li>
|
||||||
<li v-for="option in filteredOptions()" :key="option" role="option" class="m-0 p-0">
|
<li v-for="option in filteredOptions()" :key="option" role="option" class="m-0 p-0">
|
||||||
<button :class="{ active: model?.includes(option) }" @click="onOptionClicked(option)">
|
<button :class="{ active: model?.includes(option) }" @click="onOptionClicked(option)">
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,5 @@ export default interface Restaurant {
|
||||||
averageRating: number;
|
averageRating: number;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
|
googleMapsLink?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,17 @@ import RestaurantsMap from "../components/RestaurantsMap.vue";
|
||||||
import ReviewsDrawer from "../components/ReviewsDrawer.vue";
|
import ReviewsDrawer from "../components/ReviewsDrawer.vue";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useRestaurantsStore } from "../stores/restaurants";
|
import { useRestaurantsStore } from "../stores/restaurants";
|
||||||
|
import Restaurant from "../models/restaurant";
|
||||||
|
|
||||||
const drawer = ref<typeof ReviewsDrawer>();
|
const drawer = ref<typeof ReviewsDrawer>();
|
||||||
const table = ref<typeof RestaurantsTable>();
|
const table = ref<typeof RestaurantsTable>();
|
||||||
|
|
||||||
const selectedRestaurant = ref<Object>();
|
const selectedRestaurant = ref<Restaurant>();
|
||||||
const highlightedRestaurant = ref<string>();
|
const highlightedRestaurant = ref<string>();
|
||||||
|
|
||||||
const { restaurants } = storeToRefs(useRestaurantsStore());
|
const { restaurants } = storeToRefs(useRestaurantsStore());
|
||||||
|
|
||||||
function onRestaurantSelected(restaurant: Object) {
|
function onRestaurantSelected(restaurant: Restaurant) {
|
||||||
selectedRestaurant.value = restaurant;
|
selectedRestaurant.value = restaurant;
|
||||||
drawer.value?.open();
|
drawer.value?.open();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,18 @@ const valid = computed(() => {
|
||||||
return username.value !== undefined && password.value !== undefined;
|
return username.value !== undefined && password.value !== undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const errorMessage = ref<string>();
|
||||||
|
|
||||||
async function login() {
|
async function login() {
|
||||||
if (!valid.value) {
|
if (!valid.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await pb.collection("users").authWithPassword(username.value!, password.value!);
|
try {
|
||||||
|
await pb.collection("users").authWithPassword(username.value!, password.value!);
|
||||||
|
} catch (err) {
|
||||||
|
errorMessage.value = "Incorrect username or password.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -35,7 +41,9 @@ async function login() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button @click="login" :disabled="!valid">Log In</button>
|
<label v-if="errorMessage" class="text-red-500">{{ errorMessage }}</label>
|
||||||
|
|
||||||
|
<button @click="login" :disabled="!valid" class="my-3 btn btn-outline">Log In</button>
|
||||||
<p>New to DOXFOOD ? <RouterLink to="/signup">Create an account !</RouterLink></p>
|
<p>New to DOXFOOD ? <RouterLink to="/signup">Create an account !</RouterLink></p>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue