Matching UI with improved API

This commit is contained in:
2024-09-24 13:12:44 +02:00
parent 8395b4427d
commit 0b34174ce9
34 changed files with 194 additions and 545 deletions

View File

@@ -1,378 +0,0 @@
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.8 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.8">
<diagram name="Page-1" id="WevClHWmhzPAQ7FDN5po">
<mxGraphModel dx="1988" dy="473" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-4" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="310" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-5" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Products&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#008a00;strokeColor=default;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="-200" y="280" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-21" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;name: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="399" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-22" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;price: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="460" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-23" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;categoryId: Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="340" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="-XxvrMC6G6SQ8Xm1xsTM-24" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;discount: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="490" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-2" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Accounts&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#1ba1e2;strokeColor=default;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="-920" y="320" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-3" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-920" y="350" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-4" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;username: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-920" y="380" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-5" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;password: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-920" y="410" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-6" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;OrderItems&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#a20025;strokeColor=default;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="-439.99999999999994" y="360" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-7" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-439.99999999999994" y="390" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-8" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;accountId: Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-680" y="420" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-17" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Categories&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#008a00;strokeColor=default;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="39.960000000000036" y="310" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-18" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="39.960000000000036" y="340" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-19" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;name: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="39.960000000000036" y="370" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-20" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=-0.019;entryY=0.493;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;" parent="1" source="-XxvrMC6G6SQ8Xm1xsTM-23" target="xvubMpEdOjOFzFZ-NZdf-18" edge="1">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="120" y="469.9999999999998" as="sourcePoint" />
<mxPoint y="589.9999999999998" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-21" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="xvubMpEdOjOFzFZ-NZdf-20" connectable="0" vertex="1">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="4" y="-29" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-22" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="xvubMpEdOjOFzFZ-NZdf-20" connectable="0" vertex="1">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-7" y="-31" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-23" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;productId:&amp;nbsp;&lt;/i&gt;&lt;i style=&quot;background-color: initial;&quot;&gt;Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-439.99999999999994" y="450" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-24" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="xvubMpEdOjOFzFZ-NZdf-23" target="-XxvrMC6G6SQ8Xm1xsTM-4" edge="1">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-240" y="290" as="sourcePoint" />
<mxPoint x="-280.0000000000001" y="605" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-25" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="xvubMpEdOjOFzFZ-NZdf-24" connectable="0" vertex="1">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="10" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-26" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="xvubMpEdOjOFzFZ-NZdf-24" connectable="0" vertex="1">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-9" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-37" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;firstName: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-920" y="440" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-38" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;lastName: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-920" y="470" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-40" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Orders&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#a20025;strokeColor=default;fontColor=#ffffff;" parent="1" vertex="1">
<mxGeometry x="-680" y="360" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-41" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;orderId: Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-680" y="390" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-42" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;orderId:&amp;nbsp;&lt;/i&gt;&lt;i style=&quot;background-color: initial;&quot;&gt;Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-439.99999999999994" y="420" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="xvubMpEdOjOFzFZ-NZdf-47" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;quantity: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-439.99999999999994" y="480" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-1" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;icon: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="39.960000000000036" y="400" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-2" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;brandId: Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="370" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-4" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;rating: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200" y="520" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-6" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;description: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-200.00000000000003" y="430" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-7" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;shippingProgress: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="-680" y="480" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-11" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="-779.9100000000001" y="355" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-12" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="-539.9100000000001" y="395" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-13" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="-299.90999999999997" y="395" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-14" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="-60.04000000000002" y="315" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-15" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="179.92000000000002" y="345" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-16" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;rotation=0;gradientColor=#223548;strokeColor=none;gradientDirection=east;fillColor=#5b738b;rounded=0;pointerEvents=0;fontFamily=Helvetica;fontSize=16;fontColor=#FFFFFF;spacingTop=4;spacingBottom=4;spacingLeft=4;spacingRight=4;points=[];fontStyle=1" parent="1" vertex="1">
<mxGeometry x="-785.2" y="385" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-17" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;rotation=0;gradientColor=#223548;strokeColor=none;gradientDirection=east;fillColor=#5b738b;rounded=0;pointerEvents=0;fontFamily=Helvetica;fontSize=16;fontColor=#FFFFFF;spacingTop=4;spacingBottom=4;spacingLeft=4;spacingRight=4;points=[];fontStyle=1" parent="1" vertex="1">
<mxGeometry x="174.63000000000005" y="375" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-18" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" parent="1" vertex="1">
<mxGeometry x="-674.71" y="565" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-19" value="Primary Key" style="text;strokeColor=none;align=left;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="-654.8" y="560" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-20" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;rotation=0;gradientColor=#223548;strokeColor=none;gradientDirection=east;fillColor=#5b738b;rounded=0;pointerEvents=0;fontFamily=Helvetica;fontSize=16;fontColor=#FFFFFF;spacingTop=4;spacingBottom=4;spacingLeft=4;spacingRight=4;points=[];fontStyle=1" parent="1" vertex="1">
<mxGeometry x="-680" y="635" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-21" value="Unique" style="text;strokeColor=none;align=left;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="-654.8000000000001" y="630" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-24" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" parent="1" vertex="1">
<mxGeometry x="-299.90999999999997" y="425" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-25" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" parent="1" vertex="1">
<mxGeometry x="-299.90999999999997" y="455" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-26" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" parent="1" vertex="1">
<mxGeometry x="-539.9100000000001" y="425" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-27" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" parent="1" vertex="1">
<mxGeometry x="-60.04000000000002" y="375" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-28" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" parent="1" vertex="1">
<mxGeometry x="-674.71" y="600" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="Mm1_BtgNgvV1z5cDQ8i7-29" value="Foreign Key" style="text;strokeColor=none;align=left;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="-654.8" y="595" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-1" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;bankName: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="540" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-2" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;iban: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="570" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-3" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;orderedAt: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-680" y="450" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-4" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Brands&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#008a00;strokeColor=default;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="39.960000000000036" y="470" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-5" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="39.960000000000036" y="500" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-6" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;name: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="39.960000000000036" y="530" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-7" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" vertex="1" parent="1">
<mxGeometry x="180.24999999999994" y="505" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-8" value="1" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;rotation=0;gradientColor=#223548;strokeColor=none;gradientDirection=east;fillColor=#5b738b;rounded=0;pointerEvents=0;fontFamily=Helvetica;fontSize=16;fontColor=#FFFFFF;spacingTop=4;spacingBottom=4;spacingLeft=4;spacingRight=4;points=[];fontStyle=1" vertex="1" parent="1">
<mxGeometry x="174.95999999999998" y="535" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-9" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="Mm1_BtgNgvV1z5cDQ8i7-2" target="_UF3kFiP3G3Olu9frHUj-5">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-445.04" y="510" as="sourcePoint" />
<mxPoint x="-315.04" y="510" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-10" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-9">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="3" y="-28" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-11" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-9">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-4" y="-30" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-12" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;images: Array&amp;lt;String&amp;gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-200" y="610" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-13" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;specs: Array&amp;lt;String&amp;gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-200" y="580" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-14" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;inStock: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-200" y="550" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-16" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Addresses&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#1ba1e2;strokeColor=default;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="-1160" y="200" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-17" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;street: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="290" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-18" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;houseNumber: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="320" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-19" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;postalCode: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="350" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-20" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;city: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="380" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-21" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="230" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-25" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;accountId: Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="260" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-26" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" vertex="1" parent="1">
<mxGeometry x="-1017" y="234" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-27" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;Payments&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#1ba1e2;strokeColor=default;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="-1160" y="450" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-28" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="480" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-29" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;i&gt;accountId: Number&lt;/i&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="510" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-33" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" vertex="1" parent="1">
<mxGeometry x="-1017" y="485" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-36" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" vertex="1" parent="1">
<mxGeometry x="-1017" y="265" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-37" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" vertex="1" parent="1">
<mxGeometry x="-1017" y="515" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-38" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;accountRoleId: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-920" y="500" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-39" value="&lt;blockquote style=&quot;margin: 0px; border: none; padding: 0px;&quot;&gt;&lt;b&gt;&lt;u&gt;AccountRoles&lt;/u&gt;&lt;/b&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=center;fillColor=#1ba1e2;strokeColor=default;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="-1160" y="640" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-40" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;name: String&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="700" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-41" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;&lt;u&gt;id:&amp;nbsp;&lt;/u&gt;&lt;u style=&quot;background-color: initial;&quot;&gt;Number&lt;/u&gt;&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-1160" y="670" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-44" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" vertex="1" parent="1">
<mxGeometry x="-779.9100000000001" y="505" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-49" value="&lt;blockquote style=&quot;margin: 0px 0px 0px 8px; border: none; padding: 0px;&quot;&gt;orderPrice: Number&lt;/blockquote&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="1">
<mxGeometry x="-439.99999999999994" y="510" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-52" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="_UF3kFiP3G3Olu9frHUj-25" target="xvubMpEdOjOFzFZ-NZdf-3">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-890.0000000000001" y="179.99999999999994" as="sourcePoint" />
<mxPoint x="-790.0000000000001" y="179.99999999999994" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-53" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-52">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="10" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-54" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-52">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-9" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-55" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="_UF3kFiP3G3Olu9frHUj-29" target="xvubMpEdOjOFzFZ-NZdf-3">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-950.0000000000001" y="674.55" as="sourcePoint" />
<mxPoint x="-850.0000000000001" y="674.55" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-56" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-55">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="10" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-57" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-55">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-9" y="-25" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-58" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#2072B8;" vertex="1" parent="1">
<mxGeometry x="-1017" y="675" width="9.43" height="20" as="geometry" />
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-59" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="_UF3kFiP3G3Olu9frHUj-38" target="_UF3kFiP3G3Olu9frHUj-41">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-940.0000000000001" y="650" as="sourcePoint" />
<mxPoint x="-840.0000000000001" y="650" as="targetPoint" />
<Array as="points">
<mxPoint x="-950" y="515" />
<mxPoint x="-950" y="685" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-60" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-59">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="-24" y="-28" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-61" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-59">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="15" y="-29" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-62" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="xvubMpEdOjOFzFZ-NZdf-8" target="xvubMpEdOjOFzFZ-NZdf-3">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-779.9100000000001" y="604.4399999999999" as="sourcePoint" />
<mxPoint x="-679.9100000000001" y="604.4399999999999" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-63" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-62">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="-22" y="-27" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-64" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-62">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="13" y="-32" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-65" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="xvubMpEdOjOFzFZ-NZdf-42" target="xvubMpEdOjOFzFZ-NZdf-41">
<mxGeometry x="389.35999999999996" y="350" as="geometry">
<mxPoint x="-620.0000000000001" y="594.64" as="sourcePoint" />
<mxPoint x="-520.0000000000001" y="594.64" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-66" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-65">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint x="-22" y="-28" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-67" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" connectable="0" vertex="1" parent="_UF3kFiP3G3Olu9frHUj-65">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="14" y="-29" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="_UF3kFiP3G3Olu9frHUj-68" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;fillColor=#CCCCCC;" vertex="1" parent="1">
<mxGeometry x="-60.04000000000002" y="345" width="9.43" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -2,12 +2,18 @@
"data": [ "data": [
{ {
"id": 0, "id": 0,
"name": "Unregistered",
"privilegeBuy": false,
"privilegeAdminPanel": false
},
{
"id": 1,
"name": "User", "name": "User",
"privilegeBuy": true, "privilegeBuy": true,
"privilegeAdminPanel": false "privilegeAdminPanel": false
}, },
{ {
"id": 1, "id": 2,
"name": "Admin", "name": "Admin",
"privilegeBuy": true, "privilegeBuy": true,
"privilegeAdminPanel": true "privilegeAdminPanel": true

View File

@@ -22,7 +22,7 @@
"iban": "DE92500105175721645777" "iban": "DE92500105175721645777"
} }
], ],
"accountRoleId": 1 "accountRoleId": 2
}, },
{ {
"id": 1, "id": 1,
@@ -46,7 +46,7 @@
"iban": "DE12500105179557939114" "iban": "DE12500105179557939114"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
}, },
{ {
"id": 2, "id": 2,
@@ -77,7 +77,7 @@
"iban": "DE31500105175417833272" "iban": "DE31500105175417833272"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
}, },
{ {
"id": 3, "id": 3,
@@ -101,7 +101,7 @@
"iban": "DE41500105172184936679" "iban": "DE41500105172184936679"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
}, },
{ {
"id": 4, "id": 4,
@@ -125,7 +125,7 @@
"iban": "DE85500105172283979774" "iban": "DE85500105172283979774"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
}, },
{ {
"id": 5, "id": 5,
@@ -149,7 +149,7 @@
"iban": "DE45500105178862417577" "iban": "DE45500105178862417577"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
}, },
{ {
"id": 6, "id": 6,
@@ -180,7 +180,7 @@
"iban": "DE51500105177526222196" "iban": "DE51500105177526222196"
} }
], ],
"accountRoleId": 0 "accountRoleId": 1
} }
] ]
} }

