Merge branch 'feature/pricelist' into develop
This commit is contained in:
commit
a8a6cd8814
|
@ -8,7 +8,7 @@
|
|||
"homepage": "https://flaschengeist.dev/Flaschengeist",
|
||||
"description": "Modular student club administration system",
|
||||
"bugs": {
|
||||
"url" : "https://flaschengeist.dev/Flaschengeist/flaschengeist-frontend/issues"
|
||||
"url": "https://flaschengeist.dev/Flaschengeist/flaschengeist-frontend/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --config ./package.json --write '{,!(node_modules)/**/}*.ts'",
|
||||
|
@ -18,7 +18,8 @@
|
|||
"axios": "^0.21.1",
|
||||
"cordova": "^10.0.0",
|
||||
"pinia": "^2.0.0-alpha.10",
|
||||
"quasar": "^2.0.0-beta.12"
|
||||
"quasar": "^2.0.0-beta.12",
|
||||
"vuedraggable": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app": "^3.0.0-beta.13",
|
||||
|
@ -29,6 +30,8 @@
|
|||
"@types/webpack-env": "^1.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.20.0",
|
||||
"@typescript-eslint/parser": "^4.20.0",
|
||||
"electron": "^12.0.4",
|
||||
"electron-packager": "^14.1.1",
|
||||
"eslint": "^7.23.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-vue": "^7.8.0",
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1024"
|
||||
height="1024"
|
||||
viewBox="0 0 270.93333 270.93334"
|
||||
version="1.1"
|
||||
id="svg37"
|
||||
inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
|
||||
sodipodi:docname="no-image.svg">
|
||||
<defs
|
||||
id="defs31">
|
||||
<rect
|
||||
x="-328.72475"
|
||||
y="24.854798"
|
||||
width="167.56944"
|
||||
height="62.537879"
|
||||
id="rect1100" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.66"
|
||||
inkscape:cx="-222.85714"
|
||||
inkscape:cy="248.57143"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1303"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata34">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<circle
|
||||
id="path10"
|
||||
cx="135.46666"
|
||||
cy="135.46666"
|
||||
style="fill:#1976d2;fill-opacity:1;stroke-width:0.265065;opacity:1"
|
||||
r="135.46666" />
|
||||
<path
|
||||
id="path897"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1976d2;stroke-width:2.4226772;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="M 443.15234 56.630859 A 66.428573 82.142858 16.845913 0 0 371.42188 118.32031 A 66.428573 82.142858 16.845913 0 0 411.19531 216.18945 A 66.428573 82.142858 16.845913 0 0 417.72266 217.72852 C 381.34438 302.48672 370.45756 410.53422 348.14648 499.98438 C 297.07736 687.66963 128.47878 622.66455 146.45508 716.38281 C 160.50823 789.6481 350.53291 863.07431 404.40039 881.20117 C 404.36927 881.70781 404.3327 882.22702 404.30664 882.7207 C 402.76028 912.46642 410.7207 918.72461 410.7207 918.72461 L 432.67188 918.59766 C 432.67187 918.59766 438.27004 922.75802 441.00195 922.53125 C 443.73387 922.30455 446.94531 918.40625 446.94531 918.40625 L 516.89062 924.79297 C 516.89062 924.79297 519.31971 928.09007 521.125 928.27148 C 522.93029 928.45267 525.90234 925.73828 525.90234 925.73828 L 547.53125 926.69531 C 547.53125 926.69531 549.70216 931.12195 551.70508 931.54492 C 553.70725 931.97129 555.09081 928.73815 557.0293 929.03711 C 572.07401 931.3732 576.56924 937.03281 582.65039 957.60156 C 587.77505 974.93535 595.22123 992.9761 628.75781 1000.5176 C 648.78447 1005.021 775.66385 1000.6487 849.12305 998.74219 C 849.12305 998.74219 858.5188 1005.9328 870.60547 1002.9863 C 910.19976 993.33421 919.78542 952.33706 923.33398 921.09961 C 926.88262 889.86212 910.49308 842.14037 892.4043 835.26367 C 874.31552 828.38693 867.54688 834.25781 867.54688 834.25781 C 794.38713 828.15886 695.31802 794.91067 642.43555 796.74805 C 623.72658 797.39809 605.92942 809.14688 595.65625 826.28125 C 588.66186 837.94703 583.48245 859.20701 563.14258 859.07812 C 560.89588 859.11237 559.32296 855.73577 557.25 855.80078 C 555.17708 855.86568 552.45117 858.87891 552.45117 858.87891 L 531.66797 857.41211 C 531.66797 857.41211 529.50203 853.62212 527.45508 853.38477 C 525.40816 853.14741 521.82422 856.70312 521.82422 856.70312 L 449.90234 850.62305 C 449.90234 850.62305 447.13702 845.72836 444.7168 844.78711 C 442.29665 843.84582 436.68945 846.35352 436.68945 846.35352 L 415.74219 842.4082 C 415.74219 842.4082 407.89779 846.84558 405.02539 873.69922 C 358.98121 845.1153 229.07948 771.28872 265.40039 726.04102 C 306.49077 674.8517 472.92361 721.17976 650.12695 656.71875 C 726.82706 628.35646 797.61671 580.25753 845.84766 519.08398 A 66.376657 87.827348 48.964508 0 0 927.6875 519.24805 A 66.376657 87.827348 48.964508 0 0 980.98047 413.88477 A 66.376657 87.827348 48.964508 0 0 907.37109 380.61328 C 911.18565 353.15829 910.56576 324.56748 904.71094 295.1582 C 874.19541 141.87518 734.80037 48.0882 582.23438 78.189453 C 572.33148 80.143182 562.48437 82.604632 552.73047 85.566406 C 533.35813 91.448951 516.25153 99.658419 501.06836 109.80859 A 66.428573 82.142858 16.845913 0 0 458.80469 58.953125 A 66.428573 82.142858 16.845913 0 0 443.15234 56.630859 z M 598.64062 188.20703 C 604.22194 188.22929 609.06312 189.70004 612.92773 192.57031 C 630.98057 205.9773 623.87627 246.11384 597.05859 282.2168 C 570.24164 318.31869 533.86885 336.71569 515.81836 323.30664 C 497.76711 309.89888 504.87302 269.76201 531.68945 233.66016 C 549.9558 209.06922 573.57677 191.68513 592.82031 188.66992 L 592.82227 188.66992 C 594.83831 188.35408 596.78019 188.19961 598.64062 188.20703 z M 778.42969 278.44141 C 784.01155 278.46343 788.85382 279.93226 792.71875 282.80273 C 810.7689 296.21155 803.66213 336.34605 776.8457 372.44727 C 750.02943 408.54893 713.65672 426.94663 695.60547 413.53906 C 677.55528 400.13024 684.66205 359.99578 711.47852 323.89453 C 729.74482 299.3036 753.36587 281.91757 772.60938 278.90234 C 774.62562 278.58637 776.56907 278.43406 778.42969 278.44141 z M 582.87695 399.91016 C 586.53338 399.95128 589.93604 400.53593 593.03711 401.66406 C 620.67041 411.71655 618.60959 461.69743 588.43555 513.29883 C 558.26146 564.90004 511.39926 598.58305 483.76562 588.53125 C 456.13229 578.47876 458.1931 528.49788 488.36719 476.89648 C 510.90103 438.36065 543.79364 408.38759 571.19336 401.41992 C 575.31072 400.37297 579.22053 399.86903 582.87695 399.91016 z M 643.83594 816.76172 C 685.1496 816.94824 851.34766 854.11133 851.34766 854.11133 C 851.34766 854.11133 688.31573 835.12065 650.18555 833.82812 C 635.17621 833.31932 591.27148 868.21875 591.27148 868.21875 C 591.27148 868.21875 622.20895 822.68111 636.20117 817.55664 C 637.73138 816.99622 640.33478 816.74591 643.83594 816.76172 z "
|
||||
transform="scale(0.26458333)" />
|
||||
<path
|
||||
id="path10-8"
|
||||
style="fill-opacity:1;stroke-width:3.64724414;fill:#ffffff;stroke-miterlimit:4;stroke-dasharray:none;stroke:#ffffff;stroke-opacity:1;opacity:0.80057803"
|
||||
d="M 512 0 A 511.99997 511.99997 0 0 0 0 512 A 511.99997 511.99997 0 0 0 512 1024 A 511.99997 511.99997 0 0 0 659.25 1002.3672 C 706.36552 1003.0651 793.17503 1000.1943 849.12305 998.74219 C 849.12305 998.74219 858.5188 1005.9328 870.60547 1002.9863 C 910.19976 993.33421 919.78542 952.33706 923.33398 921.09961 C 926.20746 895.80534 916.007 859.7095 902.41797 843.23633 A 511.99997 511.99997 0 0 0 1024 512 A 511.99997 511.99997 0 0 0 512 0 z "
|
||||
transform="scale(0.26458333)" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.33699;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect942-1"
|
||||
width="76.028526"
|
||||
height="73.388649"
|
||||
x="-21.278112"
|
||||
y="109.32571"
|
||||
ry="3.5350549"
|
||||
transform="rotate(-30.00892)" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.391977;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect944-7"
|
||||
width="69.4505"
|
||||
height="56.151112"
|
||||
x="-17.909185"
|
||||
y="113.14103"
|
||||
ry="0"
|
||||
transform="rotate(-30.00892)" />
|
||||
<path
|
||||
id="path10-3-94"
|
||||
style="fill:#1976d2;fill-opacity:1;stroke-width:0.0683297"
|
||||
d="m 68.884108,90.871044 a 34.92124,34.92124 0 0 0 -11.74769,43.865396 l 2.70637,4.68588 a 34.92124,34.92124 0 0 0 15.52651,12.5468 L 123.2496,124.31547 a 34.92124,34.92124 0 0 0 -2.38462,-18.10096 l -4.46922,-7.73814 A 34.92124,34.92124 0 0 0 73.568128,88.16575 Z" />
|
||||
<path
|
||||
id="path897-6-8"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1976d2;stroke-width:0.165241;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 69.039278,95.120659 a 5.6025989,4.5307973 76.836993 0 0 -2.13225,6.090461 5.6025989,4.5307973 76.836993 0 0 5.68756,4.42371 5.6025989,4.5307973 76.836993 0 0 0.43811,-0.13196 c 0.74268,6.24695 3.78537,13.00002 5.51895,19.04423 3.38604,12.82723 -8.78931,14.73905 -4.53067,19.66107 1.76551,2.04052 6.4553,2.39549 11.0381,2.16453 l 3.43497,-1.98389 c -3.77377,-0.36303 -7.4885,-1.26121 -7.11831,-3.66758 0.68073,-4.42505 12.09089,-7.36618 20.358052,-17.21816 3.5626,-4.29154 6.10303,-9.54716 6.86491,-14.80546 a 4.5272563,5.9903123 18.955588 0 0 4.83918,-2.78197 4.5272563,5.9903123 18.955588 0 0 -0.44664,-8.041018 4.5272563,5.9903123 18.955588 0 0 -5.48235,0.545791 c -0.71126,-1.751685 -1.72325,-3.419125 -3.07226,-4.956384 -7.031112,-8.012324 -18.463402,-8.796579 -26.447502,-1.814405 -0.51824,0.453207 -1.01578,0.934617 -1.49084,1.442271 -0.94352,1.008269 -1.67389,2.076631 -2.2244,3.194051 a 5.6025989,4.5307973 76.836993 0 0 -4.23087,-1.562013 5.6025989,4.5307973 76.836993 0 0 -1.00374,0.396734 z m 13.67184,2.467287 c 0.33015,-0.189526 0.66651,-0.267379 0.99269,-0.229688 1.52358,0.176032 2.47307,2.788932 2.12069,5.836062 -0.35237,3.04705 -1.87308,5.37436 -3.3966,5.19813 -1.52351,-0.17613 -2.47289,-2.78913 -2.12054,-5.83615 0.24002,-2.07551 1.04204,-3.90793 2.07575,-4.742444 l 1.7e-4,-9.5e-5 c 0.10831,-0.08739 0.21769,-0.162799 0.32783,-0.225833 z m 13.69685,-0.803574 c 0.33018,-0.189545 0.6664,-0.267533 0.9926,-0.229843 1.52349,0.176235 2.4729,2.789172 2.12054,5.836151 -0.35234,3.04702 -1.87298,5.37441 -3.39651,5.1983 -1.52348,-0.17624 -2.4729,-2.78917 -2.12054,-5.83617 0.24,-2.075497 1.04211,-3.908161 2.07582,-4.742688 0.10834,-0.0874 0.21795,-0.162716 0.32809,-0.22575 z m -7.40627,13.845038 c 0.21745,-0.12213 0.43816,-0.20399 0.65981,-0.24312 1.97501,-0.34891 3.55829,2.67343 3.53636,6.75042 -0.0219,4.077 -1.64072,7.66488 -3.61571,8.01383 -1.975,0.34891 -3.55819,-2.67326 -3.53627,-6.75027 0.0164,-3.04468 0.93655,-5.93703 2.31716,-7.28321 0.20748,-0.20228 0.42129,-0.36535 0.63865,-0.48765 z m 17.352832,21.22847 c -0.10315,0.0588 -0.20272,0.11824 -0.29832,0.17807 -1.05105,0.65671 -1.69275,1.90208 -1.75,3.22056 l 1.74663,-1.00878 c 0.10074,-0.33823 0.21509,-0.61452 0.34506,-0.77008 0.0713,-0.0853 0.2166,-0.18885 0.42393,-0.30733 0.72661,-0.41504 2.22115,-1.01463 3.95458,-1.65068 l 5.84666,-3.37678 c -3.78613,1.29335 -7.9478,2.39117 -10.26846,3.71516 z" />
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.33699;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect942"
|
||||
width="76.028526"
|
||||
height="73.388649"
|
||||
x="97.497429"
|
||||
y="66.694847"
|
||||
ry="3.5350549" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.391977;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect944"
|
||||
width="69.4505"
|
||||
height="56.151112"
|
||||
x="100.86639"
|
||||
y="70.51017"
|
||||
ry="0" />
|
||||
<path
|
||||
id="path10-3"
|
||||
style="fill:#1976d2;fill-opacity:1;stroke-width:0.0683297"
|
||||
d="M 132.97782,70.510038 A 34.92124,34.92124 0 0 0 100.8663,102.61974 v 5.41128 a 34.92124,34.92124 0 0 0 7.17007,18.63021 h 55.29238 a 34.92124,34.92124 0 0 0 6.98797,-16.86711 v -8.93604 A 34.92124,34.92124 0 0 0 138.38694,70.510038 Z" />
|
||||
<path
|
||||
id="path897-6"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1976d2;stroke-width:0.165241;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 130.9868,74.267588 a 4.5307973,5.6025989 16.845913 0 0 -4.89247,4.20761 4.5307973,5.6025989 16.845913 0 0 2.71268,6.675244 4.5307973,5.6025989 16.845913 0 0 0.44538,0.10485 c -2.4812,5.78097 -3.22383,13.15053 -4.74557,19.251518 -3.4832,12.80118 -14.98258,8.3674 -13.7565,14.75951 0.5083,2.64997 4.39189,5.30288 8.47586,7.39491 h 3.96671 c -3.08632,-2.20176 -5.85386,-4.8374 -4.32979,-6.73605 2.80259,-3.4914 14.15415,-0.33165 26.2404,-4.72825 5.23137,-1.93447 10.05977,-5.215 13.34938,-9.38737 a 4.5272563,5.9903123 48.964508 0 0 5.58183,0.0112 4.5272563,5.9903123 48.964508 0 0 3.63483,-7.186478 4.5272563,5.9903123 48.964508 0 0 -5.0204,-2.26929 c 0.26017,-1.87259 0.21778,-3.82264 -0.18155,-5.82851 -2.08133,-10.454754 -11.58886,-16.851564 -21.9947,-14.798494 -0.67543,0.13326 -1.34705,0.3013 -2.01232,0.50331 -1.32131,0.40122 -2.4881,0.96108 -3.52367,1.65338 a 4.5307973,5.6025989 16.845913 0 0 -2.8825,-3.46863 4.5307973,5.6025989 16.845913 0 0 -1.0676,-0.15845 z m 10.60512,8.974304 c 0.38068,10e-4 0.71089,0.10181 0.97449,0.29758 1.2313,0.91443 0.74671,3.65194 -1.08241,6.11436 -1.82906,2.46235 -4.30989,3.71712 -5.54104,2.80255 -1.23119,-0.91448 -0.74644,-3.65202 1.08259,-6.11436 1.24587,-1.67724 2.85684,-2.8629 4.16936,-3.06855 h 1.8e-4 c 0.1375,-0.0215 0.26994,-0.0321 0.39683,-0.0316 z m 12.26265,6.15442 c 0.38072,0.001 0.71088,0.10162 0.97449,0.2974 1.23112,0.91456 0.74644,3.65206 -1.08258,6.11436 -1.82902,2.46234 -4.30984,3.71721 -5.54104,2.80274 -1.23112,-0.91456 -0.74645,-3.65206 1.08258,-6.11437 1.24586,-1.67724 2.85702,-2.86307 4.16954,-3.06873 0.13752,-0.0215 0.27011,-0.0319 0.39701,-0.0314 z m -13.33783,8.28494 c 0.24939,0.003 0.48145,0.0425 0.69297,0.11947 1.88474,0.68563 1.74421,4.094668 -0.31382,7.614168 -2.05805,3.51949 -5.25426,5.8168 -7.13902,5.13121 -1.88474,-0.68563 -1.74422,-4.09448 0.31382,-7.61399 1.53693,-2.62835 3.78032,-4.672758 5.64914,-5.147988 0.28082,-0.0714 0.54752,-0.10567 0.79691,-0.10287 z m 4.40955,27.061498 c -0.11872,-7e-4 -0.23468,0.001 -0.34739,0.005 -1.2386,0.043 -2.41713,0.80049 -3.12612,1.9136 h 2.01701 c 0.2564,-0.24251 0.4936,-0.42457 0.68395,-0.49428 0.10436,-0.0382 0.28201,-0.0552 0.52081,-0.0541 0.83678,0.004 2.43085,0.23225 4.25002,0.54842 h 6.75175 c -3.92545,-0.7736 -8.07829,-1.90434 -10.75003,-1.91848 z" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.33699;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect942-6"
|
||||
width="76.028526"
|
||||
height="73.388649"
|
||||
x="183.4511"
|
||||
y="-21.159952"
|
||||
ry="3.5350549"
|
||||
transform="rotate(27.418518)" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.391977;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect944-6"
|
||||
width="69.4505"
|
||||
height="56.151112"
|
||||
x="186.82002"
|
||||
y="-17.344629"
|
||||
ry="0"
|
||||
transform="rotate(27.418518)" />
|
||||
<path
|
||||
id="path10-3-9"
|
||||
style="fill:#1976d2;fill-opacity:1;stroke-width:0.0683297"
|
||||
d="m 202.32517,85.418659 a 34.92124,34.92124 0 0 0 -43.2904,13.715795 l -2.49182,4.803416 a 34.92124,34.92124 0 0 0 -2.21435,19.83912 l 49.0812,25.46141 a 34.92124,34.92124 0 0 0 13.97007,-11.7545 l 4.11493,-7.93223 A 34.92124,34.92124 0 0 0 207.12667,87.90949 Z" />
|
||||
<path
|
||||
id="path897-6-3"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1976d2;stroke-width:0.165241;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 198.82751,87.837273 a 4.5307973,5.6025989 44.264431 0 0 -6.28043,1.482039 4.5307973,5.6025989 44.264431 0 0 -0.6659,7.174546 4.5307973,5.6025989 44.264431 0 0 0.34706,0.298162 c -4.86454,3.98902 -8.91733,10.18876 -13.07755,14.90366 -8.9867,9.75921 -17.15262,0.52818 -19.00775,6.76684 -0.76908,2.58635 1.45663,6.72959 4.11849,10.46723 l 3.52111,1.82662 c -1.72575,-3.37564 -2.96872,-6.98962 -0.74155,-7.97318 4.09551,-1.80864 12.71689,6.22341 25.47003,7.88625 5.5345,0.69181 11.33116,0.003 16.17256,-2.18564 a 4.5272563,5.9903123 76.383026 0 0 4.94964,2.5803 4.5272563,5.9903123 76.383026 0 0 6.5358,-4.70541 4.5272563,5.9903123 76.383026 0 0 -3.41147,-4.3262 c 1.09325,-1.54243 1.95359,-3.29295 2.5228,-5.25738 2.96674,-10.23876 -2.52713,-20.295083 -12.70946,-23.264393 -0.66092,-0.192737 -1.33447,-0.352844 -2.01803,-0.479873 -1.35764,-0.252295 -2.65117,-0.292618 -3.88921,-0.154954 a 4.5307973,5.6025989 44.264431 0 0 -0.96143,-4.406339 4.5307973,5.6025989 44.264431 0 0 -0.87472,-0.632269 z m 5.28126,12.849707 c 0.33746,0.17619 0.58416,0.41773 0.72799,0.71289 0.67191,1.37871 -1.01884,3.58556 -3.7764,4.92908 -2.75748,1.34349 -5.53743,1.31492 -6.20913,-0.0638 -0.67178,-1.3787 1.01911,-3.58551 3.77656,-4.929 1.87826,-0.91512 3.85425,-1.22576 5.11402,-0.80391 l 1.7e-4,8e-5 c 0.13196,0.0442 0.25439,0.0958 0.3668,0.15469 z m 8.05112,11.10986 c 0.33749,0.17621 0.58422,0.41755 0.72807,0.71273 0.67168,1.37874 -1.01913,3.58554 -3.77655,4.929 -2.75744,1.3435 -5.53743,1.31502 -6.20922,-0.0637 -0.67169,-1.37874 1.01912,-3.58554 3.77655,-4.92901 1.87826,-0.91512 3.8545,-1.22583 5.11428,-0.80399 0.13197,0.0442 0.25445,0.0961 0.36687,0.15495 z m -15.65465,1.21237 c 0.21999,0.1175 0.4078,0.25943 0.56011,0.42515 1.3573,1.47651 -0.33727,4.43789 -3.7848,6.61434 -3.44753,2.17643 -7.34258,2.74386 -8.69992,1.26738 -1.3573,-1.47651 0.33717,-4.43772 3.78471,-6.61418 2.57461,-1.62536 5.50741,-2.40706 7.38513,-1.96834 0.28216,0.0659 0.53468,0.15833 0.75477,0.27565 z m -8.54725,26.05213 c -0.10506,-0.0553 -0.20878,-0.10718 -0.31067,-0.15553 -1.11927,-0.53219 -2.51422,-0.40249 -3.65614,0.2591 l 1.79043,0.92881 c 0.33927,-0.0972 0.63366,-0.14958 0.83473,-0.12381 0.11023,0.0142 0.27575,0.0809 0.48722,0.19181 0.74094,0.38887 2.05083,1.32553 3.52006,2.44389 l 5.9933,3.10909 c -3.12826,-2.49432 -6.2939,-5.41036 -8.65901,-6.65322 z" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
id="text1098"
|
||||
style="font-style:normal;font-weight:normal;font-size:22.57779999999999987px;line-height:1.35;font-family:sans-serif;white-space:pre;shape-inside:url(#rect1100);fill:#000000;fill-opacity:1;stroke:none;"
|
||||
x="52.690414"
|
||||
y="0"
|
||||
transform="translate(380.03788,147.12437)"><tspan
|
||||
x="-284.65002"
|
||||
y="47.162986"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:22.5778px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold Condensed';text-align:center;text-anchor:middle">Kein Bild </tspan></tspan><tspan
|
||||
x="-292.36704"
|
||||
y="78.223231"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:22.5778px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold Condensed';text-align:center;text-anchor:middle">vorhanden</tspan></tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 18 KiB |
|
@ -1,7 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="de.wu5.flaschengeist" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<widget id="de.wu5.flaschengeist" version="2.0.0-alpha.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>Flaschengeist</name>
|
||||
<description>Dynamischen Managementsystem für Studentenclubs</description>
|
||||
<description>Modular student club administration system</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import 'quasar/dist/types/feature-flag';
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module 'quasar/dist/types/feature-flag' {
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
cordova: true;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
electron: true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { app, BrowserWindow, nativeTheme } from 'electron'
|
||||
import path from 'path'
|
||||
|
||||
try {
|
||||
if (process.platform === 'win32' && nativeTheme.shouldUseDarkColors === true) {
|
||||
require('fs').unlinkSync(require('path').join(app.getPath('userData'), 'DevTools Extensions'))
|
||||
}
|
||||
} catch (_) { }
|
||||
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
/**
|
||||
* Initial window options
|
||||
*/
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1000,
|
||||
height: 600,
|
||||
useContentSize: true,
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
// More info: /quasar-cli/developing-electron-apps/electron-preload-script
|
||||
preload: path.resolve(__dirname, process.env.QUASAR_ELECTRON_PRELOAD)
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.loadURL(process.env.APP_URL)
|
||||
|
||||
if (process.env.DEBUGGING) {
|
||||
// if on DEV or Production with debug enabled
|
||||
mainWindow.webContents.openDevTools()
|
||||
} else {
|
||||
// we're on production; no access to devtools pls
|
||||
mainWindow.webContents.on('devtools-opened', () => {
|
||||
mainWindow.webContents.closeDevTools()
|
||||
})
|
||||
}
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* This file is used specifically for security reasons.
|
||||
* Here you can access Nodejs stuff and inject functionality into
|
||||
* the renderer thread (accessible there through the "window" object)
|
||||
*
|
||||
* WARNING!
|
||||
* If you import anything from node_modules, then make sure that the package is specified
|
||||
* in package.json > dependencies and NOT in devDependencies
|
||||
*
|
||||
* Example (injects window.myAPI.doAThing() into renderer thread):
|
||||
*
|
||||
* const { contextBridge } = require('electron')
|
||||
*
|
||||
* contextBridge.exposeInMainWorld('myAPI', {
|
||||
* doAThing: () => {}
|
||||
* })
|
||||
*/
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
|
@ -1,6 +1,14 @@
|
|||
import {computed} from 'vue';
|
||||
import { LocalStorage } from 'quasar';
|
||||
|
||||
const config = {
|
||||
baseURL: '/api',
|
||||
pollingInterval: 30000,
|
||||
};
|
||||
|
||||
const baseURL = computed(() =>
|
||||
LocalStorage.getItem<string>('baseURL') || config.baseURL
|
||||
);
|
||||
|
||||
export {baseURL}
|
||||
export default config;
|
||||
|
|
|
@ -90,6 +90,8 @@ declare namespace FG {
|
|||
tags?: Array<Tag>;
|
||||
type?: DrinkType;
|
||||
volumes: Array<DrinkPriceVolume>;
|
||||
uuid: string;
|
||||
receipt?: Array<string>;
|
||||
}
|
||||
interface DrinkIngredient {
|
||||
id: number;
|
||||
|
@ -130,5 +132,6 @@ declare namespace FG {
|
|||
interface Tag {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
</q-form>
|
||||
</q-card-section>
|
||||
<div class="row justify-end">
|
||||
<q-btn flat round icon="mdi-menu-down" class="cordova-only" @click="openServerSettings" />
|
||||
<q-btn v-if='$q.platform.is.cordova || $q.platform.is.electron' flat round icon="mdi-menu-down" @click="openServerSettings" />
|
||||
</div>
|
||||
<q-slide-transition class="cordova-only">
|
||||
<q-slide-transition v-if='$q.platform.is.cordova || $q.platform.is.electron'>
|
||||
<div v-show="visible">
|
||||
<q-separator />
|
||||
<q-card-section>
|
||||
|
@ -63,6 +63,7 @@ import { setBaseURL, api } from 'boot/axios';
|
|||
import { notEmpty } from 'src/utils/validators';
|
||||
import { useUserStore } from 'src/plugins/user/store';
|
||||
import PasswordInput from 'src/components/utils/PasswordInput.vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Login',
|
||||
|
@ -77,6 +78,7 @@ export default defineComponent({
|
|||
const password = ref('');
|
||||
const server = ref<string | undefined>(api.defaults.baseURL);
|
||||
const visible = ref(false);
|
||||
const $q = useQuasar()
|
||||
|
||||
function openServerSettings() {
|
||||
visible.value = !visible.value;
|
||||
|
@ -150,6 +152,7 @@ export default defineComponent({
|
|||
server,
|
||||
userid,
|
||||
visible,
|
||||
$q
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<q-carousel
|
||||
v-model="volume"
|
||||
transition-prev="slide-right"
|
||||
transition-next="slide-left"
|
||||
animated
|
||||
swipeable
|
||||
control-color="primary"
|
||||
arrows
|
||||
>
|
||||
<q-carousel-slide v-for="volume in volumes" :key="volume.id" :name="volume.id">
|
||||
<build-manual-volume-part :volume="volume" />
|
||||
</q-carousel-slide>
|
||||
</q-carousel>
|
||||
<div class="full-width row justify-center q-pa-sm">
|
||||
<div class="q-px-sm">
|
||||
<q-btn-toggle v-model="volume" :options="btn_options" rounded />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed } from 'vue';
|
||||
import { DrinkPriceVolume } from '../../store';
|
||||
import BuildManualVolumePart from './BuildManualVolumePart.vue';
|
||||
export default defineComponent({
|
||||
name: 'BuildManualVolume',
|
||||
components: { BuildManualVolumePart },
|
||||
props: {
|
||||
volumes: {
|
||||
type: Array as PropType<Array<DrinkPriceVolume>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const _volume = ref<number>();
|
||||
const volume = computed({
|
||||
get: () => {
|
||||
if (_volume.value !== undefined) {
|
||||
return _volume.value;
|
||||
}
|
||||
return props.volumes[0].id;
|
||||
},
|
||||
set: (val: number) => (_volume.value = val),
|
||||
});
|
||||
const options = computed(() => {
|
||||
let ret: Array<{ label: number; value: number }> = [];
|
||||
props.volumes.forEach((volume: DrinkPriceVolume) => {
|
||||
ret.push({ label: volume.id, value: volume.id });
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
const btn_options = computed<Array<{ label: string; value: number }>>(() => {
|
||||
const retVal: Array<{ label: string; value: number }> = [];
|
||||
props.volumes.forEach((volume: DrinkPriceVolume) => {
|
||||
retVal.push({ label: `${(<number>volume.volume).toFixed(3)}L`, value: volume.id });
|
||||
});
|
||||
return retVal;
|
||||
});
|
||||
return { volume, options, btn_options };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Zutaten</div>
|
||||
<div v-for="ingredient in volume.ingredients" :key="ingredient.id">
|
||||
<div v-if="ingredient.drink_ingredient">
|
||||
<div class="full-width row q-gutter-sm q-py-sm">
|
||||
<div class="col">
|
||||
{{ name(ingredient.drink_ingredient?.ingredient_id) }}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{
|
||||
ingredient.drink_ingredient?.volume
|
||||
? `${ingredient.drink_ingredient?.volume * 100} cl`
|
||||
: ''
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<q-separator />
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="ingredient in volume.ingredients" :key="ingredient.id">
|
||||
<div v-if="ingredient.extra_ingredient">
|
||||
<div class="full-width row q-gutter-sm q-py-sm">
|
||||
<div class="col">
|
||||
{{ ingredient.extra_ingredient?.name }}
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<q-separator />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Preise</div>
|
||||
<div class="full-width row q-gutter-sm justify-around">
|
||||
<div v-for="price in volume.prices" :key="price.id">
|
||||
<div class="text-body1">{{ price.price.toFixed(2) }}€</div>
|
||||
<q-badge v-if="price.public" class="text-caption"> öffentlich </q-badge>
|
||||
<div class="text-caption text-weight-thin">
|
||||
{{ price.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { DrinkPriceVolume, usePricelistStore } from '../../store';
|
||||
export default defineComponent({
|
||||
name: 'BuildManualVolumePart',
|
||||
props: {
|
||||
volume: {
|
||||
type: Object as PropType<DrinkPriceVolume>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
function name(id: number) {
|
||||
return store.drinks.find((a) => a.id === id)?.name;
|
||||
}
|
||||
return { name };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,317 +1,189 @@
|
|||
<template>
|
||||
<q-table
|
||||
v-model:pagination="pagination"
|
||||
title="Kalkulationstabelle"
|
||||
title="Preistabelle"
|
||||
:columns="columns"
|
||||
:rows="drinks"
|
||||
:visible-columns="visibleColumn"
|
||||
:dense="$q.screen.lt.md"
|
||||
row-key="id"
|
||||
virtual-scroll
|
||||
dense
|
||||
:filter="search"
|
||||
:filter-method="filter"
|
||||
grid
|
||||
:rows-per-page-options="[0]"
|
||||
>
|
||||
<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 #top-right>
|
||||
<div class="row justify-end q-gutter-sm">
|
||||
<q-btn label="Aufpreise">
|
||||
<search-input v-model="search" :keys="search_keys" />
|
||||
<slot></slot>
|
||||
<q-btn v-if="!public && !nodetails" label="Aufpreise">
|
||||
<q-menu anchor="center middle" self="center middle">
|
||||
<min-price-setting />
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<q-btn label="neues Getränk" color="positive" icon-right="add">
|
||||
<q-menu v-model="showNewDrink" anchor="center middle" self="center middle">
|
||||
<new-drink @close="showNewDrink = false" />
|
||||
</q-menu>
|
||||
<q-btn
|
||||
v-if="!public && !nodetails && editable && hasPermission(PERMISSIONS.CREATE)"
|
||||
color="primary"
|
||||
round
|
||||
icon="mdi-plus"
|
||||
@click="newDrink"
|
||||
>
|
||||
<q-tooltip> Neues Getränk </q-tooltip>
|
||||
</q-btn>
|
||||
<q-select
|
||||
v-model="visibleColumn"
|
||||
multiple
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
display-value="Sichtbarkeit"
|
||||
emit-value
|
||||
map-options
|
||||
:options="[...columns, ...column_calc, ...column_prices]"
|
||||
option-value="name"
|
||||
options-cover
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="drinks_props">
|
||||
<q-tr :props="drinks_props">
|
||||
<q-td auto-width>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
||||
<q-card>
|
||||
<q-img style="max-height: 256px" :src="image(props.row.uuid)">
|
||||
<div
|
||||
v-if="!public && !nodetails && editable"
|
||||
class="absolute-top-right justify-end"
|
||||
style="background-color: transparent"
|
||||
>
|
||||
<q-btn
|
||||
v-if="drinks_props.row.volumes.length === 0"
|
||||
size="xs"
|
||||
color="negative"
|
||||
round
|
||||
dense
|
||||
icon="mdi-delete"
|
||||
class="q-mx-sm"
|
||||
@click="deleteDrink(drinks_props.row)"
|
||||
icon="mdi-pencil"
|
||||
style="background-color: rgba(0, 0, 0, 0.5)"
|
||||
@click="editDrink = props.row"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td key="picture" :props="drinks_props" style="width: 128px">
|
||||
<q-img
|
||||
:src="`http://localhost/api/pricelist/drinks/${drinks_props.row.id}/picture?size=128`"
|
||||
/>
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="drinkPic"
|
||||
buttons
|
||||
label-set="Speichern"
|
||||
label-cancel="Abbrechen"
|
||||
@update:modelValue="savePicture(drinks_props.row)"
|
||||
>
|
||||
<q-file v-model="scope.value" filled>
|
||||
<template #prepend>
|
||||
<q-icon name="attach_file" />
|
||||
</template>
|
||||
</q-file>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="name" :props="drinks_props">
|
||||
{{ drinks_props.row.name }}
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.name"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
</div>
|
||||
<div class="absolute-bottom-right justify-end">
|
||||
<div class="text-subtitle1 text-right">
|
||||
{{ props.row.name }}
|
||||
</div>
|
||||
<div class="text-caption text-right">
|
||||
{{ props.row.type.name }}
|
||||
</div>
|
||||
</div>
|
||||
</q-img>
|
||||
<q-card-section>
|
||||
<q-badge
|
||||
v-for="tag in props.row.tags"
|
||||
:key="`${props.row.id}-${tag.id}`"
|
||||
class="text-caption"
|
||||
rounded
|
||||
:style="`background-color: ${tag.color}`"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</q-badge>
|
||||
</q-card-section>
|
||||
<q-card-section v-if="!public && !nodetails">
|
||||
<div class="fit row">
|
||||
<q-input
|
||||
v-model="scope.value"
|
||||
filled
|
||||
v-if="props.row.article_id"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
:model-value="props.row.article_id"
|
||||
outlined
|
||||
readonly
|
||||
label="Artikelnummer"
|
||||
dense
|
||||
autofocus
|
||||
clearable
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="drink_type" :props="drinks_props">
|
||||
{{ drinks_props.row.type.name }}
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.type"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-select
|
||||
v-model="scope.value"
|
||||
:options="drinkTypes"
|
||||
option-label="name"
|
||||
filled
|
||||
dense
|
||||
autofocus
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="article_id" :props="drinks_props">
|
||||
{{ drinks_props.row.article_id || 'o.A.' }}
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.article_id"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-input
|
||||
v-model="scope.value"
|
||||
filled
|
||||
v-if="props.row.volume"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
:model-value="props.row.volume"
|
||||
outlined
|
||||
readonly
|
||||
label="Inhalt"
|
||||
dense
|
||||
autofocus
|
||||
clearable
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="volume_package" :props="drinks_props">
|
||||
{{ drinks_props.row.volume ? `${drinks_props.row.volume} L` : 'o.A.' }}
|
||||
<q-popup-edit
|
||||
v-if="
|
||||
!drinks_props.row.volumes.some((volume) =>
|
||||
volume.ingredients.some((ingredient) => ingredient.drink_ingredient)
|
||||
)
|
||||
"
|
||||
v-slot="scope"
|
||||
v-model.number="drinks_props.row.volume"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="scope.value"
|
||||
filled
|
||||
dense
|
||||
autofocus
|
||||
type="number"
|
||||
clearable
|
||||
step="0.01"
|
||||
min="0"
|
||||
suffix="L"
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="package_size" :props="drinks_props">
|
||||
{{ drinks_props.row.package_size || 'o.A.' }}
|
||||
<q-popup-edit
|
||||
v-if="
|
||||
!drinks_props.row.volumes.some((volume) =>
|
||||
volume.ingredients.some((ingredient) => ingredient.drink_ingredient)
|
||||
)
|
||||
"
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.package_size"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="scope.value"
|
||||
filled
|
||||
v-if="props.row.package_size"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
:model-value="props.row.package_size"
|
||||
outlined
|
||||
readonly
|
||||
label="Gebindegröße"
|
||||
dense
|
||||
autofocus
|
||||
type="number"
|
||||
min="0"
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="cost_price_package_netto" :props="drinks_props">
|
||||
{{
|
||||
drinks_props.row.cost_price_package_netto
|
||||
? `${drinks_props.row.cost_price_package_netto.toFixed(2)}€`
|
||||
: 'o.A.'
|
||||
}}
|
||||
<q-popup-edit
|
||||
v-if="
|
||||
!drinks_props.row.volumes.some((volume) =>
|
||||
volume.ingredients.some((ingredient) => ingredient.drink_ingredient)
|
||||
)
|
||||
"
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.cost_price_package_netto"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="scope.value"
|
||||
filled
|
||||
dense
|
||||
autofocus
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
v-if="props.row.cost_per_package"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
:model-value="props.row.cost_per_package"
|
||||
outlined
|
||||
readonly
|
||||
label="Preis Gebinde"
|
||||
suffix="€"
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="cost_price_pro_volume" :props="drinks_props">
|
||||
{{
|
||||
drinks_props.row.cost_price_pro_volume
|
||||
? `${drinks_props.row.cost_price_pro_volume.toFixed(3)}€`
|
||||
: 'o.A.'
|
||||
}}
|
||||
<q-popup-edit
|
||||
v-if="
|
||||
!(
|
||||
!!drinks_props.row.cost_price_package_netto &&
|
||||
!!drinks_props.row.volume &&
|
||||
!!drinks_props.row.package_size
|
||||
) &&
|
||||
!drinks_props.row.volumes.some((volume) =>
|
||||
volume.ingredients.some((ingredient) => ingredient.drink_ingredient)
|
||||
)
|
||||
"
|
||||
v-slot="scope"
|
||||
v-model="drinks_props.row.cost_price_pro_volume"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink(drinks_props.row)"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="scope.value"
|
||||
filled
|
||||
dense
|
||||
autofocus
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
/>
|
||||
<q-input
|
||||
v-if="props.row.cost_per_volume"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm q-pb-lg"
|
||||
:model-value="props.row.cost_per_volume"
|
||||
outlined
|
||||
readonly
|
||||
label="Preis pro L"
|
||||
hint="Inkl. 19% Mehrwertsteuer"
|
||||
suffix="€"
|
||||
@keyup.enter="scope.set"
|
||||
dense
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="volumes" :props="drinks_props">
|
||||
<drink-price-volumes-table
|
||||
:rows="drinks_props.row.volumes"
|
||||
:visible-columns="visibleColumn"
|
||||
:columns="column_calc"
|
||||
:drink="drinks_props.row"
|
||||
@updateDrink="updateDrink(drinks_props.row)"
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section v-if="props.row.volumes.length > 0 && notLoading">
|
||||
<drink-price-volumes
|
||||
:model-value="props.row.volumes"
|
||||
:public="public"
|
||||
:nodetails="nodetails"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
<q-dialog :model-value="editDrink !== undefined" persistent>
|
||||
<drink-modify
|
||||
:drink="editDrink"
|
||||
@save="editing_drink"
|
||||
@cancel="editDrink = undefined"
|
||||
@delete="deleteDrink"
|
||||
/>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
onBeforeMount,
|
||||
ComputedRef,
|
||||
computed,
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
} from 'vue';
|
||||
import DrinkPriceVolumesTable from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumesTable.vue';
|
||||
import { useMainStore } from 'src/stores';
|
||||
import { Drink, usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import { defineComponent, onBeforeMount, ComputedRef, computed, ref } from 'vue';
|
||||
import { Drink, usePricelistStore, DrinkPriceVolume } from 'src/plugins/pricelist/store';
|
||||
import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue';
|
||||
import NewDrink from 'src/plugins/pricelist/components/CalculationTable/NewDrink.vue';
|
||||
import SearchInput from './SearchInput.vue';
|
||||
import DrinkPriceVolumes from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumes.vue';
|
||||
import DrinkModify from './DrinkModify.vue';
|
||||
import { filter, Search } from '../utils/filter';
|
||||
import { Notify } from 'quasar';
|
||||
import { sort } from '../utils/sort';
|
||||
import { DeleteObjects } from 'src/plugins/pricelist/utils/utils';
|
||||
import { hasPermission } from 'src/utils/permission';
|
||||
import { PERMISSIONS } from 'src/plugins/pricelist/permissions';
|
||||
import { baseURL } from 'src/config';
|
||||
|
||||
function sort(a: string | number, b: string | number) {
|
||||
if (a > b) return 1;
|
||||
if (b > a) return -1;
|
||||
return 0;
|
||||
}
|
||||
export default defineComponent({
|
||||
name: 'CalculationTable',
|
||||
components: { MinPriceSetting, DrinkPriceVolumesTable, NewDrink },
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
components: {
|
||||
SearchInput,
|
||||
MinPriceSetting,
|
||||
DrinkPriceVolumes,
|
||||
DrinkModify,
|
||||
},
|
||||
props: {
|
||||
public: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
nodetails: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = usePricelistStore();
|
||||
const root = getCurrentInstance()?.proxy;
|
||||
|
||||
onBeforeMount(() => {
|
||||
store.getPriceCalcColumn(user);
|
||||
void store.getDrinks();
|
||||
});
|
||||
|
||||
const user = mainStore.currentUser.userid;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'picture',
|
||||
|
@ -319,18 +191,14 @@ export default defineComponent({
|
|||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Getränkename',
|
||||
label: 'Name',
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
sort,
|
||||
filterable: true,
|
||||
public: true,
|
||||
},
|
||||
{
|
||||
name: 'article_id',
|
||||
label: 'Artikelnummer',
|
||||
field: 'article_id',
|
||||
sortable: true,
|
||||
sort,
|
||||
},
|
||||
|
||||
{
|
||||
name: 'drink_type',
|
||||
label: 'Kategorie',
|
||||
|
@ -338,6 +206,34 @@ export default defineComponent({
|
|||
format: (val: FG.DrinkType) => `${val.name}`,
|
||||
sortable: true,
|
||||
sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name),
|
||||
filterable: true,
|
||||
public: 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,
|
||||
public: true,
|
||||
},
|
||||
{
|
||||
name: 'article_id',
|
||||
label: 'Artikelnummer',
|
||||
field: 'article_id',
|
||||
sortable: true,
|
||||
sort,
|
||||
filterable: true,
|
||||
public: false,
|
||||
},
|
||||
{
|
||||
name: 'volume_package',
|
||||
|
@ -345,6 +241,7 @@ export default defineComponent({
|
|||
field: 'volume',
|
||||
sortable: true,
|
||||
sort,
|
||||
public: false,
|
||||
},
|
||||
{
|
||||
name: 'package_size',
|
||||
|
@ -352,19 +249,21 @@ export default defineComponent({
|
|||
field: 'package_size',
|
||||
sortable: true,
|
||||
sort,
|
||||
public: false,
|
||||
},
|
||||
{
|
||||
name: 'cost_price_package_netto',
|
||||
name: 'cost_per_package',
|
||||
label: 'Preis Netto/Gebinde',
|
||||
field: 'cost_price_package_netto',
|
||||
field: 'cost_per_package',
|
||||
format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''),
|
||||
sortable: true,
|
||||
sort,
|
||||
public: false,
|
||||
},
|
||||
{
|
||||
name: 'cost_price_pro_volume',
|
||||
name: 'cost_per_volume',
|
||||
label: 'Preis mit 19%/Liter',
|
||||
field: 'cost_price_pro_volume',
|
||||
field: 'cost_per_volume',
|
||||
format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''),
|
||||
sortable: true,
|
||||
sort: (a: ComputedRef, b: ComputedRef) => sort(a.value, b.value),
|
||||
|
@ -373,6 +272,35 @@ export default defineComponent({
|
|||
name: 'volumes',
|
||||
label: 'Preiskalkulation',
|
||||
field: 'volumes',
|
||||
format: (val: Array<DrinkPriceVolume>) => {
|
||||
let retVal = '';
|
||||
val.forEach((val, index) => {
|
||||
if (index > 0) {
|
||||
retVal += ', ';
|
||||
}
|
||||
retVal += val.id;
|
||||
});
|
||||
return retVal;
|
||||
},
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
name: 'receipt',
|
||||
label: 'Bauanleitung',
|
||||
field: 'receipt',
|
||||
format: (val: Array<string>) => {
|
||||
let retVal = '';
|
||||
val.forEach((value, index) => {
|
||||
if (index > 0) {
|
||||
retVal += ', ';
|
||||
}
|
||||
retVal += value;
|
||||
});
|
||||
return retVal;
|
||||
},
|
||||
filterable: true,
|
||||
sortable: false,
|
||||
public: false,
|
||||
},
|
||||
];
|
||||
const column_calc = [
|
||||
|
@ -410,16 +338,17 @@ export default defineComponent({
|
|||
field: 'public',
|
||||
},
|
||||
];
|
||||
const visibleColumn = computed({
|
||||
get: () => store.pricecalc_columns,
|
||||
set: (val) => {
|
||||
store.updatePriceCalcColumn(user, val);
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line vue/return-in-computed-property
|
||||
const pagination = computed(() => {
|
||||
rowsPerPage: store.drinks.length;
|
||||
const search_keys = computed(() =>
|
||||
columns.filter(
|
||||
(column) => column.filterable && (props.public || props.nodetails ? column.public : true)
|
||||
)
|
||||
);
|
||||
|
||||
const pagination = ref({
|
||||
sortBy: 'name',
|
||||
descending: false,
|
||||
rowsPerPage: store.drinks.length,
|
||||
});
|
||||
|
||||
const drinkTypes = computed(() => store.drinkTypes);
|
||||
|
@ -427,8 +356,12 @@ export default defineComponent({
|
|||
function updateDrink(drink: Drink) {
|
||||
void store.updateDrink(drink);
|
||||
}
|
||||
function deleteDrink(drink: Drink) {
|
||||
store.deleteDrink(drink);
|
||||
|
||||
function deleteDrink() {
|
||||
if (editDrink.value) {
|
||||
store.deleteDrink(editDrink.value);
|
||||
}
|
||||
editDrink.value = undefined;
|
||||
}
|
||||
|
||||
const showNewDrink = ref(false);
|
||||
|
@ -447,20 +380,103 @@ export default defineComponent({
|
|||
drinkPic.value = undefined;
|
||||
}
|
||||
|
||||
function savePicture(drink: Drink) {
|
||||
console.log('hier bin ich!!!', drinkPic.value);
|
||||
if (drinkPic.value && drinkPic.value instanceof File)
|
||||
store
|
||||
.upload_drink_picture(drink, drinkPic.value)
|
||||
.then(() => {
|
||||
root?.$forceUpdate();
|
||||
})
|
||||
.catch((response: Response) => {
|
||||
async function savePicture(drinkPic: File) {
|
||||
if (editDrink.value) {
|
||||
await store.upload_drink_picture(editDrink.value, drinkPic).catch((response: Response) => {
|
||||
if (response && response.status == 400) {
|
||||
onPictureRejected();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePicture() {
|
||||
if (editDrink.value) {
|
||||
await store.delete_drink_picture(editDrink.value);
|
||||
}
|
||||
}
|
||||
|
||||
const search = ref<Search>({
|
||||
value: '',
|
||||
key: '',
|
||||
label: '',
|
||||
});
|
||||
|
||||
const emptyDrink: Drink = {
|
||||
id: -1,
|
||||
article_id: undefined,
|
||||
package_size: undefined,
|
||||
name: '',
|
||||
volume: undefined,
|
||||
cost_per_volume: undefined,
|
||||
cost_per_package: undefined,
|
||||
tags: [],
|
||||
type: undefined,
|
||||
volumes: [],
|
||||
uuid: '',
|
||||
};
|
||||
|
||||
function newDrink() {
|
||||
editDrink.value = Object.assign({}, emptyDrink);
|
||||
}
|
||||
|
||||
const editDrink = ref<Drink>();
|
||||
|
||||
async function editing_drink(
|
||||
drink: Drink,
|
||||
toDeleteObjects: DeleteObjects,
|
||||
drinkPic: File | undefined,
|
||||
deletePic: boolean
|
||||
) {
|
||||
notLoading.value = false;
|
||||
for (const ingredient of toDeleteObjects.ingredients) {
|
||||
await store.deleteIngredient(ingredient);
|
||||
}
|
||||
for (const price of toDeleteObjects.prices) {
|
||||
await store.deletePrice(price);
|
||||
}
|
||||
for (const volume of toDeleteObjects.volumes) {
|
||||
await store.deleteVolume(volume, drink);
|
||||
}
|
||||
if (drink.id > 0) {
|
||||
await store.updateDrink(drink);
|
||||
} else {
|
||||
const _drink = await store.setDrink(drink);
|
||||
if (editDrink.value) {
|
||||
editDrink.value.id = _drink.id;
|
||||
}
|
||||
}
|
||||
if (deletePic) {
|
||||
await deletePicture();
|
||||
}
|
||||
if (drinkPic instanceof File) {
|
||||
await savePicture(drinkPic);
|
||||
}
|
||||
editDrink.value = undefined;
|
||||
notLoading.value = true;
|
||||
}
|
||||
|
||||
function get_volumes(drink_id: number) {
|
||||
return store.drinks.find((a) => a.id === drink_id)?.volumes;
|
||||
}
|
||||
|
||||
const notLoading = ref(true);
|
||||
|
||||
const imageloading = ref<Array<{ id: number; loading: boolean }>>([]);
|
||||
function getImageLoading(id: number) {
|
||||
const loading = imageloading.value.find((a) => a.id === id);
|
||||
if (loading) {
|
||||
return loading.loading;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function image(uuid: string | undefined) {
|
||||
if (uuid) {
|
||||
return `${baseURL.value}/pricelist/picture/${uuid}?size=256`;
|
||||
}
|
||||
return 'no-image.svg';
|
||||
}
|
||||
|
||||
return {
|
||||
drinks: computed(() => store.drinks),
|
||||
|
@ -468,14 +484,26 @@ export default defineComponent({
|
|||
columns,
|
||||
column_calc,
|
||||
column_prices,
|
||||
visibleColumn,
|
||||
drinkTypes,
|
||||
updateDrink,
|
||||
deleteDrink,
|
||||
showNewDrink,
|
||||
drinkPic,
|
||||
savePicture,
|
||||
console,
|
||||
deletePicture,
|
||||
search,
|
||||
filter,
|
||||
search_keys,
|
||||
tags: computed(() => store.tags),
|
||||
editDrink,
|
||||
editing_drink,
|
||||
get_volumes,
|
||||
notLoading,
|
||||
getImageLoading,
|
||||
newDrink,
|
||||
hasPermission,
|
||||
PERMISSIONS,
|
||||
image,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div
|
||||
v-for="(step, index) in steps"
|
||||
:key="index"
|
||||
class="full-width row q-gutter-sm justify-between q-py-sm"
|
||||
>
|
||||
<div class="row">
|
||||
<div>{{ index + 1 }}.</div>
|
||||
<div class="q-pl-sm">
|
||||
{{ step }}
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="editable"
|
||||
round
|
||||
color="negative"
|
||||
size="sm"
|
||||
icon="mdi-delete"
|
||||
@click="deleteStep(index)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="editable" class="full-width row q-gutter-sm justify-between">
|
||||
<q-input v-model="newStep" filled label="Arbeitsschritt" dense />
|
||||
<q-btn label="Schritt hinzufügen" dense @click="addStep" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'BuildManual',
|
||||
props: {
|
||||
steps: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
default: undefined,
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
deleteStep: (index: number) => index,
|
||||
addStep: (val: string) => val,
|
||||
},
|
||||
setup(_, { emit }) {
|
||||
const newStep = ref('');
|
||||
function deleteStep(index: number) {
|
||||
emit('deleteStep', index);
|
||||
}
|
||||
function addStep() {
|
||||
emit('addStep', newStep.value);
|
||||
newStep.value = '';
|
||||
}
|
||||
return { newStep, addStep, deleteStep };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,72 +0,0 @@
|
|||
<template>
|
||||
<q-btn
|
||||
v-if="pricePerVolume"
|
||||
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: [{ percentage: 100 }, { percentage: 250 }, { percentage: 300 }],
|
||||
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>
|
|
@ -0,0 +1,340 @@
|
|||
<template>
|
||||
<q-carousel
|
||||
v-model="volume"
|
||||
transition-prev="slide-right"
|
||||
transition-next="slide-left"
|
||||
animated
|
||||
swipeable
|
||||
control-color="primary"
|
||||
arrows
|
||||
:keep-alive="false"
|
||||
>
|
||||
<q-carousel-slide v-for="volume in volumes" :key="volume.id" :name="volume.id">
|
||||
<div class="full-width row">
|
||||
<q-input
|
||||
v-model.number="volume._volume"
|
||||
class="q-pa-sm col-10"
|
||||
:outlined="!editable || !volume_can_edit"
|
||||
:filled="editable && volume_can_edit"
|
||||
:readonly="!editable || !volume_can_edit"
|
||||
dense
|
||||
label="Inhalt"
|
||||
mask="#.###"
|
||||
fill-mask="0"
|
||||
suffix="L"
|
||||
min="0"
|
||||
step="0.001"
|
||||
@update:model-value="updateVolume(volume)"
|
||||
/>
|
||||
<div
|
||||
v-if="deleteable && editable && hasPermission(PERMISSIONS.DELETE_VOLUME)"
|
||||
class="q-pa-sm col-2 text-right"
|
||||
>
|
||||
<q-btn round icon="mdi-delete" size="sm" color="negative" @click="deleteVolume">
|
||||
<q-tooltip> Abgabe entfernen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<q-badge class="text-body1" color="primary"> {{ min_price.percentage }}% </q-badge>
|
||||
<div class="text-body1">{{ min_price.price.toFixed(3) }}€</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-pa-sm">
|
||||
<div v-for="(price, index) in volume.prices" :key="price.id">
|
||||
<div class="fit row justify-around q-py-sm">
|
||||
<div
|
||||
v-if="!editable || !hasPermission(PERMISSIONS.EDIT_PRICE)"
|
||||
class="text-body1 col-3"
|
||||
>
|
||||
{{ price.price.toFixed(2) }}€
|
||||
</div>
|
||||
<q-input
|
||||
v-else
|
||||
v-model.number="price.price"
|
||||
class="col-3"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
suffix="€"
|
||||
filled
|
||||
dense
|
||||
label="Preis"
|
||||
@update:model-value="change"
|
||||
/>
|
||||
<div class="text-body1 col-2">
|
||||
<q-toggle
|
||||
v-model="price.public"
|
||||
:disable="!editable || !hasPermission(PERMISSIONS.EDIT_PRICE)"
|
||||
checked-icon="mdi-earth"
|
||||
unchecked-icon="mdi-earth-off"
|
||||
@update:model-value="change"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!editable || !hasPermission(PERMISSIONS.EDIT_PRICE)"
|
||||
class="text-body1 col-5"
|
||||
>
|
||||
{{ price.description }}
|
||||
</div>
|
||||
<q-input
|
||||
v-else
|
||||
v-model="price.description"
|
||||
class="col-5"
|
||||
filled
|
||||
dense
|
||||
label="Beschreibung"
|
||||
@update:model-value="change"
|
||||
/>
|
||||
<div v-if="editable && hasPermission(PERMISSIONS.DELETE_PRICE)" class="col-1">
|
||||
<q-btn round icon="mdi-delete" color="negative" size="xs" @click="deletePrice(price)">
|
||||
<q-tooltip> Preis entfernen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator v-if="index < volume.prices.length - 1" />
|
||||
</div>
|
||||
<div
|
||||
v-if="!public && !nodetails && isUnderMinPrice"
|
||||
class="fit warning bg-red text-center text-white text-body1"
|
||||
>
|
||||
Einer der Preise ist unterhalb des niedrigsten minimal Preises.
|
||||
</div>
|
||||
<div
|
||||
v-if="editable && hasPermission(PERMISSIONS.EDIT_PRICE)"
|
||||
class="full-width row justify-end text-right"
|
||||
>
|
||||
<q-btn round icon="mdi-plus" size="sm" color="primary">
|
||||
<q-tooltip> Preis hinzufügen </q-tooltip>
|
||||
<q-menu anchor="center middle" self="center middle">
|
||||
<new-price @save="addPrice" />
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-pa-sm">
|
||||
<ingredients
|
||||
v-if="!public && !costPerVolume"
|
||||
v-model="volume.ingredients"
|
||||
:editable="editable && hasPermission(PERMISSIONS.EDIT_INGREDIENTS_DRINK)"
|
||||
@update="updateVolume(volume)"
|
||||
@delete-ingredient="deleteIngredient"
|
||||
/>
|
||||
</div>
|
||||
</q-carousel-slide>
|
||||
</q-carousel>
|
||||
<div class="full-width row justify-center q-pa-sm">
|
||||
<div class="q-px-sm">
|
||||
<q-btn-toggle v-model="volume" :options="options" rounded />
|
||||
</div>
|
||||
<div v-if="editable" class="q-px-sm">
|
||||
<q-btn class="q-px-sm" round icon="mdi-plus" color="primary" size="sm" @click="newVolume">
|
||||
<q-tooltip> Abgabe hinzufügen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, ref, onBeforeMount } from 'vue';
|
||||
import { DrinkPriceVolume } from 'src/plugins/pricelist/store';
|
||||
import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue';
|
||||
import NewPrice from 'src/plugins/pricelist/components/CalculationTable/NewPrice.vue';
|
||||
import { calc_volume, clone } from '../../utils/utils';
|
||||
import { hasPermission } from 'src/utils/permission';
|
||||
import { PERMISSIONS } from '../../permissions';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DrinkPriceVolume',
|
||||
components: { Ingredients, NewPrice },
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array as PropType<Array<DrinkPriceVolume>>,
|
||||
required: true,
|
||||
},
|
||||
costPerVolume: {
|
||||
type: undefined,
|
||||
default: undefined,
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
public: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
nodetails: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
'update:modelValue': (val: Array<DrinkPriceVolume>) => val,
|
||||
update: (val: number) => val,
|
||||
'delete-volume': (val: DrinkPriceVolume) => val,
|
||||
'delete-price': (val: FG.DrinkPrice) => val,
|
||||
'delete-ingredient': (val: FG.Ingredient) => val,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
onBeforeMount(() => {
|
||||
//volumes.value = <Array<DrinkPriceVolume>>JSON.parse(JSON.stringify(props.modelValue));
|
||||
volumes.value = clone(props.modelValue);
|
||||
});
|
||||
const volumes = ref<Array<DrinkPriceVolume>>([]);
|
||||
const _volume = ref<number | undefined>();
|
||||
const volume = computed<number | undefined>({
|
||||
get: () => {
|
||||
if (_volume.value !== undefined) {
|
||||
return _volume.value;
|
||||
}
|
||||
if (volumes.value.length > 0) {
|
||||
return volumes.value[0].id;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
set: (val: number | undefined) => (_volume.value = val),
|
||||
});
|
||||
const edit_volume = computed(() => {
|
||||
return volumes.value.find((a) => a.id === volume.value);
|
||||
});
|
||||
const options = computed<Array<{ label: string; value: number }>>(() => {
|
||||
const retVal: Array<{ label: string; value: number }> = [];
|
||||
volumes.value.forEach((volume: DrinkPriceVolume) => {
|
||||
retVal.push({ label: `${(<number>volume.volume).toFixed(3)}L`, value: volume.id });
|
||||
});
|
||||
return retVal;
|
||||
});
|
||||
|
||||
function updateVolume(_volume: DrinkPriceVolume) {
|
||||
const index = volumes.value.findIndex((a) => a.id === _volume.id);
|
||||
if (index > -1) {
|
||||
volumes.value[index].volume = calc_volume(_volume);
|
||||
volumes.value[index]._volume = <number>volumes.value[index].volume;
|
||||
}
|
||||
change();
|
||||
setTimeout(() => {
|
||||
emit('update', index);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
const volume_can_edit = computed(() => {
|
||||
if (edit_volume.value) {
|
||||
return !edit_volume.value.ingredients.some((ingredient) => ingredient.drink_ingredient);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const newVolumeId = ref(-1);
|
||||
|
||||
function newVolume() {
|
||||
const new_volume: DrinkPriceVolume = {
|
||||
id: newVolumeId.value,
|
||||
_volume: 0,
|
||||
volume: 0,
|
||||
prices: [],
|
||||
ingredients: [],
|
||||
min_prices: [],
|
||||
};
|
||||
newVolumeId.value--;
|
||||
volumes.value.push(new_volume);
|
||||
change();
|
||||
_volume.value = volumes.value[volumes.value.length - 1].id;
|
||||
}
|
||||
|
||||
function deleteVolume() {
|
||||
if (edit_volume.value) {
|
||||
if (edit_volume.value.id > 0) {
|
||||
emit('delete-volume', edit_volume.value);
|
||||
}
|
||||
const index = volumes.value.findIndex((a) => a.id === edit_volume.value?.id);
|
||||
if (index > -1) {
|
||||
_volume.value = volumes.value[0].id;
|
||||
volumes.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteable = computed(() => {
|
||||
if (edit_volume.value) {
|
||||
const has_ingredients = edit_volume.value.ingredients.length > 0;
|
||||
const has_prices = edit_volume.value.prices.length > 0;
|
||||
|
||||
return !(has_ingredients || has_prices);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
function addPrice(price: FG.DrinkPrice) {
|
||||
if (edit_volume.value) {
|
||||
edit_volume.value.prices.push(price);
|
||||
change();
|
||||
}
|
||||
}
|
||||
|
||||
function deletePrice(price: FG.DrinkPrice) {
|
||||
if (edit_volume.value) {
|
||||
const index = edit_volume.value.prices.findIndex((a) => a.id === price.id);
|
||||
if (index > -1) {
|
||||
if (edit_volume.value.prices[index].id > 0) {
|
||||
emit('delete-price', edit_volume.value.prices[index]);
|
||||
change();
|
||||
}
|
||||
edit_volume.value.prices.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteIngredient(ingredient: FG.Ingredient) {
|
||||
emit('delete-ingredient', ingredient);
|
||||
}
|
||||
|
||||
function change() {
|
||||
emit('update:modelValue', volumes.value);
|
||||
}
|
||||
|
||||
const isUnderMinPrice = computed(() => {
|
||||
if (volumes.value) {
|
||||
const this_volume = volumes.value.find((a) => a.id === volume.value);
|
||||
if (this_volume) {
|
||||
if (this_volume.min_prices.length > 0) {
|
||||
const min_price = this_volume.min_prices.sort((a, b) => {
|
||||
if (a.price > b.price) return 1;
|
||||
if (a.price < b.price) return -1;
|
||||
return 0;
|
||||
})[0];
|
||||
console.log('min_price', min_price);
|
||||
return this_volume.prices.some((a) => a.price < min_price.price);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
volumes,
|
||||
volume,
|
||||
options,
|
||||
updateVolume,
|
||||
volume_can_edit,
|
||||
newVolume,
|
||||
deleteable,
|
||||
addPrice,
|
||||
deletePrice,
|
||||
deleteVolume,
|
||||
deleteIngredient,
|
||||
change,
|
||||
isUnderMinPrice,
|
||||
hasPermission,
|
||||
PERMISSIONS,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.warning {
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
|
@ -1,193 +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"
|
||||
/>
|
||||
</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, usePricelistStore } from '../../store';
|
||||
import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue';
|
||||
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: { PriceTable, 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(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
|
||||
store.deleteVolume(volume, drink);
|
||||
}
|
||||
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',
|
||||
},
|
||||
];
|
||||
|
||||
return { addVolume, updateDrink, deleteVolume, column_prices };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,24 +1,26 @@
|
|||
<template>
|
||||
<div class="full-width">
|
||||
<div
|
||||
v-for="ingredient in ingredients"
|
||||
:key="`volume:${volume.id},ingredient:${ingredient.id}`"
|
||||
v-for="ingredient in edit_ingredients"
|
||||
:key="`ingredient:${ingredient.id}`"
|
||||
class="full-width row justify-evenly q-py-xs"
|
||||
>
|
||||
<div class="full-width row justify-evenly">
|
||||
<div v-if="ingredient.drink_ingredient" class="col">
|
||||
<div class="full-width row justify-evenly q-py-xs">
|
||||
<div class="col">
|
||||
{{ get_drink_ingredient_name(ingredient.drink_ingredient.drink_ingredient_id) }}
|
||||
{{ get_drink_ingredient_name(ingredient.drink_ingredient.ingredient_id) }}
|
||||
<q-popup-edit
|
||||
v-model="ingredient.drink_ingredient.drink_ingredient_id"
|
||||
v-if="editable"
|
||||
v-slot="scope"
|
||||
v-model="ingredient.drink_ingredient.ingredient_id"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@save="updateDrink"
|
||||
@save="updateValue"
|
||||
>
|
||||
<q-select
|
||||
v-model="ingredient.drink_ingredient.drink_ingredient_id"
|
||||
v-model="scope.ingredient_id"
|
||||
class="col q-px-sm"
|
||||
label="Getränk"
|
||||
filled
|
||||
|
@ -34,14 +36,16 @@
|
|||
<div class="col">
|
||||
{{ ingredient.drink_ingredient.volume.toFixed(3) }}L
|
||||
<q-popup-edit
|
||||
v-if="editable"
|
||||
v-slot="scope"
|
||||
v-model="ingredient.drink_ingredient.volume"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@save="updateDrink"
|
||||
@save="updateValue"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="ingredient.drink_ingredient.volume"
|
||||
v-model.number="scope.value"
|
||||
class="col q-px-sm"
|
||||
label="Volume"
|
||||
type="number"
|
||||
|
@ -63,11 +67,12 @@
|
|||
<div class="col">{{ ingredient.extra_ingredient.price.toFixed(3) }}€</div>
|
||||
</div>
|
||||
<q-popup-edit
|
||||
v-if="editable"
|
||||
v-model="ingredient.extra_ingredient"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@save="updateDrink"
|
||||
@save="updateValue"
|
||||
>
|
||||
<q-select
|
||||
v-model="ingredient.extra_ingredient"
|
||||
|
@ -78,21 +83,24 @@
|
|||
/>
|
||||
</q-popup-edit>
|
||||
</div>
|
||||
<div class="col-1 row justify-end q-pa-xs">
|
||||
<div v-if="editable" class="col-1 row justify-end q-pa-xs">
|
||||
<q-btn
|
||||
icon="mdi-delete"
|
||||
round
|
||||
size="xs"
|
||||
color="negative"
|
||||
@click="deleteIngredient(ingredient, volume)"
|
||||
/>
|
||||
@click="deleteIngredient(ingredient)"
|
||||
>
|
||||
<q-tooltip> Zutat entfernen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator />
|
||||
</div>
|
||||
<div class="full-width row justify-end q-py-xs">
|
||||
<q-btn size="sm" icon-right="add" color="positive" label="Zutat hinzufügen">
|
||||
<q-menu anchor="center middle" self="center middle">
|
||||
<div v-if="editable" class="full-width row justify-end q-py-xs">
|
||||
<q-btn size="sm" round icon="mdi-plus" color="primary">
|
||||
<q-tooltip> Neue Zutat hinzufügen </q-tooltip>
|
||||
<q-menu anchor="center middle" self="center middle" persistent>
|
||||
<div class="full-width row justify-around q-gutter-sm q-pa-sm">
|
||||
<div class="col">
|
||||
<q-select
|
||||
|
@ -105,6 +113,7 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-slide-transition>
|
||||
<q-input
|
||||
v-if="newIngredient && newIngredient.volume"
|
||||
v-model.number="newIngredientVolume"
|
||||
|
@ -116,8 +125,10 @@
|
|||
min="0"
|
||||
suffix="L"
|
||||
/>
|
||||
</q-slide-transition>
|
||||
<q-slide-transition>
|
||||
<q-input
|
||||
v-else-if="newIngredient && newIngredient.price"
|
||||
v-if="newIngredient && newIngredient.price"
|
||||
v-model="newIngredient.price"
|
||||
filled
|
||||
dense
|
||||
|
@ -129,16 +140,12 @@
|
|||
mask="#.##"
|
||||
suffix="€"
|
||||
/>
|
||||
</q-slide-transition>
|
||||
</div>
|
||||
</div>
|
||||
<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="Speichern"
|
||||
color="positive"
|
||||
@click="addIngredient(volume)"
|
||||
/>
|
||||
<div class="full-width row justify-around q-gutter-sm q-pa-sm">
|
||||
<q-btn v-close-popup flat label="Abbrechen" @click="cancelAddIngredient" />
|
||||
<q-btn v-close-popup flat label="Speichern" color="primary" @click="addIngredient" />
|
||||
</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
|
@ -147,35 +154,43 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, ref } from 'vue';
|
||||
import { DrinkPriceVolume, usePricelistStore } from '../../store';
|
||||
import { computed, defineComponent, PropType, ref, onBeforeMount, unref } from 'vue';
|
||||
import { usePricelistStore } from '../../store';
|
||||
import { clone } from '../../utils/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Ingredients',
|
||||
props: {
|
||||
ingredients: {
|
||||
type: Array as PropType<FG.Ingredient[]>,
|
||||
modelValue: {
|
||||
type: Object as PropType<Array<FG.Ingredient>>,
|
||||
required: true,
|
||||
},
|
||||
volume: {
|
||||
type: Object /*as PropType<DrinkPriceVolume>*/,
|
||||
required: true,
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: { updateDrink: () => true },
|
||||
setup(_, { emit }) {
|
||||
emits: {
|
||||
'update:modelValue': (val: Array<FG.Ingredient>) => val,
|
||||
update: () => true,
|
||||
'delete-ingredient': (val: FG.Ingredient) => val,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
onBeforeMount(() => {
|
||||
//edit_ingredients.value = <Array<FG.Ingredient>>JSON.parse(JSON.stringify(props.modelValue))
|
||||
//edit_ingredients.value = props.modelValue
|
||||
edit_ingredients.value = clone(props.modelValue);
|
||||
});
|
||||
|
||||
const store = usePricelistStore();
|
||||
|
||||
/*const emptyIngredient: FG.Ingredient = {
|
||||
id: -1,
|
||||
drink_ingredient: undefined,
|
||||
extra_ingredient: undefined,
|
||||
};*/
|
||||
const edit_ingredients = ref<Array<FG.Ingredient>>([]);
|
||||
const newIngredient = ref<FG.Drink | FG.ExtraIngredient>();
|
||||
const newIngredientVolume = ref<number>(0);
|
||||
function addIngredient(volume: DrinkPriceVolume) {
|
||||
function addIngredient() {
|
||||
let _ingredient: FG.Ingredient;
|
||||
if ((<FG.Drink>newIngredient.value)?.volume && newIngredient.value) {
|
||||
volume.ingredients.push({
|
||||
_ingredient = {
|
||||
id: -1,
|
||||
drink_ingredient: {
|
||||
id: -1,
|
||||
|
@ -183,33 +198,46 @@ export default defineComponent({
|
|||
volume: newIngredientVolume.value,
|
||||
},
|
||||
extra_ingredient: undefined,
|
||||
});
|
||||
} else if (newIngredient.value) {
|
||||
volume.ingredients.push({
|
||||
};
|
||||
} else {
|
||||
_ingredient = {
|
||||
id: -1,
|
||||
drink_ingredient: undefined,
|
||||
extra_ingredient: <FG.ExtraIngredient>newIngredient.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
updateDrink();
|
||||
edit_ingredients.value.push(_ingredient);
|
||||
emit('update:modelValue', unref(edit_ingredients));
|
||||
update();
|
||||
cancelAddIngredient();
|
||||
}
|
||||
function updateDrink() {
|
||||
console.log('updateDrink from Ingredients');
|
||||
emit('updateDrink');
|
||||
|
||||
function updateValue(value: number, initValue: number) {
|
||||
console.log('updateValue', value, initValue);
|
||||
emit('update:modelValue', unref(edit_ingredients));
|
||||
update();
|
||||
}
|
||||
|
||||
function cancelAddIngredient() {
|
||||
setTimeout(() => {
|
||||
(newIngredient.value = undefined), (newIngredientVolume.value = 0);
|
||||
}, 200);
|
||||
}
|
||||
function deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
|
||||
store.deleteIngredient(ingredient, volume);
|
||||
function deleteIngredient(ingredient: FG.Ingredient) {
|
||||
const index = edit_ingredients.value.findIndex((a) => a.id === ingredient.id);
|
||||
if (index > -1) {
|
||||
if (edit_ingredients.value[index].id > 0) {
|
||||
emit('delete-ingredient', edit_ingredients.value[index]);
|
||||
}
|
||||
edit_ingredients.value.splice(index, 1);
|
||||
}
|
||||
emit('update:modelValue', unref(edit_ingredients));
|
||||
update();
|
||||
}
|
||||
const drinks = computed(() =>
|
||||
store.drinks.filter((drink) => {
|
||||
console.log('computed drinks', drink.name, drink.cost_price_pro_volume);
|
||||
return drink.cost_price_pro_volume;
|
||||
console.log('computed drinks', drink.name, drink.cost_per_volume);
|
||||
return drink.cost_per_volume;
|
||||
})
|
||||
);
|
||||
const extra_ingredients = computed(() => store.extraIngredients);
|
||||
|
@ -218,6 +246,12 @@ export default defineComponent({
|
|||
return store.drinks.find((a) => a.id === id)?.name;
|
||||
}
|
||||
|
||||
function update() {
|
||||
setTimeout(() => {
|
||||
emit('update');
|
||||
}, 50);
|
||||
}
|
||||
|
||||
return {
|
||||
addIngredient,
|
||||
drinks,
|
||||
|
@ -225,9 +259,10 @@ export default defineComponent({
|
|||
newIngredient,
|
||||
newIngredientVolume,
|
||||
cancelAddIngredient,
|
||||
updateDrink,
|
||||
updateValue,
|
||||
deleteIngredient,
|
||||
get_drink_ingredient_name,
|
||||
edit_ingredients,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<template>
|
||||
<div class="q-pa-sm">
|
||||
<div class="q-table__title q-pa-sm">Neues Getränk</div>
|
||||
<q-form @submit="addDrink" @reset="cancelAddDrink">
|
||||
<q-input
|
||||
v-model="newDrink.name"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Getränkname"
|
||||
lazy-rules
|
||||
:rules="[notEmpty]"
|
||||
/>
|
||||
<q-input
|
||||
v-model="newDrink.article_id"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Artikelnummer"
|
||||
/>
|
||||
<q-select
|
||||
v-model="newDrink.type"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Kategorie"
|
||||
:options="drinkTypes"
|
||||
option-label="name"
|
||||
lazy-rules
|
||||
:rules="[notEmpty]"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="newDrink.volume"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Inhalt in L/Gebinde"
|
||||
type="number"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="newDrink.package_size"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Gebindegröße"
|
||||
type="number"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="newDrink.cost_per_package"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Preis Netto/Gebinde"
|
||||
type="number"
|
||||
/>
|
||||
<q-input
|
||||
v-model="cost_per_volume"
|
||||
class="col-sm-4 col-xs-6 q-pa-sm"
|
||||
filled
|
||||
label="Preis mit 19%/Liter"
|
||||
:disable="calc_price_pro_volume"
|
||||
/>
|
||||
<div class="row justify-between">
|
||||
<q-btn v-close-popup label="Abbrechen" type="reset" />
|
||||
<q-btn label="Speichern" type="submit" />
|
||||
</div>
|
||||
</q-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import { usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import { notEmpty } from 'src/utils/validators';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NewDrink',
|
||||
emits: { close: () => true },
|
||||
setup(_, { emit }) {
|
||||
const store = usePricelistStore();
|
||||
const emptyDrink: FG.Drink = {
|
||||
id: -1,
|
||||
article_id: undefined,
|
||||
package_size: undefined,
|
||||
name: '',
|
||||
volume: undefined,
|
||||
cost_per_volume: undefined,
|
||||
cost_per_package: undefined,
|
||||
tags: [],
|
||||
type: undefined,
|
||||
volumes: [],
|
||||
};
|
||||
|
||||
const calc_price_pro_volume = computed(
|
||||
() =>
|
||||
!!newDrink.value.cost_per_package &&
|
||||
!!newDrink.value.volume &&
|
||||
!!newDrink.value.package_size
|
||||
);
|
||||
const cost_per_volume = computed({
|
||||
get: () => {
|
||||
if (calc_price_pro_volume.value) {
|
||||
const retVal =
|
||||
((newDrink.value.cost_per_package || 0) /
|
||||
((newDrink.value.volume || 0) * (newDrink.value.package_size || 0))) *
|
||||
1.19;
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
newDrink.value.cost_per_volume = Math.round(retVal * 1000) / 1000;
|
||||
}
|
||||
return newDrink.value.cost_per_volume;
|
||||
},
|
||||
set: (val) => {
|
||||
newDrink.value.cost_per_volume = val;
|
||||
},
|
||||
});
|
||||
|
||||
async function addDrink() {
|
||||
// Maybe try catch and handle error (e.g. name used...)
|
||||
await store.setDrink(newDrink.value);
|
||||
cancelAddDrink();
|
||||
emit('close');
|
||||
}
|
||||
function cancelAddDrink() {
|
||||
newDrink.value = emptyDrink;
|
||||
}
|
||||
|
||||
const newDrink = ref<FG.Drink>(emptyDrink);
|
||||
return {
|
||||
drinkTypes: computed(() => store.drinkTypes),
|
||||
newDrink,
|
||||
calc_price_pro_volume,
|
||||
cost_per_volume,
|
||||
addDrink,
|
||||
cancelAddDrink,
|
||||
notEmpty,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div class="row justify-around q-pa-sm">
|
||||
<q-input
|
||||
v-model.number="newPrice.price"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
type="number"
|
||||
label="Preis"
|
||||
suffix="€"
|
||||
min="0"
|
||||
step="0.1"
|
||||
/>
|
||||
<q-input
|
||||
v-model="newPrice.description"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
label="Beschreibung"
|
||||
clearable
|
||||
/>
|
||||
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
|
||||
</div>
|
||||
<div class="row justify-between q-pa-sm">
|
||||
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
|
||||
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'NewPrice',
|
||||
emits: {
|
||||
save: (val: FG.DrinkPrice) => val,
|
||||
},
|
||||
setup(_, { emit }) {
|
||||
const emptyPrice: FG.DrinkPrice = {
|
||||
id: -1,
|
||||
price: 0,
|
||||
description: '',
|
||||
public: true,
|
||||
};
|
||||
const newPrice = ref(emptyPrice);
|
||||
function addPrice() {
|
||||
emit('save', newPrice.value);
|
||||
cancelAddPrice();
|
||||
}
|
||||
function cancelAddPrice() {
|
||||
setTimeout(() => {
|
||||
newPrice.value = emptyPrice;
|
||||
}, 200);
|
||||
}
|
||||
return { newPrice, addPrice, cancelAddPrice };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,217 +0,0 @@
|
|||
<template>
|
||||
<q-table
|
||||
v-model:pagination="pagination"
|
||||
style="max-height: 130px"
|
||||
dense
|
||||
hide-header
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
:visible-columns="visibleColumns"
|
||||
flat
|
||||
virtual-scroll
|
||||
:rows-per-page-options="[0]"
|
||||
>
|
||||
<template #body="prices_props">
|
||||
<q-tr :props="prices_props">
|
||||
<q-td key="price" :props="prices_props">
|
||||
{{ prices_props.row.price.toFixed(2) }}€
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="prices_props.row.price"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink"
|
||||
>
|
||||
<q-input
|
||||
v-model.number="scope.value"
|
||||
type="number"
|
||||
label="Preis"
|
||||
dense
|
||||
filled
|
||||
autofocus
|
||||
min="0"
|
||||
step="0.1"
|
||||
suffix="€"
|
||||
@keyup.enter="scope.set"
|
||||
/> </q-popup-edit
|
||||
></q-td>
|
||||
<q-td key="description" :props="prices_props">
|
||||
{{ prices_props.row.description }}
|
||||
<q-popup-edit
|
||||
v-slot="scope"
|
||||
v-model="prices_props.row.description"
|
||||
buttons
|
||||
label="Beschreibung"
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateDrink"
|
||||
>
|
||||
<q-input
|
||||
v-model="scope.value"
|
||||
dense
|
||||
autofocus
|
||||
filled
|
||||
clearable
|
||||
@keyup.enter="scope.set"
|
||||
/>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="public" :props="prices_props">
|
||||
<q-toggle v-model="prices_props.row.public" dense @update:modelValue="updateDrink" />
|
||||
</q-td>
|
||||
|
||||
<q-td>
|
||||
<q-btn
|
||||
color="negative"
|
||||
padding="xs"
|
||||
round
|
||||
size="xs"
|
||||
icon="mdi-delete"
|
||||
@click="deletePrice(prices_props.row, row)"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<div class="full-width row justify-end">
|
||||
<q-btn size="xs" icon-right="add" color="positive" label="Preis hinzufügen">
|
||||
<q-menu anchor="center middle" self="center middle">
|
||||
<div class="row justify-around q-pa-sm">
|
||||
<q-input
|
||||
v-model.number="newPrice.price"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
type="number"
|
||||
label="Preis"
|
||||
suffix="€"
|
||||
min="0"
|
||||
step="0.1"
|
||||
/>
|
||||
<q-input
|
||||
v-model="newPrice.description"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
label="Beschreibung"
|
||||
clearable
|
||||
/>
|
||||
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
|
||||
</div>
|
||||
<div class="row justify-between q-pa-sm">
|
||||
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
|
||||
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
|
||||
</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #no-data class="justify-end">
|
||||
<div class="full-width row justify-end">
|
||||
<q-btn size="xs" icon-right="add" color="positive" label="Preis hinzufügen">
|
||||
<q-menu anchor="center middle" self="center middle">
|
||||
<div class="row justify-around q-pa-sm">
|
||||
<q-input
|
||||
v-model.number="newPrice.price"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
type="number"
|
||||
label="Preis"
|
||||
suffix="€"
|
||||
min="0"
|
||||
step="0.1"
|
||||
/>
|
||||
<q-input
|
||||
v-model="newPrice.description"
|
||||
dense
|
||||
filled
|
||||
class="q-px-sm"
|
||||
label="Beschreibung"
|
||||
clearable
|
||||
/>
|
||||
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
|
||||
</div>
|
||||
<div class="row justify-between q-pa-sm">
|
||||
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
|
||||
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
|
||||
</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { DrinkPriceVolume, usePricelistStore } from '../../store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PriceTable',
|
||||
props: {
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
row: {
|
||||
type: Object /*as PropType<DrinkPriceVolume>*/,
|
||||
required: true,
|
||||
},
|
||||
visibleColumns: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: { updateDrink: () => true },
|
||||
setup(props, { emit }) {
|
||||
const store = usePricelistStore();
|
||||
|
||||
const emptyPrice: FG.DrinkPrice = {
|
||||
id: -1,
|
||||
price: 0,
|
||||
description: '',
|
||||
public: true,
|
||||
};
|
||||
const newPrice = ref(emptyPrice);
|
||||
function addPrice(volume: DrinkPriceVolume) {
|
||||
volume.prices.push(newPrice.value);
|
||||
updateDrink();
|
||||
cancelAddPrice();
|
||||
}
|
||||
function updateDrink() {
|
||||
emit('updateDrink');
|
||||
}
|
||||
function cancelAddPrice() {
|
||||
setTimeout(() => {
|
||||
newPrice.value = emptyPrice;
|
||||
}, 200);
|
||||
}
|
||||
function deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
|
||||
console.log(price, volume);
|
||||
store.deletePrice(price, volume);
|
||||
}
|
||||
|
||||
const pagination = ref({
|
||||
rowsPerPage: (<DrinkPriceVolume>props.row).prices.length,
|
||||
});
|
||||
|
||||
return {
|
||||
newPrice,
|
||||
addPrice,
|
||||
cancelAddPrice,
|
||||
updateDrink,
|
||||
deletePrice,
|
||||
pagination,
|
||||
console,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,354 @@
|
|||
<template>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Getränk Bearbeiten</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="full-width row">
|
||||
<q-input
|
||||
v-model="edit_drink.name"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Name"
|
||||
dense
|
||||
/>
|
||||
<q-select
|
||||
v-model="edit_drink.type"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Kategorie"
|
||||
dense
|
||||
:options="types"
|
||||
option-label="name"
|
||||
/>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-img :src="image" style="max-height: 256px" fit="contain" />
|
||||
<div class="full-width row">
|
||||
<div class="col-10 q-pa-sm">
|
||||
<q-file
|
||||
v-model="drinkPic"
|
||||
filled
|
||||
clearable
|
||||
dense
|
||||
@update:model-value="imagePreview"
|
||||
@clear="imgsrc = undefined"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="mdi-image" />
|
||||
</template>
|
||||
</q-file>
|
||||
</div>
|
||||
<div class="col-2 q-pa-sm text-right">
|
||||
<q-btn round icon="mdi-delete" color="negative" size="sm" @click="delete_pic">
|
||||
<q-tooltip> Bild entfernen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-select
|
||||
v-model="edit_drink.tags"
|
||||
multiple
|
||||
:options="tags"
|
||||
label="Tags"
|
||||
option-label="name"
|
||||
filled
|
||||
dense
|
||||
>
|
||||
<template #selected-item="item">
|
||||
<q-chip
|
||||
removable
|
||||
:tabindex="item.tabindex"
|
||||
:style="`background-color: ${item.opt.color}`"
|
||||
@remove="item.removeAtIndex(item.index)"
|
||||
>
|
||||
{{ item.opt.name }}
|
||||
</q-chip>
|
||||
</template>
|
||||
<template #option="item">
|
||||
<q-item v-bind="item.itemProps" v-on="item.itemEvents">
|
||||
<q-chip :style="`background-color: ${item.opt.color}`">
|
||||
<q-avatar v-if="item.selected" icon="mdi-check" color="positive" text-color="white" />
|
||||
{{ item.opt.name }}
|
||||
</q-chip>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="fit row">
|
||||
<q-input
|
||||
v-model="edit_drink.article_id"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Artikelnummer"
|
||||
dense
|
||||
/>
|
||||
<q-input
|
||||
v-model="edit_drink.volume"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Inhalt"
|
||||
dense
|
||||
suffix="L"
|
||||
/>
|
||||
<q-input
|
||||
v-model="edit_drink.package_size"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Gebindegröße"
|
||||
dense
|
||||
/>
|
||||
<q-input
|
||||
v-model="edit_drink.cost_per_package"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
filled
|
||||
label="Preis Gebinde"
|
||||
suffix="€"
|
||||
dense
|
||||
/>
|
||||
<q-input
|
||||
v-model="cost_per_volume"
|
||||
class="col-xs-12 col-sm-6 q-pa-sm q-pb-lg"
|
||||
:outlined="auto_cost_per_volume || hasIngredients"
|
||||
:filled="!auto_cost_per_volume && !hasIngredients"
|
||||
:readonly="auto_cost_per_volume || hasIngredients"
|
||||
label="Preis pro L"
|
||||
hint="Inkl. 19% Mehrwertsteuer"
|
||||
suffix="€"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section :key="key">
|
||||
<drink-price-volumes
|
||||
v-model="edit_volumes"
|
||||
:cost-per-volume="cost_per_volume"
|
||||
:editable="hasPermission(PERMISSIONS.EDIT_VOLUME)"
|
||||
@update="updateVolume"
|
||||
@delete-volume="deleteVolume"
|
||||
@delete-price="deletePrice"
|
||||
@delete-ingredient="deleteIngredient"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<build-manual :steps="edit_drink.receipt" @deleteStep="deleteStep" @addStep="addStep" />
|
||||
</q-card-section>
|
||||
<q-card-actions class="justify-around">
|
||||
<q-btn label="Abbrechen" @click="cancel" />
|
||||
<q-btn v-if="can_delete" label="Löschen" color="negative" @click="delete_drink" />
|
||||
<q-btn label="Speichern" color="primary" @click="save" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, onBeforeMount, computed } from 'vue';
|
||||
import { Drink, DrinkPriceVolume, usePricelistStore } from '../store';
|
||||
import DrinkPriceVolumes from './CalculationTable/DrinkPriceVolumes.vue';
|
||||
import { clone, calc_min_prices, DeleteObjects, calc_cost_per_volume } from '../utils/utils';
|
||||
import BuildManual from 'src/plugins/pricelist/components/CalculationTable/BuildManual.vue';
|
||||
import { baseURL } from 'src/config';
|
||||
import { hasPermission } from 'src/utils/permission';
|
||||
import { PERMISSIONS } from 'src/plugins/pricelist/permissions';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DrinkModify',
|
||||
components: { BuildManual, DrinkPriceVolumes },
|
||||
props: {
|
||||
drink: {
|
||||
type: Object as PropType<Drink>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
save: (
|
||||
drink: Drink,
|
||||
toDeleteObjects: DeleteObjects,
|
||||
drinkPic: File | undefined,
|
||||
deletePic: boolean
|
||||
) => (drink && toDeleteObjects) || drinkPic || deletePic,
|
||||
delete: () => true,
|
||||
cancel: () => true,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
onBeforeMount(() => {
|
||||
//edit_drink.value = <Drink>JSON.parse(JSON.stringify(props.drink));
|
||||
edit_drink.value = clone(props.drink);
|
||||
edit_volumes.value = clone(props.drink.volumes);
|
||||
});
|
||||
|
||||
const key = ref(0);
|
||||
|
||||
const store = usePricelistStore();
|
||||
|
||||
const toDeleteObjects = ref<DeleteObjects>({
|
||||
prices: [],
|
||||
volumes: [],
|
||||
ingredients: [],
|
||||
});
|
||||
|
||||
const edit_drink = ref<Drink>();
|
||||
const edit_volumes = ref<Array<DrinkPriceVolume>>([]);
|
||||
function save() {
|
||||
(<Drink>edit_drink.value).volumes = edit_volumes.value;
|
||||
emit('save', <Drink>edit_drink.value, toDeleteObjects.value, drinkPic.value, deletePic.value);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emit('cancel');
|
||||
}
|
||||
function updateVolume(index: number) {
|
||||
if (index > -1 && edit_volumes.value) {
|
||||
edit_volumes.value[index].min_prices = calc_min_prices(
|
||||
edit_volumes.value[index],
|
||||
//edit_drink.value.cost_per_volume,
|
||||
cost_per_volume.value,
|
||||
store.min_prices
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function updateVolumes() {
|
||||
setTimeout(() => {
|
||||
edit_volumes.value?.forEach((_, index) => {
|
||||
updateVolume(index);
|
||||
});
|
||||
key.value++;
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function deletePrice(price: FG.DrinkPrice) {
|
||||
toDeleteObjects.value.prices.push(price);
|
||||
}
|
||||
|
||||
function deleteVolume(volume: DrinkPriceVolume) {
|
||||
toDeleteObjects.value.volumes.push(volume);
|
||||
}
|
||||
|
||||
function deleteIngredient(ingredient: FG.Ingredient) {
|
||||
toDeleteObjects.value.ingredients.push(ingredient);
|
||||
}
|
||||
|
||||
function addStep(event: string) {
|
||||
edit_drink.value?.receipt?.push(event);
|
||||
}
|
||||
|
||||
function deleteStep(event: number) {
|
||||
edit_drink.value?.receipt?.splice(event, 1);
|
||||
}
|
||||
|
||||
const drinkPic = ref();
|
||||
const imgsrc = ref();
|
||||
|
||||
const deletePic = ref(false);
|
||||
|
||||
function delete_pic() {
|
||||
deletePic.value = true;
|
||||
imgsrc.value = undefined;
|
||||
drinkPic.value = undefined;
|
||||
if (edit_drink.value) {
|
||||
edit_drink.value.uuid = '';
|
||||
}
|
||||
}
|
||||
|
||||
function imagePreview() {
|
||||
if (drinkPic.value && drinkPic.value instanceof File) {
|
||||
let reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
imgsrc.value = e.target?.result;
|
||||
};
|
||||
|
||||
reader.readAsDataURL(drinkPic.value);
|
||||
}
|
||||
}
|
||||
|
||||
const image = computed(() => {
|
||||
if (deletePic.value) {
|
||||
return 'no-image.svg';
|
||||
}
|
||||
if (imgsrc.value) {
|
||||
return <string>imgsrc.value;
|
||||
}
|
||||
if (edit_drink.value?.uuid) {
|
||||
return `${baseURL.value}/pricelist/picture/${edit_drink.value.uuid}?size=256`;
|
||||
}
|
||||
return 'no-image.svg';
|
||||
});
|
||||
|
||||
const can_delete = computed(() => {
|
||||
if (edit_drink.value) {
|
||||
if (edit_drink.value.id < 0) {
|
||||
return false;
|
||||
}
|
||||
const _edit_drink = edit_drink.value;
|
||||
const test = _edit_drink.volumes ? _edit_drink.volumes.length === 0 : true;
|
||||
console.log(test);
|
||||
return test;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
function delete_drink() {
|
||||
emit('delete');
|
||||
}
|
||||
|
||||
const auto_cost_per_volume = computed(
|
||||
() =>
|
||||
!!(
|
||||
edit_drink.value?.cost_per_package &&
|
||||
edit_drink.value?.package_size &&
|
||||
edit_drink.value?.volume
|
||||
)
|
||||
);
|
||||
|
||||
const cost_per_volume = computed({
|
||||
get: () => {
|
||||
let retVal: number;
|
||||
if (auto_cost_per_volume.value) {
|
||||
retVal = <number>calc_cost_per_volume(<Drink>edit_drink.value);
|
||||
} else {
|
||||
retVal = <number>(<Drink>edit_drink.value).cost_per_volume;
|
||||
}
|
||||
updateVolumes();
|
||||
return retVal;
|
||||
},
|
||||
set: (val: number) => ((<Drink>edit_drink.value).cost_per_volume = val),
|
||||
});
|
||||
|
||||
const hasIngredients = computed(() =>
|
||||
edit_volumes.value?.some((a) => a.ingredients.length > 0)
|
||||
);
|
||||
|
||||
return {
|
||||
edit_drink,
|
||||
save,
|
||||
cancel,
|
||||
updateVolume,
|
||||
deletePrice,
|
||||
deleteIngredient,
|
||||
deleteVolume,
|
||||
addStep,
|
||||
deleteStep,
|
||||
tags: computed(() => store.tags),
|
||||
image,
|
||||
imgsrc,
|
||||
drinkPic,
|
||||
imagePreview,
|
||||
delete_pic,
|
||||
types: computed(() => store.drinkTypes),
|
||||
can_delete,
|
||||
delete_drink,
|
||||
auto_cost_per_volume,
|
||||
cost_per_volume,
|
||||
edit_volumes,
|
||||
key,
|
||||
hasIngredients,
|
||||
hasPermission,
|
||||
PERMISSIONS,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,48 +1,48 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-dialog v-model="edittype">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Editere Getränkeart {{ actualDrinkType.name }}</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-input v-model="newDrinkTypeName" dense label="name" filled />
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" />
|
||||
<q-btn flat color="primary" label="Speichern" @click="saveChanges()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-page padding>
|
||||
<q-table title="Getränkearten" :rows="rows" :row-key="(row) => row.id" :columns="columns">
|
||||
<q-table
|
||||
title="Getränkearten"
|
||||
:rows="rows"
|
||||
:row-key="(row) => row.id"
|
||||
:columns="columns"
|
||||
style="height: 100%"
|
||||
:pagination="pagination"
|
||||
>
|
||||
<template #top-right>
|
||||
<q-input
|
||||
v-model="newDrinkType"
|
||||
class="q-px-sm"
|
||||
dense
|
||||
placeholder="Neue Getränkeart"
|
||||
filled
|
||||
/>
|
||||
|
||||
<div></div>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
|
||||
<div class="full-width row q-gutter-sm">
|
||||
<q-input v-model="newDrinkType" dense placeholder="Neue Getränkeart" filled />
|
||||
<q-btn round color="primary" icon="mdi-plus" @click="addType">
|
||||
<q-tooltip> Getränkeart hinzufügen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<template #body="props">
|
||||
<q-tr :props="props">
|
||||
<q-td key="drinkTypeName" :props="props">
|
||||
{{ props.row.name }}
|
||||
<q-popup-edit
|
||||
v-model="props.row.name"
|
||||
buttons
|
||||
label-set="Speichern"
|
||||
label-cancel="Abbrechen"
|
||||
@save="saveChanges(props.row)"
|
||||
>
|
||||
<template #default="scope">
|
||||
<q-input v-model="scope.value" dense label="name" filled />
|
||||
</template>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="actions" :props="props">
|
||||
<q-btn
|
||||
round
|
||||
flat
|
||||
icon="mdi-pencil"
|
||||
@click="editType({ id: props.row.id, name: props.row.name })"
|
||||
icon="mdi-delete"
|
||||
color="negative"
|
||||
size="sm"
|
||||
@click="deleteType(props.row.id)"
|
||||
/>
|
||||
<q-btn round flat icon="mdi-delete" @click="deleteType(props.row.id)" />
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -54,14 +54,9 @@ export default defineComponent({
|
|||
setup() {
|
||||
const store = usePricelistStore();
|
||||
const newDrinkType = ref('');
|
||||
const newDrinkTypeName = ref('');
|
||||
const edittype = ref(false);
|
||||
const emptyDrinkType: FG.DrinkType = { id: -1, name: '' };
|
||||
const actualDrinkType = ref(emptyDrinkType);
|
||||
|
||||
onBeforeMount(() => {
|
||||
console.log(store);
|
||||
void store.getDrinkTypes();
|
||||
void store.getDrinkTypes(true);
|
||||
});
|
||||
const rows = computed(() => store.drinkTypes);
|
||||
const columns = [
|
||||
|
@ -70,14 +65,14 @@ export default defineComponent({
|
|||
label: 'Getränkeart',
|
||||
field: 'name',
|
||||
align: 'left',
|
||||
sortable: true
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
label: 'Aktionen',
|
||||
field: 'actions',
|
||||
align: 'right'
|
||||
}
|
||||
align: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
async function addType() {
|
||||
|
@ -85,30 +80,22 @@ export default defineComponent({
|
|||
newDrinkType.value = '';
|
||||
}
|
||||
|
||||
function editType(drinkType: FG.DrinkType) {
|
||||
edittype.value = true;
|
||||
actualDrinkType.value = drinkType;
|
||||
function saveChanges(drinkType: FG.DrinkType) {
|
||||
setTimeout(() => {
|
||||
const _drinkType = store.drinkTypes.find((a) => a.id === drinkType.id);
|
||||
if (_drinkType) {
|
||||
void store.changeDrinkTypeName(drinkType);
|
||||
}
|
||||
|
||||
async function saveChanges() {
|
||||
try {
|
||||
await store.changeDrinkTypeName({
|
||||
id: actualDrinkType.value.id,
|
||||
name: newDrinkTypeName.value
|
||||
});
|
||||
} catch (e) {}
|
||||
discardChanges();
|
||||
}
|
||||
|
||||
function discardChanges() {
|
||||
actualDrinkType.value = emptyDrinkType;
|
||||
newDrinkTypeName.value = '';
|
||||
edittype.value = false;
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function deleteType(id: number) {
|
||||
void store.removeDrinkType(id);
|
||||
}
|
||||
const pagination = ref({
|
||||
sortBy: 'name',
|
||||
rowsPerPage: 10,
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
|
@ -116,14 +103,10 @@ export default defineComponent({
|
|||
addType,
|
||||
newDrinkType,
|
||||
deleteType,
|
||||
edittype,
|
||||
editType,
|
||||
actualDrinkType,
|
||||
newDrinkTypeName,
|
||||
discardChanges,
|
||||
saveChanges
|
||||
saveChanges,
|
||||
pagination,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,36 +1,17 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-dialog v-model="edittype">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Editere Extrazutaten {{ actualExtraIngredient.name }}</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-input v-model="actualExtraIngredient.name" dense label="Name" filled />
|
||||
<q-input
|
||||
v-model.number="actualExtraIngredient.price"
|
||||
dense
|
||||
label="Preis"
|
||||
filled
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
suffix="€"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" />
|
||||
<q-btn flat color="primary" label="Speichern" @click="saveChanges()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-page padding>
|
||||
<q-table title="Getränkearten" :rows="rows" :row-key="(row) => row.id" :columns="columns">
|
||||
<q-table
|
||||
title="Getränkearten"
|
||||
:rows="rows"
|
||||
:row-key="(row) => row.id"
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
>
|
||||
<template #top-right>
|
||||
<div class="full-width row q-gutter-sm">
|
||||
<q-input
|
||||
v-model="newExtraIngredient.name"
|
||||
class="q-px-sm"
|
||||
dense
|
||||
placeholder="Neue Zutatenbezeichnung"
|
||||
label="Neue Zutatenbezeichnung"
|
||||
|
@ -38,7 +19,6 @@
|
|||
/>
|
||||
<q-input
|
||||
v-model.number="newExtraIngredient.price"
|
||||
class="q-px-sm"
|
||||
dense
|
||||
placeholder="Preis"
|
||||
label="Preis"
|
||||
|
@ -48,13 +28,40 @@
|
|||
step="0.1"
|
||||
suffix="€"
|
||||
/>
|
||||
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addExtraIngredient" />
|
||||
<q-btn color="primary" icon="mdi-plus" round @click="addExtraIngredient">
|
||||
<q-tooltip> Zutat hinzufügen </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #body-cell-actions="props">
|
||||
<q-td :props="props" align="right" :auto-width="true">
|
||||
<q-btn round flat icon="mdi-pencil" @click="editType(props.row)" />
|
||||
<q-btn round flat icon="mdi-delete" @click="deleteType(props.row)" />
|
||||
<template #body="props">
|
||||
<q-tr :props="props">
|
||||
<q-td key="name" :props="props" align="left">
|
||||
{{ props.row.name }}
|
||||
<q-popup-edit
|
||||
v-model="props.row.name"
|
||||
buttons
|
||||
label-set="Speichern"
|
||||
label-cancel="Abbrechen"
|
||||
@save="saveChanges(props.row)"
|
||||
>
|
||||
<template #default="scope">
|
||||
<q-input v-model="scope.value" dense label="name" filled />
|
||||
</template>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="price" :props="props" align="right">
|
||||
{{ props.row.price.toFixed(2) }}€
|
||||
</q-td>
|
||||
<q-td key="actions" :props="props" align="right" auto-width>
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-delete"
|
||||
color="negative"
|
||||
size="sm"
|
||||
@click="deleteType(props.row)"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-page>
|
||||
|
@ -75,8 +82,6 @@ export default defineComponent({
|
|||
id: -1,
|
||||
};
|
||||
const newExtraIngredient = ref<FG.ExtraIngredient>(emptyExtraIngredient);
|
||||
const edittype = ref(false);
|
||||
const actualExtraIngredient = ref(emptyExtraIngredient);
|
||||
|
||||
const rows = computed(() => store.extraIngredients);
|
||||
const columns = [
|
||||
|
@ -93,6 +98,7 @@ export default defineComponent({
|
|||
field: 'price',
|
||||
sortable: true,
|
||||
format: (val: number) => `${val.toFixed(2)}€`,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
|
@ -108,38 +114,38 @@ export default defineComponent({
|
|||
discardChanges();
|
||||
}
|
||||
|
||||
function editType(extraIngredient: FG.ExtraIngredient) {
|
||||
edittype.value = true;
|
||||
actualExtraIngredient.value = extraIngredient;
|
||||
function saveChanges(ingredient: FG.ExtraIngredient) {
|
||||
setTimeout(() => {
|
||||
const _ingredient = store.extraIngredients.find((a) => a.id === ingredient.id);
|
||||
if (_ingredient) {
|
||||
void store.updateExtraIngredient(_ingredient);
|
||||
}
|
||||
|
||||
async function saveChanges() {
|
||||
await store.updateExtraIngredient(actualExtraIngredient.value);
|
||||
setTimeout(() => discardChanges(), 200);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function discardChanges() {
|
||||
actualExtraIngredient.value = emptyExtraIngredient;
|
||||
newExtraIngredient.value.name = '';
|
||||
newExtraIngredient.value.price = 0;
|
||||
edittype.value = false;
|
||||
}
|
||||
|
||||
function deleteType(extraIngredient: FG.ExtraIngredient) {
|
||||
void store.deleteExtraIngredient(extraIngredient);
|
||||
}
|
||||
|
||||
const pagination = ref({
|
||||
sortBy: 'name',
|
||||
rowsPerPage: 10,
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
addExtraIngredient,
|
||||
newExtraIngredient,
|
||||
deleteType,
|
||||
edittype,
|
||||
editType,
|
||||
actualExtraIngredient,
|
||||
discardChanges,
|
||||
saveChanges,
|
||||
pagination,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<q-table
|
||||
title="Preisliste"
|
||||
:columns="columns"
|
||||
:rows="drinks"
|
||||
:visible-columns="visibleColumns"
|
||||
:filter="search"
|
||||
:filter-method="filter"
|
||||
dense
|
||||
:pagination="pagination"
|
||||
:fullscreen="fullscreen"
|
||||
>
|
||||
<template #top-right>
|
||||
<div class="row justify-end q-gutter-sm">
|
||||
<search-input v-model="search" :keys="options" />
|
||||
<q-select
|
||||
v-model="visibleColumns"
|
||||
multiple
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
display-value="Sichtbarkeit"
|
||||
emit-value
|
||||
map-options
|
||||
:options="options"
|
||||
option-value="name"
|
||||
options-cover
|
||||
/>
|
||||
<q-btn round icon="mdi-backburger">
|
||||
<q-tooltip anchor='top middle' self='bottom middle'> Reihenfolge ändern </q-tooltip>
|
||||
<q-menu anchor="bottom middle" self="top middle">
|
||||
<drag v-model="order" class="q-list" ghost-class="ghost" group="people" item-key="id">
|
||||
<template #item="{ element }">
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
{{ element.label }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</drag>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<slot></slot>
|
||||
<q-btn
|
||||
round
|
||||
:icon="fullscreen ? 'mdi-fullscreen-exit' : 'mdi-fullscreen'"
|
||||
@click="fullscreen = !fullscreen"
|
||||
/>
|
||||
</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="text-caption"
|
||||
rounded
|
||||
:style="`background-color: ${tag.color}`"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</q-badge>
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-public="props">
|
||||
<q-td :props="props">
|
||||
<q-toggle
|
||||
v-model="props.row.public"
|
||||
disable
|
||||
checked-icon="mdi-earth"
|
||||
unchecked-icon="mdi-earth-off"
|
||||
/>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeMount, ref, ComponentPublicInstance } from 'vue';
|
||||
import { usePricelistStore, Order } from '../store';
|
||||
import { useMainStore } from 'src/stores';
|
||||
import { Search, filter } from 'src/plugins/pricelist/utils/filter';
|
||||
import SearchInput from 'src/plugins/pricelist/components/SearchInput.vue';
|
||||
import draggable from 'vuedraggable';
|
||||
const drag: ComponentPublicInstance = <ComponentPublicInstance>draggable;
|
||||
|
||||
interface Row {
|
||||
name: string;
|
||||
label: string;
|
||||
field: string;
|
||||
sortable?: boolean;
|
||||
filterable?: boolean;
|
||||
format?: (val: never) => string;
|
||||
align?: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Pricelist',
|
||||
components: { SearchInput, drag },
|
||||
props: {
|
||||
public: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = usePricelistStore();
|
||||
const user = ref('');
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (!props.public) {
|
||||
user.value = useMainStore().currentUser.userid;
|
||||
void store.getPriceListColumnOrder(user.value);
|
||||
void store.getDrinks();
|
||||
void store.getPriceCalcColumn(user.value);
|
||||
} else {
|
||||
user.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
const _order = ref<Array<Order>>([
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Kategorie',
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
},
|
||||
{
|
||||
name: 'volume',
|
||||
label: 'Inhalt',
|
||||
},
|
||||
{
|
||||
name: 'price',
|
||||
label: 'Preis',
|
||||
},
|
||||
{
|
||||
name: 'public',
|
||||
label: 'Öffentlich',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Beschreibung',
|
||||
},
|
||||
]);
|
||||
|
||||
const order = computed<Array<Order>>({
|
||||
get: () => {
|
||||
if (props.public) {
|
||||
return _order.value;
|
||||
}
|
||||
if (store.pricelist_columns_order.length === 0) {
|
||||
return _order.value;
|
||||
}
|
||||
return store.pricelist_columns_order;
|
||||
},
|
||||
set: (val: Array<Order>) => {
|
||||
if (!props.public) {
|
||||
void store.updatePriceListColumnOrder(user.value, val);
|
||||
} else {
|
||||
_order.value = val;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const _columns: Array<Row> = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Kategorie',
|
||||
field: 'type',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
format: (val: FG.DrinkType) => val.name,
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
field: 'tags',
|
||||
filterable: true,
|
||||
|
||||
format: (val: Array<FG.Tag>) => {
|
||||
let retVal = '';
|
||||
val.forEach((tag, index) => {
|
||||
if (index >= val.length - 1 && index > 0) {
|
||||
retVal += ', ';
|
||||
}
|
||||
retVal += tag.name;
|
||||
});
|
||||
return retVal;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'volume',
|
||||
label: 'Inhalt',
|
||||
field: 'volume',
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
format: (val: number) => `${val.toFixed(3)}L`,
|
||||
},
|
||||
{
|
||||
name: 'price',
|
||||
label: 'Preis',
|
||||
field: 'price',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
format: (val: number) => `${val.toFixed(2)}€`,
|
||||
},
|
||||
{
|
||||
name: 'public',
|
||||
label: 'Öffentlich',
|
||||
field: 'public',
|
||||
format: (val: boolean) => (val ? 'Öffentlich' : 'nicht Öffentlich'),
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Beschreibung',
|
||||
field: 'description',
|
||||
filterable: true,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = computed(() => {
|
||||
const retVal: Array<Row> = [];
|
||||
if (order.value) {
|
||||
order.value.forEach((col) => {
|
||||
const _col = _columns.find((a) => a.name === col.name);
|
||||
if (_col) {
|
||||
retVal.push(_col);
|
||||
}
|
||||
});
|
||||
retVal.forEach((element, index) => {
|
||||
element.align = 'right';
|
||||
if (index === 0) {
|
||||
element.align = 'left';
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
}
|
||||
return _columns;
|
||||
});
|
||||
|
||||
const _options = computed(() => {
|
||||
const retVal: Array<{ name: string; label: string; field: string }> = [];
|
||||
columns.value.forEach((col) => {
|
||||
if (props.public) {
|
||||
if (col.name !== 'public') {
|
||||
retVal.push(col);
|
||||
}
|
||||
} else {
|
||||
retVal.push(col);
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
});
|
||||
|
||||
const _colums = computed<Array<string>>(() => {
|
||||
const retVal: Array<string> = [];
|
||||
columns.value.forEach((col) => {
|
||||
if (props.public) {
|
||||
if (col.name !== 'public') {
|
||||
retVal.push(col.name);
|
||||
}
|
||||
} else {
|
||||
retVal.push(col.name);
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
});
|
||||
|
||||
const _visibleColumns = ref(_colums.value);
|
||||
|
||||
const visibleColumns = computed({
|
||||
get: () => (props.public ? _visibleColumns.value : store.pricecalc_columns),
|
||||
set: (val) => {
|
||||
if (!props.public) {
|
||||
void store.updatePriceCalcColumn(user.value, val);
|
||||
} else {
|
||||
_visibleColumns.value = val;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const search = ref<Search>({
|
||||
value: '',
|
||||
key: '',
|
||||
label: '',
|
||||
});
|
||||
|
||||
const pagination = ref({
|
||||
sortBy: 'name',
|
||||
rowsPerPage: 10,
|
||||
});
|
||||
|
||||
const fullscreen = ref(false);
|
||||
|
||||
return {
|
||||
drinks: computed(() => store.pricelist),
|
||||
columns,
|
||||
order,
|
||||
visibleColumns,
|
||||
options: _options,
|
||||
search,
|
||||
filter,
|
||||
pagination,
|
||||
fullscreen,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
.ghost
|
||||
opacity: 0.5
|
||||
background: $accent
|
||||
</style>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<q-dialog v-model="alert">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Suche</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<div>
|
||||
Wenn du in die Suche etwas eingibst, wird in allen Spalten gesucht. Mit einem `@` Zeichen,
|
||||
kann man die Suche eingrenzen auf eine Spalte. Zumbeispiel: `Tequilaparty@Tags`
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div>Mögliche Suchbegriffe nach dem @:</div>
|
||||
<div class="fit row q-gutter-sm">
|
||||
<div v-for="key in keys" :key="key.name">
|
||||
{{ key.label }}
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn v-close-popup flat label="OK" color="primary" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-input v-model="v_model" filled dense>
|
||||
<template #append>
|
||||
<q-icon name="mdi-magnify" />
|
||||
</template>
|
||||
<template #prepend>
|
||||
<q-btn icon="mdi-help-circle" flat round @click="alert = true" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType, ref } from 'vue';
|
||||
import { Search, Col } from '../utils/filter';
|
||||
export default defineComponent({
|
||||
name: 'SearchInput',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<Search>,
|
||||
default: { value: '', key: undefined, label: '' },
|
||||
},
|
||||
keys: {
|
||||
type: Object as PropType<Array<Col>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
'update:modelValue': (val: {
|
||||
value: string;
|
||||
key: string | undefined;
|
||||
label: string | undefined;
|
||||
}) => val,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const v_model = computed<string>({
|
||||
get: () => {
|
||||
if (!props.modelValue.label || props.modelValue.label === '') {
|
||||
return `${props.modelValue.value}`;
|
||||
}
|
||||
return `${props.modelValue.value}@${props.modelValue.label}`;
|
||||
},
|
||||
set: (val: string) => {
|
||||
const split = val.toLowerCase().split('@');
|
||||
if (split.length < 2) {
|
||||
emit('update:modelValue', { value: split[0], label: undefined, key: undefined });
|
||||
} else {
|
||||
props.keys.find((key) => {
|
||||
if (key.label.toLowerCase() === split[1]) {
|
||||
console.log(key.name);
|
||||
emit('update:modelValue', { value: split[0], label: split[1], key: key.name });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const alert = ref(false);
|
||||
return { v_model, alert };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
a
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-page padding>
|
||||
<q-table
|
||||
title="Tags"
|
||||
:rows="rows"
|
||||
:row-key="(row) => row.id"
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
>
|
||||
<template #top-right>
|
||||
<q-btn color="primary" icon="mdi-plus" round>
|
||||
<q-tooltip> Tag hinzufügen </q-tooltip>
|
||||
<q-menu v-model="popup" anchor="center middle" self="center middle" persistent>
|
||||
<q-input
|
||||
v-model="newTag.name"
|
||||
filled
|
||||
dense
|
||||
label="Name"
|
||||
class="q-pa-sm"
|
||||
:rule="[notExists]"
|
||||
/>
|
||||
<q-color
|
||||
:model-value="newTag.color"
|
||||
flat
|
||||
class="q-pa-sm"
|
||||
@change="
|
||||
(val) => {
|
||||
newTag.color = val;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<div class="full-width row q-gutter-sm justify-around q-py-sm">
|
||||
<q-btn v-close-popup flat label="Abbrechen" />
|
||||
<q-btn flat label="Speichern" color="primary" @click="save" />
|
||||
</div>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</template>
|
||||
<template #header="props">
|
||||
<q-tr :props="props">
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
{{ col.label }}
|
||||
</q-th>
|
||||
<q-th auto-width />
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<q-tr :props="props">
|
||||
<q-td key="name" :props="props">
|
||||
{{ props.row.name }}
|
||||
<q-popup-edit
|
||||
v-model="props.row.name"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateTag(props.row)"
|
||||
>
|
||||
<template #default="scope">
|
||||
<q-input v-model="scope.value" :rules="[notExists]" dense filled />
|
||||
</template>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td key="color" :props="props">
|
||||
<div class="full-width row q-gutter-sm justify-end items-center">
|
||||
<div>
|
||||
{{ props.row.color }}
|
||||
</div>
|
||||
<div class="color-box" :style="`background-color: ${props.row.color};`"> </div>
|
||||
</div>
|
||||
<q-popup-edit
|
||||
v-model="props.row.color"
|
||||
buttons
|
||||
label-cancel="Abbrechen"
|
||||
label-set="Speichern"
|
||||
@update:modelValue="updateTag(props.row)"
|
||||
>
|
||||
<template #default="slot">
|
||||
<div class="full-width row justify-center">
|
||||
<q-color
|
||||
:model-value="slot.value"
|
||||
class="full-width"
|
||||
flat
|
||||
@change="(val) => (slot.value = val)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</q-popup-edit>
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-btn
|
||||
icon="mdi-delete"
|
||||
color="negative"
|
||||
round
|
||||
size="sm"
|
||||
@click="deleteTag(props.row)"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onBeforeMount, computed } from 'vue';
|
||||
import { usePricelistStore } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Tags',
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
onBeforeMount(() => {
|
||||
void store.getTags();
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
field: 'name',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
label: 'Farbe',
|
||||
field: 'color',
|
||||
},
|
||||
];
|
||||
const rows = computed(() => store.tags);
|
||||
const emptyTag = {
|
||||
id: -1,
|
||||
color: '#1976d2',
|
||||
name: '',
|
||||
};
|
||||
|
||||
async function save() {
|
||||
await store.setTag(newTag.value);
|
||||
popup.value = false;
|
||||
newTag.value = emptyTag;
|
||||
}
|
||||
|
||||
const newTag = ref(emptyTag);
|
||||
const popup = ref(false);
|
||||
function notExists(val: string) {
|
||||
const index = store.tags.findIndex((a) => a.name === val);
|
||||
if (index > -1) {
|
||||
return 'Tag existiert bereits.';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const pagination = ref({
|
||||
sortBy: 'name',
|
||||
rowsPerPage: 10,
|
||||
});
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
newTag,
|
||||
popup,
|
||||
save,
|
||||
updateTag: store.updateTag,
|
||||
notExists,
|
||||
deleteTag: store.deleteTag,
|
||||
pagination,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.color-box {
|
||||
min-width: 28px;
|
||||
min-heigh: 28px;
|
||||
max-width: 28px;
|
||||
max-height: 28px;
|
||||
border-width: 1px;
|
||||
border-color: black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="q-table__title">Cocktailbuilder</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<ingredients
|
||||
v-model="volume.ingredients"
|
||||
class="q-pa-sm"
|
||||
editable
|
||||
@update:modelValue="update"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="q-table__title">Du solltest mindest sowiel verlangen oder bezahlen:</div>
|
||||
<div class="full-width row q-gutter-sm justify-around">
|
||||
<div v-for="min_price in volume.min_prices" :key="min_price.percentage">
|
||||
<div>
|
||||
<q-badge class="text-h6" color="primary"> {{ min_price.percentage }}% </q-badge>
|
||||
</div>
|
||||
<div>{{ min_price.price.toFixed(2) }}€</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onBeforeMount, ref } from 'vue';
|
||||
import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue';
|
||||
import { DrinkPriceVolume, usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import { calc_min_prices } from '../utils/utils';
|
||||
export default defineComponent({
|
||||
name: 'CocktailBuilder',
|
||||
components: { Ingredients },
|
||||
setup() {
|
||||
onBeforeMount(() => {
|
||||
void store.get_min_prices().finally(() => {
|
||||
volume.value.min_prices = calc_min_prices(volume.value, undefined, store.min_prices);
|
||||
});
|
||||
void store.getDrinks();
|
||||
void store.getDrinkTypes();
|
||||
void store.getExtraIngredients();
|
||||
});
|
||||
const store = usePricelistStore();
|
||||
|
||||
const emptyVolume: DrinkPriceVolume = {
|
||||
id: -1,
|
||||
_volume: 0,
|
||||
min_prices: [],
|
||||
prices: [],
|
||||
ingredients: [],
|
||||
};
|
||||
const volume = ref(emptyVolume);
|
||||
|
||||
function update() {
|
||||
volume.value.min_prices = calc_min_prices(volume.value, undefined, store.min_prices);
|
||||
}
|
||||
|
||||
return { volume, update };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<calculation-table v-if="!list" nodetails>
|
||||
<q-btn icon="mdi-view-list" round @click="list = !list">
|
||||
<q-tooltip> Zur Listenansicht wechseln </q-tooltip>
|
||||
</q-btn>
|
||||
</calculation-table>
|
||||
<pricelist v-if="list">
|
||||
<q-btn icon="mdi-cards-variant" round @click="list = !list">
|
||||
<q-tooltip> Zur Kartenansicht wechseln </q-tooltip>
|
||||
</q-btn>
|
||||
</pricelist>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onBeforeMount, computed } from 'vue';
|
||||
import CalculationTable from '../components/CalculationTable.vue';
|
||||
import Pricelist from 'src/plugins/pricelist/components/Pricelist.vue';
|
||||
import { usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import { useMainStore } from 'src/stores';
|
||||
export default defineComponent({
|
||||
name: 'InnerPricelist',
|
||||
components: { Pricelist, CalculationTable },
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
const mainStore = useMainStore();
|
||||
|
||||
onBeforeMount(() => {
|
||||
void store.getDrinks();
|
||||
void store.getPriceListView(mainStore.currentUser.userid);
|
||||
});
|
||||
|
||||
const list = computed({
|
||||
get: () => store.pricelist_view,
|
||||
set: (val: boolean) => store.updatePriceListView(mainStore.currentUser.userid, val),
|
||||
});
|
||||
return { list };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<calculation-table v-if="!list" public>
|
||||
<q-btn icon="mdi-view-list" round @click="list = !list">
|
||||
<q-tooltip> Zur Listenansicht wechseln </q-tooltip>
|
||||
</q-btn>
|
||||
</calculation-table>
|
||||
<pricelist v-if="list" public>
|
||||
<q-btn icon="mdi-cards-variant" round @click="list = !list">
|
||||
<q-tooltip> Zur Kartenansicht wechseln </q-tooltip>
|
||||
</q-btn>
|
||||
</pricelist>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import CalculationTable from '../components/CalculationTable.vue';
|
||||
import Pricelist from '../components/Pricelist.vue';
|
||||
import { usePricelistStore } from '../store';
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'OuterPricelist',
|
||||
components: { Pricelist, CalculationTable },
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
|
||||
onBeforeMount(() => {
|
||||
void store.getDrinks();
|
||||
});
|
||||
|
||||
const list = ref(false);
|
||||
return { list };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,100 +0,0 @@
|
|||
<template>
|
||||
<q-page padding>
|
||||
<q-card>
|
||||
<q-table title="Getränke" :columns="columns_drinks" :rows="drinks" row-key="name">
|
||||
<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
|
||||
>
|
||||
<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
|
||||
>
|
||||
<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>
|
||||
</q-table>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onBeforeMount, computed } from 'vue';
|
||||
import { usePricelistStore } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Pricelist',
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
|
||||
onBeforeMount(() => {
|
||||
void store.getDrinks();
|
||||
});
|
||||
const drinks = computed(() => store.drinks);
|
||||
const columns_drinks = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Getränk',
|
||||
field: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
];
|
||||
return { columns_drinks, columns_volumes, columns_prices, drinks };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,175 @@
|
|||
<template>
|
||||
<q-table
|
||||
grid
|
||||
title="Rezepte"
|
||||
:rows="drinks"
|
||||
row-key="id"
|
||||
hide-header
|
||||
:filter="search"
|
||||
:filter-method="filter"
|
||||
:columns="options"
|
||||
>
|
||||
<template #top-right>
|
||||
<search-input v-model="search" :keys="search_keys" />
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
||||
<q-card>
|
||||
<q-img
|
||||
style="max-height: 256px"
|
||||
loading="lazy"
|
||||
:src="image(props.row.uuid)"
|
||||
placeholder-src="no-image.svg"
|
||||
>
|
||||
<div class="absolute-bottom-right justify-end">
|
||||
<div class="text-subtitle1 text-right">
|
||||
{{ props.row.name }}
|
||||
</div>
|
||||
<div class="text-caption text-right">
|
||||
{{ props.row.type.name }}
|
||||
</div>
|
||||
</div>
|
||||
</q-img>
|
||||
<q-card-section>
|
||||
<q-badge
|
||||
v-for="tag in props.row.tags"
|
||||
:key="`${props.row.id}-${tag.id}`"
|
||||
class="text-caption"
|
||||
rounded
|
||||
:style="`background-color: ${tag.color}`"
|
||||
>
|
||||
{{ tag.name }}
|
||||
</q-badge>
|
||||
</q-card-section>
|
||||
<build-manual-volume :volumes="props.row.volumes" />
|
||||
<q-card-section>
|
||||
<div class="text-h6">Anleitung</div>
|
||||
<build-manual :steps="props.row.receipt" :editable="false" />
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeMount, ref } from 'vue';
|
||||
import { usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import BuildManual from 'src/plugins/pricelist/components/CalculationTable/BuildManual.vue';
|
||||
import BuildManualVolume from '../components/BuildManual/BuildManualVolume.vue';
|
||||
import SearchInput from '../components/SearchInput.vue';
|
||||
import { filter, Search } from '../utils/filter';
|
||||
import { sort } from '../utils/sort';
|
||||
import { baseURL } from 'src/config';
|
||||
export default defineComponent({
|
||||
name: 'Reciepts',
|
||||
components: { BuildManual, BuildManualVolume, SearchInput },
|
||||
setup() {
|
||||
const store = usePricelistStore();
|
||||
onBeforeMount(() => {
|
||||
void store.getDrinks();
|
||||
});
|
||||
const drinks = computed(() =>
|
||||
store.drinks.filter((drink) => {
|
||||
return drink.volumes.some((volume) => volume.ingredients.length > 0);
|
||||
})
|
||||
);
|
||||
|
||||
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: 'Tag',
|
||||
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',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'prices',
|
||||
label: 'Preise',
|
||||
field: 'prices',
|
||||
},
|
||||
];
|
||||
const columns_prices = [
|
||||
{
|
||||
name: 'price',
|
||||
label: 'Preis',
|
||||
field: 'price',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Beschreibung',
|
||||
field: 'description',
|
||||
},
|
||||
{
|
||||
name: 'public',
|
||||
label: 'Öffentlich',
|
||||
field: 'public',
|
||||
},
|
||||
];
|
||||
|
||||
const search = ref<Search>({ value: '', key: '', label: '' });
|
||||
const search_keys = computed(() => columns_drinks.filter((column) => column.filterable));
|
||||
function image(uuid: string | undefined) {
|
||||
if (uuid) {
|
||||
return `${baseURL.value}/pricelist/picture/${uuid}?size=256`;
|
||||
}
|
||||
return 'no-image.svg';
|
||||
}
|
||||
return {
|
||||
drinks,
|
||||
options: [...columns_drinks, ...columns_volumes, ...columns_prices],
|
||||
search,
|
||||
filter,
|
||||
search_keys,
|
||||
image,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -32,7 +32,7 @@
|
|||
class="q-ma-none q-pa-none fit row justify-center content-start items-start"
|
||||
>
|
||||
<q-tab-panel name="pricelist">
|
||||
<calculation-table />
|
||||
<calculation-table editable />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="extra_ingredients">
|
||||
<extra-ingredients />
|
||||
|
@ -40,6 +40,9 @@
|
|||
<q-tab-panel name="drink_types">
|
||||
<drink-types />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="tags">
|
||||
<tags />
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</q-page>
|
||||
</div>
|
||||
|
@ -51,15 +54,18 @@ import { Screen } from 'quasar';
|
|||
import DrinkTypes from 'src/plugins/pricelist/components/DrinkTypes.vue';
|
||||
import CalculationTable from 'src/plugins/pricelist/components/CalculationTable.vue';
|
||||
import ExtraIngredients from 'src/plugins/pricelist/components/ExtraIngredients.vue';
|
||||
import Tags from '../components/Tags.vue';
|
||||
import { usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
import { hasPermissions } from 'src/utils/permission';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Settings',
|
||||
components: { ExtraIngredients, DrinkTypes, CalculationTable },
|
||||
components: { ExtraIngredients, DrinkTypes, Tags, CalculationTable },
|
||||
setup() {
|
||||
interface Tab {
|
||||
name: string;
|
||||
label: string;
|
||||
permissions: Array<string>;
|
||||
}
|
||||
const store = usePricelistStore();
|
||||
onBeforeMount(() => {
|
||||
|
@ -69,6 +75,7 @@ export default defineComponent({
|
|||
console.log(store.extraIngredients);
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
void store.getTags();
|
||||
void store.getDrinkTypes();
|
||||
void store.getDrinks();
|
||||
void store.get_min_prices();
|
||||
|
@ -85,12 +92,40 @@ export default defineComponent({
|
|||
},
|
||||
});
|
||||
|
||||
const tabs: Tab[] = [
|
||||
{ name: 'pricelist', label: 'Getränke' },
|
||||
{ name: 'extra_ingredients', label: 'Zutaten' },
|
||||
{ name: 'drink_types', label: 'Getränketypen' },
|
||||
const _tabs: Tab[] = [
|
||||
{ name: 'pricelist', label: 'Getränke', permissions: ['drink_edit'] },
|
||||
{
|
||||
name: 'extra_ingredients',
|
||||
label: 'Zutaten',
|
||||
permissions: ['edit_ingredients', 'delete_ingredients'],
|
||||
},
|
||||
{
|
||||
name: 'drink_types',
|
||||
label: 'Getränketypen',
|
||||
permissions: ['drink_type_edit', 'drink_type_delete'],
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
permissions: ['drink_tag_edit', 'drink_tag_create', 'drink_tag_delete'],
|
||||
},
|
||||
];
|
||||
|
||||
const tabs = computed(() => {
|
||||
const retVal: Tab[] = [];
|
||||
_tabs.forEach((tab) => {
|
||||
if (tab.permissions.length > 0) {
|
||||
if (hasPermissions(tab.permissions)) {
|
||||
retVal.push(tab);
|
||||
}
|
||||
}
|
||||
if (tab.permissions.length === 0) {
|
||||
retVal.push(tab);
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
});
|
||||
|
||||
const tab = ref<string>('pricelist');
|
||||
|
||||
return { tabs, tab, showDrawer };
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
export const PERMISSIONS = {
|
||||
CREATE: 'drink_create',
|
||||
|
||||
EDIT: 'drink_edit',
|
||||
|
||||
DELETE: 'drink_delete',
|
||||
|
||||
CREATE_TAG: 'drink_tag_create',
|
||||
|
||||
EDIT_PRICE: 'edit_price',
|
||||
DELETE_PRICE: 'delete_price',
|
||||
|
||||
EDIT_VOLUME: 'edit_volume',
|
||||
DELETE_VOLUME: 'delete_volume',
|
||||
|
||||
EDIT_INGREDIENTS_DRINK: 'edit_ingredients_drink',
|
||||
DELETE_INGREDIENTS_DRINK: 'delete_ingredients_drink',
|
||||
|
||||
EDIT_INGREDIENTS: 'edit_ingredients',
|
||||
DELETE_INGREDIENTS: 'delete_ingredients',
|
||||
|
||||
EDIT_TAG: 'drink_tag_edit',
|
||||
|
||||
DELETE_TAG: 'drink_tag_delete',
|
||||
|
||||
CREATE_TYPE: 'drink_type_create',
|
||||
|
||||
EDIT_TYPE: 'drink_type_edit',
|
||||
|
||||
DELETE_TYPE: 'drink_type_delete',
|
||||
|
||||
EDIT_MIN_PRICES: 'edit_min_prices',
|
||||
};
|
|
@ -1,9 +1,10 @@
|
|||
import { innerRoutes } from './routes';
|
||||
import { innerRoutes, outerRoutes } from './routes';
|
||||
import { FG_Plugin } from 'src/plugins';
|
||||
|
||||
const plugin: FG_Plugin.Plugin = {
|
||||
name: 'Pricelist',
|
||||
innerRoutes,
|
||||
outerRoutes,
|
||||
requiredModules: [],
|
||||
requiredBackendModules: ['pricelist'],
|
||||
version: '0.0.1',
|
||||
|
|
|
@ -19,14 +19,36 @@ export const innerRoutes: FG_Plugin.MenuRoute[] = [
|
|||
route: {
|
||||
path: 'pricelist',
|
||||
name: 'drinks-pricelist',
|
||||
component: () => import('../pages/Pricelist.vue'),
|
||||
component: () => import('../pages/InnerPricelist.vue'),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Rezepte',
|
||||
shortcut: false,
|
||||
icon: 'mdi-receipt',
|
||||
permissions: ['user'],
|
||||
route: {
|
||||
path: 'reciepts',
|
||||
name: 'reciepts',
|
||||
component: () => import('../pages/Receipts.vue'),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Cocktailbuilder',
|
||||
shortcut: false,
|
||||
icon: 'mdi-glass-cocktail',
|
||||
permissions: ['user'],
|
||||
route: {
|
||||
path: 'cocktail-builder',
|
||||
name: 'cocktail-builder',
|
||||
component: () => import('../pages/CocktailBuilder.vue'),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Einstellungen',
|
||||
icon: 'mdi-coffee-to-go',
|
||||
shortcut: false,
|
||||
permissions: ['user'],
|
||||
permissions: ['drink_edit', 'drink_tag_edit'],
|
||||
route: {
|
||||
path: 'settings',
|
||||
name: 'drinks-settings',
|
||||
|
@ -36,3 +58,16 @@ export const innerRoutes: FG_Plugin.MenuRoute[] = [
|
|||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const outerRoutes: FG_Plugin.MenuRoute[] = [
|
||||
{
|
||||
title: 'Preisliste',
|
||||
icon: 'mdi-glass-mug-variant',
|
||||
shortcut: true,
|
||||
route: {
|
||||
path: 'pricelist',
|
||||
name: 'outter-pricelist',
|
||||
component: () => import('../pages/OuterPricelist.vue'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
import { api } from 'src/boot/axios';
|
||||
import { defineStore } from 'pinia';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { computed, ComputedRef, WritableComputedRef } from 'vue';
|
||||
import {
|
||||
calc_volume,
|
||||
calc_cost_per_volume,
|
||||
calc_all_min_prices,
|
||||
} from 'src/plugins/pricelist/utils/utils';
|
||||
|
||||
interface MinPrice extends Omit<FG.MinPrices, 'price'> {
|
||||
price?: WritableComputedRef<number>;
|
||||
}
|
||||
interface DrinkPriceVolume extends Omit<Omit<FG.DrinkPriceVolume, 'volume'>, 'min_prices'> {
|
||||
interface DrinkPriceVolume extends Omit<FG.DrinkPriceVolume, 'volume'> {
|
||||
_volume: number;
|
||||
volume?: WritableComputedRef<number>;
|
||||
min_prices: MinPrice[];
|
||||
volume?: number;
|
||||
}
|
||||
interface Drink extends Omit<Omit<FG.Drink, 'cost_price_pro_volume'>, 'volumes'> {
|
||||
|
||||
interface Drink extends Omit<Omit<FG.Drink, 'cost_per_volume'>, 'volumes'> {
|
||||
volumes: DrinkPriceVolume[];
|
||||
cost_price_pro_volume: WritableComputedRef<number | undefined>;
|
||||
_cost_price_pro_volume?: number;
|
||||
cost_per_volume?: number;
|
||||
_cost_per_volume?: number;
|
||||
}
|
||||
|
||||
interface Pricelist {
|
||||
name: string;
|
||||
type: FG.DrinkType;
|
||||
tags: Array<FG.Tag>;
|
||||
volume: number;
|
||||
price: number;
|
||||
public: boolean;
|
||||
description: string;
|
||||
}
|
||||
|
||||
class DrinkPriceVolume implements DrinkPriceVolume {
|
||||
|
@ -24,23 +34,7 @@ class DrinkPriceVolume implements DrinkPriceVolume {
|
|||
this.prices = prices;
|
||||
this.ingredients = ingredients;
|
||||
this.min_prices = [];
|
||||
this.volume = computed<number>({
|
||||
get: () => {
|
||||
if (this.ingredients.some((ingredient) => !!ingredient.drink_ingredient)) {
|
||||
let retVal = 0;
|
||||
this.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient?.volume) {
|
||||
retVal += ingredient.drink_ingredient.volume;
|
||||
}
|
||||
});
|
||||
this._volume = retVal;
|
||||
return retVal;
|
||||
} else {
|
||||
return this._volume;
|
||||
}
|
||||
},
|
||||
set: (val) => (this._volume = val),
|
||||
});
|
||||
this.volume = calc_volume(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +49,8 @@ class Drink {
|
|||
cost_per_package,
|
||||
tags,
|
||||
type,
|
||||
uuid,
|
||||
receipt,
|
||||
}: FG.Drink) {
|
||||
this.id = id;
|
||||
this.article_id = article_id;
|
||||
|
@ -62,25 +58,21 @@ class Drink {
|
|||
this.name = name;
|
||||
this.volume = volume;
|
||||
this.cost_per_package = cost_per_package;
|
||||
this.cost_per_volume = cost_per_volume;
|
||||
this.cost_price_pro_volume = computed({
|
||||
get: () => {
|
||||
if (!!this.volume && !!this.package_size && !!this.cost_per_package) {
|
||||
const retVal =
|
||||
((this.cost_per_package || 0) / ((this.volume || 0) * (this.package_size || 0))) * 1.19;
|
||||
this._cost_price_pro_volume = Math.round(retVal * 1000) / 1000;
|
||||
}
|
||||
|
||||
return this._cost_price_pro_volume;
|
||||
},
|
||||
set: (val) => (this._cost_price_pro_volume = val),
|
||||
});
|
||||
this._cost_per_volume = cost_per_volume;
|
||||
this.cost_per_volume = calc_cost_per_volume(this);
|
||||
this.tags = tags;
|
||||
this.type = type;
|
||||
this.volumes = [];
|
||||
this.uuid = uuid;
|
||||
this.receipt = receipt || [];
|
||||
}
|
||||
}
|
||||
|
||||
interface Order {
|
||||
label: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const usePricelistStore = defineStore({
|
||||
id: 'pricelist',
|
||||
|
||||
|
@ -88,8 +80,11 @@ export const usePricelistStore = defineStore({
|
|||
drinkTypes: [] as Array<FG.DrinkType>,
|
||||
drinks: [] as Array<Drink>,
|
||||
extraIngredients: [] as Array<FG.ExtraIngredient>,
|
||||
pricecalc_columns: [] as Array<string>,
|
||||
min_prices: [] as Array<number>,
|
||||
tags: [] as Array<FG.Tag>,
|
||||
pricecalc_columns: [] as Array<string>,
|
||||
pricelist_view: false as boolean,
|
||||
pricelist_columns_order: [] as Array<Order>,
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
@ -157,7 +152,7 @@ export const usePricelistStore = defineStore({
|
|||
});
|
||||
this.drinks.push(_drink);
|
||||
});
|
||||
this.create_min_prices();
|
||||
calc_all_min_prices(this.drinks, this.min_prices);
|
||||
},
|
||||
sortPrices(volume: DrinkPriceVolume) {
|
||||
volume.prices.sort((a, b) => {
|
||||
|
@ -166,51 +161,36 @@ export const usePricelistStore = defineStore({
|
|||
return 0;
|
||||
});
|
||||
},
|
||||
deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
|
||||
api
|
||||
.delete(`pricelist/prices/${price.id}`)
|
||||
.then(() => {
|
||||
const index = volume.prices.findIndex((a) => a.id == price.id);
|
||||
if (index > -1) {
|
||||
volume.prices.splice(index, 1);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.warn(err));
|
||||
async deletePrice(price: FG.DrinkPrice) {
|
||||
await api.delete(`pricelist/prices/${price.id}`);
|
||||
},
|
||||
deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
|
||||
api
|
||||
.delete(`pricelist/volumes/${volume.id}`)
|
||||
.then(() => {
|
||||
async deleteVolume(volume: DrinkPriceVolume, drink: Drink) {
|
||||
await api.delete(`pricelist/volumes/${volume.id}`);
|
||||
const index = drink.volumes.findIndex((a) => a.id === volume.id);
|
||||
if (index > -1) {
|
||||
drink.volumes.splice(index, 1);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.warn(err));
|
||||
},
|
||||
deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
|
||||
api
|
||||
.delete(`pricelist/ingredients/${ingredient.id}`)
|
||||
.then(() => {
|
||||
const index = volume.ingredients.findIndex((a) => a.id === ingredient.id);
|
||||
if (index > -1) {
|
||||
volume.ingredients.splice(index, 1);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.warn(err));
|
||||
async deleteIngredient(ingredient: FG.Ingredient) {
|
||||
await api.delete(`pricelist/ingredients/${ingredient.id}`);
|
||||
},
|
||||
async setDrink(drink: FG.Drink) {
|
||||
const { data } = await api.post<FG.Drink>('pricelist/drinks', drink);
|
||||
async setDrink(drink: Drink) {
|
||||
const { data } = await api.post<FG.Drink>('pricelist/drinks', {
|
||||
...drink,
|
||||
});
|
||||
const _drink = new Drink(data);
|
||||
data.volumes.forEach((volume) => {
|
||||
const _volume = new DrinkPriceVolume(volume);
|
||||
_drink.volumes.push(_volume);
|
||||
});
|
||||
this.drinks.push(_drink);
|
||||
this.create_min_prices();
|
||||
calc_all_min_prices(this.drinks, this.min_prices);
|
||||
return _drink;
|
||||
},
|
||||
async updateDrink(drink: Drink) {
|
||||
const { data } = await api.put<FG.Drink>(`pricelist/drinks/${drink.id}`, drink);
|
||||
const { data } = await api.put<FG.Drink>(`pricelist/drinks/${drink.id}`, {
|
||||
...drink,
|
||||
});
|
||||
const index = this.drinks.findIndex((a) => a.id === data.id);
|
||||
if (index > -1) {
|
||||
const _drink = new Drink(data);
|
||||
|
@ -220,7 +200,7 @@ export const usePricelistStore = defineStore({
|
|||
});
|
||||
this.drinks[index] = _drink;
|
||||
}
|
||||
this.create_min_prices();
|
||||
calc_all_min_prices(this.drinks, this.min_prices);
|
||||
},
|
||||
deleteDrink(drink: Drink) {
|
||||
api
|
||||
|
@ -233,81 +213,101 @@ export const usePricelistStore = defineStore({
|
|||
})
|
||||
.catch((err) => console.warn(err));
|
||||
},
|
||||
getPriceCalcColumn(userid: string) {
|
||||
api
|
||||
.get(`pricelist/users/${userid}/pricecalc_columns`)
|
||||
.then(({ data }: AxiosResponse<Array<string>>) => {
|
||||
if (data.length > 0) {
|
||||
this.pricecalc_columns = data;
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
},
|
||||
updatePriceCalcColumn(userid: string, data: Array<string>) {
|
||||
api
|
||||
.put(`pricelist/users/${userid}/pricecalc_columns`, data)
|
||||
.then(() => {
|
||||
this.pricecalc_columns = data;
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
},
|
||||
async get_min_prices() {
|
||||
const { data } = await api.get<Array<number>>('pricelist/settings/min_prices');
|
||||
this.min_prices = data;
|
||||
},
|
||||
async set_min_prices() {
|
||||
await api.post<Array<number>>('pricelist/settings/min_prices', this.min_prices);
|
||||
this.create_min_prices();
|
||||
},
|
||||
create_min_prices() {
|
||||
this.drinks.forEach((drink) => {
|
||||
drink.volumes.forEach((volume) => {
|
||||
volume.min_prices = [];
|
||||
this.min_prices.forEach((min_price) => {
|
||||
let computedMinPrice: ComputedRef;
|
||||
if (drink.cost_price_pro_volume) {
|
||||
computedMinPrice = computed<number>(
|
||||
() =>
|
||||
(<number>(<unknown>drink.cost_price_pro_volume) *
|
||||
<number>(<unknown>volume.volume) *
|
||||
min_price) /
|
||||
100
|
||||
);
|
||||
} else {
|
||||
computedMinPrice = computed<number>(() => {
|
||||
let retVal = 0;
|
||||
let extraIngredientPrice = 0;
|
||||
volume.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient) {
|
||||
const _drink = usePricelistStore().drinks.find(
|
||||
(a) => a.id === ingredient.drink_ingredient?.ingredient_id
|
||||
);
|
||||
retVal +=
|
||||
ingredient.drink_ingredient.volume *
|
||||
<number>(<unknown>_drink?.cost_price_pro_volume);
|
||||
}
|
||||
if (ingredient.extra_ingredient) {
|
||||
extraIngredientPrice += ingredient.extra_ingredient.price;
|
||||
}
|
||||
});
|
||||
return (retVal * min_price) / 100 + extraIngredientPrice;
|
||||
});
|
||||
}
|
||||
volume.min_prices.push({ percentage: min_price, price: computedMinPrice });
|
||||
});
|
||||
});
|
||||
});
|
||||
calc_all_min_prices(this.drinks, this.min_prices);
|
||||
},
|
||||
async upload_drink_picture(drink: Drink, file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
await api.post(`pricelist/drinks/${drink.id}/picture`, formData, {
|
||||
const { data } = await api.post<FG.Drink>(`pricelist/drinks/${drink.id}/picture`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
const _drink = this.drinks.find((a) => a.id === drink.id);
|
||||
if (_drink) {
|
||||
_drink.uuid = data.uuid;
|
||||
}
|
||||
},
|
||||
async delete_drink_picture(drink: Drink) {
|
||||
await api.delete(`pricelist/drinks/${drink.id}/picture`);
|
||||
drink.uuid = '';
|
||||
},
|
||||
async getTags() {
|
||||
const { data } = await api.get<Array<FG.Tag>>('/pricelist/tags');
|
||||
this.tags = data;
|
||||
},
|
||||
async setTag(tag: FG.Tag) {
|
||||
const { data } = await api.post<FG.Tag>('/pricelist/tags', tag);
|
||||
this.tags.push(data);
|
||||
},
|
||||
async updateTag(tag: FG.Tag) {
|
||||
const { data } = await api.put<FG.Tag>(`/pricelist/tags/${tag.id}`, tag);
|
||||
const index = this.tags.findIndex((a) => a.id === data.id);
|
||||
if (index > -1) {
|
||||
this.tags[index] = data;
|
||||
}
|
||||
},
|
||||
async deleteTag(tag: FG.Tag) {
|
||||
await api.delete(`/pricelist/tags/${tag.id}`);
|
||||
const index = this.tags.findIndex((a) => a.id === tag.id);
|
||||
if (index > -1) {
|
||||
this.tags.splice(index, 1);
|
||||
}
|
||||
},
|
||||
async getPriceCalcColumn(userid: string) {
|
||||
const { data } = await api.get<Array<string>>(`pricelist/users/${userid}/pricecalc_columns`);
|
||||
this.pricecalc_columns = data;
|
||||
},
|
||||
async updatePriceCalcColumn(userid: string, data: Array<string>) {
|
||||
await api.put<Array<string>>(`pricelist/users/${userid}/pricecalc_columns`, data);
|
||||
this.pricecalc_columns = data;
|
||||
},
|
||||
async getPriceListView(userid: string) {
|
||||
const { data } = await api.get<{ value: boolean }>(`pricelist/users/${userid}/pricelist`);
|
||||
this.pricelist_view = data.value;
|
||||
},
|
||||
async updatePriceListView(userid: string, data: boolean) {
|
||||
await api.put<Array<string>>(`pricelist/users/${userid}/pricelist`, { value: data });
|
||||
this.pricelist_view = data;
|
||||
},
|
||||
async getPriceListColumnOrder(userid: string) {
|
||||
const { data } = await api.get<Array<Order>>(
|
||||
`pricelist/users/${userid}/pricecalc_columns_order`
|
||||
);
|
||||
this.pricelist_columns_order = data;
|
||||
},
|
||||
async updatePriceListColumnOrder(userid: string, data: Array<Order>) {
|
||||
await api.put<Array<string>>(`pricelist/users/${userid}/pricecalc_columns_order`, data);
|
||||
this.pricelist_columns_order = data;
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
pricelist() {
|
||||
const retVal: Array<Pricelist> = [];
|
||||
this.drinks.forEach((drink) => {
|
||||
drink.volumes.forEach((volume) => {
|
||||
volume.prices.forEach((price) => {
|
||||
retVal.push({
|
||||
name: drink.name,
|
||||
type: <FG.DrinkType>drink.type,
|
||||
tags: <Array<FG.Tag>>drink.tags,
|
||||
volume: <number>volume.volume,
|
||||
price: price.price,
|
||||
public: price.public,
|
||||
description: <string>price.description,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return retVal;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { DrinkPriceVolume, MinPrice, Drink };
|
||||
export { DrinkPriceVolume, Drink, Order };
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { Drink } from '../store';
|
||||
|
||||
function filter(
|
||||
rows: Array<Drink>,
|
||||
terms: Search,
|
||||
cols: Array<Col>,
|
||||
cellValue: { (col: Col, row: Drink): string }
|
||||
) {
|
||||
if (terms.value) {
|
||||
return rows.filter((row) => {
|
||||
if (!terms.key || terms.key === '') {
|
||||
return cols.some((col) => {
|
||||
const val = cellValue(col, row) + '';
|
||||
const haystack = val === 'undefined' || val === 'null' ? '' : val.toLowerCase();
|
||||
return haystack.indexOf(terms.value) !== -1;
|
||||
});
|
||||
}
|
||||
const index = cols.findIndex((col) => col.name === terms.key);
|
||||
const val = cellValue(cols[index], row) + '';
|
||||
const haystack = val === 'undefined' || val === 'null' ? '' : val.toLowerCase();
|
||||
return haystack.indexOf(terms.value) !== -1;
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
interface Search {
|
||||
value: string;
|
||||
label: string | undefined;
|
||||
key: string | undefined;
|
||||
}
|
||||
|
||||
interface Col {
|
||||
name: string;
|
||||
label: string;
|
||||
field: string;
|
||||
}
|
||||
|
||||
export { filter, Search, Col };
|
|
@ -0,0 +1,7 @@
|
|||
function sort(a: string | number, b: string | number) {
|
||||
if (a > b) return 1;
|
||||
if (b > a) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export { sort };
|
|
@ -0,0 +1,84 @@
|
|||
import { Drink, DrinkPriceVolume, usePricelistStore } from 'src/plugins/pricelist/store';
|
||||
|
||||
function calc_volume(volume: DrinkPriceVolume) {
|
||||
if (volume.ingredients.some((ingredient) => !!ingredient.drink_ingredient)) {
|
||||
let retVal = 0;
|
||||
volume.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient?.volume) {
|
||||
retVal += ingredient.drink_ingredient.volume;
|
||||
}
|
||||
});
|
||||
return retVal;
|
||||
} else {
|
||||
return volume._volume;
|
||||
}
|
||||
}
|
||||
|
||||
function calc_cost_per_volume(drink: Drink) {
|
||||
let retVal = drink._cost_per_volume;
|
||||
if (!!drink.volume && !!drink.package_size && !!drink.cost_per_package) {
|
||||
retVal =
|
||||
((drink.cost_per_package || 0) / ((drink.volume || 0) * (drink.package_size || 0))) * 1.19;
|
||||
}
|
||||
|
||||
return retVal ? Math.round(retVal * 1000) / 1000 : retVal;
|
||||
}
|
||||
|
||||
function calc_all_min_prices(drinks: Array<Drink>, min_prices: Array<number>) {
|
||||
drinks.forEach((drink) => {
|
||||
drink.volumes.forEach((volume) => {
|
||||
volume.min_prices = calc_min_prices(volume, drink.cost_per_volume, min_prices);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function helper(volume: DrinkPriceVolume, min_price: number) {
|
||||
let retVal = 0;
|
||||
let extraIngredientPrice = 0;
|
||||
volume.ingredients.forEach((ingredient) => {
|
||||
if (ingredient.drink_ingredient) {
|
||||
const _drink = usePricelistStore().drinks.find(
|
||||
(a) => a.id === ingredient.drink_ingredient?.ingredient_id
|
||||
);
|
||||
retVal += ingredient.drink_ingredient.volume * <number>(<unknown>_drink?.cost_per_volume);
|
||||
}
|
||||
if (ingredient.extra_ingredient) {
|
||||
extraIngredientPrice += ingredient.extra_ingredient.price;
|
||||
}
|
||||
});
|
||||
return (retVal * min_price) / 100 + extraIngredientPrice;
|
||||
}
|
||||
|
||||
function calc_min_prices(
|
||||
volume: DrinkPriceVolume,
|
||||
cost_per_volume: number | undefined,
|
||||
min_prices: Array<number>
|
||||
) {
|
||||
const retVal: Array<FG.MinPrices> = [];
|
||||
volume.min_prices = [];
|
||||
if (min_prices) {
|
||||
min_prices.forEach((min_price) => {
|
||||
let computedMinPrice: number;
|
||||
if (cost_per_volume) {
|
||||
computedMinPrice = (cost_per_volume * <number>volume.volume * min_price) / 100;
|
||||
} else {
|
||||
computedMinPrice = helper(volume, min_price);
|
||||
}
|
||||
retVal.push({ percentage: min_price, price: computedMinPrice });
|
||||
});
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
function clone<T>(o: T): T {
|
||||
return <T>JSON.parse(JSON.stringify(o));
|
||||
}
|
||||
|
||||
interface DeleteObjects {
|
||||
prices: Array<FG.DrinkPrice>;
|
||||
volumes: Array<DrinkPriceVolume>;
|
||||
ingredients: Array<FG.Ingredient>;
|
||||
}
|
||||
export { DeleteObjects };
|
||||
|
||||
export { calc_volume, calc_cost_per_volume, calc_all_min_prices, calc_min_prices, clone };
|
303
yarn.lock
303
yarn.lock
|
@ -904,7 +904,7 @@
|
|||
lodash "^4.17.19"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@electron/get@^1.3.1":
|
||||
"@electron/get@^1.0.1", "@electron/get@^1.3.1", "@electron/get@^1.6.0":
|
||||
version "1.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab"
|
||||
integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==
|
||||
|
@ -1155,7 +1155,7 @@
|
|||
"@quasar/quasar-app-extension-qcalendar@file:deps/quasar-ui-qcalendar/app-extension":
|
||||
version "4.0.0-alpha.1"
|
||||
dependencies:
|
||||
"@quasar/quasar-ui-qcalendar" "link:../../../../../.cache/yarn/v6/npm-@quasar-quasar-app-extension-qcalendar-4.0.0-alpha.1-58d88196-365a-4cd9-b287-335eb40035ac-1617715095074/node_modules/@quasar/ui"
|
||||
"@quasar/quasar-ui-qcalendar" "link:../../Library/Caches/Yarn/v6/npm-@quasar-quasar-app-extension-qcalendar-4.0.0-alpha.1-c42f9f4b-6777-438d-bf23-9bdcfb44ae8c-1618669700018/node_modules/@quasar/ui"
|
||||
|
||||
"@quasar/quasar-ui-qcalendar@link:deps/quasar-ui-qcalendar/ui":
|
||||
version "0.0.0"
|
||||
|
@ -1318,6 +1318,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.7.tgz#1cb61fd0c85cb87e728c43107b5fd82b69bc9ef8"
|
||||
integrity sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA==
|
||||
|
||||
"@types/node@^14.6.2":
|
||||
version "14.14.41"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615"
|
||||
integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
|
@ -2182,6 +2187,21 @@ arrify@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
|
||||
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
|
||||
|
||||
asar@^2.0.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/asar/-/asar-2.1.0.tgz#97c6a570408c4e38a18d4a3fb748a621b5a7844e"
|
||||
integrity sha512-d2Ovma+bfqNpvBzY/KU8oPY67ZworixTpkjSx0PCXnQi67c2cXmssaTxpFDUM0ttopXoGx/KRxNg/GDThYbXQA==
|
||||
dependencies:
|
||||
chromium-pickle-js "^0.2.0"
|
||||
commander "^2.20.0"
|
||||
cuint "^0.2.2"
|
||||
glob "^7.1.3"
|
||||
minimatch "^3.0.4"
|
||||
mkdirp "^0.5.1"
|
||||
tmp-promise "^1.0.5"
|
||||
optionalDependencies:
|
||||
"@types/glob" "^7.1.1"
|
||||
|
||||
asn1.js@^5.2.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||
|
@ -2259,6 +2279,11 @@ atob@^2.1.2:
|
|||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
author-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450"
|
||||
integrity sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=
|
||||
|
||||
autoprefixer@9.8.6:
|
||||
version "9.8.6"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
|
||||
|
@ -2412,7 +2437,7 @@ bl@^4.0.3:
|
|||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
bluebird@^3.5.0, bluebird@^3.5.5, bluebird@^3.7.2:
|
||||
bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.5, bluebird@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||
|
@ -2607,7 +2632,7 @@ buffer-alloc@^1.2.0:
|
|||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
|
||||
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
@ -2911,6 +2936,11 @@ chrome-trace-event@^1.0.2:
|
|||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
chromium-pickle-js@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205"
|
||||
integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=
|
||||
|
||||
ci-info@2.0.0, ci-info@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
|
||||
|
@ -3160,7 +3190,7 @@ concat-map@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
concat-stream@^1.5.0:
|
||||
concat-stream@^1.5.0, concat-stream@^1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
|
@ -3505,6 +3535,13 @@ cross-spawn@^6.0.0:
|
|||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-zip@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-zip/-/cross-zip-3.1.0.tgz#2b7d33f2a893bf83e232ccbabf4c6c706f6b313c"
|
||||
integrity sha512-aX02l0SD3KE27pMl69gkxDdDM5D3u9Ic4Je+2b1B2fP0dWnlWWY6ns2Vk5DEgCXJRhL3GasSpicNQRNbDkq0+w==
|
||||
dependencies:
|
||||
rimraf "^3.0.0"
|
||||
|
||||
crypto-browserify@^3.11.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||
|
@ -3680,6 +3717,11 @@ csstype@^2.6.8:
|
|||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.16.tgz#544d69f547013b85a40d15bff75db38f34fe9c39"
|
||||
integrity sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q==
|
||||
|
||||
cuint@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
|
||||
integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=
|
||||
|
||||
currently-unhandled@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
||||
|
@ -3699,7 +3741,7 @@ dashdash@^1.12.0:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
|
@ -4047,6 +4089,14 @@ electron-notarize@^0.1.1:
|
|||
debug "^4.1.1"
|
||||
fs-extra "^8.0.1"
|
||||
|
||||
electron-notarize@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-0.2.1.tgz#759e8006decae19134f82996ed910db26d9192cc"
|
||||
integrity sha512-oZ6/NhKeXmEKNROiFmRNfytqu3cxqC95sjooG7kBXQVEUSQkZnbiAhxVh5jXngL881G197pbwpeVPJyM7Ikmxw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
fs-extra "^8.1.0"
|
||||
|
||||
electron-osx-sign@^0.4.11:
|
||||
version "0.4.17"
|
||||
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.17.tgz#2727ca0c79e1e4e5ccd3861fb3da9c3c913b006c"
|
||||
|
@ -4059,11 +4109,43 @@ electron-osx-sign@^0.4.11:
|
|||
minimist "^1.2.0"
|
||||
plist "^3.0.1"
|
||||
|
||||
electron-packager@^14.1.1:
|
||||
version "14.2.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-packager/-/electron-packager-14.2.1.tgz#e1884eee608455e71e96342717e0527d25a329df"
|
||||
integrity sha512-g6y3BVrAOz/iavKD+VMFbehrQcwCWuA3CZvVbmmbQuCfegGA1ytwWn0BNIDDrEdbuz31Fti7mnNHhb5L+3Wq9A==
|
||||
dependencies:
|
||||
"@electron/get" "^1.6.0"
|
||||
asar "^2.0.1"
|
||||
cross-zip "^3.0.0"
|
||||
debug "^4.0.1"
|
||||
electron-notarize "^0.2.0"
|
||||
electron-osx-sign "^0.4.11"
|
||||
fs-extra "^8.1.0"
|
||||
galactus "^0.2.1"
|
||||
get-package-info "^1.0.0"
|
||||
junk "^3.1.0"
|
||||
parse-author "^2.0.0"
|
||||
plist "^3.0.0"
|
||||
rcedit "^2.0.0"
|
||||
resolve "^1.1.6"
|
||||
sanitize-filename "^1.6.0"
|
||||
semver "^6.0.0"
|
||||
yargs-parser "^16.0.0"
|
||||
|
||||
electron-to-chromium@^1.3.649:
|
||||
version "1.3.704"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.704.tgz#894205a237cbe0097d63da8f6d19e605dd13ab51"
|
||||
integrity sha512-6cz0jvawlUe4h5AbfQWxPzb+8LzVyswGAWiGc32EJEmfj39HTQyNPkLXirc7+L4x5I6RgRkzua8Ryu5QZqc8cA==
|
||||
|
||||
electron@^12.0.4:
|
||||
version "12.0.4"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-12.0.4.tgz#c2ca4710d0e4da7db6d31c4f55777b08bfcb08e5"
|
||||
integrity sha512-A8Lq3YMZ1CaO1z5z5nsyFxIwkgwXLHUwL2pf9MVUHpq7fv3XUewCMD98EnLL3DdtiyCvw5KMkeT1WGsZh8qFug==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^14.6.2"
|
||||
extract-zip "^1.0.3"
|
||||
|
||||
elementtree@0.1.7, elementtree@^0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0"
|
||||
|
@ -4183,7 +4265,7 @@ errno@^0.1.3, errno@~0.1.7:
|
|||
dependencies:
|
||||
prr "~1.0.1"
|
||||
|
||||
error-ex@^1.3.1:
|
||||
error-ex@^1.2.0, error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
|
||||
|
@ -4577,6 +4659,16 @@ extglob@^2.0.4:
|
|||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
extract-zip@^1.0.3:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
|
||||
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
|
||||
dependencies:
|
||||
concat-stream "^1.6.2"
|
||||
debug "^2.6.9"
|
||||
mkdirp "^0.5.4"
|
||||
yauzl "^2.10.0"
|
||||
|
||||
extsprintf@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
|
||||
|
@ -4633,6 +4725,13 @@ faye-websocket@^0.11.3:
|
|||
dependencies:
|
||||
websocket-driver ">=0.5.1"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
figgy-pudding@^3.5.1:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
|
||||
|
@ -4733,7 +4832,7 @@ find-cache-dir@^3.3.1:
|
|||
make-dir "^3.0.2"
|
||||
pkg-dir "^4.1.0"
|
||||
|
||||
find-up@^2.1.0:
|
||||
find-up@^2.0.0, find-up@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||
integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
|
||||
|
@ -4776,6 +4875,14 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
|
||||
integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
|
||||
|
||||
flora-colossus@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-1.0.1.tgz#aba198425a8185341e64f9d2a6a96fd9a3cbdb93"
|
||||
integrity sha512-d+9na7t9FyH8gBJoNDSi28mE4NgQVGGvxQ4aHtFRetjyh5SXjuus+V5EZaxFmFdXVemSOrx0lsgEl/ZMjnOWJA==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
fs-extra "^7.0.0"
|
||||
|
||||
flush-write-stream@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
|
||||
|
@ -4875,6 +4982,24 @@ fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1:
|
|||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
|
||||
integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^8.0.1, fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
|
@ -4934,6 +5059,15 @@ functional-red-black-tree@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
galactus@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/galactus/-/galactus-0.2.1.tgz#cbed2d20a40c1f5679a35908e2b9415733e78db9"
|
||||
integrity sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
flora-colossus "^1.0.0"
|
||||
fs-extra "^4.0.0"
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
|
@ -4974,6 +5108,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
|
|||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
get-package-info@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-package-info/-/get-package-info-1.0.0.tgz#6432796563e28113cd9474dbbd00052985a4999c"
|
||||
integrity sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=
|
||||
dependencies:
|
||||
bluebird "^3.1.1"
|
||||
debug "^2.2.0"
|
||||
lodash.get "^4.0.0"
|
||||
read-pkg-up "^2.0.0"
|
||||
|
||||
get-stream@^4.0.0, get-stream@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
|
||||
|
@ -6278,6 +6422,11 @@ jsprim@^1.2.2:
|
|||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
junk@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
|
||||
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
|
||||
|
||||
keyv@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
|
||||
|
@ -6369,6 +6518,16 @@ lines-and-columns@^1.1.6:
|
|||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
||||
|
||||
load-json-file@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
|
||||
integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^2.2.0"
|
||||
pify "^2.0.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
loader-runner@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
|
||||
|
@ -6455,6 +6614,11 @@ lodash.flatten@^4.4.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
|
||||
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
|
||||
|
||||
lodash.get@^4.0.0:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
|
@ -6907,7 +7071,7 @@ mixin-deep@^1.2.0:
|
|||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||
|
@ -7119,7 +7283,7 @@ nopt@^5.0.0:
|
|||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
normalize-package-data@^2.0.0:
|
||||
normalize-package-data@^2.0.0, normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
|
@ -7677,6 +7841,20 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
|
|||
pbkdf2 "^3.0.3"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
parse-author@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-author/-/parse-author-2.0.0.tgz#d3460bf1ddd0dfaeed42da754242e65fb684a81f"
|
||||
integrity sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=
|
||||
dependencies:
|
||||
author-regex "^1.0.0"
|
||||
|
||||
parse-json@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
|
||||
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
|
||||
dependencies:
|
||||
error-ex "^1.2.0"
|
||||
|
||||
parse-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
|
||||
|
@ -7763,6 +7941,13 @@ path-to-regexp@0.1.7:
|
|||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-type@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
|
||||
integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
|
||||
dependencies:
|
||||
pify "^2.0.0"
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
|
@ -7779,6 +7964,11 @@ pbkdf2@^3.0.3:
|
|||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
|
@ -7854,7 +8044,7 @@ pkg-up@^3.1.0:
|
|||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
plist@^3.0.1:
|
||||
plist@^3.0.0, plist@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc"
|
||||
integrity sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ==
|
||||
|
@ -8525,6 +8715,11 @@ rc@^1.2.8:
|
|||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
rcedit@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-2.3.0.tgz#951685a079db98a4cc8c21ebab75e374d5a0b108"
|
||||
integrity sha512-h1gNEl9Oai1oijwyJ1WYqYSXTStHnOcv1KYljg/8WM4NAg3H1KBK3azIaKkQ1WQl+d7PoJpcBMscPfLXVKgCLQ==
|
||||
|
||||
read-chunk@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-3.2.0.tgz#2984afe78ca9bfbbdb74b19387bf9e86289c16ca"
|
||||
|
@ -8551,6 +8746,23 @@ read-package-json-fast@^2.0.1:
|
|||
normalize-package-data "^2.0.0"
|
||||
npm-normalize-package-bin "^1.0.0"
|
||||
|
||||
read-pkg-up@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
|
||||
integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
|
||||
dependencies:
|
||||
find-up "^2.0.0"
|
||||
read-pkg "^2.0.0"
|
||||
|
||||
read-pkg@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
|
||||
integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
|
||||
dependencies:
|
||||
load-json-file "^2.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^2.0.0"
|
||||
|
||||
read@1, read@~1.0.1:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
|
||||
|
@ -8795,7 +9007,7 @@ resolve-url@^0.2.1:
|
|||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
||||
|
||||
resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.15.1:
|
||||
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.15.1:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
|
||||
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
|
||||
|
@ -8944,6 +9156,13 @@ safe-regex@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sanitize-filename@^1.6.0:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378"
|
||||
integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==
|
||||
dependencies:
|
||||
truncate-utf8-bytes "^1.0.0"
|
||||
|
||||
sass-loader@10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.0.tgz#1727fcc0c32ab3eb197cda61d78adf4e9174a4b3"
|
||||
|
@ -9306,6 +9525,11 @@ sort-keys@^1.0.0:
|
|||
dependencies:
|
||||
is-plain-obj "^1.0.0"
|
||||
|
||||
sortablejs@1.10.2:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
|
||||
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
|
||||
|
||||
source-list-map@^2.0.0, source-list-map@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
|
@ -9613,6 +9837,11 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
|||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
|
||||
|
||||
strip-bom@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
|
||||
|
@ -9862,6 +10091,21 @@ timsort@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tmp-promise@^1.0.5:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-1.1.0.tgz#bb924d239029157b9bc1d506a6aa341f8b13e64c"
|
||||
integrity sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==
|
||||
dependencies:
|
||||
bluebird "^3.5.0"
|
||||
tmp "0.1.0"
|
||||
|
||||
tmp@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877"
|
||||
integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==
|
||||
dependencies:
|
||||
rimraf "^2.6.3"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
@ -9950,6 +10194,13 @@ tough-cookie@~2.5.0:
|
|||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
truncate-utf8-bytes@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
|
||||
integrity sha1-QFkjkJWS1W94pYGENLC3hInKXys=
|
||||
dependencies:
|
||||
utf8-byte-length "^1.0.1"
|
||||
|
||||
ts-loader@8.0.17:
|
||||
version "8.0.17"
|
||||
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc"
|
||||
|
@ -10252,6 +10503,11 @@ use@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
utf8-byte-length@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
|
||||
integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
@ -10396,6 +10652,13 @@ vue@3.0.11:
|
|||
"@vue/runtime-dom" "3.0.11"
|
||||
"@vue/shared" "3.0.11"
|
||||
|
||||
vuedraggable@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.0.1.tgz#3bcaab0808b7944030b7d9a29f9a63d59dfa12c5"
|
||||
integrity sha512-7qN5jhB1SLfx5P+HCm3JUW+pvgA1bSLgYLSVOeLWBDH9z+zbaEH0OlyZBVMLOxFR+JUHJjwDD0oy7T4r9TEgDA==
|
||||
dependencies:
|
||||
sortablejs "1.10.2"
|
||||
|
||||
vuex@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.0.tgz#ac877aa76a9c45368c979471e461b520d38e6cf5"
|
||||
|
@ -10802,6 +11065,14 @@ yargs-parser@^13.1.2:
|
|||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^16.0.0:
|
||||
version "16.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1"
|
||||
integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
|
@ -10843,6 +11114,14 @@ yargs@^13.3.2:
|
|||
y18n "^4.0.0"
|
||||
yargs-parser "^13.1.2"
|
||||
|
||||
yauzl@^2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
|
Loading…
Reference in New Issue