init project first version
This commit is contained in:
208
src/views/ApiKey.vue
Normal file
208
src/views/ApiKey.vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>API Key</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">API Key</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<form v-on:submit.prevent="doSaveKey(apiKey)">
|
||||
<ion-item>
|
||||
<ion-label position="fixed">ApiKey: </ion-label>
|
||||
<ion-input
|
||||
type="text"
|
||||
inputmode="text"
|
||||
minlength="1"
|
||||
maxlength="255"
|
||||
name="key"
|
||||
v-model="apiKey"
|
||||
required
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-button
|
||||
slot="start"
|
||||
color="primary"
|
||||
@click="() => router.push('/tabs/Cocktails')"
|
||||
>
|
||||
<ion-icon :icon="arrowBackCircleOutline"></ion-icon>
|
||||
Back
|
||||
</ion-button>
|
||||
<ion-button slot="start" color="danger" @click="doRestoreKey()">
|
||||
<ion-icon :icon="reloadCircleOutline"></ion-icon>
|
||||
Restore
|
||||
</ion-button>
|
||||
<ion-button slot="end" color="success" type="submit">
|
||||
<ion-icon :icon="saveOutline"></ion-icon>
|
||||
Save
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
</form>
|
||||
|
||||
<div class="checker">
|
||||
<ion-button expand="block" color="secondary" @click="doCheckKey()">
|
||||
<ion-icon :icon="helpCircleOutline"></ion-icon>
|
||||
Check Key
|
||||
</ion-button>
|
||||
<ion-item v-if="apiKey == '1'">
|
||||
<p>
|
||||
ApiKey is only test key! Get your own on Patreon
|
||||
<ion-button href="https://www.patreon.com/thedatadb"
|
||||
>CocktailDB</ion-button
|
||||
>
|
||||
</p>
|
||||
</ion-item>
|
||||
</div>
|
||||
<!--<ion-button expand="block" color="secondary" @click="fetchRandomCocktail()">
|
||||
<ion-icon :icon="helpCircleOutline"></ion-icon>
|
||||
fetch
|
||||
</ion-button>-->
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
actionSheetController,
|
||||
alertController,
|
||||
} from "@ionic/vue";
|
||||
import {
|
||||
trash,
|
||||
saveOutline,
|
||||
refreshOutline,
|
||||
arrowBackCircleOutline,
|
||||
helpCircleOutline,
|
||||
reloadCircleOutline,
|
||||
} from "ionicons/icons";
|
||||
export default {
|
||||
name: "ApiKey",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const { apiKey, updateApiKey, restoreApiKey, testApiKey, fetchRandomCocktail } = useStorage();
|
||||
|
||||
const doCheckKey = async () => {
|
||||
let data = await testApiKey();
|
||||
if (data && data != "error") {
|
||||
const alert = await alertController.create({
|
||||
header: "Success",
|
||||
subHeader: "DB connected successfull",
|
||||
message: "Your API Key is valid and can be used",
|
||||
buttons: ["OK"],
|
||||
});
|
||||
await alert.present();
|
||||
} else {
|
||||
const alert = await alertController.create({
|
||||
header: "Fail",
|
||||
subHeader: "Db connection failed",
|
||||
message:
|
||||
"Your API Key may be invalid or your internet connection is broken",
|
||||
buttons: ["OK"],
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
};
|
||||
|
||||
const doRestoreKey = async () => {
|
||||
const actionSheet = await actionSheetController.create({
|
||||
header: "Restore API Key",
|
||||
buttons: [
|
||||
{
|
||||
text: "Restore API Key will remove your current key and set it to the default test key!",
|
||||
role: "destructive",
|
||||
icon: trash,
|
||||
handler: () => {
|
||||
restoreApiKey();
|
||||
window.scrollTo({ top: 0, left: 0 });
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
icon: close,
|
||||
role: "cancel",
|
||||
handler: () => {
|
||||
// Nothing to do, action sheet is automatically closed
|
||||
window.scrollTo({ top: 0, left: 0 });
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await actionSheet.present();
|
||||
};
|
||||
|
||||
const doSaveKey = async (key) => {
|
||||
let data = await updateApiKey(key);
|
||||
if (data && data != false) {
|
||||
const alert = await alertController.create({
|
||||
header: "Success",
|
||||
message: "Your API Key has been saved",
|
||||
buttons: ["OK"],
|
||||
});
|
||||
await alert.present();
|
||||
} else {
|
||||
const alert = await alertController.create({
|
||||
header: "Fail",
|
||||
message: "Your API Key coudn't be saved :(",
|
||||
buttons: ["OK"],
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
apiKey,
|
||||
doSaveKey,
|
||||
doCheckKey,
|
||||
doRestoreKey,
|
||||
fetchRandomCocktail,
|
||||
trash,
|
||||
saveOutline,
|
||||
refreshOutline,
|
||||
router,
|
||||
arrowBackCircleOutline,
|
||||
helpCircleOutline,
|
||||
reloadCircleOutline,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ion-item {
|
||||
width: 95%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.checker {
|
||||
margin-top: 50px;
|
||||
}
|
||||
</style>
|
||||
344
src/views/Cocktails.vue
Normal file
344
src/views/Cocktails.vue
Normal file
@@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Cocktails</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large"
|
||||
>Cocktaillist
|
||||
{{ cocktails ? "#" + cocktails.length : "#0" }}</ion-title
|
||||
>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-button
|
||||
color="success"
|
||||
expand="full"
|
||||
@click="() => router.push('/tabs/New')"
|
||||
>
|
||||
<ion-icon :icon="addCircleOutline"></ion-icon>
|
||||
Add new cocktail
|
||||
</ion-button>
|
||||
|
||||
<ion-list v-if="cocktails">
|
||||
<ion-item-sliding v-for="cocktail in cocktails" :key="cocktail.name">
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option @click="doDeleteCocktail(cocktail)" color="danger">
|
||||
<ion-icon slot="icon-only" :icon="trash"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option
|
||||
@click="
|
||||
() => router.push(`/tabs/New/${cocktails.indexOf(cocktail)}`)
|
||||
"
|
||||
color="warning"
|
||||
>
|
||||
<ion-icon slot="icon-only" :icon="createOutline"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>
|
||||
<span
|
||||
@click="
|
||||
() =>
|
||||
router.push(`/tabs/Shake/${cocktails.indexOf(cocktail)}`)
|
||||
"
|
||||
>
|
||||
<ion-icon
|
||||
class="golden"
|
||||
v-if="cocktail.favourite"
|
||||
:icon="star"
|
||||
></ion-icon>
|
||||
{{ cocktail.name }}
|
||||
<ion-icon
|
||||
class="green"
|
||||
v-if="cocktail.image"
|
||||
:icon="camera"
|
||||
></ion-icon>
|
||||
</span>
|
||||
</h2>
|
||||
<p>by {{ cocktail.author }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option
|
||||
@click="showShareOptions(cocktail)"
|
||||
color="secondary"
|
||||
>
|
||||
<ion-icon slot="icon-only" :icon="shareSocialOutline"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option
|
||||
@click="doFavouriseCocktail(cocktail)"
|
||||
color="success"
|
||||
>
|
||||
<ion-icon slot="icon-only" :icon="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-list>
|
||||
|
||||
<ion-button
|
||||
color="primary"
|
||||
expand="full"
|
||||
@click="() => router.push('/tabs/Apikey')"
|
||||
>
|
||||
<ion-icon :icon="keyOutline"></ion-icon>
|
||||
Your API Key
|
||||
</ion-button>
|
||||
|
||||
<ion-button color="danger" expand="full" @click="doRestoreCocktails()">
|
||||
<ion-icon :icon="removeCircleOutline"></ion-icon>
|
||||
Restore Cocktails
|
||||
</ion-button>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
import { useRouter } from "vue-router";
|
||||
import { SocialSharing } from "@ionic-native/social-sharing";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonItemOption,
|
||||
IonItemOptions,
|
||||
IonItemSliding,
|
||||
IonIcon,
|
||||
IonButton,
|
||||
actionSheetController,
|
||||
} from "@ionic/vue";
|
||||
import {
|
||||
addCircleOutline,
|
||||
removeCircleOutline,
|
||||
star,
|
||||
trash,
|
||||
camera,
|
||||
chevronDownCircleOutline,
|
||||
shareSocialOutline,
|
||||
createOutline,
|
||||
logoFacebook,
|
||||
logoTwitter,
|
||||
logoWhatsapp,
|
||||
logoInstagram,
|
||||
mailOutline,
|
||||
callOutline,
|
||||
eyeOutline,
|
||||
keyOutline,
|
||||
} from "ionicons/icons";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Cocktails",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonItemOption,
|
||||
IonItemOptions,
|
||||
IonItemSliding,
|
||||
IonIcon,
|
||||
IonButton,
|
||||
},
|
||||
setup() {
|
||||
const { cocktails, favouriseCocktail, removeCocktail, restoreCocktails } =
|
||||
useStorage();
|
||||
const router = useRouter();
|
||||
const socialSharing = SocialSharing;
|
||||
const publicPath = process.env.BASE_URL;
|
||||
|
||||
const doDeleteCocktail = async (cocktail) => {
|
||||
const actionSheet = await actionSheetController.create({
|
||||
header: cocktail.name,
|
||||
buttons: [
|
||||
{
|
||||
text: "Delete",
|
||||
role: "destructive",
|
||||
icon: trash,
|
||||
handler: () => {
|
||||
removeCocktail(cocktail);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
icon: close,
|
||||
role: "cancel",
|
||||
handler: () => {
|
||||
// Nothing to do, action sheet is automatically closed
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await actionSheet.present();
|
||||
};
|
||||
|
||||
const doFavouriseCocktail = async (cocktail) => {
|
||||
favouriseCocktail(cocktail);
|
||||
};
|
||||
|
||||
const showShareOptions = async (cocktail) => {
|
||||
//console.log("share ", cocktail.name);
|
||||
var options = {
|
||||
message: `Check out this fresh cocktail ${cocktail.name} at CocktailShaker APP`, // not supported on some apps (Facebook, Instagram)
|
||||
subject: `i found a new cocktail you could be interested in: ${cocktail.name}`, // fi. for email
|
||||
files: [`${publicPath}img/glasses/${cocktail.glass}.svg`], // an array of filenames either locally or remotely
|
||||
url: "www.northscorp.de/cocktailshaker",
|
||||
chooserTitle: "Share " + cocktail.name, // Android only, you can override the default share sheet title
|
||||
//appPackageName: 'com.apple.social.facebook', // Android only, you can provide id of the App you want to share with
|
||||
//iPadCoordinates: '0,0,0,0' //IOS only iPadCoordinates for where the popover should be point. Format with x,y,width,height
|
||||
};
|
||||
|
||||
const actionSheet = await actionSheetController.create({
|
||||
header: "Share Cocktail: " + cocktail.name,
|
||||
buttons: [
|
||||
{
|
||||
text: "Email",
|
||||
icon: mailOutline,
|
||||
handler: () => {
|
||||
socialSharing.shareViaEmail(
|
||||
options.message + "\n" + options.url,
|
||||
options.subject
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Facebook",
|
||||
icon: logoFacebook,
|
||||
handler: () => {
|
||||
socialSharing.shareViaFacebook(
|
||||
options.message,
|
||||
options.files[0],
|
||||
options.url
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Instagram",
|
||||
icon: logoInstagram,
|
||||
handler: () => {
|
||||
socialSharing.shareViaInstagram(
|
||||
options.message,
|
||||
options.files[0]
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "SMS",
|
||||
icon: callOutline,
|
||||
handler: () => {
|
||||
socialSharing.shareViaSMS(options.message);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Twitter",
|
||||
icon: logoTwitter,
|
||||
handler: () => {
|
||||
socialSharing.shareViaTwitter(
|
||||
options.message,
|
||||
options.files[0],
|
||||
options.url
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Whatsapp",
|
||||
icon: logoWhatsapp,
|
||||
handler: () => {
|
||||
socialSharing.shareViaWhatsApp(
|
||||
options.message,
|
||||
options.files[0],
|
||||
options.url
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
icon: close,
|
||||
role: "cancel",
|
||||
handler: () => {
|
||||
// Nothing to do, action sheet is automatically closed
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await actionSheet.present();
|
||||
};
|
||||
|
||||
const doRestoreCocktails = async () => {
|
||||
const actionSheet = await actionSheetController.create({
|
||||
header: "Cocktail",
|
||||
buttons: [
|
||||
{
|
||||
text: "Restore Cocktails will remove all changes made to the cocktaillist!",
|
||||
role: "destructive",
|
||||
icon: trash,
|
||||
handler: () => {
|
||||
restoreCocktails();
|
||||
window.scrollTo({ top: 0, left: 0 });
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
icon: close,
|
||||
role: "cancel",
|
||||
handler: () => {
|
||||
// Nothing to do, action sheet is automatically closed
|
||||
window.scrollTo({ top: 0, left: 0 });
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await actionSheet.present();
|
||||
};
|
||||
|
||||
return {
|
||||
cocktails,
|
||||
doDeleteCocktail,
|
||||
doFavouriseCocktail,
|
||||
showShareOptions,
|
||||
doRestoreCocktails,
|
||||
router,
|
||||
addCircleOutline,
|
||||
removeCircleOutline,
|
||||
star,
|
||||
trash,
|
||||
camera,
|
||||
chevronDownCircleOutline,
|
||||
shareSocialOutline,
|
||||
createOutline,
|
||||
eyeOutline,
|
||||
keyOutline,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ion-list {
|
||||
padding-bottom: 35px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
.golden {
|
||||
color: gold;
|
||||
}
|
||||
.green {
|
||||
color: limegreen;
|
||||
}
|
||||
</style>
|
||||
252
src/views/EditCocktail.vue
Normal file
252
src/views/EditCocktail.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ mode }} Cocktail</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">{{ mode }} Cocktail</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<form v-on:submit.prevent="doEditCocktail(cocktail)">
|
||||
<ion-item>
|
||||
<ion-label position="floating">Name</ion-label>
|
||||
<ion-input
|
||||
type="text"
|
||||
inputmode="text"
|
||||
minlength="3"
|
||||
maxlength="100"
|
||||
name="name"
|
||||
v-model.trim="cocktail.name"
|
||||
required
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-divider>
|
||||
<ion-label slot="start" position="fixed">Ingredients</ion-label>
|
||||
<ion-button slot="end" @click="addIngredientField()">
|
||||
+ Add
|
||||
</ion-button>
|
||||
</ion-item-divider>
|
||||
<ion-item
|
||||
v-for="(ingredient, index) in cocktail.ingredients"
|
||||
:key="index"
|
||||
v-bind:item="ingredient"
|
||||
v-bind:index="index"
|
||||
>
|
||||
<!--amount-->
|
||||
<ion-input
|
||||
placeholder="amount"
|
||||
type="number"
|
||||
name="{{ingredient}}-amount"
|
||||
:value="ingredient.amount"
|
||||
v-model.number="cocktail.ingredients[index].amount"
|
||||
max="100"
|
||||
min="1"
|
||||
required
|
||||
></ion-input>
|
||||
<!--Unit-->
|
||||
<ion-select
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
name="{{ingredient}}-unit"
|
||||
:value="ingredient.unit"
|
||||
:v-model="cocktail.ingredients[index].unit"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="unit"
|
||||
v-for="unit in units"
|
||||
:key="unit"
|
||||
>{{ unit }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
<!--Ingredient-->
|
||||
<ion-select
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
placeholder="ingredient"
|
||||
name="{{ingredient}}-ingredient"
|
||||
:value="ingredient.ingredient"
|
||||
:v-model="cocktail.ingredients[index].ingredient"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="ingred"
|
||||
v-for="ingred in ingredientList"
|
||||
:key="ingred"
|
||||
>{{ ingred.name }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
<p>{{ingredient.ingredient === cocktail.ingredients[index].ingredient}}</p>
|
||||
|
||||
<ion-icon
|
||||
:icon="trash"
|
||||
@click="removeIngredient(ingredient)"
|
||||
></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Directions</ion-label>
|
||||
<ion-textarea
|
||||
rows="6"
|
||||
type="text"
|
||||
inputmode="text"
|
||||
minlength="3"
|
||||
maxlength="250"
|
||||
name="directions"
|
||||
v-model="cocktail.directions"
|
||||
spellcheck="true"
|
||||
required
|
||||
></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="fixed">Glass</ion-label>
|
||||
<ion-select
|
||||
placeholder="Select glass"
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
name="glass"
|
||||
v-model="cocktail.glass"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="glass"
|
||||
v-for="glass in glasses"
|
||||
:key="glass"
|
||||
>{{ glass }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-button
|
||||
slot="start"
|
||||
color="danger"
|
||||
@click="() => router.push('/tabs/Cocktails')"
|
||||
>
|
||||
<ion-icon :icon="trash"></ion-icon>
|
||||
Cancel
|
||||
</ion-button>
|
||||
<ion-button slot="end" color="success" type="submit">
|
||||
<!--v-bind:disabled="errors.any()"-->
|
||||
<ion-icon :icon="saveOutline"></ion-icon>
|
||||
Save
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
</form>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
//import router from "@/router";
|
||||
import glassJson from "../data/glasses.json";
|
||||
import ingredientJson from "../data/ingredients.json";
|
||||
import unitJson from "../data/units.json";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonTextarea,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonItemGroup,
|
||||
IonItemDivider,
|
||||
} from "@ionic/vue";
|
||||
import { trash, saveOutline,refreshOutline } from "ionicons/icons";
|
||||
export default {
|
||||
name: "Cocktails",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonTextarea,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonItemGroup,
|
||||
IonItemDivider,
|
||||
},
|
||||
methods: {
|
||||
addIngredientField() {
|
||||
this.cocktail.ingredients.push({
|
||||
quantity: 0,
|
||||
unit: "ml",
|
||||
ingredient: null,
|
||||
});
|
||||
},
|
||||
removeIngredient(item) {
|
||||
const index = this.cocktail.ingredients.indexOf(item);
|
||||
if (index > -1) {
|
||||
this.cocktail.ingredients.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const { cocktails } = useStorage();
|
||||
return {
|
||||
mode: "Edit",
|
||||
cocktails: cocktails,
|
||||
ingredientList: ingredientJson,
|
||||
glasses: glassJson,
|
||||
units: unitJson,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
cocktail(){
|
||||
let editCocktail = this.cocktails[parseInt(this.$route.params.id, 10)];
|
||||
//console.log(parseInt(this.$route.params.id, 10), editCocktail)
|
||||
return editCocktail
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
//const { editCocktail } = useStorage();
|
||||
|
||||
const doEditCocktail = async (cocktail) => {
|
||||
//console.log("editing: ", cocktail);
|
||||
cocktail?cocktail:false;
|
||||
//editCocktail(cocktail);
|
||||
//router.push("/tabs/Cocktails")
|
||||
};
|
||||
|
||||
return {
|
||||
doEditCocktail,
|
||||
trash,
|
||||
saveOutline,
|
||||
refreshOutline,
|
||||
router,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ion-item {
|
||||
width: 95%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
155
src/views/MotionDetector.vue
Normal file
155
src/views/MotionDetector.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title> Motion Detector </ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title id="top" size="large"> Motion Detector </ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<!-- View Shake button -->
|
||||
<div class="container">
|
||||
<ion-button
|
||||
color="success"
|
||||
expand="block"
|
||||
v-if="platform.indexOf('mobile') > -1"
|
||||
@click="activateMotionSensor()"
|
||||
>Activate motion sensor</ion-button
|
||||
>
|
||||
|
||||
<ion-list-header>
|
||||
<ion-label>Data</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>Platform:</ion-label>
|
||||
{{ platform }}
|
||||
</ion-item>
|
||||
<ion-item> <ion-label>x:</ion-label></ion-item>
|
||||
<ion-item> <ion-label>y:</ion-label></ion-item>
|
||||
<ion-item> <ion-label>z:</ion-label></ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-item>
|
||||
{{state.acc}}
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive } from "vue";
|
||||
import {
|
||||
getPlatforms,
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
} from "@ionic/vue";
|
||||
|
||||
//import { Motion } from "@capacitor/motion";
|
||||
export default {
|
||||
name: "Shake",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
platform: getPlatforms(),
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
acc: null,
|
||||
});
|
||||
|
||||
const requestDeviceMotion = () => {
|
||||
if (window.DeviceMotionEvent == null) {
|
||||
showError("DeviceMotion is not supported.");
|
||||
} else if (DeviceMotionEvent.requestPermission) {
|
||||
DeviceMotionEvent.requestPermission().then(
|
||||
function (state) {
|
||||
if (state == "granted") {
|
||||
createMotionSubscription();
|
||||
} else {
|
||||
//console.log("Permission denied by user");
|
||||
}
|
||||
},
|
||||
function (err) {
|
||||
showError(err);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// no need for permission
|
||||
//console.log("no need for permission");
|
||||
createMotionSubscription();
|
||||
}
|
||||
};
|
||||
|
||||
const createMotionSubscription = async () => {
|
||||
if (window.DeviceOrientationEvent) {
|
||||
//console.log("start listening");
|
||||
window.addEventListener(
|
||||
"deviceorientation",
|
||||
function (e) {
|
||||
state.acc = e;
|
||||
//console.log("Device motion event:", e.acceleration.x);
|
||||
},
|
||||
false
|
||||
);
|
||||
setTimeout(function () {
|
||||
//console.log("pause listening");
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const showError = () => {
|
||||
//console.log("Motion error", error);
|
||||
};
|
||||
|
||||
//createMotionSubscription();
|
||||
requestDeviceMotion();
|
||||
return { state, requestDeviceMotion, createMotionSubscription };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
padding-bottom: 35px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
ion-badge {
|
||||
margin: 0 3px 0 3px;
|
||||
}
|
||||
ion-list,
|
||||
#shorter {
|
||||
width: 90%;
|
||||
}
|
||||
ion-button {
|
||||
margin: 50px;
|
||||
}
|
||||
</style>
|
||||
258
src/views/NewCocktail.vue
Normal file
258
src/views/NewCocktail.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{ mode }} Cocktail</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">{{ mode }} Cocktail</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<form v-on:submit.prevent="doAddCocktail(cocktail)">
|
||||
<ion-item>
|
||||
<ion-label position="floating">Name</ion-label>
|
||||
<ion-input
|
||||
type="text"
|
||||
inputmode="text"
|
||||
minlength="3"
|
||||
maxlength="100"
|
||||
name="name"
|
||||
v-model.trim="cocktail.name"
|
||||
required
|
||||
></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-divider>
|
||||
<ion-label slot="start" position="fixed">Ingredients</ion-label>
|
||||
<ion-button slot="end" @click="addIngredientField()">
|
||||
+ Add
|
||||
</ion-button>
|
||||
</ion-item-divider>
|
||||
<ion-item
|
||||
v-for="(ingredient, index) in cocktail.ingredients"
|
||||
:key="index"
|
||||
v-bind:item="ingredient"
|
||||
v-bind:index="index"
|
||||
>
|
||||
<ion-input
|
||||
:placeholder="ingredient.amount"
|
||||
type="number"
|
||||
name="{{ingredient}}-amount"
|
||||
v-model.number="cocktail.ingredients[index].amount"
|
||||
max="100"
|
||||
min="1"
|
||||
required
|
||||
></ion-input>
|
||||
<ion-select
|
||||
:value="ingredient.unit"
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
name="{{ingredient}}-unit"
|
||||
:v-model="cocktail.ingredients[index].unit"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="unit"
|
||||
v-for="unit in units"
|
||||
:key="unit"
|
||||
>{{ unit }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
<ion-select
|
||||
placeholder="add Ingredient"
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
name="{{ingredient}}-name"
|
||||
:v-model="cocktail.ingredients[index].ingredient"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="ingred"
|
||||
v-for="ingred in ingredients"
|
||||
:key="ingred"
|
||||
>{{ ingred.name }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
|
||||
<ion-icon
|
||||
:icon="trash"
|
||||
@click="removeIngredient(ingredient)"
|
||||
></ion-icon>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Directions</ion-label>
|
||||
<ion-textarea
|
||||
rows="6"
|
||||
type="text"
|
||||
inputmode="text"
|
||||
minlength="3"
|
||||
maxlength="250"
|
||||
name="directions"
|
||||
v-model="cocktail.directions"
|
||||
spellcheck="true"
|
||||
required
|
||||
></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="fixed">Glass</ion-label>
|
||||
<ion-select
|
||||
placeholder="Select glass"
|
||||
ok-text="Okay"
|
||||
cancel-text="Dismiss"
|
||||
name="glass"
|
||||
v-model="cocktail.glass"
|
||||
required
|
||||
>
|
||||
<ion-select-option
|
||||
:value="glass"
|
||||
v-for="glass in glasses"
|
||||
:key="glass"
|
||||
>{{ glass }}</ion-select-option
|
||||
>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-button
|
||||
slot="start"
|
||||
color="danger"
|
||||
@click="() => router.push('/tabs/Cocktails')"
|
||||
>
|
||||
<ion-icon :icon="trash"></ion-icon>
|
||||
Cancel
|
||||
</ion-button>
|
||||
<ion-button
|
||||
slot="secondary"
|
||||
color="warning"
|
||||
@click="resetForm()"
|
||||
>
|
||||
<ion-icon :icon="refreshOutline"></ion-icon>
|
||||
Reset
|
||||
</ion-button>
|
||||
<ion-button slot="end" color="success" type="submit">
|
||||
<!--v-bind:disabled="errors.any()"-->
|
||||
<ion-icon :icon="createOutline"></ion-icon>
|
||||
Add
|
||||
</ion-button>
|
||||
</ion-toolbar>
|
||||
</form>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
//import router from "@/router";
|
||||
import glassJson from "../data/glasses.json";
|
||||
import ingredientJson from "../data/ingredients.json";
|
||||
import unitJson from "../data/units.json";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonTextarea,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonItemGroup,
|
||||
IonItemDivider,
|
||||
} from "@ionic/vue";
|
||||
import { trash, createOutline,refreshOutline } from "ionicons/icons";
|
||||
export default {
|
||||
name: "Cocktails",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonLabel,
|
||||
IonItem,
|
||||
IonInput,
|
||||
IonTextarea,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonItemGroup,
|
||||
IonItemDivider,
|
||||
},
|
||||
methods: {
|
||||
addIngredientField() {
|
||||
this.cocktail.ingredients.push({
|
||||
amount: 0,
|
||||
unit: "ml",
|
||||
ingredient: null,
|
||||
});
|
||||
},
|
||||
removeIngredient(item) {
|
||||
const index = this.cocktail.ingredients.indexOf(item);
|
||||
if (index > -1) {
|
||||
this.cocktail.ingredients.splice(index, 1);
|
||||
}
|
||||
},
|
||||
resetForm(){
|
||||
this.cocktail = {
|
||||
name: null,
|
||||
ingredients: [{ amount: 0, unit: "ml", ingredient: null }],
|
||||
directions: null,
|
||||
glass: null,
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mode: "New", //"Edit"
|
||||
cocktail: {
|
||||
name: null,
|
||||
ingredients: [{ amount: 0, unit: "ml", ingredient: null }],
|
||||
directions: null,
|
||||
glass: null,
|
||||
},
|
||||
ingredients: ingredientJson,
|
||||
glasses: glassJson,
|
||||
units: unitJson,
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const { addCocktail } = useStorage();
|
||||
|
||||
const doAddCocktail = async (cocktail) => {
|
||||
//console.log("adding: ", cocktail.name);
|
||||
addCocktail(cocktail);
|
||||
router.push("/tabs/Cocktails")
|
||||
};
|
||||
|
||||
return {
|
||||
doAddCocktail,
|
||||
trash,
|
||||
createOutline,
|
||||
refreshOutline,
|
||||
router,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ion-item {
|
||||
width: 95%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
69
src/views/PageNotFound.vue
Normal file
69
src/views/PageNotFound.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Error 404</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<container>
|
||||
<h1>Error 404</h1>
|
||||
<p>We are sorry, this page does not exist</p>
|
||||
<br />
|
||||
<ion-button color="success" expand="block" href="/"
|
||||
>Back to home</ion-button
|
||||
>
|
||||
</container>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonButton,
|
||||
} from "@ionic/vue";
|
||||
|
||||
export default {
|
||||
name: "404",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButton,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
container {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
container p {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
color: #8c8c8c;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
container ion-button {
|
||||
margin: auto;
|
||||
width: 70%;
|
||||
}
|
||||
</style>
|
||||
206
src/views/Shake.vue
Normal file
206
src/views/Shake.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{
|
||||
chosenCocktail ? chosenCocktail.name : "Shake"
|
||||
}}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title id="top" size="large">
|
||||
{{ chosenCocktail ? chosenCocktail.name : "Shake" }}
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<!-- View Shake button -->
|
||||
<div class="container" v-if="!chosenCocktail">
|
||||
<h1 class="ion-text-center">
|
||||
Shake your phone <br />
|
||||
to get a Cocktail
|
||||
</h1>
|
||||
<ion-button color="success" expand="block" @click="doShake()"
|
||||
>Start shaking</ion-button
|
||||
>
|
||||
<ion-img
|
||||
class="image-icon"
|
||||
:src="`${publicPath}img/glasses/Martini.svg`"
|
||||
alt="CocktailShaker"
|
||||
></ion-img>
|
||||
</div>
|
||||
|
||||
<!-- Show cocktail -->
|
||||
<div class="container" v-else>
|
||||
<div class="images">
|
||||
<ion-img
|
||||
class="image-icon"
|
||||
v-if="!chosenCocktail.image"
|
||||
:src="`${publicPath}img/glasses/${chosenCocktail.glass}.svg`"
|
||||
:alt="'Glass: ' + chosenCocktail.glass"
|
||||
></ion-img>
|
||||
<ion-img
|
||||
class="bigimage"
|
||||
v-else
|
||||
:src="chosenCocktail.image.webviewPath"
|
||||
:alt="chosenCocktail.glass"
|
||||
></ion-img>
|
||||
</div>
|
||||
<ion-list-header>
|
||||
<ion-label>Ingredients</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-list>
|
||||
<ion-item
|
||||
v-for="ingredient in chosenCocktail.ingredients"
|
||||
:key="ingredient"
|
||||
>
|
||||
<div v-if="ingredient.amount">
|
||||
{{ ingredient.amount }} {{ ingredient.unit }}
|
||||
{{ ingredient.ingredient }}
|
||||
<span class="small"> {{ ingredient.label }}</span>
|
||||
</div>
|
||||
<div v-else>Spezial: {{ ingredient.special }}</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<hr />
|
||||
<ion-list-header>
|
||||
<ion-label>Directions</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item id="shorter">{{ chosenCocktail.directions }}</ion-item>
|
||||
<hr />
|
||||
<div v-if="chosenCocktail.garnish">
|
||||
<ion-list-header>
|
||||
<ion-label>Garnish</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item id="shorter">{{ chosenCocktail.garnish }}</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="buttonContainer">
|
||||
<!--Take Photo -->
|
||||
<ion-button color="secondary" @click="takePhoto(chosenCocktail)">
|
||||
<ion-icon :icon="camera"></ion-icon>
|
||||
</ion-button>
|
||||
<!--Next Cocktail-->
|
||||
<ion-button color="success" @click="doShake()"
|
||||
>Shake again</ion-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
import {
|
||||
getPlatforms,
|
||||
IonPage,
|
||||
IonIcon,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
IonImg,
|
||||
} from "@ionic/vue";
|
||||
import { camera } from "ionicons/icons";
|
||||
export default {
|
||||
name: "Shake",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonIcon,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
IonImg,
|
||||
},
|
||||
data: function () {
|
||||
const { cocktails } = useStorage();
|
||||
return {
|
||||
cocktails: cocktails,
|
||||
chosenCocktail: null,
|
||||
publicPath: process.env.BASE_URL,
|
||||
platform: getPlatforms(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
doShake: function () {
|
||||
const {fetchRandomCocktail} = useStorage();
|
||||
window.scrollTo({ top: 0, left: 0, behavior: "smooth" });
|
||||
if(Math.random() > 0.8){
|
||||
fetchRandomCocktail();
|
||||
}
|
||||
this.chosenCocktail =
|
||||
this.cocktails[Math.floor(Math.random() * this.cocktails.length)];
|
||||
//console.log(this.chosenCocktail.name);
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { takePhoto } = useStorage();
|
||||
|
||||
return {
|
||||
camera,
|
||||
takePhoto,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
padding-bottom: 35px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
ion-badge {
|
||||
margin: 0 3px 0 3px;
|
||||
}
|
||||
ion-list,
|
||||
#shorter {
|
||||
width: 90%;
|
||||
}
|
||||
.images {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
ion-img.image-icon {
|
||||
filter: invert(68%) sepia(39%) saturate(476%) hue-rotate(86deg)
|
||||
brightness(118%) contrast(119%);
|
||||
height: 60px;
|
||||
margin: auto;
|
||||
border: none;
|
||||
}
|
||||
ion-img.bigimage {
|
||||
max-width: 70%;
|
||||
height: auto;
|
||||
border: 1px solid white;
|
||||
padding: 3px;
|
||||
margin: auto;
|
||||
}
|
||||
.small {
|
||||
font-size: smaller;
|
||||
color: rgb(173, 173, 173);
|
||||
}
|
||||
.buttonContainer {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
ion-button {
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
34
src/views/Tabs.vue
Normal file
34
src/views/Tabs.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-tabs>
|
||||
<ion-tab-bar slot="bottom">
|
||||
|
||||
<ion-tab-button tab="Shake" href="/tabs/shake">
|
||||
<ion-icon :icon="dice" />
|
||||
<ion-label>Shake</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="Cocktails" href="/tabs/cocktails">
|
||||
<ion-icon :icon="listOutline" />
|
||||
<ion-label>Cocktails</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { IonTabBar, IonTabButton, IonTabs, IonLabel, IonIcon, IonPage } from '@ionic/vue';
|
||||
import { dice, listOutline } from 'ionicons/icons';
|
||||
|
||||
export default {
|
||||
name: 'Tabs',
|
||||
components: { IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage },
|
||||
setup() {
|
||||
return {
|
||||
dice,
|
||||
listOutline
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
172
src/views/ViewCocktail.vue
Normal file
172
src/views/ViewCocktail.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>{{
|
||||
chosenCocktail ? chosenCocktail.name : "View"
|
||||
}}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title id="top" size="large">
|
||||
{{ chosenCocktail ? chosenCocktail.name : "View" }}
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<!-- View wait -->
|
||||
<div class="container" v-if="!chosenCocktail">
|
||||
<h1 class="ion-text-center">Please wait</h1>
|
||||
</div>
|
||||
<!-- Show cocktail -->
|
||||
<div class="container" v-else>
|
||||
<div class="images">
|
||||
<ion-img
|
||||
class="image-icon"
|
||||
v-if="!chosenCocktail.image"
|
||||
:src="`${publicPath}img/glasses/${chosenCocktail.glass}.svg`"
|
||||
:alt="'Glass: ' + chosenCocktail.glass"
|
||||
></ion-img>
|
||||
<ion-img
|
||||
class="bigimage"
|
||||
v-else
|
||||
:src="chosenCocktail.image.webviewPath"
|
||||
:alt="chosenCocktail.glass"
|
||||
></ion-img>
|
||||
</div>
|
||||
|
||||
<ion-list-header>
|
||||
<ion-label>Ingredients</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-list>
|
||||
<ion-item
|
||||
v-for="ingredient in chosenCocktail.ingredients"
|
||||
:key="ingredient"
|
||||
>
|
||||
<div v-if="ingredient.amount">
|
||||
{{ ingredient.amount }} {{ ingredient.unit }}
|
||||
{{ ingredient.ingredient }}
|
||||
<span class="small"> {{ ingredient.label }}</span>
|
||||
</div>
|
||||
<div v-else>Spezial: {{ ingredient.special }}</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<hr />
|
||||
<ion-list-header>
|
||||
<ion-label>Directions</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item id="shorter">{{ chosenCocktail.directions }}</ion-item>
|
||||
<hr />
|
||||
<div v-if="chosenCocktail.garnish">
|
||||
<ion-list-header>
|
||||
<ion-label>Garnish</ion-label>
|
||||
</ion-list-header>
|
||||
<ion-item id="shorter">{{ chosenCocktail.garnish }}</ion-item>
|
||||
</div>
|
||||
|
||||
<ion-button
|
||||
color="success"
|
||||
expand="block"
|
||||
@click="() => router.push(`/tabs/Shake`)"
|
||||
>get a new Cocktail</ion-button
|
||||
>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStorage } from "@/composables/useStorage";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
IonImg,
|
||||
} from "@ionic/vue";
|
||||
export default {
|
||||
name: "Shake",
|
||||
components: {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonItem,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonLabel,
|
||||
IonButton,
|
||||
IonImg,
|
||||
},
|
||||
data() {
|
||||
const { cocktails } = useStorage();
|
||||
return {
|
||||
cocktails: cocktails,
|
||||
publicPath: process.env.BASE_URL
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
chosenCocktail() {
|
||||
return this.cocktails[parseInt(this.$route.params.id, 10)];
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
return {
|
||||
router
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
padding-bottom: 35px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
ion-badge {
|
||||
margin: 0 3px 0 3px;
|
||||
}
|
||||
ion-list,
|
||||
#shorter {
|
||||
width: 90%;
|
||||
}
|
||||
ion-button {
|
||||
margin: 50px;
|
||||
}
|
||||
.images {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
ion-img.image-icon {
|
||||
filter: invert(68%) sepia(39%) saturate(476%) hue-rotate(86deg)
|
||||
brightness(118%) contrast(119%);
|
||||
height: 60px;
|
||||
margin: auto;
|
||||
border: none;
|
||||
}
|
||||
ion-img.bigimage {
|
||||
max-width: 70%;
|
||||
height: auto;
|
||||
border: 1px solid white;
|
||||
padding: 3px;
|
||||
margin: auto;
|
||||
}
|
||||
.small {
|
||||
font-size: smaller;
|
||||
color: rgb(173, 173, 173);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user