View File

@@ -22,20 +22,20 @@ account.post("/login", (req: Request, res: Response) => {
if (account != null) { if (account != null) {
if (account.dataValues.password == req.body.password) { if (account.dataValues.password == req.body.password) {
// Status: 200 OK // Status: 200 OK
res.status(200).json(account).send() res.status(200).json(account)
} else { } else {
// Status: 401 Unauthorized // Status: 401 Unauthorized
res.status(401).json({ res.status(401).json({
code: 401, code: 401,
message: "Unauthorized" message: "Unauthorized"
}).send() })
} }
} else { } else {
// Status: 400 Bad request // Status: 400 Bad request
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: "Bad Request" message: "Bad Request"
}).send() })
} }
} }
) )
@@ -50,7 +50,7 @@ account.post("/", (req: Request, res: Response) => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: "Username too short!" message: "Username too short!"
}).send() })
} }
// Check if password is valid // Check if password is valid
@@ -60,20 +60,20 @@ account.post("/", (req: Request, res: Response) => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: "Password too short!" message: "Password too short!"
}).send() })
} }
// Create account // Create account
Account.create(req.body) Account.create(req.body)
.then(account => { .then(account => {
// Status: 201 Created // Status: 201 Created
res.status(201).json(account).send() res.status(201).json(account)
}).catch(reason => { }).catch(reason => {
// Status: 409 Conflict // Status: 409 Conflict
res.status(409).json({ res.status(409).json({
code: 409, code: 409,
message: "Username already in use" message: "Username already in use"
}).send() })
}) })
}) })
@@ -84,13 +84,13 @@ account.patch("/", (req: Request, res: Response) => {
}) })
.then(account => { .then(account => {
// Status: 200 OK // Status: 200 OK
res.status(200).json(account).send() res.status(200).json(account)
}) })
.catch(error => { .catch(error => {
// Status: 400 Bad request // Status: 400 Bad request
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: error message: error
}).send() })
}) })
}) })

