Merge branch 'next' of groeger-clan.duckdns.org:newgeruecht-vue into next
| 
						 | 
				
			
			@ -2,6 +2,9 @@
 | 
			
		|||
.thumbs.db
 | 
			
		||||
node_modules
 | 
			
		||||
 | 
			
		||||
# We use yarn, so ignore npm
 | 
			
		||||
package-lock.json
 | 
			
		||||
 | 
			
		||||
# Quasar core related directories
 | 
			
		||||
.quasar
 | 
			
		||||
/dist
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
    "@quasar/extras": "^1.9.10",
 | 
			
		||||
    "@vue/composition-api": "^0.6.4",
 | 
			
		||||
    "axios": "^0.21.0",
 | 
			
		||||
    "cordova": "^10.0.0",
 | 
			
		||||
    "core-js": "^3.7.0",
 | 
			
		||||
    "quasar": "^1.14.5",
 | 
			
		||||
    "vue-router": "3.3.2"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<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="svg8"
 | 
			
		||||
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
 | 
			
		||||
   sodipodi:docname="flaschengeist-logo-dark.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="0.7"
 | 
			
		||||
     inkscape:cx="427.27412"
 | 
			
		||||
     inkscape:cy="505.5186"
 | 
			
		||||
     inkscape:document-units="mm"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1015"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="0"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(0,-26.06665)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g875"
 | 
			
		||||
       transform="matrix(0.04643093,-0.35277275,0.35277275,0.04643093,7.9696963,229.75997)"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
      <g
 | 
			
		||||
         id="g819"
 | 
			
		||||
         transform="translate(-233.70212,301.68819)"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
	<path
 | 
			
		||||
   id="path817"
 | 
			
		||||
   d="m 206.836,94.524 c 0,-17.132 0,-94.524 0,-94.524 h -12.723 -7.534 -12.717 c 0,0 0,77.392 0,94.524 0,17.132 -32.207,37.929 -32.207,62.346 0,24.411 0,201.009 0,207.236 0,6.229 4.166,16.615 13.501,16.615 6.355,0 22.012,0 31.435,0 4.45,0 7.534,0 7.534,0 9.423,0 25.073,0 31.429,0 9.342,0 13.513,-10.387 13.513,-16.615 0,-6.228 0,-182.826 0,-207.236 -0.001,-24.417 -32.231,-45.203 -32.231,-62.346 z m -25.857,27.757 c -2.085,2.847 -4.415,6.437 -6.947,10.579 -2.562,4.153 -4.99,8.993 -7.459,14.244 -0.941,2.684 -1.906,5.496 -2.713,8.313 -0.029,2.986 -0.976,5.188 -0.563,8.935 0.25,6.948 0.523,14.256 0.801,21.82 0.5,15.133 0.593,31.284 0.721,47.422 -0.035,32.287 -1.011,64.588 -2.521,88.801 -0.662,12.107 -1.748,22.191 -2.26,29.256 -0.627,7.064 -1.15,11.107 -1.15,11.107 0,0 -0.528,-4.043 -1.156,-11.107 -0.511,-7.064 -1.598,-17.148 -2.26,-29.256 -1.51,-24.213 -2.487,-56.514 -2.521,-88.801 0.116,-16.139 0.209,-32.289 0.72,-47.422 0.279,-7.563 0.552,-14.872 0.802,-21.82 -0.122,-3.224 1.092,-7.604 1.784,-11.09 1.342,-3.282 2.829,-6.327 4.247,-9.196 1.726,-2.638 3.358,-5.165 4.926,-7.564 1.784,-2.19 3.474,-4.258 5.054,-6.222 3.288,-3.788 6.21,-6.989 8.761,-9.487 5.042,-5.042 8.278,-7.517 8.278,-7.517 0,0 -2.524,3.224 -6.544,9.005 z"
 | 
			
		||||
   inkscape:connector-curvature="0"
 | 
			
		||||
   style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1" />
 | 
			
		||||
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g821"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g823"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g825"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g827"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g829"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g831"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g833"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g835"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g837"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g839"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g841"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g843"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g845"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g847"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g849"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.44061947;stroke-opacity:1"
 | 
			
		||||
       d="m 154.04963,46.75413 c -2.62014,0.516924 -5.22545,1.168424 -7.80617,1.95206 -42.75163,12.981828 -43.91253,68.68501 -54.129684,109.64785 -13.512037,49.65839 -58.120549,32.45922 -53.364321,57.25551 4.247764,22.14545 69.262455,44.34715 71.908285,44.72513 7.43909,1.06272 -52.780019,-26.79368 -40.437456,-42.16974 10.871821,-13.54384 54.907216,-1.28617 101.792266,-18.34148 41.23972,-15.24969 76.0405,-52.05406 67.35884,-95.66274 -8.0739,-40.556134 -44.95534,-65.37088 -85.32176,-57.40659 z m 2.80071,29.231473 5.1e-4,-9.9e-5 c 2.13365,-0.334266 3.95652,0.01931 5.31987,1.031879 4.77648,3.547266 2.89647,14.166887 -4.19904,23.719127 -7.09532,9.55196 -16.7189,14.41932 -21.49476,10.87151 -4.77606,-3.54747 -2.89605,-14.166665 4.19913,-23.718615 4.83297,-6.506353 11.08277,-11.106027 16.17429,-11.903802 z m 47.56936,23.874148 c 2.13386,-0.334401 3.95691,0.01915 5.32037,1.031789 4.77577,3.54775 2.89554,14.16692 -4.19964,23.7187 -7.09514,9.55189 -16.7186,14.41945 -21.49466,10.87203 -4.77578,-3.54775 -2.89554,-14.16693 4.19965,-23.71872 4.83296,-6.50635 11.08277,-11.10602 16.17428,-11.903799 z M 151.12801,132.2755 c 2.17877,-0.55401 4.13861,-0.53197 5.77959,0.065 7.31131,2.65972 6.76636,15.88363 -1.21719,29.5365 -7.98356,13.65282 -20.38249,22.56458 -27.69389,19.90504 -7.31132,-2.65972 -6.76637,-15.88364 1.21719,-29.53651 5.96208,-10.19594 14.66479,-18.12654 21.9143,-19.97007 z"
 | 
			
		||||
       id="path897"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       sodipodi:nodetypes="cccsssccccccscccccccccccccccc" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
 | 
			
		||||
       id="path962"
 | 
			
		||||
       cx="128.25729"
 | 
			
		||||
       cy="26.431171"
 | 
			
		||||
       rx="17.575893"
 | 
			
		||||
       ry="21.733631"
 | 
			
		||||
       transform="rotate(16.845913)" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.29560357;stroke-opacity:1"
 | 
			
		||||
       id="path964"
 | 
			
		||||
       cx="245.22121"
 | 
			
		||||
       cy="-179.49768"
 | 
			
		||||
       rx="18.099041"
 | 
			
		||||
       ry="22.821976"
 | 
			
		||||
       transform="matrix(0.33166949,0.94339565,-0.88087771,0.47334392,0,0)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 7.2 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,139 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<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="svg8"
 | 
			
		||||
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
 | 
			
		||||
   sodipodi:docname="flaschengeist-logo-white-with-kontur.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="0.7"
 | 
			
		||||
     inkscape:cx="427.27412"
 | 
			
		||||
     inkscape:cy="505.5186"
 | 
			
		||||
     inkscape:document-units="mm"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1015"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="0"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(0,-26.06665)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g875"
 | 
			
		||||
       transform="matrix(0.04643093,-0.35277275,0.35277275,0.04643093,7.9696963,229.75997)"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
      <g
 | 
			
		||||
         id="g819"
 | 
			
		||||
         transform="translate(-233.70212,301.68819)"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
	<path
 | 
			
		||||
   id="path817"
 | 
			
		||||
   d="m 206.836,94.524 c 0,-17.132 0,-94.524 0,-94.524 h -12.723 -7.534 -12.717 c 0,0 0,77.392 0,94.524 0,17.132 -32.207,37.929 -32.207,62.346 0,24.411 0,201.009 0,207.236 0,6.229 4.166,16.615 13.501,16.615 6.355,0 22.012,0 31.435,0 4.45,0 7.534,0 7.534,0 9.423,0 25.073,0 31.429,0 9.342,0 13.513,-10.387 13.513,-16.615 0,-6.228 0,-182.826 0,-207.236 -0.001,-24.417 -32.231,-45.203 -32.231,-62.346 z m -25.857,27.757 c -2.085,2.847 -4.415,6.437 -6.947,10.579 -2.562,4.153 -4.99,8.993 -7.459,14.244 -0.941,2.684 -1.906,5.496 -2.713,8.313 -0.029,2.986 -0.976,5.188 -0.563,8.935 0.25,6.948 0.523,14.256 0.801,21.82 0.5,15.133 0.593,31.284 0.721,47.422 -0.035,32.287 -1.011,64.588 -2.521,88.801 -0.662,12.107 -1.748,22.191 -2.26,29.256 -0.627,7.064 -1.15,11.107 -1.15,11.107 0,0 -0.528,-4.043 -1.156,-11.107 -0.511,-7.064 -1.598,-17.148 -2.26,-29.256 -1.51,-24.213 -2.487,-56.514 -2.521,-88.801 0.116,-16.139 0.209,-32.289 0.72,-47.422 0.279,-7.563 0.552,-14.872 0.802,-21.82 -0.122,-3.224 1.092,-7.604 1.784,-11.09 1.342,-3.282 2.829,-6.327 4.247,-9.196 1.726,-2.638 3.358,-5.165 4.926,-7.564 1.784,-2.19 3.474,-4.258 5.054,-6.222 3.288,-3.788 6.21,-6.989 8.761,-9.487 5.042,-5.042 8.278,-7.517 8.278,-7.517 0,0 -2.524,3.224 -6.544,9.005 z"
 | 
			
		||||
   inkscape:connector-curvature="0"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
 | 
			
		||||
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g821"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g823"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g825"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g827"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g829"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g831"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g833"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g835"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g837"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g839"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g841"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g843"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g845"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g847"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g849"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#0c0000;stroke-width:1.66533339;stroke-opacity:1"
 | 
			
		||||
       d="M 440.92969 56.667969 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.75781 217.64648 C 381.35143 302.41695 370.46475 410.50548 348.14648 499.98438 C 297.07737 687.66963 128.47878 622.66455 146.45508 716.38281 C 162.50962 800.08215 408.23439 883.99329 418.23438 885.42188 C 446.35062 889.43845 218.75133 784.15526 265.40039 726.04102 C 306.49074 674.8517 472.92361 721.17976 650.12695 656.71875 C 726.85809 628.34499 797.67407 580.21865 845.90625 519.00977 A 66.376657 87.827349 48.964508 0 0 927.6875 519.24805 A 66.376657 87.827349 48.964508 0 0 980.98047 413.88477 A 66.376657 87.827349 48.964508 0 0 907.36328 380.67383 C 911.18715 353.20036 910.57006 324.58905 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.33782 91.455119 516.21653 99.676893 501.02148 109.8418 A 66.428573 82.142858 16.845913 0 0 458.80469 58.953125 A 66.428573 82.142858 16.845913 0 0 440.92969 56.667969 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.5489 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 "
 | 
			
		||||
       transform="matrix(0.26458333,0,0,0.26458333,0,26.06665)"
 | 
			
		||||
       id="path897" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 7.4 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<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="svg8"
 | 
			
		||||
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
 | 
			
		||||
   sodipodi:docname="flaschengeist-logo-white.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="0.7"
 | 
			
		||||
     inkscape:cx="427.27412"
 | 
			
		||||
     inkscape:cy="505.5186"
 | 
			
		||||
     inkscape:document-units="mm"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1015"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="0"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(0,-26.06665)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g875"
 | 
			
		||||
       transform="matrix(0.04643093,-0.35277275,0.35277275,0.04643093,7.9696963,229.75997)"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
      <g
 | 
			
		||||
         id="g819"
 | 
			
		||||
         transform="translate(-233.70212,301.68819)"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
	<path
 | 
			
		||||
   id="path817"
 | 
			
		||||
   d="m 206.836,94.524 c 0,-17.132 0,-94.524 0,-94.524 h -12.723 -7.534 -12.717 c 0,0 0,77.392 0,94.524 0,17.132 -32.207,37.929 -32.207,62.346 0,24.411 0,201.009 0,207.236 0,6.229 4.166,16.615 13.501,16.615 6.355,0 22.012,0 31.435,0 4.45,0 7.534,0 7.534,0 9.423,0 25.073,0 31.429,0 9.342,0 13.513,-10.387 13.513,-16.615 0,-6.228 0,-182.826 0,-207.236 -0.001,-24.417 -32.231,-45.203 -32.231,-62.346 z m -25.857,27.757 c -2.085,2.847 -4.415,6.437 -6.947,10.579 -2.562,4.153 -4.99,8.993 -7.459,14.244 -0.941,2.684 -1.906,5.496 -2.713,8.313 -0.029,2.986 -0.976,5.188 -0.563,8.935 0.25,6.948 0.523,14.256 0.801,21.82 0.5,15.133 0.593,31.284 0.721,47.422 -0.035,32.287 -1.011,64.588 -2.521,88.801 -0.662,12.107 -1.748,22.191 -2.26,29.256 -0.627,7.064 -1.15,11.107 -1.15,11.107 0,0 -0.528,-4.043 -1.156,-11.107 -0.511,-7.064 -1.598,-17.148 -2.26,-29.256 -1.51,-24.213 -2.487,-56.514 -2.521,-88.801 0.116,-16.139 0.209,-32.289 0.72,-47.422 0.279,-7.563 0.552,-14.872 0.802,-21.82 -0.122,-3.224 1.092,-7.604 1.784,-11.09 1.342,-3.282 2.829,-6.327 4.247,-9.196 1.726,-2.638 3.358,-5.165 4.926,-7.564 1.784,-2.19 3.474,-4.258 5.054,-6.222 3.288,-3.788 6.21,-6.989 8.761,-9.487 5.042,-5.042 8.278,-7.517 8.278,-7.517 0,0 -2.524,3.224 -6.544,9.005 z"
 | 
			
		||||
   inkscape:connector-curvature="0"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1" />
 | 
			
		||||
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g821"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g823"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g825"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g827"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g829"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g831"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g833"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g835"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g837"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g839"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g841"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g843"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g845"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g847"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g849"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#0000d2;stroke-opacity:1">
 | 
			
		||||
