init project first version

This commit is contained in:
2021-06-17 20:31:10 +02:00
commit 60bf85d0d3
52 changed files with 17943 additions and 0 deletions

208
src/views/ApiKey.vue Normal file
View 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
View 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
View 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>

View 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
View 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>

View 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
View 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">&nbsp;&nbsp; {{ 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
View 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
View 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">&nbsp;&nbsp; {{ 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>