View File

@@ -7,7 +7,7 @@ export const category = Router()
category.get("/", (req: Request, res: Response, next: NextFunction) => { category.get("/", (req: Request, res: Response, next: NextFunction) => {
Category.findAll() Category.findAll()
.then(categories => { .then(categories => {
res.status(200).json(categories).send() res.status(200).json(categories)
}) })
}) })
@@ -15,13 +15,13 @@ category.get("/", (req: Request, res: Response, next: NextFunction) => {
category.post("/", (req: Request, res: Response, next: NextFunction) => { category.post("/", (req: Request, res: Response, next: NextFunction) => {
Category.create(req.body) Category.create(req.body)
.then(category => { .then(category => {
res.status(201).json(category).send() res.status(201).json(category)
}) })
.catch(error => { .catch(error => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: error message: error
}).send() })
}) })
}) })
@@ -31,12 +31,12 @@ category.delete("/:id", (req: Request, res: Response, next: NextFunction) => {
where: { id: req.params.id } where: { id: req.params.id }
}) })
.then(category => { .then(category => {
res.status(200).json(category).send() res.status(200).json(category)
}) })
.catch(error => { .catch(error => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: error message: error
}).send() })
}) })
}) })

View File

@@ -2,6 +2,8 @@ import { Router, Request, Response } from "express";
import { Order } from "../models/order.model"; import { Order } from "../models/order.model";
import { Product } from "../models/product.model"; import { Product } from "../models/product.model";
import { OrderItem } from "../models/orderItem.model"; import { OrderItem } from "../models/orderItem.model";
import { Brand } from "../models/brand.model";
import { Category } from "../models/category.model";
export const order = Router() export const order = Router()
@@ -10,7 +12,21 @@ order.get("/:id", (req: Request, res: Response) => {
Order.findAll({ Order.findAll({
where: { accountId: req.params.id }, where: { accountId: req.params.id },
include: [ include: [
{ model: OrderItem, include: [ Product ] } {
model: OrderItem,
include: [
{
model: Product,
include: [ Brand, Category ],
attributes: {
exclude: [
"categoryId",
"brandId"
]
}
},
]
}
] ]
}) })
.then(orders => { .then(orders => {
@@ -42,6 +58,6 @@ order.post("/", (req: Request, res: Response) => {
} }
// Created // Created
res.status(201).json(order).send() res.status(201).json(order)
}) })
}) })

View File

@@ -36,7 +36,7 @@ product.get("/:productId", (req: Request, res: Response) => {
} }
) )
.then(product => { .then(product => {
res.status(200).json(product).send() res.status(200).json(product)
}) })
}) })
@@ -44,13 +44,13 @@ product.get("/:productId", (req: Request, res: Response) => {
product.post("/", (req: Request, res: Response) => { product.post("/", (req: Request, res: Response) => {
Product.create(req.body) Product.create(req.body)
.then(product => { .then(product => {
res.status(201).json(product).send() res.status(201).json(product)
}) })
.catch(error => { .catch(error => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: error message: error
}).send() })
}) })
}) })
@@ -60,12 +60,12 @@ product.delete("/:id", (req: Request, res: Response) => {
where: { id: req.params.id } where: { id: req.params.id }
}) })
.then(product => { .then(product => {
res.status(200).json(product).send() res.status(200).json(product)
}) })
.catch(error => { .catch(error => {
res.status(400).json({ res.status(400).json({
code: 400, code: 400,
message: error message: error
}).send() })
}) })
}) })

View File