</g>
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.44061947;stroke-opacity:1"
 | 
			
		||||
       d="m 154.04963,46.75413 c -2.62014,0.516924 -5.22545,1.168424 -7.80617,1.95206 -42.75163,12.981828 -43.91253,68.68501 -54.129684,109.64785 -13.512037,49.65839 -58.120549,32.45922 -53.364321,57.25551 4.247764,22.14545 69.262455,44.34715 71.908285,44.72513 7.43909,1.06272 -52.780019,-26.79368 -40.437456,-42.16974 10.871821,-13.54384 54.907216,-1.28617 101.792266,-18.34148 41.23972,-15.24969 76.0405,-52.05406 67.35884,-95.66274 -8.0739,-40.556134 -44.95534,-65.37088 -85.32176,-57.40659 z m 2.80071,29.231473 5.1e-4,-9.9e-5 c 2.13365,-0.334266 3.95652,0.01931 5.31987,1.031879 4.77648,3.547266 2.89647,14.166887 -4.19904,23.719127 -7.09532,9.55196 -16.7189,14.41932 -21.49476,10.87151 -4.77606,-3.54747 -2.89605,-14.166665 4.19913,-23.718615 4.83297,-6.506353 11.08277,-11.106027 16.17429,-11.903802 z m 47.56936,23.874148 c 2.13386,-0.334401 3.95691,0.01915 5.32037,1.031789 4.77577,3.54775 2.89554,14.16692 -4.19964,23.7187 -7.09514,9.55189 -16.7186,14.41945 -21.49466,10.87203 -4.77578,-3.54775 -2.89554,-14.16693 4.19965,-23.71872 4.83296,-6.50635 11.08277,-11.10602 16.17428,-11.903799 z M 151.12801,132.2755 c 2.17877,-0.55401 4.13861,-0.53197 5.77959,0.065 7.31131,2.65972 6.76636,15.88363 -1.21719,29.5365 -7.98356,13.65282 -20.38249,22.56458 -27.69389,19.90504 -7.31132,-2.65972 -6.76637,-15.88364 1.21719,-29.53651 5.96208,-10.19594 14.66479,-18.12654 21.9143,-19.97007 z"
 | 
			
		||||
       id="path897"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       sodipodi:nodetypes="cccsssccccccscccccccccccccccc" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
 | 
			
		||||
       id="path962"
 | 
			
		||||
       cx="128.25729"
 | 
			
		||||
       cy="26.431171"
 | 
			
		||||
       rx="17.575893"
 | 
			
		||||
       ry="21.733631"
 | 
			
		||||
       transform="rotate(16.845913)" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.29560357;stroke-opacity:1"
 | 
			
		||||
       id="path964"
 | 
			
		||||
       cx="245.22121"
 | 
			
		||||
       cy="-179.49768"
 | 
			
		||||
       rx="18.099041"
 | 
			
		||||
       ry="22.821976"
 | 
			
		||||
       transform="matrix(0.33166949,0.94339565,-0.88087771,0.47334392,0,0)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 7.2 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<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="svg8"
 | 
			
		||||
   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
 | 
			
		||||
   sodipodi:docname="flaschengeist-logo.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="0.7"
 | 
			
		||||
     inkscape:cx="152.98841"
 | 
			
		||||
     inkscape:cy="505.5186"
 | 
			
		||||
     inkscape:document-units="mm"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1015"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="0"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5">
 | 
			
		||||
    <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 />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Ebene 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-26.06665)">
 | 
			
		||||
    <circle
 | 
			
		||||
       id="path10"
 | 
			
		||||
       cx="135.46666"
 | 
			
		||||
       cy="161.53333"
 | 
			
		||||
       style="stroke-width:0.26506463;fill:#1976d2;fill-opacity:1"
 | 
			
		||||
       r="135.46666" />
 | 
			
		||||
    <g
 | 
			
		||||
       id="g875"
 | 
			
		||||
       transform="matrix(0.04643093,-0.35277275,0.35277275,0.04643093,7.9696963,229.75997)"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1">
 | 
			
		||||
      <g
 | 
			
		||||
         id="g819"
 | 
			
		||||
         transform="translate(-233.70212,301.68819)"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1">
 | 
			
		||||
        <path
 | 
			
		||||
           id="path817"
 | 
			
		||||
           d="m 206.836,94.524 c 0,-17.132 0,-94.524 0,-94.524 h -12.723 -7.534 -12.717 c 0,0 0,77.392 0,94.524 0,17.132 -32.207,37.929 -32.207,62.346 0,24.411 0,201.009 0,207.236 0,6.229 4.166,16.615 13.501,16.615 6.355,0 22.012,0 31.435,0 4.45,0 7.534,0 7.534,0 9.423,0 25.073,0 31.429,0 9.342,0 13.513,-10.387 13.513,-16.615 0,-6.228 0,-182.826 0,-207.236 -0.001,-24.417 -32.231,-45.203 -32.231,-62.346 z m -25.857,27.757 c -2.085,2.847 -4.415,6.437 -6.947,10.579 -2.562,4.153 -4.99,8.993 -7.459,14.244 -0.941,2.684 -1.906,5.496 -2.713,8.313 -0.029,2.986 -0.976,5.188 -0.563,8.935 0.25,6.948 0.523,14.256 0.801,21.82 0.5,15.133 0.593,31.284 0.721,47.422 -0.035,32.287 -1.011,64.588 -2.521,88.801 -0.662,12.107 -1.748,22.191 -2.26,29.256 -0.627,7.064 -1.15,11.107 -1.15,11.107 0,0 -0.528,-4.043 -1.156,-11.107 -0.511,-7.064 -1.598,-17.148 -2.26,-29.256 -1.51,-24.213 -2.487,-56.514 -2.521,-88.801 0.116,-16.139 0.209,-32.289 0.72,-47.422 0.279,-7.563 0.552,-14.872 0.802,-21.82 -0.122,-3.224 1.092,-7.604 1.784,-11.09 1.342,-3.282 2.829,-6.327 4.247,-9.196 1.726,-2.638 3.358,-5.165 4.926,-7.564 1.784,-2.19 3.474,-4.258 5.054,-6.222 3.288,-3.788 6.21,-6.989 8.761,-9.487 5.042,-5.042 8.278,-7.517 8.278,-7.517 0,0 -2.524,3.224 -6.544,9.005 z"
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         id="g821"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g823"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g825"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g827"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g829"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g831"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g833"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g835"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g837"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g839"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g841"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g843"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g845"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g847"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g849"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#1976d2;stroke-opacity:1" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.44061947;stroke-opacity:1"
 | 
			
		||||
       d="m 154.04963,46.75413 c -2.62014,0.516924 -5.22545,1.168424 -7.80617,1.95206 -42.75163,12.981828 -43.91253,68.68501 -54.129684,109.64785 -13.512037,49.65839 -58.120549,32.45922 -53.364321,57.25551 4.247764,22.14545 69.262455,44.34715 71.908285,44.72513 7.43909,1.06272 -52.780019,-26.79368 -40.437456,-42.16974 10.871821,-13.54384 54.907216,-1.28617 101.792266,-18.34148 41.23972,-15.24969 76.0405,-52.05406 67.35884,-95.66274 -8.0739,-40.556134 -44.95534,-65.37088 -85.32176,-57.40659 z m 2.80071,29.231473 5.1e-4,-9.9e-5 c 2.13365,-0.334266 3.95652,0.01931 5.31987,1.031879 4.77648,3.547266 2.89647,14.166887 -4.19904,23.719127 -7.09532,9.55196 -16.7189,14.41932 -21.49476,10.87151 -4.77606,-3.54747 -2.89605,-14.166665 4.19913,-23.718615 4.83297,-6.506353 11.08277,-11.106027 16.17429,-11.903802 z m 47.56936,23.874148 c 2.13386,-0.334401 3.95691,0.01915 5.32037,1.031789 4.77577,3.54775 2.89554,14.16692 -4.19964,23.7187 -7.09514,9.55189 -16.7186,14.41945 -21.49466,10.87203 -4.77578,-3.54775 -2.89554,-14.16693 4.19965,-23.71872 4.83296,-6.50635 11.08277,-11.10602 16.17428,-11.903799 z M 151.12801,132.2755 c 2.17877,-0.55401 4.13861,-0.53197 5.77959,0.065 7.31131,2.65972 6.76636,15.88363 -1.21719,29.5365 -7.98356,13.65282 -20.38249,22.56458 -27.69389,19.90504 -7.31132,-2.65972 -6.76637,-15.88364 1.21719,-29.53651 5.96208,-10.19594 14.66479,-18.12654 21.9143,-19.97007 z"
 | 
			
		||||
       id="path897"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       sodipodi:nodetypes="cccsssccccccscccccccccccccccc" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
 | 
			
		||||
       id="path962"
 | 
			
		||||
       cx="128.25729"
 | 
			
		||||
       cy="26.431171"
 | 
			
		||||
       rx="17.575893"
 | 
			
		||||
       ry="21.733631"
 | 
			
		||||
       transform="rotate(16.845913)" />
 | 
			
		||||
    <ellipse
 | 
			
		||||
       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.29560357;stroke-opacity:1"
 | 
			
		||||
       id="path964"
 | 
			
		||||
       cx="245.22121"
 | 
			
		||||
       cy="-179.49768"
 | 
			
		||||
       rx="18.099041"
 | 
			
		||||
       ry="22.821976"
 | 
			
		||||
       transform="matrix(0.33166949,0.94339565,-0.88087771,0.47334392,0,0)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 7.3 KiB  | 
