[pricelist] cleanup some code

This commit is contained in:
Tim Gröger 2021-04-13 16:20:32 +02:00
parent 9b0679278c
commit 0626cf993f
10 changed files with 73 additions and 558 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<q-table <q-table
v-model:pagination="pagination" v-model:pagination="pagination"
title="Kalkulationstabelle" title="Preistabelle"
:columns="columns" :columns="columns"
:rows="drinks" :rows="drinks"
:visible-columns="visibleColumn" :visible-columns="visibleColumn"
@ -14,12 +14,12 @@
<template #top-right> <template #top-right>
<div class="row justify-end q-gutter-sm"> <div class="row justify-end q-gutter-sm">
<search-input v-model="search" :keys="search_keys" /> <search-input v-model="search" :keys="search_keys" />
<q-btn label="Aufpreise"> <q-btn v-if="!public && !nodetails" label="Aufpreise">
<q-menu anchor="center middle" self="center middle"> <q-menu anchor="center middle" self="center middle">
<min-price-setting /> <min-price-setting />
</q-menu> </q-menu>
</q-btn> </q-btn>
<q-select <!--<q-select
v-model="visibleColumn" v-model="visibleColumn"
multiple multiple
filled filled
@ -31,8 +31,14 @@
:options="[...columns, ...column_calc, ...column_prices]" :options="[...columns, ...column_calc, ...column_prices]"
option-value="name" option-value="name"
options-cover options-cover
/> />-->
<q-btn color="primary" round icon="mdi-plus" @click="newDrink"> <q-btn
v-if="!public && !nodetails && editable"
color="primary"
round
icon="mdi-plus"
@click="newDrink"
>
<!--<q-menu v-model="showNewDrink" anchor="center middle" self="center middle" persistent> <!--<q-menu v-model="showNewDrink" anchor="center middle" self="center middle" persistent>
<new-drink @close="showNewDrink = false" /> <new-drink @close="showNewDrink = false" />
</q-menu>--> </q-menu>-->
@ -49,7 +55,11 @@
props.row.uuid ? `/api/pricelist/picture/${props.row.uuid}?size=256` : 'no-image.svg' props.row.uuid ? `/api/pricelist/picture/${props.row.uuid}?size=256` : 'no-image.svg'
" "
> >
<div class="absolute-top-right justify-end" style="background-color: transparent"> <div
v-if="!public && !nodetails && editable"
class="absolute-top-right justify-end"
style="background-color: transparent"
>
<q-btn <q-btn
round round
icon="mdi-pencil" icon="mdi-pencil"
@ -77,7 +87,7 @@
{{ tag.name }} {{ tag.name }}
</q-badge> </q-badge>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section v-if="!public && !nodetails">
<div class="fit row"> <div class="fit row">
<q-input <q-input
v-if="props.row.article_id" v-if="props.row.article_id"
@ -131,7 +141,7 @@
</div> </div>
</q-card-section> </q-card-section>
<q-card-section v-if="props.row.volumes.length > 0 && notLoading"> <q-card-section v-if="props.row.volumes.length > 0 && notLoading">
<drink-price-volumes :model-value="props.row.volumes" /> <drink-price-volumes :model-value="props.row.volumes" :public="public" :nodetails="nodetails" />
</q-card-section> </q-card-section>
</q-card> </q-card>
</div> </div>
@ -168,15 +178,38 @@ export default defineComponent({
DrinkPriceVolumes, DrinkPriceVolumes,
DrinkModify, DrinkModify,
}, },
setup() { props: {
public: {
type: Boolean,
default: false,
},
editable: {
type: Boolean,
default: false,
},
nodetails: {
type: Boolean,
default: false,
},
},
setup(props) {
const mainStore = useMainStore(); const mainStore = useMainStore();
const store = usePricelistStore(); const store = usePricelistStore();
onBeforeMount(() => { onBeforeMount(() => {
void store.getPriceCalcColumn(user); void store.getDrinks();
try {
user.value = mainStore.currentUser.userid;
} catch {
user.value = undefined;
}
if (user.value) {
store.getPriceCalcColumn(user.value);
}
}); });
const user = mainStore.currentUser.userid; const user = ref<string>();
const columns = [ const columns = [
{ {
@ -190,6 +223,7 @@ export default defineComponent({
sortable: true, sortable: true,
sort, sort,
filterable: true, filterable: true,
public: true,
}, },
{ {
@ -200,6 +234,7 @@ export default defineComponent({
sortable: true, sortable: true,
sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name), sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name),
filterable: true, filterable: true,
public: true,
}, },
{ {
name: 'tags', name: 'tags',
@ -216,6 +251,7 @@ export default defineComponent({
return retVal; return retVal;
}, },
filterable: true, filterable: true,
public: true,
}, },
{ {
name: 'article_id', name: 'article_id',
@ -224,6 +260,7 @@ export default defineComponent({
sortable: true, sortable: true,
sort, sort,
filterable: true, filterable: true,
public: false,
}, },
{ {
name: 'volume_package', name: 'volume_package',
@ -231,6 +268,7 @@ export default defineComponent({
field: 'volume', field: 'volume',
sortable: true, sortable: true,
sort, sort,
public: false,
}, },
{ {
name: 'package_size', name: 'package_size',
@ -238,6 +276,7 @@ export default defineComponent({
field: 'package_size', field: 'package_size',
sortable: true, sortable: true,
sort, sort,
public: false
}, },
{ {
name: 'cost_per_package', name: 'cost_per_package',
@ -246,6 +285,7 @@ export default defineComponent({
format: (val: number | null) => (val ? `${val.toFixed(3)}` : ''), format: (val: number | null) => (val ? `${val.toFixed(3)}` : ''),
sortable: true, sortable: true,
sort, sort,
public: false,
}, },
{ {
name: 'cost_per_volume', name: 'cost_per_volume',
@ -287,6 +327,7 @@ export default defineComponent({
}, },
filterable: true, filterable: true,
sortable: false, sortable: false,
public: false
}, },
]; ];
const column_calc = [ const column_calc = [
@ -327,11 +368,11 @@ export default defineComponent({
const visibleColumn = computed({ const visibleColumn = computed({
get: () => store.pricecalc_columns, get: () => store.pricecalc_columns,
set: (val) => { set: (val) => {
store.updatePriceCalcColumn(user, val); store.updatePriceCalcColumn(<string>user.value, val);
}, },
}); });
const search_keys = computed(() => columns.filter((column) => column.filterable)); const search_keys = computed(() => columns.filter((column) => column.filterable && ((props.public || props.nodetails) ? column.public : true)));
const pagination = ref({ const pagination = ref({
sortBy: 'name', sortBy: 'name',

View File

@ -31,7 +31,7 @@ export default defineComponent({
name: 'BuildManual', name: 'BuildManual',
props: { props: {
steps: { steps: {
type: Array as PropType<Array<string>>, type: Array as PropType<Array<string>> || undefined,
required: true, required: true,
}, },
editable: { editable: {

View File

@ -1,66 +0,0 @@
<template>
<q-btn color="positive" icon-right="add" label="Abgabe hinzufügen" size="xs">
<q-menu anchor="center middle" self="center middle">
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newVolume.volume"
filled
dense
label="Liter"
type="number"
min="0"
step="0.01"
/>
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddVolume" />
<q-btn v-close-popup label="Speichern" color="primary" @click="addVolume(rows)" />
</div>
</q-menu>
</q-btn>
</template>
<script lang="ts">
import { DrinkPriceVolume } from '../../../store';
import { ref, defineComponent } from 'vue';
export default defineComponent({
name: 'NewVolume',
props: {
pricePerVolume: {
type: undefined,
required: true,
},
},
emits: { addVolume: (val: DrinkPriceVolume) => !!val },
setup(_, { emit }) {
const emptyVolume: DrinkPriceVolume = {
id: -1,
_volume: 0,
min_prices: [],
prices: [],
ingredients: [],
};
const newVolume = ref<DrinkPriceVolume>(emptyVolume);
function cancelAddVolume() {
setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
newVolume.value = emptyVolume;
}, 200);
}
function addVolume() {
emit('addVolume', <DrinkPriceVolume>newVolume.value);
}
return {
addVolume,
cancelAddVolume,
newVolume,
};
},
});
</script>
<style scoped></style>

View File

@ -30,7 +30,7 @@
</q-btn> </q-btn>
</div> </div>
</div> </div>
<div class="full-width row q-gutter-sm q-pa-sm justify-around"> <div v-if="!public && !nodetails" class="full-width row q-gutter-sm q-pa-sm justify-around">
<div v-for="(min_price, index) in volume.min_prices" :key="index"> <div v-for="(min_price, index) in volume.min_prices" :key="index">
<q-badge class="text-body1" color="primary"> {{ min_price.percentage }}% </q-badge> <q-badge class="text-body1" color="primary"> {{ min_price.percentage }}% </q-badge>
<div class="text-body1">{{ min_price.price.toFixed(3) }}</div> <div class="text-body1">{{ min_price.price.toFixed(3) }}</div>
@ -93,6 +93,7 @@
</div> </div>
<div class="q-pa-sm"> <div class="q-pa-sm">
<ingredients <ingredients
v-if="!public"
v-model="volume.ingredients" v-model="volume.ingredients"
:editable="editable" :editable="editable"
@update="updateVolume(volume)" @update="updateVolume(volume)"
@ -131,6 +132,14 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
public: {
type: Boolean,
default: false,
},
nodetails: {
type: Boolean,
default: false,
}
}, },
emits: { emits: {
'update:modelValue': (val: Array<DrinkPriceVolume>) => val, 'update:modelValue': (val: Array<DrinkPriceVolume>) => val,

View File

@ -1,204 +0,0 @@
<template>
<q-table
:columns="columns"
:rows="rows"
dense
:visible-columns="visibleColumns"
row-key="id"
flat
>
<template #header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template #body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
v-if="!drink.cost_price_pro_volume"
size="sm"
color="accent"
round
dense
:icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'"
@click="props.expand = !props.expand"
/>
<q-btn
v-if="props.row.ingredients.length === 0 && props.row.prices.length === 0"
size="xs"
color="negative"
round
dense
icon="mdi-delete"
class="q-mx-sm"
@click="deleteVolume(props.row, drink)"
/>
</q-td>
<q-td key="volume" :props="props">
{{ parseFloat(props.row.volume).toFixed(3) }}L
<q-popup-edit
v-if="rows.cost_price_pro_volume"
v-model="props.row.volume"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
@save="updateVolume(props.row, drink)"
>
<q-input v-model.number="props.row.volume" dense filled type="number" suffix="L" />
</q-popup-edit>
</q-td>
<q-td key="min_prices" :props="props">
<div
v-for="(min_price, index) in props.row.min_prices"
:key="`min_prices` + index"
class="row justify-between"
>
<div class="col">
<q-badge color="primary">{{ min_price.percentage }}%</q-badge>
</div>
<div class="col" style="text-align: end">
{{ min_price.price ? min_price.price.toFixed(3) : Number(0).toFixed(2) }}
</div>
</div>
</q-td>
<q-td key="prices" :props="props">
<!--<price-table
:columns="column_prices"
:rows="props.row.prices"
:row="props.row"
:visible-columns="visibleColumns"
@updateDrink="updateDrink"
/>-->
</q-td>
</q-tr>
<q-tr v-show="props.expand" :props="props">
<q-td colspan="100%">
<ingredients
:ingredients="props.row.ingredients"
:volume="props.row"
@updateDrink="updateDrink"
@addIngredient="addIngredient"
@deleteIngredient="deleteIngredient"
/>
</q-td>
</q-tr>
</template>
<template #bottom>
<div class="full-width row justify-end">
<new-volume
:price-per-volume="drink.cost_price_pro_volume"
@addVolume="addVolume($event, drink)"
/>
</div>
</template>
<template #no-data>
<div class="full-width row justify-end">
<new-volume
:price-per-volume="drink.cost_price_pro_volume"
@addVolume="addVolume($event, drink)"
/>
</div>
</template>
</q-table>
</template>
<script lang="ts">
import { Drink, DrinkPriceVolume } from '../../store';
import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue';
import NewVolume from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumeTable/NewVolume.vue';
import { PropType, defineComponent } from 'vue';
const columns = [
{
name: 'volume',
label: 'Abgabe in l',
field: 'volume',
},
{
name: 'min_prices',
label: 'Minimal Preise',
field: 'min_prices',
},
{
name: 'prices',
label: 'Preise',
field: 'prices',
},
];
export default defineComponent({
name: 'DrinkPriceVolumsTable',
components: { Ingredients, NewVolume },
props: {
visibleColumns: {
type: Array,
default: columns,
},
columns: {
type: Array,
default: columns,
},
rows: {
type: Array as PropType<Array<DrinkPriceVolume>>,
required: true,
},
drink: {
type: Object as PropType<Drink>,
required: true,
},
},
emits: { updateDrink: () => true },
setup(_, { emit }) {
//const store = usePricelistStore();
function addVolume(volume: DrinkPriceVolume, drink: Drink) {
drink.volumes.push(volume);
updateDrink();
}
function updateDrink() {
emit('updateDrink');
}
function deleteVolume() {
//store.deleteVolume(volume, drink);
return;
}
const column_prices = [
{
name: 'price',
label: 'Preis',
field: 'price',
format: (val: number) => `${val.toFixed(2)}`,
},
{
name: 'description',
label: 'Beschreibung',
field: 'description',
},
{
name: 'public',
label: 'Öffentlich',
field: 'public',
},
];
function addIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
volume.ingredients.push(ingredient);
updateDrink();
}
function deleteIngredient() {
//store.deleteIngredient(ingredient, volume);
return;
}
return { addVolume, updateDrink, deleteVolume, column_prices, addIngredient, deleteIngredient };
},
});
</script>
<style scoped></style>

View File

@ -141,7 +141,7 @@
</div> </div>
<div class="full-width row jusitfy-between q-gutter-sm q-pa-sm"> <div class="full-width row jusitfy-between q-gutter-sm q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddIngredient" /> <q-btn v-close-popup label="Abbrechen" @click="cancelAddIngredient" />
<q-btn v-close-popup label="Speichern" color="positive" @click="addIngredient" /> <q-btn v-close-popup label="Speichern" color="primary" @click="addIngredient" />
</div> </div>
</q-menu> </q-menu>
</q-btn> </q-btn>

View File

@ -1,265 +0,0 @@
<template>
<div>
<q-table
class="full-width"
title="Getränke"
:columns="columns_drinks"
:rows="drinks"
row-key="name"
:visible-columns="visibleColumn"
:filter="search"
:filter-method="filter"
>
<template #top-right>
<div class="row q-gutter-sm">
<search-input v-model="search" :keys="search_keys" />
<q-select
v-model="visibleColumn"
multiple
filled
dense
options-dense
display-value="Sichtbarkeit"
emit-value
map-options
:options="[...columns_drinks, ...columns_volumes, ...columns_prices]"
option-value="name"
options-cover
/>
</div>
</template>
<template #body-cell-tags="props">
<q-td :props="props">
<q-badge
v-for="tag in props.row.tags"
:key="`${props.row.id}-${tag.id}`"
class="q-ma-xs"
rounded
:style="`background-color: ${tag.color}`"
>
{{ tag.name }}
</q-badge>
</q-td>
</template>
<template #body-cell-volumes="props">
<q-td :props="props">
<q-table
:columns="columns_volumes"
:rows="props.row.volumes"
hide-header
:hide-bottom="props.row.volumes.length < 5"
flat
:visible-columns="visibleColumn"
>
<template #body-cell-prices="props_volumes">
<q-td :props="props_volumes">
<q-table
:columns="columns_prices"
:rows="props_volumes.row.prices"
hide-header
:hide-bottom="props_volumes.row.prices.length < 5"
flat
:visible-columns="visibleColumn"
>
<template #body-cell-public="props_prices">
<q-td :props="props_prices">
<q-toggle v-model="props_prices.row.public" disable />
</q-td>
</template>
</q-table>
</q-td>
</template>
</q-table>
</q-td>
</template>
<template #body-cell-picture="drinks_props">
<q-td :props="drinks_props" style="min-width: 256px">
<q-img
loading="lazy"
:src="
drinks_props.row.uuid
? `/api/pricelist/picture/${drinks_props.row.uuid}?size=256`
: 'no-image.svg'
"
placeholder-src="no-image.svg"
fit="contain"
style="max-height: 300px"
>
<template #error>
<div class="absolute-full flex flex-center bg-negative text-white">
Cannot load image
</div>
</template>
</q-img>
</q-td>
</template>
</q-table>
</div>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, computed, ref } from 'vue';
import { usePricelistStore } from '../store';
import { useMainStore } from 'src/stores';
import SearchInput from './SearchInput.vue';
import { filter, Search } from '../utils/filter';
import { sort } from '../utils/sort';
export default defineComponent({
name: 'Pricelist',
components: { SearchInput },
filters: {
setVolume(volume: number) {
if (volume * 10 > 1) {
return `${volume}l`;
}
return `${volume * 100}cl`;
},
setCurrency(price: number) {
return `${price.toFixed(2)}`;
},
},
props: {
public: {
type: Boolean,
default: false,
},
},
setup() {
let user: string | null;
onBeforeMount(() => {
void store.getDrinks();
try {
user = mainStore.currentUser.userid;
} catch {
user = null;
}
if (user) {
store.getPriceCalcColumn(user);
}
});
const store = usePricelistStore();
const drinks = computed(() => store.drinks);
const columns_drinks = [
{
name: 'picture',
label: 'Bild',
align: 'center',
},
{
name: 'name',
label: 'Name',
field: 'name',
align: 'center',
sortable: true,
filterable: true,
},
{
name: 'drink_type',
label: 'Kategorie',
field: 'type',
format: (val: FG.DrinkType) => `${val.name}`,
sortable: true,
sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name),
filterable: true,
},
{
name: 'tags',
label: 'Tags',
field: 'tags',
format: (val: Array<FG.Tag>) => {
let retVal = '';
val.forEach((tag, index) => {
if (index > 0) {
retVal += ', ';
}
retVal += tag.name;
});
return retVal;
},
filterable: true,
},
{
name: 'volumes',
label: 'Preise',
field: 'volumes',
align: 'center',
},
];
const columns_volumes = [
{
name: 'volume',
label: 'Inhalt',
field: 'volume',
format: (val: number) => `${val.toFixed(3)}L`,
align: 'left',
},
{
name: 'prices',
label: 'Preise',
field: 'prices',
},
];
const columns_prices = [
{
name: 'price',
label: 'Preis',
field: 'price',
format: (val: number) => `${val.toFixed(2)}`,
},
{
name: 'description',
label: 'Beschreibung',
field: 'description',
},
{
name: 'public',
label: 'Öffentlich',
field: 'public',
},
];
const canBeVisible = createVisibleColumns();
const _visibleColumns = ref(canBeVisible);
const visibleColumn = computed({
get: () => {
if (user) {
return store.pricecalc_columns;
} else {
return _visibleColumns.value;
}
},
set: (val) => {
if (user) {
store.updatePriceCalcColumn(user, val);
} else {
_visibleColumns.value = val;
}
},
});
const mainStore = useMainStore();
function createVisibleColumns() {
const retVal: Array<string> = [];
columns_drinks.forEach((drink) => retVal.push(drink.name));
columns_volumes.forEach((volume) => retVal.push(volume.name));
columns_prices.forEach((price) => {
if (user || price.name !== 'public') retVal.push(price.name);
});
return retVal;
}
const search = ref<Search>({ label: '', value: '', key: '' });
const search_keys = computed(() => columns_drinks.filter((column) => column.filterable));
return {
columns_drinks,
columns_volumes,
columns_prices,
drinks,
visibleColumn,
search,
filter,
search_keys,
};
},
});
</script>

View File

@ -1,12 +1,12 @@
<template> <template>
<pricelist /> <calculation-table nodetails />
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Pricelist from '../components/Pricelist.vue'; import CalculationTable from '../components/CalculationTable.vue';
export default defineComponent({ export default defineComponent({
name: 'InnerPricelist', name: 'InnerPricelist',
components: { Pricelist }, components: {CalculationTable},
setup() { setup() {
return {}; return {};
}, },

View File

@ -1,12 +1,12 @@
<template> <template>
<pricelist public /> <calculation-table public />
</template> </template>
<script> <script>
import Pricelist from '../components/Pricelist.vue'; import CalculationTable from '../components/CalculationTable.vue';
export default { export default {
name: 'OuterPricelist', name: 'OuterPricelist',
components: { Pricelist }, components: { CalculationTable },
}; };
</script> </script>

View File

@ -32,7 +32,7 @@
class="q-ma-none q-pa-none fit row justify-center content-start items-start" class="q-ma-none q-pa-none fit row justify-center content-start items-start"
> >
<q-tab-panel name="pricelist"> <q-tab-panel name="pricelist">
<calculation-table /> <calculation-table editable />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="extra_ingredients"> <q-tab-panel name="extra_ingredients">
<extra-ingredients /> <extra-ingredients />