@@ -1,27 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTheme } from 'vuetify/lib/framework.mjs'; import { useTheme } from 'vuetify/lib/framework.mjs';
import { useUserStore } from './data/stores/userStore';
import { i18n } from './plugins/i18n'; import { i18n } from './plugins/i18n';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import vuetify from './plugins/vuetify'; import vuetify from './plugins/vuetify';
import navigationItems from './components/navigationItems.vue'; import navigationItems from './components/navigationItems.vue';
import { useProductStore } from './data/stores/productStore'; import { useProductStore } from './data/stores/productStore';
import { useCategoryStore } from './data/stores/categoryStore'; import { useCategoryStore } from './data/stores/categoryStore';
import { usePreferencesStore } from './data/stores/preferencesStore';
const userStore = useUserStore() const preferencesStore = usePreferencesStore()
const productStore = useProductStore() const productStore = useProductStore()
const categoryStore = useCategoryStore() const categoryStore = useCategoryStore()
const theme = useTheme() const theme = useTheme()
const navRail = ref(vuetify.display.mobile) const navRail = ref(vuetify.display.mobile)
theme.global.name.value = userStore.theme theme.global.name.value = preferencesStore.theme
productStore.fetchAllProducts() productStore.fetchAllProducts()
categoryStore.fetchAllCategories() categoryStore.fetchAllCategories()
// Global watcher // Global watcher
watch(() => userStore.language, () => { watch(() => preferencesStore.language, () => {
i18n.global.locale = userStore.language i18n.global.locale = preferencesStore.language
}, { immediate: true }) }, { immediate: true })
</script> </script>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from '@/data/stores/userStore';
import { useBasketStore } from '@/data/stores/basketStore'; import { useBasketStore } from '@/data/stores/basketStore';
import { useAccountStore } from '@/data/stores/accountStore';
const userStore = useUserStore() const accountStore = useAccountStore()
const basketStore = useBasketStore() const basketStore = useBasketStore()
const navRail = defineModel("navRail", { type: Boolean }) const navRail = defineModel("navRail", { type: Boolean })
</script> </script>
@@ -36,16 +36,16 @@ const navRail = defineModel("navRail", { type: Boolean })
</v-list-subheader> </v-list-subheader>
<v-expand-transition> <v-expand-transition>
<div v-if="!userStore.loggedIn"> <div v-if="accountStore.userAccount.id == null">
<v-list-item v-if="!userStore.loggedIn" :title="$t('menu.login')" prepend-icon="mdi-login" to="/login" link /> <v-list-item v-if="accountStore.userAccount.id == null" :title="$t('menu.login')" prepend-icon="mdi-login" to="/login" link />
</div> </div>
</v-expand-transition> </v-expand-transition>
<v-expand-transition> <v-expand-transition>
<div v-if="userStore.loggedIn"> <div v-if="accountStore.userAccount.id != null">
<v-list-item :title="$t('menu.account')" prepend-icon="mdi-account" to="/account" link /> <v-list-item :title="$t('menu.account')" prepend-icon="mdi-account" to="/account" link />
<v-list-item :title="$t('menu.orders')" prepend-icon="mdi-cart-check" to="/orders" link /> <v-list-item :title="$t('menu.orders')" prepend-icon="mdi-cart-check" to="/orders" link />
<v-list-item :title="$t('menu.logout')" prepend-icon="mdi-logout" @click="userStore.logout" link /> <v-list-item :title="$t('menu.logout')" prepend-icon="mdi-logout" @click="accountStore.logout" link />
</div> </div>
</v-expand-transition> </v-expand-transition>

View File

@@ -1,15 +1,14 @@
import { AccountRole } from "./accountRole"
import { AddressModel } from "./addressModel"
import { PaymentModel } from "./paymentModel"
export class AccountModel { export class AccountModel {
id: number id: number
username: string = "" username: string = ""
password: string = "" password: string = ""
street: string = ""
houseNumber: number = 0
postalCode: number = 0
city: string = ""
firstName: string = "" firstName: string = ""
lastName: string = "" lastName: string = ""
createdAt: string = "" addresses: Array<AddressModel> = [ new AddressModel() ]
updatedAt: string = "" payments: Array<PaymentModel> = [ new PaymentModel() ]
bankName: string = "" accountRole: AccountRole = new AccountRole()
iban: string = ""
} }

View File

@@ -0,0 +1,5 @@
export class AccountRole {
name: string = ""
privilegeBuy: boolean = false
privilegeAdminPanel: boolean = false
}

View File

@@ -0,0 +1,6 @@
export class AddressModel {
street: string = ""
houseNumber: number = 0
postalCode: number = 0
city: string = ""
}

View File

@@ -1,10 +1,7 @@
import { ProductModel } from "./productModel"
export class BasketItemModel { export class BasketItemModel {
id: number = -1 id: number = -1
brand: string = ""
name: string = ""
categoryName: string = ""
categoryIcon: string = ""
price: number = 0
discount: number = 0
quantity: number = 1 quantity: number = 1
product: ProductModel = new ProductModel()
} }

View File

@@ -0,0 +1,4 @@
export class BrandModel {
id: number = 0
name: string = ""
}

View File

@@ -2,6 +2,4 @@ export class CategoryModel {
id: number = -1 id: number = -1
name: string name: string
icon: string icon: string
createdAt: string = ""
updatedAt: string = ""
} }

View File

@@ -2,8 +2,7 @@ import { OrderedItemModel } from "./orderedItemModel"
export class OrderModel { export class OrderModel {
accountId: number accountId: number
totalPrice: number
shippingProgress: number shippingProgress: number
orderItem: Array<OrderedItemModel> orderItems: Array<OrderedItemModel>
createdAt: string orderedAt: string
} }

View File

@@ -0,0 +1,4 @@
export class PaymentModel {
bankName: string = ""
iban: string = ""
}

View File

@@ -1,13 +1,16 @@
import { BrandModel } from "./brandModel"
import { CategoryModel } from "./categoryModel"
export class ProductModel { export class ProductModel {
id: number = -1 id: number = 0
brand: string category: CategoryModel = new CategoryModel()
name: string brand: BrandModel = new BrandModel()
name: string = ""
description: string = "" description: string = ""
specs: Array<string> = []
categoryId: number
price: number = 0 price: number = 0
discount: number = 0 discount: number = 0
rating: number = 1 rating: number = 1
inStock: number
specs: Array<string> = []
images: Array<string> = [""] images: Array<string> = [""]
storedItems: number
} }

View File