| 
						 | 
				
			
			@ -15,10 +15,62 @@
 | 
			
		|||
    <allow-intent href="geo:*" />
 | 
			
		||||
    <platform name="android">
 | 
			
		||||
        <allow-intent href="market:*" />
 | 
			
		||||
        <icon density="ldpi" src="res/android/ldpi.png" />
 | 
			
		||||
        <icon density="mdpi" src="res/android/mdpi.png" />
 | 
			
		||||
        <icon density="hdpi" src="res/android/hdpi.png" />
 | 
			
		||||
        <icon density="xhdpi" src="res/android/xhdpi.png" />
 | 
			
		||||
        <icon density="xxhdpi" src="res/android/xxhdpi.png" />
 | 
			
		||||
        <icon density="xxxhdpi" src="res/android/xxxhdpi.png" />
 | 
			
		||||
        <splash density="land-ldpi" src="res/screen/android/splash-land-ldpi.png" />
 | 
			
		||||
        <splash density="port-ldpi" src="res/screen/android/splash-port-ldpi.png" />
 | 
			
		||||
        <splash density="land-mdpi" src="res/screen/android/splash-land-mdpi.png" />
 | 
			
		||||
        <splash density="port-mdpi" src="res/screen/android/splash-port-mdpi.png" />
 | 
			
		||||
        <splash density="land-hdpi" src="res/screen/android/splash-land-hdpi.png" />
 | 
			
		||||
        <splash density="port-hdpi" src="res/screen/android/splash-port-hdpi.png" />
 | 
			
		||||
        <splash density="land-xhdpi" src="res/screen/android/splash-land-xhdpi.png" />
 | 
			
		||||
        <splash density="port-xhdpi" src="res/screen/android/splash-port-xhdpi.png" />
 | 
			
		||||
        <splash density="land-xxhdpi" src="res/screen/android/splash-land-xxhdpi.png" />
 | 
			
		||||
        <splash density="port-xxhdpi" src="res/screen/android/splash-port-xxhdpi.png" />
 | 
			
		||||
        <splash density="land-xxxhdpi" src="res/screen/android/splash-land-xxxhdpi.png" />
 | 
			
		||||
        <splash density="port-xxxhdpi" src="res/screen/android/splash-port-xxxhdpi.png" />
 | 
			
		||||
    </platform>
 | 
			
		||||
    <platform name="ios">
 | 
			
		||||
        <allow-intent href="itms:*" />
 | 
			
		||||
        <allow-intent href="itms-apps:*" />
 | 
			
		||||
        <icon height="57" src="res/ios/icon.png" width="57" />
 | 
			
		||||
        <icon height="114" src="res/ios/icon@2x.png" width="114" />
 | 
			
		||||
        <icon height="40" src="res/ios/icon-20@2x.png" width="40" />
 | 
			
		||||
        <icon height="60" src="res/ios/icon-20@3x.png" width="60" />
 | 
			
		||||
        <icon height="29" src="res/ios/icon-29.png" width="29" />
 | 
			
		||||
        <icon height="58" src="res/ios/icon-29@2x.png" width="58" />
 | 
			
		||||
        <icon height="87" src="res/ios/icon-29@3x.png" width="87" />
 | 
			
		||||
        <icon height="80" src="res/ios/icon-40@2x.png" width="80" />
 | 
			
		||||
        <icon height="120" src="res/ios/icon-60@2x.png" width="120" />
 | 
			
		||||
        <icon height="180" src="res/ios/icon-60@3x.png" width="180" />
 | 
			
		||||
        <icon height="20" src="res/ios/icon-20.png" width="20" />
 | 
			
		||||
        <icon height="40" src="res/ios/icon-40.png" width="40" />
 | 
			
		||||
        <icon height="50" src="res/ios/icon-50.png" width="50" />
 | 
			
		||||
        <icon height="100" src="res/ios/icon-50@2x.png" width="100" />
 | 
			
		||||
        <icon height="72" src="res/ios/icon-72.png" width="72" />
 | 
			
		||||
        <icon height="144" src="res/ios/icon-72@2x.png" width="144" />
 | 
			
		||||
        <icon height="76" src="res/ios/icon-76.png" width="76" />
 | 
			
		||||
        <icon height="152" src="res/ios/icon-76@2x.png" width="152" />
 | 
			
		||||
        <icon height="167" src="res/ios/icon-83.5@2x.png" width="167" />
 | 
			
		||||
        <icon height="1024" src="res/ios/icon-1024.png" width="1024" />
 | 
			
		||||
        <icon height="48" src="res/ios/icon-24@2x.png" width="48" />
 | 
			
		||||
        <icon height="55" src="res/ios/icon-27.5@2x.png" width="55" />
 | 
			
		||||
        <icon height="88" src="res/ios/icon-44@2x.png" width="88" />
 | 
			
		||||
        <icon height="172" src="res/ios/icon-86@2x.png" width="172" />
 | 
			
		||||
        <icon height="196" src="res/ios/icon-98@2x.png" width="196" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@2x~iphone~anyany.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@2x~iphone~comany.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@2x~iphone~comcom.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@3x~iphone~anyany.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@3x~iphone~anycom.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@3x~iphone~comany.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@2x~ipad~anyany.png" />
 | 
			
		||||
        <splash src="res/screen/ios/Default@2x~ipad~comany.png" />
 | 
			
		||||
    </platform>
 | 
			
		||||
    <allow-navigation href="about:*" />
 | 
			
		||||
    <preference name="SplashMaintainAspectRatio" value="true" />
 | 
			
		||||
