add basic functionality
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
ff2dea6a76
commit
d5b29bdc91
@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useShoppingListStore } from '../store/shoppingList';
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const store = useShoppingListStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isValid = ref(false);
|
||||||
|
const item = reactive({
|
||||||
|
name: '',
|
||||||
|
price: 0.99,
|
||||||
|
user: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = ref(null);
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
store.items.push({
|
||||||
|
name: item.name,
|
||||||
|
price: item.price * 100,
|
||||||
|
user: store.users.find(user => user.name === item.user),
|
||||||
|
});
|
||||||
|
router.push('/shopping-list');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-form ref="form" v-model="isValid" lazy-validation>
|
||||||
|
<v-text-field
|
||||||
|
label="Item Name"
|
||||||
|
required
|
||||||
|
v-model="item.name"
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
label="Price"
|
||||||
|
suffix="€"
|
||||||
|
required
|
||||||
|
v-model="item.price"
|
||||||
|
/>
|
||||||
|
<v-select
|
||||||
|
label="User"
|
||||||
|
:items="store.users.map(user => user.name)"
|
||||||
|
required
|
||||||
|
v-model="item.user"
|
||||||
|
/>
|
||||||
|
<v-btn color="primary" @click="submit()">add item</v-btn>
|
||||||
|
</v-form>
|
||||||
|
</template>
|
@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, defineProps } from 'vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
tabs: Array,
|
||||||
|
title: String,
|
||||||
|
});
|
||||||
|
const tab = ref(null);
|
||||||
|
|
||||||
|
const back = () => {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-app-bar
|
||||||
|
:collapse="false"
|
||||||
|
:collapse-on-scroll="true"
|
||||||
|
absolute
|
||||||
|
color="blue accent-4"
|
||||||
|
dark
|
||||||
|
scroll-target="#scrolling-techniques-6"
|
||||||
|
>
|
||||||
|
<v-btn icon @click="back()">
|
||||||
|
<v-icon>mdi-arrow-left</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-toolbar-title>{{ title }}</v-toolbar-title>
|
||||||
|
<template v-if="tabs?.length" v-slot:extension>
|
||||||
|
<v-tabs align-with-title v-model="tab">
|
||||||
|
<slot name="tabs">
|
||||||
|
<v-tab v-for="tab of tabs" :value="tab.name.toLowerCase().replaceAll(' ', '-')">{{ tab.name }}</v-tab>
|
||||||
|
</slot>
|
||||||
|
</v-tabs>
|
||||||
|
</template>
|
||||||
|
</v-app-bar>
|
||||||
|
<v-main>
|
||||||
|
<slot :tab="tab"/>
|
||||||
|
</v-main>
|
||||||
|
</template>
|
@ -1,152 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container>
|
|
||||||
<v-row class="text-center">
|
|
||||||
<v-col cols="12">
|
|
||||||
<v-img
|
|
||||||
:src="logo"
|
|
||||||
class="my-3"
|
|
||||||
contain
|
|
||||||
height="200"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col class="mb-4">
|
|
||||||
<h1 class="display-2 font-weight-bold mb-3">
|
|
||||||
Welcome to the Vuetify 3 Beta
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<h4>Vite Preview</h4>
|
|
||||||
|
|
||||||
<p class="subheading font-weight-regular">
|
|
||||||
For help and collaboration with other Vuetify developers,
|
|
||||||
<br>please join our online
|
|
||||||
<a
|
|
||||||
href="https://community.vuetifyjs.com"
|
|
||||||
target="_blank"
|
|
||||||
>Discord Community</a>
|
|
||||||
</p>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-5">
|
|
||||||
What's next?
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(next, i) in whatsNext"
|
|
||||||
:key="i"
|
|
||||||
:href="next.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ next.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-5">
|
|
||||||
Important Links
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(link, i) in importantLinks"
|
|
||||||
:key="i"
|
|
||||||
:href="link.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ link.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-5">
|
|
||||||
Ecosystem
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(eco, i) in ecosystem"
|
|
||||||
:key="i"
|
|
||||||
:href="eco.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ eco.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import logo from '../assets/logo.svg'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
ecosystem: [
|
|
||||||
{
|
|
||||||
text: 'vuetify-loader',
|
|
||||||
href: 'https://github.com/vuetifyjs/vuetify-loader/tree/next',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'github',
|
|
||||||
href: 'https://github.com/vuetifyjs/vuetify/tree/next',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'awesome-vuetify',
|
|
||||||
href: 'https://github.com/vuetifyjs/awesome-vuetify',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
importantLinks: [
|
|
||||||
{
|
|
||||||
text: 'Chat',
|
|
||||||
href: 'https://community.vuetifyjs.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Made with Vuetify',
|
|
||||||
href: 'https://madewithvuejs.com/vuetify',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Twitter',
|
|
||||||
href: 'https://twitter.com/vuetifyjs',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Articles',
|
|
||||||
href: 'https://medium.com/vuetify',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
logo,
|
|
||||||
whatsNext: [
|
|
||||||
{
|
|
||||||
text: 'Explore components',
|
|
||||||
href: 'https://vuetifyjs.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Roadmap',
|
|
||||||
href: 'https://vuetifyjs.com/introduction/roadmap/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Frequently Asked Questions',
|
|
||||||
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps } from 'vue';
|
||||||
|
import { groupItemsByUser } from '../store/shoppingList';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
items: Array,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-list v-if="items.length" lines="theee" select-strategy="multiple">
|
||||||
|
<div v-for="(items, user) of groupItemsByUser(items)">
|
||||||
|
<v-list-subheader>{{ user }}</v-list-subheader>
|
||||||
|
<div v-for="item in items">
|
||||||
|
<v-list-item @click="item.isBought = !item.isBought">
|
||||||
|
<v-list-item-title>{{ item.name }}</v-list-item-title>
|
||||||
|
<v-list-item-subtitle>{{ item.price / 100 }} €</v-list-item-subtitle>
|
||||||
|
</v-list-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-list>
|
||||||
|
<p v-else>There are no items in this list.</p>
|
||||||
|
</template>
|
@ -0,0 +1,30 @@
|
|||||||
|
const lukas = {
|
||||||
|
name: 'lukas',
|
||||||
|
};
|
||||||
|
|
||||||
|
const laura = {
|
||||||
|
name: 'laura'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const users = [ lukas, laura ];
|
||||||
|
|
||||||
|
export const dummyList = [
|
||||||
|
{
|
||||||
|
name: 'Banana',
|
||||||
|
price: 200,
|
||||||
|
user: lukas,
|
||||||
|
isBought: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Apple',
|
||||||
|
price: 150,
|
||||||
|
user: lukas,
|
||||||
|
isBought: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Butter',
|
||||||
|
price: 149,
|
||||||
|
user: laura,
|
||||||
|
isBought: false,
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,36 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { dummyList, users } from './dummyData';
|
||||||
|
|
||||||
|
type User = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShoppingItem = {
|
||||||
|
name: string;
|
||||||
|
price: number;
|
||||||
|
isBought: boolean;
|
||||||
|
user: User;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useShoppingListStore = defineStore('shoppingList', {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
items: dummyList as Array<ShoppingItem>,
|
||||||
|
users: users as Array<User>,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {},
|
||||||
|
getters: {
|
||||||
|
openItems: state => state.items.filter(item => !item.isBought),
|
||||||
|
boughtItems: state => state.items.filter(item => !!item.isBought),
|
||||||
|
itemsByUser: state => groupItemsByUser(state.items),
|
||||||
|
openItemsByUser: state => groupItemsByUser(state.items.filter(item => !item.isBought)),
|
||||||
|
boughtItemsByUser: state => groupItemsByUser(state.items.filter(item => !!item.isBought)),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const groupItemsByUser = items => items.reduce((groups, item) => {
|
||||||
|
if (!groups[item.user.name]) groups[item.user.name] = [];
|
||||||
|
groups[item.user.name].push(item);
|
||||||
|
return groups;
|
||||||
|
}, {});
|
@ -0,0 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import DefaultPage from '../components/DefaultPage.vue';
|
||||||
|
import AddItemForm from '../components/AddItemForm.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DefaultPage title="Add Item">
|
||||||
|
<v-container>
|
||||||
|
<AddItemForm/>
|
||||||
|
</v-container>
|
||||||
|
</DefaultPage>
|
||||||
|
</template>
|
@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import DefaultPage from '../components/DefaultPage.vue';
|
||||||
|
import ItemList from '../components/ItemList.vue';
|
||||||
|
import { useShoppingListStore } from '../store/shoppingList';
|
||||||
|
|
||||||
|
const store = useShoppingListStore();
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ name: 'open items' },
|
||||||
|
{ name: 'bought items' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DefaultPage :tabs="tabs" v-slot="slot" title="Shopping List">
|
||||||
|
<v-container>
|
||||||
|
<v-window v-model="slot.tab">
|
||||||
|
<v-window-item value="open-items">
|
||||||
|
<ItemList :items="store.openItems"></ItemList>
|
||||||
|
</v-window-item>
|
||||||
|
<v-window-item value="bought-items">
|
||||||
|
<ItemList :items="store.boughtItems"></ItemList>
|
||||||
|
</v-window-item>
|
||||||
|
</v-window>
|
||||||
|
</v-container>
|
||||||
|
<v-card-text style="display: flex; justify-content: end">
|
||||||
|
<v-btn
|
||||||
|
icon="mdi-plus"
|
||||||
|
color="red"
|
||||||
|
to="/add-item"
|
||||||
|
elevation="8"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
</DefaultPage>
|
||||||
|
</template>
|
Loading…
Reference in New Issue