@@ -1,15 +0,0 @@
import { CategoryModel } from "./categoryModel"
export class ProductWithCategoryModel {
id: number = -1
brand: string
name: string
description: string = ""
specs: Array<string> = []
category: CategoryModel = new CategoryModel()
price: number = 0
discount: number = 0
rating: number = 1
images: Array<string> = [""]
storedItems: number
}

View File

@@ -1,21 +1,16 @@
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core"; import { useLocalStorage } from "@vueuse/core";
import { ThemeEnum } from "../enums/themeEnums"; import { defineStore } from "pinia";
import { LanguageEnum } from "../enums/languageEnum";
import { AccountModel } from "../models/accountModel"; import { AccountModel } from "../models/accountModel";
import { loginAccount, registerAccount, updateAccount } from "../api/accountApi";
import { useFeedbackStore } from "./feedbackStore";
import { BannerStateEnum } from "../enums/bannerStateEnum";
import { OrderModel } from "../models/orderModel"; import { OrderModel } from "../models/orderModel";
import { useFeedbackStore } from "./feedbackStore";
import { loginAccount, registerAccount, updateAccount } from "../api/accountApi";
import { getUserOrders } from "../api/orderApi"; import { getUserOrders } from "../api/orderApi";
import { BannerStateEnum } from "../enums/bannerStateEnum";
export const useUserStore = defineStore('userStore', { export const useAccountStore = defineStore("accountStore", {
state: () => ({ state: () => ({
theme: useLocalStorage<ThemeEnum>("hackmycart/userStore/theme", ThemeEnum.DARKRED), userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountModel()),
language: useLocalStorage<LanguageEnum>("hackmycart/userStore/language", LanguageEnum.GERMAN), orders: useLocalStorage<Array<OrderModel>>("hackmycart/accountStore/orders", [ new OrderModel() ])
userAccount: useLocalStorage<AccountModel>("hackmycart/userStore/userAccount", new AccountModel()),
loggedIn: useLocalStorage<Boolean>("hackmycart/userStore/loggedIn", false),
orders: useLocalStorage<Array<OrderModel>>("hackmycart/userStore/orders", [])
}), }),
actions: { actions: {

View File

@@ -4,11 +4,10 @@ import { calcProductPrice } from "@/scripts/productScripts";
import { BasketItemModel } from "../models/basketItemModel"; import { BasketItemModel } from "../models/basketItemModel";
import { useFeedbackStore } from "./feedbackStore"; import { useFeedbackStore } from "./feedbackStore";
import { BannerStateEnum } from "../enums/bannerStateEnum"; import { BannerStateEnum } from "../enums/bannerStateEnum";
import { OrderModel } from "../models/orderModel";
import { useUserStore } from "./userStore";
import { addOrder } from "../api/orderApi"; import { addOrder } from "../api/orderApi";
import { useAccountStore } from "./accountStore";
export const useBasketStore = defineStore('basket', { export const useBasketStore = defineStore('basketStore', {
state: () => ({ state: () => ({
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/productsInBasket", []) itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/productsInBasket", [])
}), }),
@@ -31,7 +30,7 @@ export const useBasketStore = defineStore('basket', {
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED) feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED)
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) => this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
basketItemModel.productId != item.productId basketItemModel.product.id != item.product.id
) )
}, },
@@ -40,15 +39,16 @@ export const useBasketStore = defineStore('basket', {
feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED) feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED)
// Product is already in the basket, increase number of items // Product is already in the basket, increase number of items
if (this.itemsInBasket.find((basketItem) => basketItem.productId == item.productId)) { if (this.itemsInBasket.find((basketItem) => basketItem.productId == item.product.id)) {
this.itemsInBasket.find((basketItem) => basketItem.productId == item.productId).quantity += item.quantity this.itemsInBasket.find((basketItem) =>
basketItem.productId == item.product.id).quantity += item.quantity
} else { } else {
this.itemsInBasket.push(item) this.itemsInBasket.push(item)
} }
}, },
takeOrder() { takeOrder() {
const userStore = useUserStore() const accountStore = useAccountStore()
// //
// const order = new OrderModel() // const order = new OrderModel()
// order.accountId = userStore.userAccount.id // order.accountId = userStore.userAccount.id
@@ -56,7 +56,7 @@ export const useBasketStore = defineStore('basket', {
// //
// console.log(order) // console.log(order)
addOrder(userStore.userAccount.id, this.itemsInBasket) addOrder(accountStore.userAccount.id, this.itemsInBasket)
} }
} }
}) })

View File

@@ -0,0 +1,11 @@
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
import { ThemeEnum } from "../enums/themeEnums";
import { LanguageEnum } from "../enums/languageEnum";
export const usePreferencesStore = defineStore('preferencesStore', {
state: () => ({
theme: useLocalStorage<ThemeEnum>("hackmycart/preferencesStore/theme", ThemeEnum.DARKRED),
language: useLocalStorage<LanguageEnum>("hackmycart/preferencesStore/language", LanguageEnum.GERMAN)
}),
})

View File