</widget>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,15 +13,19 @@
 | 
			
		|||
  "author": "Apache Cordova Team",
 | 
			
		||||
  "license": "Apache-2.0",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "cordova-android": "^9.0.0",
 | 
			
		||||
    "cordova-ios": "^6.1.1",
 | 
			
		||||
    "cordova-plugin-splashscreen": "^6.0.0",
 | 
			
		||||
    "cordova-plugin-whitelist": "^1.3.4"
 | 
			
		||||
  },
 | 
			
		||||
  "cordova": {
 | 
			
		||||
    "plugins": {
 | 
			
		||||
      "cordova-plugin-whitelist": {}
 | 
			
		||||
      "cordova-plugin-whitelist": {},
 | 
			
		||||
      "cordova-plugin-splashscreen": {}
 | 
			
		||||
    },
 | 
			
		||||
    "platforms": [
 | 
			
		||||
      "ios"
 | 
			
		||||
      "ios",
 | 
			
		||||
      "android"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 After Width: | Height: | Size: 3.0 KiB  | 
| 
		 After Width: | Height: | Size: 4.3 KiB  | 
| 
		 After Width: | Height: | Size: 5.6 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 411 B  | 
| 
		 After Width: | Height: | Size: 715 B  | 
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 866 B  | 
| 
		 After Width: | Height: | Size: 1017 B  | 
| 
		 After Width: | Height: | Size: 566 B  | 
| 
		 After Width: | Height: | Size: 1.0 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 715 B  | 
| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 After Width: | Height: | Size: 923 B  | 
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.8 KiB  | 
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 After Width: | Height: | Size: 3.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 After Width: | Height: | Size: 3.9 KiB  | 
| 
		 After Width: | Height: | Size: 4.2 KiB  | 
| 
		 After Width: | Height: | Size: 3.0 KiB  | 
| 
		 After Width: | Height: | Size: 5.4 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 5.3 KiB  | 
| 
		 After Width: | Height: | Size: 4.1 KiB  | 
| 
		 After Width: | Height: | Size: 4.8 KiB  | 
| 
		 After Width: | Height: | Size: 5.0 KiB  | 
| 
		 After Width: | Height: | Size: 6.2 KiB  | 
| 
		 After Width: | Height: | Size: 41 KiB  | 
| 
		 After Width: | Height: | Size: 5.6 KiB  | 
| 
		 After Width: | Height: | Size: 2.8 KiB  | 
| 
		 After Width: | Height: | Size: 2.9 KiB  | 
| 
		 After Width: | Height: | Size: 4.6 KiB  | 
| 
		 After Width: | Height: | Size: 5.9 KiB  | 
| 
		 After Width: | Height: | Size: 40 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 6.5 KiB  | 
| 
		 After Width: | Height: | Size: 5.0 KiB  | 
| 
		 After Width: | Height: | Size: 5.4 KiB  | 
| 
		 After Width: | Height: | Size: 16 KiB  | 
| 
		 After Width: | Height: | Size: 25 KiB  | 
| 
		 After Width: | Height: | Size: 33 KiB  | 
| 
						 | 
				
			
			@ -3,6 +3,8 @@ import { boot } from 'quasar/wrappers';
 | 
			
		|||
import config from '../config';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import { StateInterface } from 'src/store';
 | 
			
		||||
import { LocalStorage } from 'quasar';
 | 
			
		||||
import { Notify } from 'quasar';
 | 
			
		||||
 | 
			
		||||
declare module 'vue/types/vue' {
 | 
			
		||||
  interface Vue {
 | 
			
		||||
| 
						 | 
				
			
			@ -10,11 +12,29 @@ declare module 'vue/types/vue' {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setBaseUrl = (url: string) => {
 | 
			
		||||
  LocalStorage.set('baseURL', url);
 | 
			
		||||
  axios.defaults.baseURL = url;
 | 
			
		||||
  Notify.create({
 | 
			
		||||
    message: 'Serveraddresse gespeichert',
 | 
			
		||||
    position: 'bottom',
 | 
			
		||||
    caption: `${url}`,
 | 
			
		||||
    color: 'positive'
 | 
			
		||||
  });
 | 
			
		||||
  setTimeout(() => {
 | 
			
		||||
    window.location.reload();
 | 
			
		||||
  }, 5000);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default boot<Store<StateInterface>>(({ Vue, store, router }) => {
 | 
			
		||||
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
 | 
			
		||||
  Vue.prototype.$axios = axios;
 | 
			
		||||
  const baseURL = <string | undefined>LocalStorage.getItem('baseURL');
 | 
			
		||||
  if (baseURL) {
 | 
			
		||||
    axios.defaults.baseURL = baseURL;
 | 
			
		||||
  } else {
 | 
			
		||||
    axios.defaults.baseURL = config.baseURL;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  /***
 | 
			
		||||
   * Intercept requests and insert Token if available
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ interface Props {
 | 
			
		|||
  label?: string;
 | 
			
		||||
  readonly: boolean;
 | 
			
		||||
  type: string;
 | 
			
		||||
  rules: Array<any>;
 | 
			
		||||
  rules: Array<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ export default defineComponent({
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    rules: {
 | 
			
		||||
      default: []
 | 
			
		||||
      default: () => { return [] }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  setup(props: Props, { emit }: { emit: any }) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,10 +27,11 @@ declare namespace FG {
 | 
			
		|||
    id: number;
 | 
			
		||||
    time: Date;
 | 
			
		||||
    amount: number;
 | 
			
		||||
    reversal?: this;
 | 
			
		||||
    reversal_id: number;
 | 
			
		||||
    sender_id?: string;
 | 
			
		||||
    receiver_id?: string;
 | 
			
		||||
    author_id?: string;
 | 
			
		||||
    original_id?: number;
 | 
			
		||||
  }
 | 
			
		||||
  interface Event {
 | 
			
		||||
    id: number;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,8 @@
 | 
			
		|||
 | 
			
		||||
        <q-toolbar-title>
 | 
			
		||||
          <router-link :to="{ name: 'dashboard' }" style="text-decoration: none; color: inherit">
 | 
			
		||||
            <q-avatar>
 | 
			
		||||
              <img src="logo.svg" />
 | 
			
		||||
            <q-avatar rounded>
 | 
			
		||||
              <img src="flaschengeist-logo-white.svg" />
 | 
			
		||||
            </q-avatar>
 | 
			
		||||
            <span class="gt-xs"> Flaschengeist </span>
 | 
			
		||||
          </router-link>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,8 @@
 | 
			
		|||
    <q-header elevated>
 | 
			
		||||
      <q-toolbar>
 | 
			
		||||
        <q-toolbar-title>
 | 
			
		||||
          <q-avatar>
 | 
			
		||||
            <img src="logo.svg" />
 | 
			
		||||
          <q-avatar rounded>
 | 
			
		||||
            <img src="flaschengeist-logo-white.svg" />
 | 
			
		||||
          </q-avatar>
 | 
			
		||||
          <span class="gt-xs">
 | 
			
		||||
            Flaschengeist
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,21 @@
 | 
			
		|||
          </div>
 | 
			
		||||
        </q-form>
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
      <div class="row justify-end">
 | 
			
		||||
        <q-btn flat round icon="mdi-menu-down" class="cordova-only" @click="openServerSettings" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <q-slide-transition class="cordova-only">
 | 
			
		||||
        <div v-show="visible">
 | 
			
		||||
          <q-separator />
 | 
			
		||||
          <q-card-section>
 | 
			
		||||
            <q-form ref="ServerSettingsForm" @submit="changeUrl" class="q-gutter-md">
 | 
			
		||||
              <div class="text-h6">Servereinstellung</div>
 | 
			
		||||
              <q-input filled label="Server" dense v-model="server" />
 | 
			
		||||
              <q-btn size="xs" dense color="primary" label="Speichern" type="submit" />
 | 
			
		||||
            </q-form>
 | 
			
		||||
          </q-card-section>
 | 
			
		||||
        </div>
 | 
			
		||||
      </q-slide-transition>
 | 
			
		||||
    </q-card>
 | 
			
		||||
  </q-page>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +58,7 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
import { defineComponent, ref } from '@vue/composition-api';
 | 
			
		||||
import { Loading, Notify } from 'quasar';
 | 
			
		||||
import { setBaseUrl } from 'boot/axios';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  // name: 'PageName'
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +69,18 @@ export default defineComponent({
 | 
			
		|||
    const userid = ref('');
 | 
			
		||||
    const password = ref('');
 | 
			
		||||
    const rules = [(val: string) => (val && val.length > 0) || 'Feld darf nicht leer sein!'];
 | 
			
		||||
    const server = ref<string | undefined>(root.$axios.defaults.baseURL);
 | 
			
		||||
    const visible = ref(false);
 | 
			
		||||
 | 
			
		||||
    function openServerSettings() {
 | 
			
		||||
      visible.value = !visible.value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function changeUrl() {
 | 
			
		||||
      if (server.value) {
 | 
			
		||||
        setBaseUrl(server.value);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function doLogin() {
 | 
			
		||||
      Loading.show({
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +144,17 @@ export default defineComponent({
 | 
			
		|||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { userid, password, doLogin, doReset, rules };
 | 
			
		||||
    return {
 | 
			
		||||
      userid,
 | 
			
		||||
      password,
 | 
			
		||||
      doLogin,
 | 
			
		||||
      doReset,
 | 
			
		||||
      rules,
 | 
			
		||||
      server,
 | 
			
		||||
      changeUrl,
 | 
			
		||||
      visible,
 | 
			
		||||
      openServerSettings
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@
 | 
			
		|||
  >
 | 
			
		||||
    <div class="fit row justify-center content-center items-center">
 | 
			
		||||
      <q-img
 | 
			
		||||
        :src="$q.dark.isActive ? 'logo.svg' : 'logo-dark.svg'"
 | 
			
		||||
        :src="$q.dark.isActive ? 'flaschengeist-logo.svg' : 'flaschengeist-logo.svg'"
 | 
			
		||||
        class="col-12 q-ma-md"
 | 
			
		||||
        style="min-width: 200px; max-width: 400px"
 | 
			
		||||
      />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-card>
 | 
			
		||||
    <BalanceHeader @update:user="userUpdated" :showSelector="showSelector" @open-history="openHistory"/>
 | 
			
		||||
    <q-separator />
 | 
			
		||||
 | 
			
		||||
    <q-card-section class="row q-col-gutter-md" v-if="shortCuts">
 | 
			
		||||
      <div :key="index" v-for="(shortcut, index) in shortCuts" class="col-4">
 | 
			
		||||
        <q-btn
 | 
			
		||||
          push
 | 
			
		||||
          v-if="shortcut"
 | 
			
		||||
          color="primary"
 | 
			
		||||
          style="width: 100%"
 | 
			
		||||
          :label="shortcut.toFixed(2).toString() + ' €'"
 | 
			
		||||
          @click="changeBalance(shortcut)"
 | 
			
		||||
        >
 | 
			
		||||
          <q-popup-proxy context-menu>
 | 
			
		||||
            <q-btn label="Entfernen" @click="removeShortcut(shortcut)" />
 | 
			
		||||
          </q-popup-proxy>
 | 
			
		||||
          <q-tooltip>Rechtsklick um Verknüpfung zu entfernen</q-tooltip>
 | 
			
		||||
        </q-btn>
 | 
			
		||||
      </div></q-card-section
 | 
			
		||||
    >
 | 
			
		||||
    <q-card-section class="row q-col-gutter-md items-center">
 | 
			
		||||
      <div class="col-sm-4 col-xs-12">
 | 
			
		||||
        <q-input
 | 
			
		||||
          v-model.number="amount"
 | 
			
		||||
          type="number"
 | 
			
		||||
          filled
 | 
			
		||||
          label="Eigener Betrag"
 | 
			
		||||
          step="0.1"
 | 
			
		||||
          min="0"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-sm-4 col-xs-6">
 | 
			
		||||
        <q-btn
 | 
			
		||||
          style="width: 100%"
 | 
			
		||||
          color="primary"
 | 
			
		||||
          label="Anschreiben"
 | 
			
		||||
          @click="changeBalance(amount * -1)"
 | 
			
		||||
          ><q-tooltip>Rechtsklick um Betrag als Verknüpfung hinzuzufügen</q-tooltip>
 | 
			
		||||
          <q-popup-proxy context-menu v-model="showAddShortcut">
 | 
			
		||||
            <q-btn label="neue Verknüpfung" @click="addShortcut"></q-btn>
 | 
			
		||||
          </q-popup-proxy>
 | 
			
		||||
        </q-btn>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-sm-4 col-xs-6">
 | 
			
		||||
        <q-btn
 | 
			
		||||
          v-if="canAddCredit"
 | 
			
		||||
          style="width: 100%"
 | 
			
		||||
          color="secondary"
 | 
			
		||||
          label="Gutschreiben"
 | 
			
		||||
          @click="changeBalance(amount)"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </q-card-section>
 | 
			
		||||
  </q-card>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, ref, defineComponent, onBeforeMount } from '@vue/composition-api';
 | 
			
		||||
import { hasPermission } from 'src/utils/permission';
 | 
			
		||||
import { StateInterfaceBalance } from '../store/balance';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import BalanceHeader from '../components/BalanceHeader.vue';
 | 
			
		||||
import PERMISSIONS from '../permissions';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'BalanceAdd',
 | 
			
		||||
  components: { BalanceHeader },
 | 
			
		||||
  setup(_, { root, emit }) {
 | 
			
		||||
    onBeforeMount(() => {
 | 
			
		||||
      void store.dispatch('balance/getShortcuts');
 | 
			
		||||
      if (store.state.balance.transactions.length == 0)
 | 
			
		||||
        // No transaction, load at most six since yesterday
 | 
			
		||||
        void store.dispatch('balance/getTransactions', {
 | 
			
		||||
          filter: { limit: 6, from: new Date(new Date().setDate(new Date().getDate() - 1)) }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    const store = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
 | 
			
		||||
    const amount = ref<number>(0);
 | 
			
		||||
    const showAddShortcut = ref(false);
 | 
			
		||||
    const user = ref(store.state.user.currentUser);
 | 
			
		||||
    const shortCuts = ref(store.state.balance.shortcuts);
 | 
			
		||||
 | 
			
		||||
    const canAddCredit = computed(() => hasPermission(PERMISSIONS.CREDIT, store));
 | 
			
		||||
    const showSelector = computed(
 | 
			
		||||
      () => hasPermission(PERMISSIONS.DEBIT, store) || hasPermission(PERMISSIONS.CREDIT, store)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    function addShortcut() {
 | 
			
		||||
      if (amount.value != 0) void store.dispatch('balance/addShortcut', amount.value * -1);
 | 
			
		||||
    }
 | 
			
		||||
    function removeShortcut(shortcut: number) {
 | 
			
		||||
      void store.dispatch('balance/removeShortcut', shortcut);
 | 
			
		||||
    }
 | 
			
		||||
    function userUpdated(selectedUser: FG.User) {
 | 
			
		||||
      user.value = selectedUser;
 | 
			
		||||
    }
 | 
			
		||||
    function changeBalance(amount: number) {
 | 
			
		||||
      store
 | 
			
		||||
        .dispatch('balance/changeBalance', { amount: amount, user: user.value?.userid })
 | 
			
		||||
        .catch(err => console.log(err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function openHistory() {
 | 
			
		||||
      emit('open-history')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      user,
 | 
			
		||||
      addShortcut,
 | 
			
		||||
      canAddCredit,
 | 
			
		||||
      removeShortcut,
 | 
			
		||||
      showAddShortcut,
 | 
			
		||||
      changeBalance,
 | 
			
		||||
      amount,
 | 
			
		||||
      showSelector,
 | 
			
		||||
      shortCuts,
 | 
			
		||||
      userUpdated,
 | 
			
		||||
      openHistory
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,15 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-card-section class="fit row justify-left content-center items-center q-col-gutter-sm">
 | 
			
		||||
    <div class="text-h6 col-6">
 | 
			
		||||
    <div class="text-h6 col-5">
 | 
			
		||||
      Aktueller Stand: {{ balance.balance.toFixed(2) }} €
 | 
			
		||||
      <q-badge color="negative" align="top" v-if="isLocked"> gesperrt </q-badge>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="showSelector" class="col-6">
 | 
			
		||||
      <UserSelector :user="user" @update:user="userUpdated" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="col-1 justify-end">
 | 
			
		||||
      <q-btn round flat icon="mdi-format-list-checks" @click="openHistory" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </q-card-section>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +46,11 @@ export default defineComponent({
 | 
			
		|||
      emit('update:user', selectedUser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { user, balance, isLocked, userUpdated };
 | 
			
		||||
    function openHistory() {
 | 
			
		||||
      emit('open-history');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { user, balance, isLocked, userUpdated, openHistory };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,10 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-page padding class="fit row justify-left q-col-gutter-sm">
 | 
			
		||||
    <div class="col-12">
 | 
			
		||||
  <q-card>
 | 
			
		||||
        <BalanceHeader @update:user="senderUpdated" :showSelector="showSelector" />
 | 
			
		||||
    <BalanceHeader @update:user="senderUpdated" :showSelector="showSelector" @open-history="openHistory"/>
 | 
			
		||||
    <q-separator />
 | 
			
		||||
    <q-card-section class="row q-col-gutter-md items-center">
 | 
			
		||||
      <div class="col-sm-4 col-xs-12">
 | 
			
		||||
            <q-input
 | 
			
		||||
              v-model.number="amount"
 | 
			
		||||
              type="number"
 | 
			
		||||
              filled
 | 
			
		||||
              label="Betrag"
 | 
			
		||||
              step="0.1"
 | 
			
		||||
              min="0"
 | 
			
		||||
            />
 | 
			
		||||
        <q-input v-model.number="amount" type="number" filled label="Betrag" step="0.1" min="0" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-sm-4 col-xs-6">
 | 
			
		||||
        <UserSelector :user="receiver" @update:user="receiverUpdated" label="Empfänger" />
 | 
			
		||||
| 
						 | 
				
			
			@ -29,11 +20,6 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </q-card-section>
 | 
			
		||||
  </q-card>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-for="(transaction, index) in transactions" v-bind:key="index" class="col-sm-4 col-xs-6">
 | 
			
		||||
      <Transaction :transaction.sync="transactions[index]" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </q-page>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
| 
						 | 
				
			
			@ -42,21 +28,19 @@ import { hasPermission } from 'src/utils/permission';
 | 
			
		|||
import { StateInterfaceBalance } from '../store/balance';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import UserSelector from 'src/plugins/user/components/UserSelector.vue';
 | 
			
		||||
import Transaction from '../components/Transaction.vue';
 | 
			
		||||
import BalanceHeader from '../components/BalanceHeader.vue';
 | 
			
		||||
import PERMISSIONS from '../permissions';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'BalanceTransfer',
 | 
			
		||||
  components: { Transaction, BalanceHeader, UserSelector },
 | 
			
		||||
  setup(_, { root }) {
 | 
			
		||||
  components: { BalanceHeader, UserSelector },
 | 
			
		||||
  setup(_, { root, emit }) {
 | 
			
		||||
    const store: Store<StateInterfaceBalance> = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
 | 
			
		||||
    const showSelector = computed(() => hasPermission(PERMISSIONS.SEND_OTHER, store));
 | 
			
		||||
    const sender = ref(store.state.user.currentUser);
 | 
			
		||||
    const receiver = ref<FG.User | undefined>(undefined);
 | 
			
		||||
    const amount = ref<number>(0);
 | 
			
		||||
    const transactions = computed(() => store.state.balance.transactions.slice().reverse());
 | 
			
		||||
 | 
			
		||||
    const sendDisabled = computed(() => {
 | 
			
		||||
      return !(
 | 
			
		||||
| 
						 | 
				
			
			@ -85,16 +69,20 @@ export default defineComponent({
 | 
			
		|||
        .catch(err => console.log(err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function openHistory() {
 | 
			
		||||
      emit('open-history');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      sender,
 | 
			
		||||
      receiver,
 | 
			
		||||
      amount,
 | 
			
		||||
      sendAmount,
 | 
			
		||||
      transactions,
 | 
			
		||||
      showSelector,
 | 
			
		||||
      senderUpdated,
 | 
			
		||||
      receiverUpdated,
 | 
			
		||||
      sendDisabled
 | 
			
		||||
      sendDisabled,
 | 
			
		||||
      openHistory
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +1,37 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-card v-bind:class="{ 'bg-grey': isReversed }">
 | 
			
		||||
    <q-card-section class="row items-start justify-between">
 | 
			
		||||
      <div class="col text-center">
 | 
			
		||||
<div>
 | 
			
		||||
  <q-card flat square>
 | 
			
		||||
    <q-card-section class="row items-center justify-between" horizontal>
 | 
			
		||||
      <div class="col-5 text-left q-px-sm">
 | 
			
		||||
        <div
 | 
			
		||||
          v-bind:class="{ 'text-negative': isNegative() }"
 | 
			
		||||
          class="text-weight-bold"
 | 
			
		||||
          style="font-size: 2em"
 | 
			
		||||
          class="text-weight-bold text-h6"
 | 
			
		||||
        >
 | 
			
		||||
          <span v-if="isNegative()">-</span>{{ transaction.amount.toFixed(2) }} €
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>{{ text }}</div>
 | 
			
		||||
        <div>{{ timeStr }}</div>
 | 
			
		||||
        <div class="text-caption">{{ text }}</div>
 | 
			
		||||
        <div class="text-caption">{{ timeStr }}</div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col" style="text-align: right">
 | 
			
		||||
      <div class="col-5 q-px-sm text-center">
 | 
			
		||||
        <div class="text-subtitle1" v-if="isReversed">
 | 
			
		||||
          Storniert 
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-2 q-pr-sm" style="text-align: right">
 | 
			
		||||
        <q-btn
 | 
			
		||||
          color="negative"
 | 
			
		||||
          :color="isReversed ? 'positive' : 'negative'"
 | 
			
		||||
          aria-label="Reverse transaction"
 | 
			
		||||
          icon="mdi-trash-can"
 | 
			
		||||
          square
 | 
			
		||||
          :icon="!isReversed ? 'mdi-trash-can' : 'mdi-check-bold'"
 | 
			
		||||
          size="sm"
 | 
			
		||||
          round
 | 
			
		||||
          :disable="!canReverse"
 | 
			
		||||
          @click="reverse"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </q-card-section>
 | 
			
		||||
  </q-card>
 | 
			
		||||
  <q-separator />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +85,9 @@ export default defineComponent({
 | 
			
		|||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const isReversed = computed(() => props.transaction.reversal != undefined);
 | 
			
		||||
    const isReversed = computed(
 | 
			
		||||
      () => props.transaction.reversal_id != undefined || props.transaction.original_id != undefined
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const canReverse = computed(
 | 
			
		||||
      () =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,121 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-page padding class="fit row justify-left q-col-gutter-sm">
 | 
			
		||||
    <div class="col-12">
 | 
			
		||||
      <q-card>
 | 
			
		||||
        <BalanceHeader @update:user="userUpdated" :showSelector="showSelector" />
 | 
			
		||||
        <q-separator />
 | 
			
		||||
 | 
			
		||||
        <q-card-section class="row q-col-gutter-md" v-if="shortCuts">
 | 
			
		||||
          <div :key="index" v-for="(shortcut, index) in shortCuts" class="col-4">
 | 
			
		||||
            <q-btn
 | 
			
		||||
              push
 | 
			
		||||
              v-if="shortcut"
 | 
			
		||||
              color="primary"
 | 
			
		||||
              style="width: 100%"
 | 
			
		||||
              :label="shortcut.toFixed(2).toString() + ' €'"
 | 
			
		||||
              @click="changeBalance(shortcut)"
 | 
			
		||||
            >
 | 
			
		||||
              <q-popup-proxy context-menu>
 | 
			
		||||
                <q-btn label="Entfernen" @click="removeShortcut(shortcut)" />
 | 
			
		||||
              </q-popup-proxy>
 | 
			
		||||
              <q-tooltip>Rechtsklick um Verknüpfung zu entfernen</q-tooltip>
 | 
			
		||||
            </q-btn>
 | 
			
		||||
          </div></q-card-section
 | 
			
		||||
        >
 | 
			
		||||
        <q-card-section class="row q-col-gutter-md items-center">
 | 
			
		||||
          <div class="col-sm-4 col-xs-12">
 | 
			
		||||
            <q-input
 | 
			
		||||
              v-model.number="amount"
 | 
			
		||||
              type="number"
 | 
			
		||||
              filled
 | 
			
		||||
              label="Eigener Betrag"
 | 
			
		||||
              step="0.1"
 | 
			
		||||
              min="0"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="col-sm-4 col-xs-6">
 | 
			
		||||
            <q-btn
 | 
			
		||||
              style="width: 100%"
 | 
			
		||||
              color="primary"
 | 
			
		||||
              label="Anschreiben"
 | 
			
		||||
              @click="changeBalance(amount * -1)"
 | 
			
		||||
              ><q-tooltip>Rechtsklick um Betrag als Verknüpfung hinzuzufügen</q-tooltip>
 | 
			
		||||
              <q-popup-proxy context-menu v-model="showAddShortcut">
 | 
			
		||||
                <q-btn label="neue Verknüpfung" @click="addShortcut"></q-btn>
 | 
			
		||||
              </q-popup-proxy>
 | 
			
		||||
            </q-btn>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="col-sm-4 col-xs-6">
 | 
			
		||||
            <q-btn
 | 
			
		||||
              style="width: 100%"
 | 
			
		||||
              color="secondary"
 | 
			
		||||
              label="Gutschreiben"
 | 
			
		||||
              @click="changeBalance(amount)"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
      </q-card>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-for="(transaction, index) in transactions" v-bind:key="index" class="col-md-4 col-sm-6">
 | 
			
		||||
      <!-- TODO: In Vue3 use v-model:transaction="..." -->
 | 
			
		||||
      <Transaction :transaction.sync="transactions[index]" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </q-page>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, ref, defineComponent, onBeforeMount } from '@vue/composition-api';
 | 
			
		||||
import { hasPermission } from 'src/utils/permission';
 | 
			
		||||
import { StateInterfaceBalance } from '../store/balance';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import Transaction from '../components/Transaction.vue';
 | 
			
		||||
import BalanceHeader from '../components/BalanceHeader.vue';
 | 
			
		||||
import PERMISSIONS from '../permissions';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'BalanceAdd',
 | 
			
		||||
  components: { Transaction, BalanceHeader },
 | 
			
		||||
  setup(_, { root }) {
 | 
			
		||||
    onBeforeMount(() => void store.dispatch('balance/getShortcuts'));
 | 
			
		||||
    const store = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
 | 
			
		||||
    const amount = ref<number>(0);
 | 
			
		||||
    const showAddShortcut = ref(false);
 | 
			
		||||
    const transactions = computed(() => store.state.balance.transactions.slice().reverse());
 | 
			
		||||
    const user = ref(store.state.user.currentUser);
 | 
			
		||||
    const shortCuts = ref(store.state.balance.shortcuts);
 | 
			
		||||
 | 
			
		||||
    const showSelector = computed(
 | 
			
		||||
      () => hasPermission(PERMISSIONS.DEBIT, store) || hasPermission(PERMISSIONS.CREDIT, store)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    function addShortcut() {
 | 
			
		||||
      if (amount.value != 0) void store.dispatch('balance/addShortcut', amount.value * -1);
 | 
			
		||||
    }
 | 
			
		||||
    function removeShortcut(shortcut: number) {
 | 
			
		||||
      void store.dispatch('balance/removeShortcut', shortcut);
 | 
			
		||||
    }
 | 
			
		||||
    function userUpdated(selectedUser: FG.User) {
 | 
			
		||||
      user.value = selectedUser;
 | 
			
		||||
    }
 | 
			
		||||
    function changeBalance(amount: number) {
 | 
			
		||||
      store
 | 
			
		||||
        .dispatch('balance/changeBalance', { amount: amount, user: user.value?.userid })
 | 
			
		||||
        .catch(err => console.log(err));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      user,
 | 
			
		||||
      addShortcut,
 | 
			
		||||
      removeShortcut,
 | 
			
		||||
      showAddShortcut,
 | 
			
		||||
      changeBalance,
 | 
			
		||||
      transactions,
 | 
			
		||||
      amount,
 | 
			
		||||
      showSelector,
 | 
			
		||||
      shortCuts,
 | 
			
		||||
      userUpdated
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -11,25 +11,24 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
// TODO: Load all users / balances
 | 
			
		||||
 | 
			
		||||
// TODO: Fill usefull data
 | 
			
		||||
 | 
			
		||||
import { computed, defineComponent } from '@vue/composition-api';
 | 
			
		||||
import { StateInterfaceBalance } from '../store/balance';
 | 
			
		||||
import { ref, defineComponent, onMounted } from '@vue/composition-api';
 | 
			
		||||
import { StateInterfaceBalance, BalancesResponse } from '../store/balance';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  // name: 'PageName'
 | 
			
		||||
  setup(_, { root }) {
 | 
			
		||||
    const store = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
 | 
			
		||||
    const rows = computed(() => {
 | 
			
		||||
      const fo: Array<{ userid: string; balance: number }> = [];
 | 
			
		||||
      store.state.balance.balances.forEach((value, key) =>
 | 
			
		||||
        fo.push(Object.assign(value, { userid: key }))
 | 
			
		||||
    onMounted(
 | 
			
		||||
      () =>
 | 
			
		||||
        void store
 | 
			
		||||
          .dispatch('balance/getBalances')
 | 
			
		||||
          .then((balances: Array<BalancesResponse>) => rows.value.push(...balances))
 | 
			
		||||
    );
 | 
			
		||||
      return fo;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const rows = ref(new Array<BalancesResponse>());
 | 
			
		||||
 | 
			
		||||
    const columns = [
 | 
			
		||||
      {
 | 
			
		||||
| 
						 | 
				
			
			@ -41,16 +40,22 @@ export default defineComponent({
 | 
			
		|||
        sortable: true
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'balance',
 | 
			
		||||
        label: 'Kontostand',
 | 
			
		||||
        field: 'balance',
 | 
			
		||||
        name: 'credit',
 | 
			
		||||
        label: 'Haben',
 | 
			
		||||
        field: 'credit',
 | 
			
		||||
        format: (val: number) => val.toFixed(2)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'limit',
 | 
			
		||||
        label: 'Limit',
 | 
			
		||||
        field: 'limit',
 | 
			
		||||
        format: (val: number) => (val === null ? 'keins' : val)
 | 
			
		||||
        name: 'debit',
 | 
			
		||||
        label: 'Soll',
 | 
			
		||||
        field: 'debit',
 | 
			
		||||
        format: (val: number) => val.toFixed(2)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'balance',
 | 
			
		||||
        label: 'Kontostand',
 | 
			
		||||
        format: (_: undefined, row: { debit: number; credit: number }) =>
 | 
			
		||||
          (row.credit - row.debit).toFixed(2)
 | 
			
		||||
      }
 | 
			
		||||
    ];
 | 
			
		||||
    return { rows, columns };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,48 +1,118 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <q-page padding v-if="checkMain">
 | 
			
		||||
      <q-card>
 | 
			
		||||
        <q-card-section>
 | 
			
		||||
          <q-list v-for="(mainRoute, index) in mainRoutes" :key="'mainRoute' + index">
 | 
			
		||||
            <essential-link
 | 
			
		||||
              v-for="(route, index2) in mainRoute.children"
 | 
			
		||||
              :key="'route' + index2"
 | 
			
		||||
              :title="route.title"
 | 
			
		||||
              :icon="route.icon"
 | 
			
		||||
              :link="route.name"
 | 
			
		||||
              :permissions="route.meta.permissions"
 | 
			
		||||
    <q-tabs v-model="tab" v-if="$q.screen.gt.sm">
 | 
			
		||||
      <q-tab
 | 
			
		||||
        v-for="(tabindex, index) in tabs"
 | 
			
		||||
        :key="'tab' + index"
 | 
			
		||||
        :name="tabindex.name"
 | 
			
		||||
        :label="tabindex.label"
 | 
			
		||||
      />
 | 
			
		||||
    </q-tabs>
 | 
			
		||||
    <div class="fit row justify-end" v-else>
 | 
			
		||||
      <q-btn flat round icon="mdi-menu" @click="showDrawer = !showDrawer; show = false" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <q-drawer side="right" v-model="showDrawer" @click="showDrawer = !showDrawer" behavior="mobile">
 | 
			
		||||
      <q-list v-model="tab" v-if="!$q.screen.gt.sm && !show">
 | 
			
		||||
        <q-item
 | 
			
		||||
          v-for="(tabindex, index) in tabs"
 | 
			
		||||
          :key="'tab' + index"
 | 
			
		||||
          :active="tab == tabindex.name"
 | 
			
		||||
          clickable
 | 
			
		||||
          @click="tab = tabindex.name"
 | 
			
		||||
        >
 | 
			
		||||
          <q-item-label>{{ tabindex.label }}</q-item-label>
 | 
			
		||||
        </q-item>
 | 
			
		||||
      </q-list>
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
      </q-card>
 | 
			
		||||
      <q-list v-if="show">
 | 
			
		||||
      <div
 | 
			
		||||
        v-for="(transaction, index) in transactions"
 | 
			
		||||
        v-bind:key="index"
 | 
			
		||||
        class="col-sm-12"
 | 
			
		||||
      >
 | 
			
		||||
        <!-- TODO: In Vue3 use v-model:transaction="..." -->
 | 
			
		||||
        <Transaction :transaction.sync="transactions[index]" />
 | 
			
		||||
      </div>
 | 
			
		||||
      </q-list>
 | 
			
		||||
    </q-drawer>
 | 
			
		||||
    <q-page padding class="fit row justify-left q-col-gutter-sm">
 | 
			
		||||
      <q-tab-panels
 | 
			
		||||
        v-model="tab"
 | 
			
		||||
        style="background-color: transparent"
 | 
			
		||||
        class="q-pa-none col-12"
 | 
			
		||||
        animated
 | 
			
		||||
      >
 | 
			
		||||
        <q-tab-panel name="add" class="q-px-xs">
 | 
			
		||||
          <BalanceAdd @open-history="showDrawer = !showDrawer; show = showDrawer"/>
 | 
			
		||||
        </q-tab-panel>
 | 
			
		||||
        <q-tab-panel name="transfer" class="q-px-xs">
 | 
			
		||||
          <BalanceTransfer @open-history="showDrawer = !showDrawer; show = showDrawer"/>
 | 
			
		||||
        </q-tab-panel>
 | 
			
		||||
      </q-tab-panels>
 | 
			
		||||
    </q-page>
 | 
			
		||||
    <router-view />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent } from '@vue/composition-api';
 | 
			
		||||
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
 | 
			
		||||
import mainRoutes from 'src/plugins/balance/routes';
 | 
			
		||||
import { computed, defineComponent, ref, onMounted } from '@vue/composition-api';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import { BalanceInterface } from 'src/plugins/balance/store/balance';
 | 
			
		||||
import setLoadingBar from 'src/utils/loading';
 | 
			
		||||
import { StateInterface } from 'src/store';
 | 
			
		||||
import { hasPermissions, hasSomePermissions } from 'src/utils/permission';
 | 
			
		||||
import PERMISSIONS from '../permissions';
 | 
			
		||||
import { Screen } from 'quasar';
 | 
			
		||||
import BalanceAdd from '../components/BalanceAdd.vue';
 | 
			
		||||
import BalanceTransfer from '../components/BalanceTransfer.vue';
 | 
			
		||||
import Transaction from '../components/Transaction.vue';
 | 
			
		||||
import { StateInterfaceBalance } from '../store/balance';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  // name: 'PageName'
 | 
			
		||||
  components: { EssentialLink },
 | 
			
		||||
  name: 'BalanceManage',
 | 
			
		||||
  components: { BalanceAdd, BalanceTransfer, Transaction },
 | 
			
		||||
  setup(_, { root }) {
 | 
			
		||||
    const store = <Store<StateInterface>>root.$store;
 | 
			
		||||
    const loading = computed(() => {
 | 
			
		||||
      return (<BalanceInterface>store.state.balance).loading > 0;
 | 
			
		||||
    });
 | 
			
		||||
    const checkMain = computed(() => {
 | 
			
		||||
      return root.$route.matched.length == 2;
 | 
			
		||||
    const store = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
    const now = new Date()
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      void store.dispatch('balance/getTransactions', {filter: {from: new Date(now.getFullYear(), now.getMonth(), now.getDay())}})
 | 
			
		||||
    })
 | 
			
		||||
    const transactions = computed(() =>
 | 
			
		||||
      store.state.balance.transactions
 | 
			
		||||
        .filter(t => t.original_id == undefined)
 | 
			
		||||
        .sort((a, b) => (a.time >= b.time ? -1 : 1))
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const canAdd = () =>
 | 
			
		||||
      hasSomePermissions([PERMISSIONS.DEBIT, PERMISSIONS.CREDIT, PERMISSIONS.DEBIT_OWN], store);
 | 
			
		||||
 | 
			
		||||
    interface Tab {
 | 
			
		||||
      name: string;
 | 
			
		||||
      label: string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tabs: Tab[] = [
 | 
			
		||||
      ...(canAdd() ? [{ name: 'add', label: 'Anschreiben' }] : []),
 | 
			
		||||
      ...(hasSomePermissions([PERMISSIONS.SEND, PERMISSIONS.SEND_OTHER], store)
 | 
			
		||||
        ? [{ name: 'transfer', label: 'Übertragen' }]
 | 
			
		||||
        : [])
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    const drawer = ref<boolean>(false);
 | 
			
		||||
 | 
			
		||||
    const showDrawer = computed({
 | 
			
		||||
      get: () => {
 | 
			
		||||
        return !Screen.gt.sm && drawer.value;
 | 
			
		||||
      },
 | 
			
		||||
      set: (val: boolean) => {
 | 
			
		||||
        drawer.value = val;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    setLoadingBar(loading);
 | 
			
		||||
 | 
			
		||||
    return { checkMain, mainRoutes };
 | 
			
		||||
    const tab = ref<string>(canAdd() ? 'add' : 'transfer');
 | 
			
		||||
    const show = ref<boolean>(false);
 | 
			
		||||
    return {
 | 
			
		||||
      showDrawer,
 | 
			
		||||
      tab,
 | 
			
		||||
      tabs,
 | 
			
		||||
      transactions,
 | 
			
		||||
      show
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,154 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <q-page padding>
 | 
			
		||||
    <q-card>
 | 
			
		||||
      <q-card-section class="text-center">
 | 
			
		||||
        <div class="text-h4">Aktueller Stand</div>
 | 
			
		||||
        <div style="font-size: 2em">{{ balance.toFixed(2) }} €</div>
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
      <q-separator />
 | 
			
		||||
      <q-card-section>
 | 
			
		||||
        <q-table
 | 
			
		||||
          title="Buchungen"
 | 
			
		||||
          :data="data"
 | 
			
		||||
          :columns="columns"
 | 
			
		||||
          row-key="id"
 | 
			
		||||
          :pagination.sync="pagination"
 | 
			
		||||
          :loading="loading"
 | 
			
		||||
          @request="onRequest"
 | 
			
		||||
          binary-state-sort
 | 
			
		||||
        >
 | 
			
		||||
          <template v-slot:top>
 | 
			
		||||
            <q-toggle v-model="showCancelled" label="Stornierte einblenden" />
 | 
			
		||||
          </template>
 | 
			
		||||
          <template v-slot:body-cell="props">
 | 
			
		||||
            <q-td :props="props" v-bind:class="{ 'bg-grey': props.row.reversal_id != null }">
 | 
			
		||||
              {{ props.value }}
 | 
			
		||||
            </q-td>
 | 
			
		||||
          </template>
 | 
			
		||||
        </q-table>
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
    </q-card>
 | 
			
		||||
  </q-page>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, onMounted, ref } from '@vue/composition-api';
 | 
			
		||||
import { StateInterfaceBalance, TransactionsResponse } from '../store/balance';
 | 
			
		||||
import { Store } from 'vuex';
 | 
			
		||||
import { formatDateTime } from 'src/utils/datetime';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  // name: 'PageName'
 | 
			
		||||
  setup(_, { root }) {
 | 
			
		||||
    const store = <Store<StateInterfaceBalance>>root.$store;
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      void store.dispatch('balance/getBalance');
 | 
			
		||||
      void store.dispatch('user/getUsers').then(() =>
 | 
			
		||||
        onRequest({
 | 
			
		||||
          pagination: pagination.value,
 | 
			
		||||
          filter: undefined
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const showCancelled = ref(false);
 | 
			
		||||
    const data = ref<FG.Transaction[]>([]);
 | 
			
		||||
    const loading = ref(false);
 | 
			
		||||
    const pagination = ref({
 | 
			
		||||
      sortBy: 'time',
 | 
			
		||||
      descending: false,
 | 
			
		||||
      page: 1,
 | 
			
		||||
      rowsPerPage: 3,
 | 
			
		||||
      rowsNumber: 10
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    interface PaginationInterface {
 | 
			
		||||
      sortBy: string;
 | 
			
		||||
      descending: boolean;
 | 
			
		||||
      page: number;
 | 
			
		||||
      rowsPerPage: number;
 | 
			
		||||
      rowsNumber: number;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function onRequest(props: { pagination: PaginationInterface; filter?: string }) {
 | 
			
		||||
      const { page, rowsPerPage, sortBy, descending } = props.pagination;
 | 
			
		||||
 | 
			
		||||
      loading.value = true;
 | 
			
		||||
      // get all rows if "All" (0) is selected
 | 
			
		||||
      const fetchCount = rowsPerPage === 0 ? pagination.value.rowsNumber : rowsPerPage;
 | 
			
		||||
      // calculate starting row of data
 | 
			
		||||
      const startRow = (page - 1) * rowsPerPage;
 | 
			
		||||
      store
 | 
			
		||||
        .dispatch('balance/getTransactions', {
 | 
			
		||||
          filter: {
 | 
			
		||||
            offset: startRow,
 | 
			
		||||
            limit: fetchCount,
 | 
			
		||||
            showCancelled: showCancelled.value,
 | 
			
		||||
            showReversals: false
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((result: TransactionsResponse) => {
 | 
			
		||||
          // clear out existing data and add new
 | 
			
		||||
          data.value.splice(0, data.value.length, ...result.transactions);
 | 
			
		||||
          // don't forget to update local pagination object
 | 
			
		||||
          pagination.value.page = page;
 | 
			
		||||
          pagination.value.rowsPerPage = rowsPerPage;
 | 
			
		||||
          pagination.value.sortBy = sortBy;
 | 
			
		||||
          pagination.value.descending = descending;
 | 
			
		||||
          if (result.count) pagination.value.rowsNumber = result.count;
 | 
			
		||||
        })
 | 
			
		||||
        .finally(() => (loading.value = false));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const balance = computed(
 | 
			
		||||
      () =>
 | 
			
		||||
        store.state.balance.balances.get((<FG.User>store.state.user.currentUser).userid)?.balance ||
 | 
			
		||||
        NaN
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const columns = [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'time',
 | 
			
		||||
        label: 'Datum',
 | 
			
		||||
        field: 'time',
 | 
			
		||||
        required: true,
 | 
			
		||||
        sortable: true,
 | 
			
		||||
        format: (val: Date) => formatDateTime(new Date(val), true, true, true)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'text',
 | 
			
		||||
        label: 'Text',
 | 
			
		||||
        format: (_: undefined, row: FG.Transaction) => {
 | 
			
		||||
          if (row.sender_id == null) return 'Gutschrift';
 | 
			
		||||
          else {
 | 
			
		||||
            if (row.receiver_id == null) return 'Angeschrieben';
 | 
			
		||||
            else {
 | 
			
		||||
              if (row.receiver_id === store.state.user.currentUser?.userid) return 'Bekommen von X';
 | 
			
		||||
              else return 'Gesendet an X';
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'amount',
 | 
			
		||||
        label: 'Betrag',
 | 
			
		||||
        field: 'amount',
 | 
			
		||||
        format: (val: number) => `${val.toFixed(2)}€`
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'author_id',
 | 
			
		||||
        label: 'Benutzer',
 | 
			
		||||
        field: 'author_id',
 | 
			
		||||
        format: (val: string) => {
 | 
			
		||||
          const user = store.state.user.users.filter(x => x.userid == val);
 | 
			
		||||
          if (user.length > 0) return user[0].display_name;
 | 
			
		||||
          else return val;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return { data, pagination, onRequest, loading, balance, columns, showCancelled };
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -7,32 +7,32 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
 | 
			
		|||
    icon: 'mdi-cash-100',
 | 
			
		||||
    path: 'balance',
 | 
			
		||||
    name: 'balance',
 | 
			
		||||
    redirect: { name: 'balance-add' },
 | 
			
		||||
    redirect: { name: 'balance-view' },
 | 
			
		||||
    meta: { permissions: ['user'] },
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        title: 'Anschreiben',
 | 
			
		||||
        title: 'Übersicht',
 | 
			
		||||
        icon: 'mdi-cash-plus',
 | 
			
		||||
        path: 'add',
 | 
			
		||||
        name: 'balance-add',
 | 
			
		||||
        shortcut: true,
 | 
			
		||||
        meta: { permissions: [permissions.DEBIT_OWN, permissions.SHOW] },
 | 
			
		||||
        component: () => import('../pages/Add.vue')
 | 
			
		||||
        path: 'overview',
 | 
			
		||||
        name: 'balance-view',
 | 
			
		||||
        meta: { permissions: [permissions.SHOW] },
 | 
			
		||||
        component: () => import('../pages/Overview.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        title: 'Übertragen',
 | 
			
		||||
        icon: 'mdi-cash-refund',
 | 
			
		||||
        path: 'transfer',
 | 
			
		||||
        name: 'balance-transfer',
 | 
			
		||||
        meta: { permissions: [permissions.SEND] },
 | 
			
		||||
        component: () => import('../pages/Transfer.vue')
 | 
			
		||||
        title: 'Buchen',
 | 
			
		||||
        icon: 'mdi-cash-plus',
 | 
			
		||||
        path: 'change',
 | 
			
		||||
        name: 'balance-change',
 | 
			
		||||
        shortcut: true,
 | 
			
		||||
        meta: { permissions: [permissions.DEBIT_OWN, permissions.SHOW] },
 | 
			
		||||
        component: () => import('../pages/MainPage.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        title: 'Verwaltung',
 | 
			
		||||
        icon: 'mdi-account-cash',
 | 
			
		||||
        path: 'admin',
 | 
			
		||||
        name: 'balance-admin',
 | 
			
		||||
        meta: { permissions: [permissions.DEBIT_OWN, permissions.SHOW] },
 | 
			
		||||
        meta: { permissions: [permissions.SET_LIMIT, permissions.SHOW_OTHER] },
 | 
			
		||||
        component: () => import('../pages/Admin.vue')
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
 | 
			
		||||
import { StateInterface } from 'src/store';
 | 
			
		||||
import store, { StateInterface } from 'src/store';
 | 
			
		||||
import { axios } from 'src/boot/axios';
 | 
			
		||||
import { AxiosResponse } from 'axios';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +9,15 @@ interface BalanceResponse {
 | 
			
		|||
  debit: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface BalancesResponse extends BalanceResponse {
 | 
			
		||||
  userid: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TransactionsResponse {
 | 
			
		||||
  transactions: Array<FG.Transaction>;
 | 
			
		||||
  count?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UserBalance extends BalanceResponse {
 | 
			
		||||
  limit: number | null;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +40,10 @@ const state: BalanceInterface = {
 | 
			
		|||
  loading: 0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function fixTransaction(t: FG.Transaction) {
 | 
			
		||||
  t.time = new Date(t.time);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const mutations: MutationTree<BalanceInterface> = {
 | 
			
		||||
  setBalance(state, data: { userid: string; balance: BalanceResponse }) {
 | 
			
		||||
    state.balances.set(
 | 
			
		||||
| 
						 | 
				
			
			@ -59,9 +72,13 @@ const mutations: MutationTree<BalanceInterface> = {
 | 
			
		|||
  addTransaction(state, data: FG.Transaction) {
 | 
			
		||||
    state.transactions.push(data);
 | 
			
		||||
  },
 | 
			
		||||
  addTransactions(state, data: [FG.Transaction]) {
 | 
			
		||||
    state.transactions.push(...data);
 | 
			
		||||
    state.transactions.sort((a, b) => (a.time <= b.time ? -1 : 1));
 | 
			
		||||
  },
 | 
			
		||||
  reverseTransaction(state, data: { transaction: FG.Transaction; reversal: FG.Transaction }) {
 | 
			
		||||
    const idx = state.transactions.findIndex(value => value.id === data.transaction.id);
 | 
			
		||||
    data.transaction.reversal = data.reversal;
 | 
			
		||||
    data.transaction.reversal_id = data.reversal.id;
 | 
			
		||||
    if (idx > -1) state.transactions[idx] = data.transaction;
 | 
			
		||||
    else state.transactions.push(data.transaction);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +87,7 @@ const mutations: MutationTree<BalanceInterface> = {
 | 
			
		|||
const actions: ActionTree<BalanceInterface, StateInterface> = {
 | 
			
		||||
  addShortcut({ commit, state, rootState }, shortcut) {
 | 
			
		||||
    const sc = [...state.shortcuts, shortcut];
 | 
			
		||||
    sc.sort().reverse();
 | 
			
		||||
    sc.sort();
 | 
			
		||||
 | 
			
		||||
    const user = <FG.User>rootState.user.currentUser;
 | 
			
		||||
    return axios.put(`/users/${user.userid}/balance/shortcuts`, sc).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +126,37 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
 | 
			
		|||
      })
 | 
			
		||||
      .finally(() => commit('setLoading', false));
 | 
			
		||||
  },
 | 
			
		||||
  getBalances() {
 | 
			
		||||
    return axios.get('/balance').then(({ data }: AxiosResponse<Array<BalancesResponse>>) => {
 | 
			
		||||
      return data;
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  getTransactions(
 | 
			
		||||
    { commit, rootState },
 | 
			
		||||
    payload: {
 | 
			
		||||
      userid?: string;
 | 
			
		||||
      filter?: {
 | 
			
		||||
        limit?: number;
 | 
			
		||||
        offset?: number;
 | 
			
		||||
        from?: Date;
 | 
			
		||||
        to?: Date;
 | 
			
		||||
        showReversals?: boolean;
 | 
			
		||||
        showCancelled?: boolean;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  ) {
 | 
			
		||||
    commit('setLoading');
 | 
			
		||||
    if (!payload.userid) payload.userid = (<FG.User>rootState.user.currentUser).userid;
 | 
			
		||||
    if (!payload.filter) payload.filter = { limit: 10 };
 | 
			
		||||
    return axios
 | 
			
		||||
      .get(`/users/${payload.userid}/balance/transactions`, { params: payload.filter || {} })
 | 
			
		||||
      .then(({ data }: AxiosResponse<TransactionsResponse>) => {
 | 
			
		||||
        data.transactions.forEach(t => fixTransaction(t));
 | 
			
		||||
        commit('addTransactions', data.transactions);
 | 
			
		||||
        return data;
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => commit('setLoading', false));
 | 
			
		||||
  },
 | 
			
		||||
  getLimit({ rootState, commit }) {
 | 
			
		||||
    commit('setLoading');
 | 
			
		||||
    axios
 | 
			
		||||
| 
						 | 
				
			
			@ -125,18 +173,26 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
 | 
			
		|||
  revert({ dispatch, commit }, transaction: FG.Transaction) {
 | 
			
		||||
    return axios
 | 
			
		||||
      .delete(`/balance/${transaction.id}`)
 | 
			
		||||
      .then((response: AxiosResponse<FG.Transaction>) => {
 | 
			
		||||
        commit('reverseTransaction', { transaction: transaction, reversal: response.data });
 | 
			
		||||
      .then(({ data }: AxiosResponse<FG.Transaction>) => {
 | 
			
		||||
        fixTransaction(data);
 | 
			
		||||
        commit('reverseTransaction', { transaction: transaction, reversal: data });
 | 
			
		||||
        dispatch('getBalance').catch(err => console.warn(err));
 | 
			
		||||
      });
 | 
			
		||||
  },
 | 
			
		||||
  changeBalance({ dispatch, commit }, data: { amount: number; user: string; sender?: string }) {
 | 
			
		||||
  changeBalance(
 | 
			
		||||
    { dispatch, commit, rootState },
 | 
			
		||||
    data: { amount: number; user: string; sender?: string }
 | 
			
		||||
  ) {
 | 
			
		||||
    commit('setLoading');
 | 
			
		||||
    return axios
 | 
			
		||||
      .put(`/users/${data.user}/balance`, data)
 | 
			
		||||
      .then((response: AxiosResponse<FG.Transaction>) => {
 | 
			
		||||
        const transaction = response.data;
 | 
			
		||||
        transaction.time = new Date(transaction.time);
 | 
			
		||||
        fixTransaction(transaction);
 | 
			
		||||
        if (
 | 
			
		||||
          data.user == rootState.user.currentUser?.userid ||
 | 
			
		||||
          data.sender === rootState.user.currentUser?.userid
 | 
			
		||||
        )
 | 
			
		||||
          commit('addTransaction', transaction);
 | 
			
		||||
        commit(state.balances.has(data.user) ? 'changeBalance' : 'setBalance', {
 | 
			
		||||
          userid: data.user,
 | 
			
		||||
| 
						 | 
				
			
			@ -152,6 +208,7 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
 | 
			
		|||
      .catch(err => {
 | 
			
		||||
        console.debug(err);
 | 
			
		||||
        // Maybe Balance changed
 | 
			
		||||
        void dispatch('getTransactions', {});
 | 
			
		||||
        return dispatch('getBalance', data.sender ? data.sender : data.user);
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => commit('setLoading', false));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,12 +45,12 @@
 | 
			
		|||
            <job
 | 
			
		||||
              :job="job"
 | 
			
		||||
              :jobCanDelete="jobDeleteDisabled"
 | 
			
		||||
              @setStart="setStart"
 | 
			
		||||
              @setRequired="setRequired"
 | 
			
		||||
              @setEnd="setEnd"
 | 
			
		||||
              @setComment="setComment"
 | 
			
		||||
              @setJobType="setJobType"
 | 
			
		||||
              @removeJob="removeJob(index)"
 | 
			
		||||
              @set-start="setStart"
 | 
			
		||||
              @set-required="setRequired"
 | 
			
		||||
              @set-end="setEnd"
 | 
			
		||||
              @set-comment="setComment"
 | 
			
		||||
              @set-job-type="setJobType"
 | 
			
		||||
              @remove-job="removeJob(index)"
 | 
			
		||||
            />
 | 
			
		||||
          </q-card>
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,7 @@ export default defineComponent({
 | 
			
		|||
      event.value.id = NaN;
 | 
			
		||||
      event.value.start = new Date();
 | 
			
		||||
      event.value.description = '';
 | 
			
		||||
      delete event.value['type'];
 | 
			
		||||
      event.value.type = {id: -1, name: ''};
 | 
			
		||||
      event.value.jobs = [Object.assign({}, newJob.value)];
 | 
			
		||||
    }
 | 
			
		||||
    function notEmpty(val: string) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,33 +89,33 @@ export default defineComponent({
 | 
			
		|||
 | 
			
		||||
    function setStart(value: Date) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      emit('setStart', { job: props.job, value });
 | 
			
		||||
      emit('set-start', { job: props.job, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setEnd(value: Date) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      emit('setEnd', { job: props.job, value });
 | 
			
		||||
      emit('set-end', { job: props.job, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setComment(value: string) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      emit('setComment', { job: props.job, value });
 | 
			
		||||
      emit('set-comment', { job: props.job, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setJobType(value: FG.JobType) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      console.log('setJobType', value);
 | 
			
		||||
      emit('setJobType', { job: props.job, value });
 | 
			
		||||
      emit('set-job-type', { job: props.job, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setRequired(value: number) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      emit('setRequired', { job: props.job, value });
 | 
			
		||||
      emit('set-required', { job: props.job, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeJob() {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
      emit('removeJob');
 | 
			
		||||
      emit('remove-job');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function notEmpty(val: string) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,5 @@
 | 
			
		|||
import { FG_Plugin } from 'src/plugins';
 | 
			
		||||
 | 
			
		||||
/*const permissions = {
 | 
			
		||||
  // Show own and others balance
 | 
			
		||||
  SHOW: 'balance_show',
 | 
			
		||||
  SHOW_OTHER: 'balance_show_others',
 | 
			
		||||
  // Credit balance (give)
 | 
			
		||||
  CREDIT: 'balance_credit',
 | 
			
		||||
  // Debit balance (take)
 | 
			
		||||
  DEBIT: 'balance_debit',
 | 
			
		||||
  // Debit own balance only
 | 
			
		||||
  DEBIT_OWN: 'balance_debit_own',
 | 
			
		||||
  // Send from to other
 | 
			
		||||
  SEND: 'balance_send',
 | 
			
		||||
  // Send from other to another
 | 
			
		||||
  SEND_OTHER: 'balance_send_others',
 | 
			
		||||
  // Can set limit for users
 | 
			
		||||
  SET_LIMIT: 'balance_set_limit',
 | 
			
		||||
  //Allow sending / sub while exceeding the set limit
 | 
			
		||||
  EXCEED_LIMIT: 'balance_exceed_limit'
 | 
			
		||||
};*/
 | 
			
		||||
 | 
			
		||||
const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
 | 
			
		||||
  {
 | 
			
		||||
    title: 'Dienste',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,12 +31,7 @@
 | 
			
		|||
          label="Zeit"
 | 
			
		||||
          filled
 | 
			
		||||
        />
 | 
			
		||||
        <q-select
 | 
			
		||||
          class="col-xs-12 col-sm-6 q-px-sm"
 | 
			
		||||
          :options="options"
 | 
			
		||||
          v-model="option"
 | 
			
		||||
          filled
 | 
			
		||||
        />
 | 
			
		||||
        <q-select class="col-xs-12 col-sm-6 q-px-sm" :options="options" v-model="option" filled />
 | 
			
		||||
      </div>
 | 
			
		||||
    </q-card-section>
 | 
			
		||||
    <q-card-actions align="right" v-if="!isEdit">
 | 
			
		||||
| 
						 | 
				
			
			@ -59,8 +54,8 @@ export default defineComponent({
 | 
			
		|||
  name: 'Sessions',
 | 
			
		||||
  props: {
 | 
			
		||||
    session: {
 | 
			
		||||
      required: true,
 | 
			
		||||
    },
 | 
			
		||||
      required: true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  setup(props: { session: FG.Session }, { root }) {
 | 
			
		||||
    const store = <Store<StateInterface>>root.$store;
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +87,7 @@ export default defineComponent({
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    function deleteSession(token: string) {
 | 
			
		||||
      store.dispatch('session/deleteSession', token).catch((error) => {
 | 
			
		||||
      store.dispatch('session/deleteSession', token).catch(error => {
 | 
			
		||||
        console.warn(error);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +108,7 @@ export default defineComponent({
 | 
			
		|||
            return (lifetime.value / (60 * 60 * 24)).toFixed(2);
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      set: (val) => {
 | 
			
		||||
      set: val => {
 | 
			
		||||
        if (val) {
 | 
			
		||||
          switch (option.value) {
 | 
			
		||||
            case options.value[0]:
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +122,7 @@ export default defineComponent({
 | 
			
		|||
              break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    function edit(value: boolean) {
 | 
			
		||||
| 
						 | 
				
			
			@ -139,11 +134,8 @@ export default defineComponent({
 | 
			
		|||
      console.log(lifetime.value);
 | 
			
		||||
      isEdit.value = false;
 | 
			
		||||
      void store
 | 
			
		||||
        .dispatch(
 | 
			
		||||
          'session/updateSession',
 | 
			
		||||
          {lifetime: lifetime.value, token: props.session.token}
 | 
			
		||||
        )
 | 
			
		||||
        .catch((error) => {
 | 
			
		||||
        .dispatch('session/updateSession', { lifetime: lifetime.value, token: props.session.token })
 | 
			
		||||
        .catch(error => {
 | 
			
		||||
          console.log(error);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -159,8 +151,8 @@ export default defineComponent({
 | 
			
		|||
      option,
 | 
			
		||||
      lifetime,
 | 
			
		||||
      computedLifetime,
 | 
			
		||||
      save,
 | 
			
		||||
      save
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ function loadCurrentSession() {
 | 
			
		|||
const state: SessionInterface = {
 | 
			
		||||
  sessions: [],
 | 
			
		||||
  currentSession: loadCurrentSession() || undefined,
 | 
			
		||||
  loading: false,
 | 
			
		||||
  loading: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mutations: MutationTree<SessionInterface> = {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,11 +44,11 @@ const mutations: MutationTree<SessionInterface> = {
 | 
			
		|||
    state.loading = value;
 | 
			
		||||
  },
 | 
			
		||||
  updateSession(state, session: FG.Session) {
 | 
			
		||||
    const index = state.sessions.findIndex((x) => x.token == session.token);
 | 
			
		||||
    const index = state.sessions.findIndex(x => x.token == session.token);
 | 
			
		||||
    if (index > -1) {
 | 
			
		||||
      state.sessions[index] = session;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
        commit('setCurrentSession', response.data.session);
 | 
			
		||||
        commit('user/setCurrentUser', response.data.user, { root: true });
 | 
			
		||||
        commit('user/setCurrentPermissions', response.data.permissions, {
 | 
			
		||||
          root: true,
 | 
			
		||||
          root: true
 | 
			
		||||
        });
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error: AxiosError) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
   */
 | 
			
		||||
  logout({ dispatch, rootState }) {
 | 
			
		||||
    if (rootState.session.currentSession) {
 | 
			
		||||
      dispatch('deleteSession', rootState.session.currentSession.token).catch((error) => {
 | 
			
		||||
      dispatch('deleteSession', rootState.session.currentSession.token).catch(error => {
 | 
			
		||||
        console.log(error);
 | 
			
		||||
        void dispatch('clearCurrent', false);
 | 
			
		||||
      });
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
        if (token === rootState.session.currentSession?.token) {
 | 
			
		||||
          void dispatch('clearCurrent', false);
 | 
			
		||||
        } else {
 | 
			
		||||
          dispatch('getSessions').catch((error) => {
 | 
			
		||||
          dispatch('getSessions').catch(error => {
 | 
			
		||||
            throw error;
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +116,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
    void Router.push({
 | 
			
		||||
      name: 'login',
 | 
			
		||||
      query: redirect ? { redirect: Router.currentRoute.fullPath } : {},
 | 
			
		||||
      params: { logout: 'true' },
 | 
			
		||||
      params: { logout: 'true' }
 | 
			
		||||
    }).then(() => {
 | 
			
		||||
      commit('clearCurrentSession');
 | 
			
		||||
      commit('user/clearCurrentUser', null, { root: true });
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
    axios
 | 
			
		||||
      .get('/auth')
 | 
			
		||||
      .then((response: AxiosResponse<FG.Session[]>) => {
 | 
			
		||||
        response.data.forEach((session) => {
 | 
			
		||||
        response.data.forEach(session => {
 | 
			
		||||
          session.expires = new Date(session.expires);
 | 
			
		||||
        });
 | 
			
		||||
        commit('setSessions', response.data);
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +143,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
          commit('setCurrentSession', currentSession);
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
      .catch(error => {
 | 
			
		||||
        throw error;
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -160,7 +160,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
          commit('setCurrentSession', response.data);
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch((err) => console.log(err))
 | 
			
		||||
      .catch(err => console.log(err))
 | 
			
		||||
      .finally(() => {
 | 
			
		||||
        commit('setLoading', false);
 | 
			
		||||
      });
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +173,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
 | 
			
		|||
    return axios.post('/auth/reset', data).catch((error: AxiosError) => {
 | 
			
		||||
      return Promise.reject(error.response);
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getters: GetterTree<SessionInterface, StateInterface> = {
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +185,7 @@ const getters: GetterTree<SessionInterface, StateInterface> = {
 | 
			
		|||
  },
 | 
			
		||||
  loading(state) {
 | 
			
		||||
    return state.loading;
 | 
			
		||||
  },
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const sessions: Module<SessionInterface, StateInterface> = {
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +193,7 @@ const sessions: Module<SessionInterface, StateInterface> = {
 | 
			
		|||
  state,
 | 
			
		||||
  mutations,
 | 
			
		||||
  actions,
 | 
			
		||||
  getters,
 | 
			
		||||
  getters
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default sessions;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,3 +9,8 @@ export function hasPermissions(needed: string[], store: Store<StateInterface>) {
 | 
			
		|||
  const permissions = store.state.user.currentPermissions;
 | 
			
		||||
  return needed.every(value => permissions.includes(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function hasSomePermissions(needed: string[], store: Store<StateInterface>) {
 | 
			
		||||
  const permissions = store.state.user.currentPermissions;
 | 
			
		||||
  return needed.some(value => permissions.includes(value));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||