@@ -3,13 +3,13 @@ import { defineStore } from "pinia";
import { getAllProducts } from "../api/productApi"; import { getAllProducts } from "../api/productApi";
import { SortOrder } from "../enums/sortOrderEnum"; import { SortOrder } from "../enums/sortOrderEnum";
import { CategoryModel } from "../models/categoryModel"; import { CategoryModel } from "../models/categoryModel";
import { ProductWithCategoryModel } from "../models/productWithCategoryModel"; import { ProductModel } from "../models/productModel";
export const useProductStore = defineStore("productStore", { export const useProductStore = defineStore("productStore", {
state: () => ({ state: () => ({
products: useLocalStorage<Array<ProductWithCategoryModel>>("hackmycart/productStore/products", []), products: useLocalStorage<Array<ProductModel>>("hackmycart/productStore/products", []),
filteredProducts: useLocalStorage<Array<ProductWithCategoryModel>>("hackmycart/productStore/filteredProducts", []), filteredProducts: useLocalStorage<Array<ProductModel>>("hackmycart/productStore/filteredProducts", []),
sortOrder: useLocalStorage<SortOrder>("hackmycart/productStore/sortOrder", SortOrder.NAMEATOZ), sortOrder: useLocalStorage<SortOrder>("hackmycart/productStore/sortOrder", SortOrder.NAMEATOZ),
filteredCategory: useLocalStorage<CategoryModel>("hackmycart/productStore/filteredCategory", new CategoryModel()), filteredCategory: useLocalStorage<CategoryModel>("hackmycart/productStore/filteredCategory", new CategoryModel()),
onlyDiscounts: useLocalStorage<Boolean>("hackmycart/productStore/onlyDiscounts", false) onlyDiscounts: useLocalStorage<Boolean>("hackmycart/productStore/onlyDiscounts", false)
@@ -28,20 +28,20 @@ export const useProductStore = defineStore("productStore", {
if (this.filteredCategory.id == -1 || this.filteredCategory.id == 0) { if (this.filteredCategory.id == -1 || this.filteredCategory.id == 0) {
this.filteredProducts = this.products this.filteredProducts = this.products
} else { } else {
this.filteredProducts = this.products.filter((product: ProductWithCategoryModel) => this.filteredProducts = this.products.filter((product: ProductModel) =>
product.category.id == this.filteredCategory.id product.category.id == this.filteredCategory.id
) )
} }
if (this.onlyDiscounts) { if (this.onlyDiscounts) {
this.filteredProducts = this.filteredProducts.filter((product: ProductWithCategoryModel) => this.filteredProducts = this.filteredProducts.filter((product: ProductModel) =>
product.discount > 0 product.discount > 0
) )
} }
}, },
sortProducts() { sortProducts() {
this.filteredProducts.sort((a: ProductWithCategoryModel, b: ProductWithCategoryModel) => { this.filteredProducts.sort((a: ProductModel, b: ProductModel) => {
switch (this.sortOrder) switch (this.sortOrder)
{ {
case SortOrder.PRICELOWTOHIGH: { case SortOrder.PRICELOWTOHIGH: {

View File

@@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from '@/data/stores/userStore';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue'; import outlinedButton from '@/components/outlinedButton.vue';
import { useAccountStore } from '@/data/stores/accountStore';
const userStore = useUserStore() const accountStore = useAccountStore()
</script> </script>
<template> <template>
@@ -13,14 +13,14 @@ const userStore = useUserStore()
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('account.username')" :label="$t('account.username')"
v-model="userStore.userAccount.username" v-model="accountStore.userAccount.username"
disabled disabled
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('account.password')" :label="$t('account.password')"
v-model="userStore.userAccount.password" v-model="accountStore.userAccount.password"
type="password" type="password"
/> />
</v-col> </v-col>
@@ -30,13 +30,13 @@ const userStore = useUserStore()
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.firstName')" :label="$t('userInfo.firstName')"
v-model="userStore.userAccount.firstName" v-model="accountStore.userAccount.firstName"
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.lastName')" :label="$t('userInfo.lastName')"
v-model="userStore.userAccount.lastName" v-model="accountStore.userAccount.lastName"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -45,13 +45,13 @@ const userStore = useUserStore()
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.street')" :label="$t('userInfo.street')"
v-model="userStore.userAccount.street" v-model="accountStore.userAccount.addresses[0].street"
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.houseNumber')" :label="$t('userInfo.houseNumber')"
v-model="userStore.userAccount.houseNumber" v-model="accountStore.userAccount.addresses[0].houseNumber"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -60,13 +60,13 @@ const userStore = useUserStore()
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.postalCode')" :label="$t('userInfo.postalCode')"
v-model="userStore.userAccount.postalCode" v-model="accountStore.userAccount.addresses[0].postalCode"
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.city')" :label="$t('userInfo.city')"
v-model="userStore.userAccount.city" v-model="accountStore.userAccount.addresses[0].city"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -75,13 +75,13 @@ const userStore = useUserStore()
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.bankName')" :label="$t('userInfo.bankName')"
v-model="userStore.userAccount.bankName" v-model="accountStore.userAccount.payments[0].bankName"
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
:label="$t('userInfo.iban')" :label="$t('userInfo.iban')"
v-model="userStore.userAccount.iban" v-model="accountStore.userAccount.payments[0].iban"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -89,7 +89,7 @@ const userStore = useUserStore()
<template #actions> <template #actions>
<outlined-button <outlined-button
@click="userStore.updateAccount()" @click="accountStore.updateAccount()"
prepend-icon="mdi-content-save" prepend-icon="mdi-content-save"
color="green" color="green"
> >

View File

@@ -3,13 +3,13 @@ import { useBasketStore } from '@/data/stores/basketStore';
import productsTable from './productsTable.vue'; import productsTable from './productsTable.vue';
import alertBanner from '@/components/alertBanner.vue'; import alertBanner from '@/components/alertBanner.vue';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
import { useUserStore } from '@/data/stores/userStore';
import orderingDialog from './orderingDialog.vue'; import orderingDialog from './orderingDialog.vue';
import outlinedButton from '@/components/outlinedButton.vue'; import outlinedButton from '@/components/outlinedButton.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import { useAccountStore } from '@/data/stores/accountStore';
const basketStore = useBasketStore() const basketStore = useBasketStore()
const userStore = useUserStore() const accountStore = useAccountStore()
const showOrderingDialog = ref() const showOrderingDialog = ref()
</script> </script>
@@ -47,7 +47,7 @@ const showOrderingDialog = ref()
<template #actions> <template #actions>
<outlined-button <outlined-button
prepend-icon="mdi-basket-check" prepend-icon="mdi-basket-check"
:disabled="basketStore.itemsInBasket.length == 0 || userStore.userAccount.id == null" :disabled="basketStore.itemsInBasket.length == 0 || accountStore.userAccount.id == null"
variant="outlined" variant="outlined"
color="green" color="green"
@click="showOrderingDialog = true" @click="showOrderingDialog = true"

View File

@@ -1,16 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from '@/data/stores/userStore';
import { ref } from 'vue'; import { ref } from 'vue';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue'; import outlinedButton from '@/components/outlinedButton.vue';
import { useAccountStore } from '@/data/stores/accountStore';
const userStore = useUserStore() const accountStore = useAccountStore()
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false }) const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
const username = ref("duranduran") const username = ref("duranduran")
const password = ref("H4nn0ver") const password = ref("H4nn0ver")
function startLogin() { function startLogin() {
userStore.login(username.value, password.value) accountStore.login(username.value, password.value)
} }
</script> </script>

View File

@@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { AccountModel } from '@/data/models/accountModel'; import { AccountModel } from '@/data/models/accountModel';
import { useUserStore } from '@/data/stores/userStore';
import { ref } from 'vue'; import { ref } from 'vue';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
import outlinedButton from '@/components/outlinedButton.vue'; import outlinedButton from '@/components/outlinedButton.vue';
import { useAccountStore } from '@/data/stores/accountStore';
const newUser = ref(new AccountModel()) const newUser = ref(new AccountModel())
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false }) const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
const userStore = useUserStore() const accountStore = useAccountStore()
</script> </script>
<template> <template>
@@ -54,12 +54,12 @@ const userStore = useUserStore()
<v-text-field <v-text-field
:label="$t('userInfo.street')" :label="$t('userInfo.street')"
prepend-icon="mdi-numeric" prepend-icon="mdi-numeric"
v-model="newUser.street" v-model="newUser.addresses[0].street"
clearable clearable
/> />
</v-col> </v-col>
<v-col cols="4"> <v-col cols="4">
<v-text-field :label="$t('userInfo.houseNumber')" v-model="newUser.houseNumber" clearable /> <v-text-field :label="$t('userInfo.houseNumber')" v-model="newUser.addresses[0].houseNumber" clearable />
</v-col> </v-col>
</v-row> </v-row>
@@ -68,12 +68,12 @@ const userStore = useUserStore()
<v-text-field <v-text-field
:label="$t('userInfo.postalCode')" :label="$t('userInfo.postalCode')"
prepend-icon="mdi-city" prepend-icon="mdi-city"
v-model="newUser.postalCode" v-model="newUser.addresses[0].postalCode"
clearable clearable
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field :label="$t('userInfo.city')" v-model="newUser.city" clearable /> <v-text-field :label="$t('userInfo.city')" v-model="newUser.addresses[0].city" clearable />
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
@@ -88,7 +88,7 @@ const userStore = useUserStore()
<outlined-button <outlined-button
prepend-icon="mdi-account-plus" prepend-icon="mdi-account-plus"
@click="userStore.registerAccount(newUser)" @click="accountStore.registerAccount(newUser)"
> >
{{ $t('account.register') }} {{ $t('account.register') }}
</outlined-button> </outlined-button>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from '@/data/stores/userStore'; import { useAccountStore } from '@/data/stores/accountStore';
import ordersCard from './ordersCard.vue'; import ordersCard from './ordersCard.vue';
const userStore = useUserStore() const accountStore = useAccountStore()
function getDotColor(order, step: number) { function getDotColor(order, step: number) {
if (order.shippingProgress == step) if (order.shippingProgress == step)
@@ -27,7 +27,7 @@ function formatDateTimeString(string: string) {
<template> <template>
<v-container max-width="1000"> <v-container max-width="1000">
<v-row v-for="order in userStore.orders"> <v-row v-for="order in accountStore.orders">
<v-col> <v-col>
<orders-card :order="order" /> <orders-card :order="order" />
</v-col> </v-col>

View File

@@ -29,8 +29,8 @@ function formatDateTimeString(string: string) {
<template> <template>
<card-view <card-view
:title="$t('orders.orderFrom') + ' ' + formatDateTimeString(order.createdAt) + ' ' + $t('oclock')" :title="$t('orders.orderFrom') + ' ' + formatDateTimeString(order.orderedAt) + ' ' + $t('oclock')"
:subtitle="$t('totalPrice') + ': ' + order.totalPrice + ' €'" :subtitle="$t('totalPrice') + ': ' + 0 + ' €'"
> >
<v-timeline direction="horizontal" side="start" size="x-large"> <v-timeline direction="horizontal" side="start" size="x-large">
<v-timeline-item :dot-color="getDotColor(order, 1)" icon="mdi-basket-check"> <v-timeline-item :dot-color="getDotColor(order, 1)" icon="mdi-basket-check">
@@ -64,9 +64,9 @@ function formatDateTimeString(string: string) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="orderItem in order.orderItem"> <tr v-for="orderItem in order.orderItems">
<td>{{ orderItem.quantity }}x</td> <td>{{ orderItem.quantity }}x</td>
<td>{{ orderItem.product.brand }}</td> <td>{{ orderItem.product.brand.name }}</td>
<td>{{ orderItem.product.name }}</td> <td>{{ orderItem.product.name }}</td>
<td>{{ orderItem.product.price }} </td> <td>{{ orderItem.product.price }} </td>
</tr> </tr>

View File

@@ -1,20 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { ThemeEnum } from '@/data/enums/themeEnums'; import { ThemeEnum } from '@/data/enums/themeEnums';
import { useTheme } from 'vuetify/lib/framework.mjs'; import { useTheme } from 'vuetify/lib/framework.mjs';
import { useUserStore } from '@/data/stores/userStore';
import { i18n } from '@/plugins/i18n'; import { i18n } from '@/plugins/i18n';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
import { usePreferencesStore } from '@/data/stores/preferencesStore';
const userStore = useUserStore() const preferencesStore = usePreferencesStore()
const theme = useTheme() const theme = useTheme()
const themeEnums = Object.values(ThemeEnum) const themeEnums = Object.values(ThemeEnum)
function changeTheme() { function changeTheme() {
theme.global.name.value = userStore.theme theme.global.name.value = preferencesStore.theme
} }
function changeLanguage() { function changeLanguage() {
i18n.global.locale = userStore.language i18n.global.locale = preferencesStore.language
} }
</script> </script>
@@ -24,7 +24,7 @@ function changeLanguage() {
<v-row> <v-row>
<v-col> <v-col>
<v-select <v-select
v-model="userStore.theme" v-model="preferencesStore.theme"
:items="themeEnums" :items="themeEnums"
:label="$t('preferences.selectedTheme')" :label="$t('preferences.selectedTheme')"
@update:model-value="changeTheme" @update:model-value="changeTheme"
@@ -34,7 +34,7 @@ function changeLanguage() {
<v-row> <v-row>
<v-col> <v-col>
<v-select v-model="userStore.language" :items="$i18n.availableLocales" :label="$t('preferences.language')" <v-select v-model="preferencesStore.language" :items="$i18n.availableLocales" :label="$t('preferences.language')"
@update:model-value="changeLanguage" @update:model-value="changeLanguage"
/> />
</v-col> </v-col>

View File

@@ -3,16 +3,16 @@ import productCard from "./productCard.vue"
import productDetails from "./productDetailsDialog.vue" import productDetails from "./productDetailsDialog.vue"
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { useProductStore } from "@/data/stores/productStore"; import { useProductStore } from "@/data/stores/productStore";
import { ProductWithCategoryModel } from "@/data/models/productWithCategoryModel";
import alertBanner from "@/components/alertBanner.vue"; import alertBanner from "@/components/alertBanner.vue";
import filterNavDrawer from "./filterNavDrawer.vue"; import filterNavDrawer from "./filterNavDrawer.vue";
import { ProductModel } from '@/data/models/productModel';
const productStore = useProductStore() const productStore = useProductStore()
const showProductDetails = ref(false) const showProductDetails = ref(false)
const dialogProduct = ref(new ProductWithCategoryModel()) const dialogProduct = ref(new ProductModel())
function showProductDialog(product: ProductWithCategoryModel) { function showProductDialog(product: ProductModel) {
dialogProduct.value = product dialogProduct.value = product
showProductDetails.value = true showProductDetails.value = true
} }

View File

@@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ProductWithCategoryModel } from '@/data/models/productWithCategoryModel'; import { ProductModel } from '@/data/models/productModel';
import cardView from '@/components/cardView.vue'; import cardView from '@/components/cardView.vue';
defineProps({ defineProps({
product: { product: {
required: true, required: true,
type: ProductWithCategoryModel type: ProductModel
} }
}) })
</script> </script>
@@ -65,11 +65,11 @@ defineProps({
</div> </div>
<div style="position: absolute; bottom: 0; right: 0;" class="pr-2 pb-2"> <div style="position: absolute; bottom: 0; right: 0;" class="pr-2 pb-2">
<div v-if="product.storedItems > 5" class="text-green-lighten-1"> <div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.storedItems]) }} {{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div> </div>
<div v-else-if="product.storedItems > 0" class="text-orange-lighten-1"> <div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.storedItems]) }} {{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div> </div>
<div v-else class="text-red"> <div v-else class="text-red">
{{ $t("product.soldOut") }} {{ $t("product.soldOut") }}

View File

@@ -4,13 +4,13 @@ import { ModelRef, ref, watch } from 'vue';
import { useBasketStore } from '@/data/stores/basketStore'; import { useBasketStore } from '@/data/stores/basketStore';
import { calcProductPrice, productToBasketItem } from '@/scripts/productScripts'; import { calcProductPrice, productToBasketItem } from '@/scripts/productScripts';
import ActionDialog from '@/components/actionDialog.vue' import ActionDialog from '@/components/actionDialog.vue'
import { ProductWithCategoryModel } from '@/data/models/productWithCategoryModel'; import { ProductModel } from '@/data/models/productModel';
import outlinedButton from '@/components/outlinedButton.vue'; import outlinedButton from '@/components/outlinedButton.vue';
const props = defineProps({ const props = defineProps({
product: { product: {
type: ProductWithCategoryModel, type: ProductModel,
default: new ProductWithCategoryModel() default: new ProductModel()
} }
}) })
@@ -32,7 +32,7 @@ watch(() => props.product.images, () => {
<template> <template>
<action-dialog <action-dialog
:title="product.brand + ': ' + product.name" :title="product.brand.name + ': ' + product.name"
:icon="product.category.icon" :icon="product.category.icon"
:subtitle="product.category.name" :subtitle="product.category.name"
v-model="showDialog" v-model="showDialog"
@@ -107,11 +107,11 @@ watch(() => props.product.images, () => {
<v-row> <v-row>
<v-col cols="3"> <v-col cols="3">
<div class="pt-3"> <div class="pt-3">
<div v-if="product.storedItems > 5" class="text-green-lighten-1"> <div v-if="product.inStock > 5" class="text-green-lighten-1">
{{ $t("product.storedItemsAvailable", [product.storedItems]) }} {{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div> </div>
<div v-else-if="product.storedItems > 0" class="text-orange-lighten-1"> <div v-else-if="product.inStock > 0" class="text-orange-lighten-1">
{{ $t("product.storedItemsAvailable", [product.storedItems]) }} {{ $t("product.storedItemsAvailable", [product.inStock]) }}
</div> </div>
<div v-else class="text-red"> <div v-else class="text-red">
{{ $t("product.soldOut") }} {{ $t("product.soldOut") }}
@@ -147,14 +147,14 @@ watch(() => props.product.images, () => {
:max="10" :max="10"
density="comfortable" density="comfortable"
:hide-details="true" :hide-details="true"
:disabled="product.storedItems == 0" :disabled="product.inStock == 0"
/> />
<outlined-button <outlined-button
prepend-icon="mdi-cart-plus" prepend-icon="mdi-cart-plus"
@click="addProductToBasket" @click="addProductToBasket"
height="50" height="50"
:disabled="product.storedItems == 0" :disabled="product.inStock == 0"
> >
{{ $t('addToBasket') }} {{ $t('addToBasket') }}
</outlined-button> </outlined-button>

View File

@@ -1,9 +1,8 @@
import { BasketItemModel } from "@/data/models/basketItemModel"; import { BasketItemModel } from "@/data/models/basketItemModel";
import { CategoryModel } from "@/data/models/categoryModel"; import { CategoryModel } from "@/data/models/categoryModel";
import { ProductModel } from "@/data/models/productModel"; import { ProductModel } from "@/data/models/productModel";
import { ProductWithCategoryModel } from "@/data/models/productWithCategoryModel";
export function calcProductPrice(product: ProductWithCategoryModel, quantity: number = 1): number { export function calcProductPrice(product: ProductModel, quantity: number = 1): number {
return calcPrice(product.price, product.discount, quantity) return calcPrice(product.price, product.discount, quantity)
} }
@@ -29,7 +28,7 @@ export function calcPrice(price: number, discount: number = 0, quantity: number
* *
* @returns BasketItemModel * @returns BasketItemModel
*/ */
export function productToBasketItem(product: ProductWithCategoryModel, quantity: number): BasketItemModel { export function productToBasketItem(product: ProductModel, quantity: number): BasketItemModel {
let result = new BasketItemModel() let result = new BasketItemModel()
result.productId = product.id result.productId = product.id