Merge branch 'development'
This commit is contained in:
commit
7525fee573
47 changed files with 1928 additions and 718 deletions
160
Document/DFD.drawio
Normal file
160
Document/DFD.drawio
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<mxfile host="app.diagrams.net" modified="2023-12-06T08:01:38.095Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" etag="_eur9a1JThKBMt8wZCV-" version="22.1.5" type="github">
|
||||
<diagram name="Page-1" id="3qw3VTkeZNw5AftXRyRj">
|
||||
<mxGraphModel dx="875" dy="484" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-2" value="0" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=20;fillColor=#ffffff;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;swimlaneFillColor=#ffffff;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="305" y="70" width="190" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-4" value="EDM" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="caRcK8bNx-rz16oAYpbI-2">
|
||||
<mxGeometry y="20" width="190" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-3" value="&nbsp;D1<span style="white-space: pre;">	</span><span style="white-space: pre;">	</span>File" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.dataStoreID;align=left;spacingLeft=3;points=[[0,0],[0.5,0],[1,0],[0,0.5],[1,0.5],[0,1],[0.5,1],[1,1]];" vertex="1" parent="1">
|
||||
<mxGeometry x="631" y="105" width="160" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-6" value="Admin" style="html=1;dashed=0;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="36" y="95" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-7" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">path<br style="border-color: var(--border-color);">file" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="-0.1243" y="45" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="136" y="105" as="sourcePoint" />
|
||||
<mxPoint x="305" y="105" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-11" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry x="-0.1243" y="-60" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="136" y="130" as="sourcePoint" />
|
||||
<mxPoint x="305" y="130" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-12" value="User" style="html=1;dashed=0;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="351" y="270" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-15" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">path<br style="border-color: var(--border-color);">file" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="-0.0424" y="50" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="495" y="110.14" as="sourcePoint" />
|
||||
<mxPoint x="631" y="109.83" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-16" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry x="0.1009" y="-60" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="495" y="130.31" as="sourcePoint" />
|
||||
<mxPoint x="631" y="130" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-17" value="search term" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="0.0059" y="40" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="391.6871428571429" y="270" as="sourcePoint" />
|
||||
<mxPoint x="391.00142857142856" y="150" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-18" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;labelBackgroundColor=none;startArrow=classic;startFill=1;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry y="-40" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="411.6871428571429" y="270" as="sourcePoint" />
|
||||
<mxPoint x="411.00142857142856" y="150" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-19" value="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=20;fillColor=#ffffff;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;swimlaneFillColor=#ffffff;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="319" y="440" width="190" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-20" value="Manage" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="caRcK8bNx-rz16oAYpbI-19">
|
||||
<mxGeometry y="20" width="190" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-21" value="&nbsp;D1<span style="white-space: pre;">	</span><span style="white-space: pre;">	</span>File" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.dataStoreID;align=left;spacingLeft=3;points=[[0,0],[0.5,0],[1,0],[0,0.5],[1,0.5],[0,1],[0.5,1],[1,1]];" vertex="1" parent="1">
|
||||
<mxGeometry x="631" y="465" width="160" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-22" value="pathname<br>title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry x="0.0057" y="-60" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="509" y="490.31" as="sourcePoint" />
|
||||
<mxPoint x="630" y="490" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-23" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">path<br style="border-color: var(--border-color);">file" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="0.0022" y="50" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="510" y="470.31" as="sourcePoint" />
|
||||
<mxPoint x="630" y="470" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-25" value="Admin" style="html=1;dashed=0;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="50" y="455" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-26" value="pathname<br>title<br>description<br>keyword<br>category<br>path<br>file" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="-0.0533" y="49" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="150" y="469.17" as="sourcePoint" />
|
||||
<mxPoint x="319" y="469.17" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-27" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry x="-0.0533" y="-56" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="150" y="494.17" as="sourcePoint" />
|
||||
<mxPoint x="319" y="494.17" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-30" value="2" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=20;fillColor=#ffffff;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;swimlaneFillColor=#ffffff;verticalAlign=middle;" vertex="1" parent="1">
|
||||
<mxGeometry x="319" y="610" width="190" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-31" value="Search" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="caRcK8bNx-rz16oAYpbI-30">
|
||||
<mxGeometry y="20" width="190" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-32" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="caRcK8bNx-rz16oAYpbI-31" target="caRcK8bNx-rz16oAYpbI-21">
|
||||
<mxGeometry x="0.59" y="-29" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="510" y="660.31" as="sourcePoint" />
|
||||
<mxPoint x="631" y="660" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
<Array as="points">
|
||||
<mxPoint x="710" y="660" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-33" value="User" style="html=1;dashed=0;whiteSpace=wrap;" vertex="1" parent="1">
|
||||
<mxGeometry x="364" y="810" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-34" value="search term" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="caRcK8bNx-rz16oAYpbI-25">
|
||||
<mxGeometry x="0.1497" y="10" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="100" y="510" as="sourcePoint" />
|
||||
<mxPoint x="319" y="635" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
<Array as="points">
|
||||
<mxPoint x="120" y="635" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-37" value="search term" style="endArrow=classic;html=1;rounded=0;labelBackgroundColor=none;" edge="1" parent="1">
|
||||
<mxGeometry x="0.0059" y="40" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="399.71" y="810" as="sourcePoint" />
|
||||
<mxPoint x="399.71" y="690" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-38" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;" edge="1" parent="1">
|
||||
<mxGeometry y="-29" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="430" y="810" as="sourcePoint" />
|
||||
<mxPoint x="430" y="690" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="caRcK8bNx-rz16oAYpbI-39" value="pathname<br style="border-color: var(--border-color);">title<br style="border-color: var(--border-color);">description<br style="border-color: var(--border-color);">keyword<br style="border-color: var(--border-color);">category<br style="border-color: var(--border-color);">fileName<br style="border-color: var(--border-color);">fileType<br style="border-color: var(--border-color);">fileSize" style="endArrow=none;html=1;rounded=0;startArrow=classic;startFill=1;labelBackgroundColor=none;endFill=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1" source="caRcK8bNx-rz16oAYpbI-25" target="caRcK8bNx-rz16oAYpbI-31">
|
||||
<mxGeometry x="-0.5455" y="-30" width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="120" y="690" as="sourcePoint" />
|
||||
<mxPoint x="450" y="700" as="targetPoint" />
|
||||
<mxPoint as="offset" />
|
||||
<Array as="points">
|
||||
<mxPoint x="100" y="660" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
|
|
@ -20,9 +20,11 @@
|
|||
"@tsconfig/node18": "^18.2.2",
|
||||
"axios": "^1.6.2",
|
||||
"keycloak-js": "^23.0.0",
|
||||
"mime": "^4.0.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"pinia": "^2.1.7",
|
||||
"quasar": "^2.14.0",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"vite-plugin-pwa": "^0.17.2",
|
||||
"vue": "^3.3.9",
|
||||
"vue-router": "^4.2.5"
|
||||
|
|
@ -32,6 +34,7 @@
|
|||
"@quasar/vite-plugin": "^1.6.0",
|
||||
"@rushstack/eslint-patch": "^1.6.0",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^20.10.0",
|
||||
"@vitejs/plugin-vue": "^4.5.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
|
|
|
|||
84
Services/client/pnpm-lock.yaml
generated
84
Services/client/pnpm-lock.yaml
generated
|
|
@ -17,6 +17,9 @@ dependencies:
|
|||
keycloak-js:
|
||||
specifier: ^23.0.0
|
||||
version: 23.0.0
|
||||
mime:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
nanoid:
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.4
|
||||
|
|
@ -26,6 +29,9 @@ dependencies:
|
|||
quasar:
|
||||
specifier: ^2.14.0
|
||||
version: 2.14.0
|
||||
socket.io-client:
|
||||
specifier: ^4.7.2
|
||||
version: 4.7.2
|
||||
vite-plugin-pwa:
|
||||
specifier: ^0.17.2
|
||||
version: 0.17.2(vite@5.0.2)(workbox-build@7.0.0)(workbox-window@7.0.0)
|
||||
|
|
@ -49,6 +55,9 @@ devDependencies:
|
|||
'@types/jsdom':
|
||||
specifier: ^21.1.6
|
||||
version: 21.1.6
|
||||
'@types/mime-types':
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4
|
||||
'@types/node':
|
||||
specifier: ^20.10.0
|
||||
version: 20.10.0
|
||||
|
|
@ -1801,6 +1810,10 @@ packages:
|
|||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
dev: true
|
||||
|
||||
/@socket.io/component-emitter@3.1.0:
|
||||
resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
|
||||
dev: false
|
||||
|
||||
/@surma/rollup-plugin-off-main-thread@2.2.3:
|
||||
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
|
||||
dependencies:
|
||||
|
|
@ -1840,6 +1853,10 @@ packages:
|
|||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
dev: true
|
||||
|
||||
/@types/mime-types@2.1.4:
|
||||
resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
|
||||
dev: true
|
||||
|
||||
/@types/node@20.10.0:
|
||||
resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==}
|
||||
dependencies:
|
||||
|
|
@ -2823,6 +2840,25 @@ packages:
|
|||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
dev: true
|
||||
|
||||
/engine.io-client@6.5.3:
|
||||
resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==}
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.0
|
||||
debug: 4.3.4
|
||||
engine.io-parser: 5.2.1
|
||||
ws: 8.11.0
|
||||
xmlhttprequest-ssl: 2.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/engine.io-parser@5.2.1:
|
||||
resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dev: false
|
||||
|
||||
/entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
|
@ -3967,6 +4003,12 @@ packages:
|
|||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
/mime@4.0.0:
|
||||
resolution: {integrity: sha512-pzhgdeqU5pJ9t5WK9m4RT4GgGWqYJylxUf62Yb9datXRwdcw5MjiD1BYI5evF8AgTXN9gtKX3CFLvCUL5fAhEA==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -4709,6 +4751,30 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/socket.io-client@4.7.2:
|
||||
resolution: {integrity: sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.0
|
||||
debug: 4.3.4
|
||||
engine.io-client: 6.5.3
|
||||
socket.io-parser: 4.2.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/socket.io-parser@4.2.4:
|
||||
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.0
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -5676,6 +5742,19 @@ packages:
|
|||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
/ws@8.11.0:
|
||||
resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: ^5.0.2
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/ws@8.14.2:
|
||||
resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
|
@ -5703,6 +5782,11 @@ packages:
|
|||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||
dev: true
|
||||
|
||||
/xmlhttprequest-ssl@2.0.0:
|
||||
resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
|
|
|
|||
BIN
Services/client/src/assets/logo-edm.png
Normal file
BIN
Services/client/src/assets/logo-edm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
|
|
@ -21,15 +21,22 @@ const props = withDefaults(
|
|||
>
|
||||
<q-card style="width: 400px">
|
||||
<q-card-section>
|
||||
<span class="text-h6">
|
||||
<q-icon name="error" color="negative" size="2.5rem" />แจ้งเตือนการลบ
|
||||
</span>
|
||||
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||
<div class="q-pa-sm">
|
||||
<div style="border-radius: 50%" class="bg-secondary q-pa-sm">
|
||||
<q-icon
|
||||
name="mdi-trash-can-outline"
|
||||
color="negative"
|
||||
size="2.5rem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="q-my-none">ยืนยันการลบข้อมูล</h6>
|
||||
<p class="q-my-none">ต้องการยืนยันการลบข้อมูลนี้หรือไม่</p>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
ถ้าดำเนินการต่อจะทำการลบ
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-primary">
|
||||
<q-space />
|
||||
<q-btn
|
||||
|
|
@ -41,10 +48,9 @@ const props = withDefaults(
|
|||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
color="negative"
|
||||
v-close-popup
|
||||
label="ลบ"
|
||||
class="text-red"
|
||||
@click="() => $emit('confirm')"
|
||||
id="dialogDeleteConfirm"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -34,9 +34,14 @@ const emit = defineEmits([
|
|||
'update:keyword',
|
||||
'update:category',
|
||||
'filechange',
|
||||
'reset',
|
||||
'submit',
|
||||
])
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
})
|
||||
|
||||
function keydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && props.open === true) {
|
||||
emit('update:open', false)
|
||||
|
|
@ -50,6 +55,7 @@ function reset() {
|
|||
emit('update:description', '')
|
||||
emit('update:keyword', '')
|
||||
emit('update:category', '')
|
||||
emit('reset')
|
||||
}
|
||||
|
||||
function submit() {
|
||||
|
|
@ -61,7 +67,6 @@ function submit() {
|
|||
keyword: props.keyword,
|
||||
category: props.category,
|
||||
})
|
||||
emit('update:open', !open), reset()
|
||||
}
|
||||
|
||||
const createKeyword = ((val, done) => {
|
||||
|
|
@ -106,12 +111,13 @@ const file = ref<File | undefined>()
|
|||
class="q-pa-md"
|
||||
side="right"
|
||||
tabindex="0"
|
||||
v-click-outside="() => $emit('update:open', false)"
|
||||
:width="300"
|
||||
:breakpoint="500"
|
||||
:model-value="open"
|
||||
@update:model-value="(v) => $emit('update:open', v)"
|
||||
>
|
||||
<q-form @submit.prevent="submit">
|
||||
<q-form @submit.prevent="submit" v-if="open">
|
||||
<q-toolbar class="q-mb-md q-pa-none">
|
||||
<q-toolbar-title>
|
||||
<span class="text-weight-bold" v-if="mode === 'create'">
|
||||
|
|
|
|||
|
|
@ -1,20 +1,45 @@
|
|||
<script setup lang="ts">
|
||||
import { useFileInfoStore } from '@/stores/file-info-data'
|
||||
import mime from 'mime'
|
||||
|
||||
const { mimeFileMapping } = useFileInfoStore()
|
||||
|
||||
defineProps<{ fileMimeType: string | undefined; size: string }>()
|
||||
defineProps<{
|
||||
fileMimeType: string | undefined
|
||||
fileName: string | undefined
|
||||
size: string
|
||||
}>()
|
||||
|
||||
function getIcon(mimeType: string) {
|
||||
return mimeType && mimeFileMapping.hasOwnProperty(mimeType)
|
||||
? mimeFileMapping[mimeType].icon
|
||||
: 'mdi-file-question-outline'
|
||||
function getIcon(mimeType: string | undefined, fileName: string | undefined) {
|
||||
if (mimeType === undefined) {
|
||||
return 'mdi-file-question-outline'
|
||||
}
|
||||
|
||||
const extFomMime = mime.getExtension(mimeType)
|
||||
if (extFomMime) {
|
||||
return mimeFileMapping[mimeType].icon
|
||||
}
|
||||
if (fileName && fileName.includes('.')) {
|
||||
return 'mdi-file-outline'
|
||||
}
|
||||
return 'mdi-file-question-outline'
|
||||
}
|
||||
function getColor(mimeType: string) {
|
||||
return mimeType && mimeFileMapping.hasOwnProperty(mimeType)
|
||||
? mimeFileMapping[mimeType].color
|
||||
: 'blue-11'
|
||||
|
||||
function getColor(mimeType: string | undefined, fileName: string | undefined) {
|
||||
if (mimeType === undefined) {
|
||||
return 'grey-5'
|
||||
}
|
||||
|
||||
const extFomMime = mime.getExtension(mimeType)
|
||||
if (extFomMime) {
|
||||
return mimeFileMapping[mimeType].color
|
||||
}
|
||||
if (fileName && fileName.includes('.')) {
|
||||
return 'blue-11'
|
||||
}
|
||||
return 'grey-5'
|
||||
}
|
||||
|
||||
function getIconSize(s: string) {
|
||||
type SizeMapping = {
|
||||
[key: string]: string
|
||||
|
|
@ -29,8 +54,8 @@ function getIconSize(s: string) {
|
|||
|
||||
<template>
|
||||
<q-icon
|
||||
:name="fileMimeType && getIcon(fileMimeType)"
|
||||
:color="fileMimeType && getColor(fileMimeType)"
|
||||
:name="fileMimeType && getIcon(fileMimeType, fileName)"
|
||||
:color="fileMimeType && getColor(fileMimeType, fileName)"
|
||||
:size="getIconSize(size)"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const {
|
|||
updateFile,
|
||||
deleteFile,
|
||||
checkFile,
|
||||
checkFileName,
|
||||
refaceFile,
|
||||
} = useTreeDataStore()
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ const currentIcon = computed(() =>
|
|||
currentDept.value === 0
|
||||
? 'mdi-file-cabinet'
|
||||
: currentDept.value === 1
|
||||
? 'inbox'
|
||||
? 'o_inbox'
|
||||
: 'o_folder_open',
|
||||
)
|
||||
|
||||
|
|
@ -61,8 +62,9 @@ const fileFormData = ref<{
|
|||
category?: string[]
|
||||
}>({})
|
||||
const fileFormType = ref<'edit' | 'create'>('create')
|
||||
const fileFormError = ref<{ fileExist?: boolean }>({})
|
||||
const fileFormError = ref<{ fileExist?: boolean; fileName2Long?: boolean }>({})
|
||||
const fileExistNotification = ref<boolean>(false)
|
||||
const fileFormComponent = ref<InstanceType<typeof FileForm>>()
|
||||
|
||||
function triggerFolderDelete(pathname: string) {
|
||||
deleteFormType.value = 'deleteFolder'
|
||||
|
|
@ -175,18 +177,31 @@ async function submitFileForm(
|
|||
fileFormData.value = {}
|
||||
fileFormState.value = false
|
||||
currentParam.value = undefined
|
||||
fileFormComponent.value?.reset()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-h6 q-mt-md" v-if="currentDept > 2">
|
||||
{{ DEPT_NAME[currentDept] }}
|
||||
<div class="flex justify-between items-center">
|
||||
<div>{{ DEPT_NAME[currentDept] }}</div>
|
||||
<q-btn
|
||||
v-if="currentDept != 4 && action"
|
||||
outline
|
||||
push
|
||||
class="q-px-md q-ml-md"
|
||||
:label="'สร้าง' + DEPT_NAME[currentDept]"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click.stop="() => triggerFolderCreate()"
|
||||
id="listViewFolderCreate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid q-mt-md">
|
||||
<div
|
||||
v-for="(value, index) in currentFolder"
|
||||
:data-pathname="value.pathname"
|
||||
>
|
||||
<div v-for="value in currentFolder" :data-pathname="value.pathname">
|
||||
<div
|
||||
:style="{
|
||||
position: 'relative',
|
||||
|
|
@ -246,7 +261,7 @@ async function submitFileForm(
|
|||
alignItems: 'center',
|
||||
padding: currentDept > 2 ? '.5rem 0' : '.5rem',
|
||||
}"
|
||||
@click="() => triggerFolderCreate()"
|
||||
@click.stop="() => triggerFolderCreate()"
|
||||
id="triggerFolderCreateFileItem"
|
||||
>
|
||||
<div
|
||||
|
|
@ -265,7 +280,7 @@ async function submitFileForm(
|
|||
color="white"
|
||||
size="10px"
|
||||
style="position: absolute; bottom: 0"
|
||||
:style="{ right: currentDept > 2 ? '.5rem' : '1.75rem' }"
|
||||
:style="{ right: currentDept > 2 ? '.5rem' : '1rem' }"
|
||||
>
|
||||
<q-icon name="add" color="primary" size="1.5rem" />
|
||||
</q-btn>
|
||||
|
|
@ -278,17 +293,33 @@ async function submitFileForm(
|
|||
}"
|
||||
style="max-width: 100%"
|
||||
>
|
||||
สร้าง{{ DEPT_NAME[currentDept] }}ใหม่
|
||||
สร้าง{{ DEPT_NAME[currentDept] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="grid-column: 1 / span 5"
|
||||
class="text-h6 q-mt-md"
|
||||
v-if="currentDept > 2"
|
||||
>
|
||||
เอกสาร
|
||||
<div style="grid-column: 1 / span 5" class="q-mt-md" v-if="currentDept > 2">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<span class="text-h6 q-mr-md">เอกสาร</span>
|
||||
<span class="text-body text-grey"
|
||||
>จำนวน {{ currentFile.length }} รายการ</span
|
||||
>
|
||||
</div>
|
||||
<q-btn
|
||||
outline
|
||||
push
|
||||
v-if="action"
|
||||
class="q-px-md q-ml-md"
|
||||
label="สร้างเอกสาร"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click.stop="() => triggerFileCreate()"
|
||||
id="listViewFileCreate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid q-mt-md">
|
||||
<div v-for="(value, index) in currentFile" :data-pathname="value.pathname">
|
||||
|
|
@ -309,7 +340,8 @@ async function submitFileForm(
|
|||
<div class="q-px-md flex items-center justify-center">
|
||||
<file-icon
|
||||
size="preview"
|
||||
:fileMimeType="value.fileType ? value.fileType : 'unknow'"
|
||||
:fileMimeType="value.fileType ? value.fileType : undefined"
|
||||
:fileName="value.fileName ? value.fileName : undefined"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -353,7 +385,7 @@ async function submitFileForm(
|
|||
maxWidth: '100%',
|
||||
}"
|
||||
class="dashed"
|
||||
@click="() => triggerFileCreate()"
|
||||
@click.stop="() => triggerFileCreate()"
|
||||
id="triggerFileCreateFileItem"
|
||||
>
|
||||
<div
|
||||
|
|
@ -365,7 +397,7 @@ async function submitFileForm(
|
|||
round
|
||||
color="white"
|
||||
size="10px"
|
||||
style="position: absolute; right: 1.75rem; bottom: 0"
|
||||
style="position: absolute; right: 1rem; bottom: 0"
|
||||
>
|
||||
<q-icon name="add" color="primary" size="1.5rem" />
|
||||
</q-btn>
|
||||
|
|
@ -374,13 +406,14 @@ async function submitFileForm(
|
|||
class="text-overflow-handle block q-px-md text-center"
|
||||
style="max-width: 100%"
|
||||
>
|
||||
สร้างไฟล์ใหม่
|
||||
สร้างเอกสาร
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<file-form
|
||||
ref="fileFormComponent"
|
||||
:mode="fileFormType"
|
||||
:error="fileFormError"
|
||||
v-model:open="fileFormState"
|
||||
|
|
@ -388,7 +421,13 @@ async function submitFileForm(
|
|||
v-model:description="fileFormData.description"
|
||||
v-model:keyword="fileFormData.keyword"
|
||||
v-model:category="fileFormData.category"
|
||||
@filechange="(name: string) => (fileFormError.fileExist = checkFile(name))"
|
||||
@reset="() => (fileFormError = {})"
|
||||
@filechange="
|
||||
(name: string) => (
|
||||
(fileFormError.fileExist = checkFile(name)),
|
||||
(fileFormError.fileName2Long = checkFileName(name))
|
||||
)
|
||||
"
|
||||
@submit="submitFileForm"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,56 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineEmits(['edit', 'delete'])
|
||||
|
||||
const props =
|
||||
defineProps<{
|
||||
nameId: string
|
||||
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
nameId: string
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-btn @click.stop icon="more_vert" color="grey" flat dense :data-testid="`action${props.nameId}`">
|
||||
<q-menu auto-close>
|
||||
<q-btn
|
||||
@click.stop
|
||||
icon="more_vert"
|
||||
color="grey"
|
||||
flat
|
||||
dense
|
||||
:data-testid="`action${props.nameId}`"
|
||||
>
|
||||
<q-menu v-model="open">
|
||||
<q-list dense>
|
||||
<q-item clickable @click="() => $emit('edit')" id="FileltemActionEdit">
|
||||
<q-item
|
||||
clickable
|
||||
id="FileltemActionEdit"
|
||||
@click.stop="
|
||||
() => {
|
||||
open = false
|
||||
$emit('edit')
|
||||
}
|
||||
"
|
||||
>
|
||||
<q-item-section>
|
||||
<div class="row items-center white">
|
||||
<q-icon name="edit" color="positive" />
|
||||
<q-icon name="o_edit" color="positive" />
|
||||
<span class="q-ml-sm">แก้ไข</span>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
clickable
|
||||
@click="() => $emit('delete')"
|
||||
@click.stop="
|
||||
() => {
|
||||
open = false
|
||||
$emit('delete')
|
||||
}
|
||||
"
|
||||
id="FileltemActiondelete"
|
||||
>
|
||||
<q-item-section>
|
||||
<div class="row items-center white">
|
||||
<q-icon name="delete" color="negative" />
|
||||
<q-icon name="mdi-trash-can-outline" color="negative" />
|
||||
<span class="q-ml-sm">ลบ</span>
|
||||
</div>
|
||||
</q-item-section>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ function confirmDelete() {
|
|||
|
||||
setTimeout(() => {
|
||||
isActFoundFile.value = true
|
||||
}, 300)
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +252,8 @@ onMounted(() => {
|
|||
<div class="q-px-md flex items-center justify-center">
|
||||
<file-icon
|
||||
size="preview"
|
||||
:fileMimeType="value.fileType ? value.fileType : 'unknow'"
|
||||
:fileName="value?.fileName ? value.fileType : undefined"
|
||||
:fileMimeType="value?.fileType ? value.fileType : undefined"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -298,13 +299,18 @@ onMounted(() => {
|
|||
:rows="filterFoundFile"
|
||||
:columns="columns"
|
||||
row-key="name"
|
||||
hide-bottom
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{
|
||||
rowsPerPage: 0,
|
||||
}"
|
||||
class="cursor"
|
||||
>
|
||||
<template v-slot:body-cell-name="nameData">
|
||||
<q-td style="width: 50%" @click="() => getFileInfo(nameData.row)">
|
||||
<file-icon size="list" :fileMimeType="nameData.row.fileType" />
|
||||
<file-icon
|
||||
size="list"
|
||||
:fileMimeType="nameData.row.fileType"
|
||||
:fileName="nameData.row.fileName"
|
||||
/>
|
||||
{{ nameData.row.fileName }}
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
@ -312,7 +318,7 @@ onMounted(() => {
|
|||
<template v-slot:body-cell-fileType="typeData">
|
||||
<q-td>
|
||||
<div class="justify-center">
|
||||
{{ getType(typeData.row.fileType) }}
|
||||
{{ getType(typeData.row.fileType, typeData.row.fileName) }}
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
@ -320,7 +326,7 @@ onMounted(() => {
|
|||
<template v-slot:body-cell-actions="actionData">
|
||||
<q-td class="justify-center">
|
||||
<div>
|
||||
<q-icon class="q-ma-sm" name="info" size="2em" color="primary" />
|
||||
<q-icon class="q-ma-sm" name="o_info" size="2em" color="primary" />
|
||||
<q-tooltip
|
||||
anchor="center left"
|
||||
self="center right"
|
||||
|
|
@ -334,7 +340,7 @@ onMounted(() => {
|
|||
flat
|
||||
color="positive"
|
||||
dense
|
||||
icon="edit"
|
||||
icon="o_edit"
|
||||
@click="
|
||||
() => triggerFileEdit(actionData.row, actionData.row.pathname)
|
||||
"
|
||||
|
|
@ -344,7 +350,7 @@ onMounted(() => {
|
|||
flat
|
||||
color="negative"
|
||||
dense
|
||||
icon="delete"
|
||||
icon="mdi-trash-can-outline"
|
||||
@click="() => triggerFileDelete(actionData.row.pathname)"
|
||||
id="listViewFileDelete"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -14,13 +14,16 @@ const props = withDefaults(
|
|||
)
|
||||
|
||||
const emit = defineEmits(['update:open', 'update:name', 'submit'])
|
||||
const offensiveWord = ref<boolean>(false)
|
||||
const offensiveWord = ref<boolean>()
|
||||
const errorMessage = ref<string>('')
|
||||
const inputNull = ref<boolean>(false)
|
||||
|
||||
function checkOffensiveWord(input: string) {
|
||||
return /[\\?%:|"<>#]/.test(input)
|
||||
}
|
||||
|
||||
function reset() {
|
||||
offensiveWord.value = undefined
|
||||
emit('update:name', undefined)
|
||||
}
|
||||
|
||||
|
|
@ -34,8 +37,9 @@ function keydown(e: KeyboardEvent) {
|
|||
function submit() {
|
||||
emit('submit', {
|
||||
mode: props.mode,
|
||||
name: props.name,
|
||||
name: props.name?.trim(),
|
||||
})
|
||||
|
||||
emit('update:open', !open), reset()
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +54,19 @@ onUnmounted(() => window.addEventListener('keydown', keydown))
|
|||
class="q-pa-md"
|
||||
side="right"
|
||||
tabindex="0"
|
||||
v-click-outside="() => $emit('update:open', false)"
|
||||
:width="300"
|
||||
:breakpoint="500"
|
||||
:model-value="open"
|
||||
@update:model-value="(v) => $emit('update:open', v)"
|
||||
>
|
||||
<q-form @submit.prevent="submit">
|
||||
<q-form @submit.prevent="submit" v-if="open">
|
||||
<q-toolbar class="q-mb-md q-pa-none">
|
||||
<q-toolbar-title>
|
||||
<span class="text-weight-bold" v-if="mode === 'create'">
|
||||
<span class="text-weight-bold text-primary" v-if="mode === 'create'">
|
||||
สร้าง{{ tree }}
|
||||
</span>
|
||||
<span class="text-weight-bold" v-if="mode === 'edit'">
|
||||
<span class="text-weight-bold text-primary" v-if="mode === 'edit'">
|
||||
แก้ไข{{ tree }}
|
||||
</span>
|
||||
</q-toolbar-title>
|
||||
|
|
@ -74,7 +79,6 @@ onUnmounted(() => window.addEventListener('keydown', keydown))
|
|||
color="red"
|
||||
@click="
|
||||
() => {
|
||||
offensiveWord = false
|
||||
$emit('update:open', !open)
|
||||
}
|
||||
"
|
||||
|
|
@ -90,9 +94,14 @@ onUnmounted(() => window.addEventListener('keydown', keydown))
|
|||
dense
|
||||
class="q-my-sm"
|
||||
placeholder="กรอกชื่อ"
|
||||
:model-value="name"
|
||||
error-message="คำต้องห้านจะเปลี่ยนเป็น - เมื่อกดสร้าง"
|
||||
:model-value="props.name"
|
||||
:error-message="
|
||||
!!props.name
|
||||
? 'คำต้องห้านจะเปลี่ยนเป็น - เมื่อกดสร้าง'
|
||||
: 'โปรดกรอกข้อมูล'
|
||||
"
|
||||
:error="offensiveWord"
|
||||
:rules="[(v) => !!props.name]"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
$emit('update:name', v)
|
||||
|
|
@ -112,7 +121,6 @@ onUnmounted(() => window.addEventListener('keydown', keydown))
|
|||
flat
|
||||
@click="
|
||||
() => {
|
||||
offensiveWord = false
|
||||
$emit('update:open', false), reset()
|
||||
}
|
||||
"
|
||||
|
|
|
|||
|
|
@ -16,15 +16,19 @@ watch(visible, () => {
|
|||
<q-dialog transition-show="scale" transition-hide="scale" v-model="visible">
|
||||
<q-card style="width: 400px">
|
||||
<q-card-section>
|
||||
<span class="text-h6">
|
||||
<q-icon name="error" color="negative" size="2.5rem" />{{
|
||||
title
|
||||
}}</span
|
||||
>
|
||||
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||
<div class="q-pa-sm">
|
||||
<div style="border-radius: 50%" class="bg-secondary q-pa-sm">
|
||||
<q-icon name="mdi-alert" color="negative" size="2.5rem" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="q-my-none">{{ title }}</h6>
|
||||
<p class="q-my-none">{{ msg }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">{{ msg }}</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-primary">
|
||||
<q-space />
|
||||
<q-btn
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ const fileFormData = ref<{
|
|||
category?: string[]
|
||||
}>({})
|
||||
const fileFormType = ref<'edit' | 'create'>('create')
|
||||
const fileFormComponent = ref<InstanceType<typeof FileForm>>()
|
||||
|
||||
const dialogDeleteState = ref<boolean>(false)
|
||||
const deleteFormPath = ref<string>('')
|
||||
|
|
@ -176,6 +177,7 @@ async function submitFileForm(
|
|||
fileFormData.value = {}
|
||||
fileFormState.value = false
|
||||
currentParam.value = undefined
|
||||
fileFormComponent.value?.reset()
|
||||
}
|
||||
|
||||
const columnsFolder: QTableProps['columns'] = [
|
||||
|
|
@ -195,7 +197,6 @@ const columnsFolder: QTableProps['columns'] = [
|
|||
label: 'สร้างโดย',
|
||||
field: 'createdBy',
|
||||
style: 'width: 20px',
|
||||
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
|
|
@ -271,13 +272,13 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
<q-btn
|
||||
outline
|
||||
push
|
||||
class="q-px-md q-ml-md q-py-sm"
|
||||
class="q-px-md q-ml-md"
|
||||
:label="'สร้าง' + currentLevel"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click="() => triggerFolderCreate()"
|
||||
@click.stop="() => triggerFolderCreate()"
|
||||
id="listViewFolderCreate"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -288,10 +289,11 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
bordered
|
||||
:rows="listDataFolder"
|
||||
:columns="columnsFolder"
|
||||
row-key="name"
|
||||
hide-bottom
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{
|
||||
rowsPerPage: 20,
|
||||
}"
|
||||
@row-click="onRowClick"
|
||||
row-key="name"
|
||||
class="cursor"
|
||||
v-if="currentDept != 4"
|
||||
>
|
||||
|
|
@ -301,6 +303,13 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
{{ nameRow.row.name }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-createdBy="createdByRow">
|
||||
<q-td>
|
||||
<div class="justify-center center-content">
|
||||
{{ createdByRow.row.createdBy }}
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-createdAt="createdAtRow">
|
||||
<q-td>
|
||||
<div class="justify-center">
|
||||
|
|
@ -311,7 +320,13 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
<template v-slot:body-cell-actions="actionsRow">
|
||||
<q-td class="justify-center">
|
||||
<div>
|
||||
<q-icon class="q-ma-sm" name="info" size="2em" color="primary" />
|
||||
<q-icon
|
||||
@click.stop
|
||||
class="q-ma-sm"
|
||||
name="o_info"
|
||||
size="2em"
|
||||
color="primary"
|
||||
/>
|
||||
<q-tooltip
|
||||
anchor="center left"
|
||||
self="center right"
|
||||
|
|
@ -325,7 +340,7 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
flat
|
||||
color="positive"
|
||||
dense
|
||||
icon="edit"
|
||||
icon="o_edit"
|
||||
@click.stop="
|
||||
triggerFolderEdit(
|
||||
actionsRow.row.name,
|
||||
|
|
@ -334,11 +349,13 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
"
|
||||
id="listViewFolderEdit"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
color="negative"
|
||||
dense
|
||||
icon="delete"
|
||||
:data-testid="actionsRow.row.name"
|
||||
icon="mdi-trash-can-outline"
|
||||
@click.stop="triggerFolderDelete(actionsRow.row.pathname)"
|
||||
id="listViewFolderDelete"
|
||||
/>
|
||||
|
|
@ -360,13 +377,13 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
v-if="props.mode == 'admin'"
|
||||
outline
|
||||
push
|
||||
class="q-px-md q-ml-md q-py-sm"
|
||||
class="q-px-md q-ml-md"
|
||||
label="สร้างเอกสาร"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click="() => triggerFileCreate()"
|
||||
@click.stop="() => triggerFileCreate()"
|
||||
id="listViewFileCreate"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -377,8 +394,9 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
:rows="listDataFile"
|
||||
:columns="columnsFile"
|
||||
row-key="name"
|
||||
hide-bottom
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{
|
||||
rowsPerPage: 20,
|
||||
}"
|
||||
class="cursor"
|
||||
>
|
||||
<template v-slot:body-cell-name="nameRow">
|
||||
|
|
@ -393,7 +411,7 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
"
|
||||
id="listViewGetFileInfo"
|
||||
>
|
||||
<file-icon size="list" :fileMimeType="nameRow.row.fileType" />
|
||||
<file-icon size="list" :fileMimeType="nameRow.row.fileType" :fileName="nameRow.row.fileName" />
|
||||
{{ nameRow.row.fileName }}
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
@ -401,7 +419,7 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
<template v-slot:body-cell-fileType="fileTypeRow">
|
||||
<q-td>
|
||||
<div class="justify-center">
|
||||
{{ getType(fileTypeRow.row.fileType) }}
|
||||
{{ getType(fileTypeRow.row.fileType, fileTypeRow.row.fileName) }}
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
@ -409,7 +427,12 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
<template v-slot:body-cell-actions="actionsRow">
|
||||
<q-td class="justify-center">
|
||||
<div>
|
||||
<q-icon class="q-ma-sm" name="info" size="2em" color="primary" />
|
||||
<q-icon
|
||||
class="q-ma-sm"
|
||||
name="o_info"
|
||||
size="2em"
|
||||
color="primary"
|
||||
/>
|
||||
<q-tooltip
|
||||
anchor="center left"
|
||||
self="center right"
|
||||
|
|
@ -423,8 +446,8 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
flat
|
||||
color="positive"
|
||||
dense
|
||||
icon="edit"
|
||||
@click="
|
||||
icon="o_edit"
|
||||
@click.stop="
|
||||
() => triggerFileEdit(actionsRow.row, actionsRow.row.pathname)
|
||||
"
|
||||
id="listViewFileEdit"
|
||||
|
|
@ -433,8 +456,8 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
flat
|
||||
color="negative"
|
||||
dense
|
||||
icon="delete"
|
||||
@click="() => triggerFileDelete(actionsRow.row.pathname)"
|
||||
icon="mdi-trash-can-outline"
|
||||
@click.stop="() => triggerFileDelete(actionsRow.row.pathname)"
|
||||
id="listViewFileDelete"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -445,6 +468,7 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
</div>
|
||||
|
||||
<file-form
|
||||
ref="fileFormComponent"
|
||||
:mode="fileFormType"
|
||||
:error="fileFormError"
|
||||
v-model:open="fileFormState"
|
||||
|
|
@ -452,6 +476,7 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
v-model:description="fileFormData.description"
|
||||
v-model:keyword="fileFormData.keyword"
|
||||
v-model:category="fileFormData.category"
|
||||
@reset="() => (fileFormError = {})"
|
||||
@filechange="(name: string) => (fileFormError.fileExist = checkFile(name))"
|
||||
@submit="submitFileForm"
|
||||
/>
|
||||
|
|
@ -488,6 +513,10 @@ const onRowClick = (evt: Event, row: TreeDataFolder, index: number) => {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.center-content {
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,14 +48,20 @@ async function submitFolderForm(value: {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(getCabinet)
|
||||
onMounted(async () => {
|
||||
await getCabinet()
|
||||
|
||||
const sessionCurrentPath = sessionStorage.getItem('currentPath')
|
||||
|
||||
if (sessionCurrentPath) await getFolder(sessionCurrentPath)
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<section id="header" class="q-px-md q-pt-md q-pb-none">
|
||||
<div class="q-my-md row items-center">
|
||||
<div class="col">
|
||||
<h5 class="q-my-none" v-if="mode === 'admin'">จัดเก็บเอกสาร</h5>
|
||||
<h5 class="q-my-none" v-else>สืบค้นเอกสาร</h5>
|
||||
<h5 class="q-my-none" v-else>สืบค้นผลงาน</h5>
|
||||
</div>
|
||||
<search-bar :mode="mode" v-if="mode === 'admin'" />
|
||||
</div>
|
||||
|
|
@ -68,16 +74,27 @@ onMounted(getCabinet)
|
|||
<div class="col-12 col-md-3">
|
||||
<div class="bg-white rounded-borders shadow-5">
|
||||
<div
|
||||
class="q-px-md q-py-sm text-primary bg-grey-1 pointer"
|
||||
class="q-px-md q-py-sm text-primary bg-grey-1 pointer row justify-between items-center"
|
||||
id="container-header"
|
||||
@click="
|
||||
() => {
|
||||
currentPath = ''
|
||||
getFolder(currentPath)
|
||||
}
|
||||
"
|
||||
>
|
||||
<span class="block q-my-sm text-weight-bold">ตู้จัดเก็บเอกสาร</span>
|
||||
<span
|
||||
class="block q-my-sm text-weight-bold"
|
||||
@click="
|
||||
() => {
|
||||
currentPath = ''
|
||||
getFolder(currentPath)
|
||||
}
|
||||
"
|
||||
>ตู้จัดเก็บเอกสาร</span
|
||||
>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="add"
|
||||
@click.stop="() => triggerFolderCreate()"
|
||||
id="createFolder"
|
||||
/>
|
||||
</div>
|
||||
<q-separator />
|
||||
<div class="q-pa-md">
|
||||
|
|
@ -117,6 +134,21 @@ onMounted(getCabinet)
|
|||
v-if="currentPath === '/' || !currentPath"
|
||||
label="ตู้เอกสารทั้งหมด"
|
||||
/>
|
||||
<q-btn
|
||||
v-if="
|
||||
mode === 'admin' &&
|
||||
viewMode === 'view_module' &&
|
||||
currentDept === 0
|
||||
"
|
||||
class="q-px-md q-ml-md al"
|
||||
label="สร้างตู้เก็บเอกสาร"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click.stop="() => triggerFolderCreate()"
|
||||
id="createFolder"
|
||||
/>
|
||||
<q-breadcrumbs-el
|
||||
class="text-primary pointer"
|
||||
v-for="(fragments, index) in currentPath
|
||||
|
|
@ -138,22 +170,6 @@ onMounted(getCabinet)
|
|||
/>
|
||||
</q-breadcrumbs>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="
|
||||
mode === 'admin' &&
|
||||
viewMode === 'view_module' &&
|
||||
currentDept === 0
|
||||
"
|
||||
class="q-px-md q-ml-md al"
|
||||
label="สร้างตู้เก็บเอกสาร"
|
||||
type="submit"
|
||||
color="primary"
|
||||
dense
|
||||
icon="add"
|
||||
@click="() => triggerFolderCreate()"
|
||||
id="createFolder"
|
||||
/>
|
||||
</span>
|
||||
<div>
|
||||
<q-btn
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const accountName = ref<string>(getUsername())
|
|||
<template>
|
||||
<div
|
||||
@click="() => (dropdownOpen = !dropdownOpen)"
|
||||
class="row q-px-md cursor"
|
||||
class="row q-px-xs cursor"
|
||||
id="app-toolbar-title"
|
||||
>
|
||||
<div class="col">
|
||||
|
|
@ -17,14 +17,14 @@ const accountName = ref<string>(getUsername())
|
|||
</q-avatar>
|
||||
</div>
|
||||
|
||||
<div class="cow">
|
||||
<div>
|
||||
<div class="row q-pl-sm">
|
||||
<span class="text-body1">
|
||||
<span class="text-body1" style="font-size:13px">
|
||||
{{ accountName }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row q-pl-sm">
|
||||
<span class="text-caption text-grey"> เจ้าหน้าที่ </span>
|
||||
<span class="text-caption text-grey" style="font-size:10px"> เจ้าหน้าที่ </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,19 +15,23 @@ defineEmits(['update:notification', 'confirm', 'cancel'])
|
|||
>
|
||||
<q-card style="width: 400px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">
|
||||
<q-icon
|
||||
name="error"
|
||||
color="warning"
|
||||
size="2.5rem"
|
||||
/>เตือนพบไฟล์ชื่อซ้ำในระบบ
|
||||
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||
<div class="q-pa-sm">
|
||||
<div style="border-radius: 50%" class="bg-secondary q-pa-sm">
|
||||
<q-icon name="mdi-alert-outline" color="warning" size="2.5rem" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="q-my-none">ยืนยันการเพิ่มข้อมูล</h6>
|
||||
<p class="q-my-none">
|
||||
พบข้อมูลในระบบ หากดำเนินการต่อ
|
||||
ข้อมูลที่มีอยู่จะถูกแทนที่ด้วยข้อมูลใหม่
|
||||
ต้องการยืนยันการเพิ่มข้อมูลนี้หรือไม่
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
หากดำเนินการต่อข้อมูลจะถูกเขียนทับ
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-primary">
|
||||
<q-space />
|
||||
<q-btn
|
||||
|
|
@ -37,10 +41,9 @@ defineEmits(['update:notification', 'confirm', 'cancel'])
|
|||
@click="() => $emit('cancel')"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
label="ดำเนินการต่อ"
|
||||
v-close-popup
|
||||
class="text-red"
|
||||
color="warning"
|
||||
@click="() => $emit('confirm')"
|
||||
/>
|
||||
</q-card-actions>
|
||||
|
|
|
|||
15
Services/client/src/lib/directives.ts
Normal file
15
Services/client/src/lib/directives.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import type { ObjectDirective } from 'vue'
|
||||
|
||||
export const clickOutside = {
|
||||
beforeMount(element, binding) {
|
||||
element.clickOutsideEvent = (e: MouseEvent) => {
|
||||
if (!(element === e.target || element.contains(e.target))) {
|
||||
binding.value(e)
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', element.clickOutsideEvent)
|
||||
},
|
||||
unmounted(element) {
|
||||
document.removeEventListener('click', element.clickOutsideEvent)
|
||||
},
|
||||
} satisfies ObjectDirective
|
||||
|
|
@ -14,6 +14,11 @@ login().then(async () => {
|
|||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.directive(
|
||||
'click-outside',
|
||||
(await import('@/lib/directives')).clickOutside,
|
||||
)
|
||||
|
||||
app.use((await import('./router')).default)
|
||||
app.use(pinia)
|
||||
app.use(Quasar, {
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@ function clearAdvSearchData() {
|
|||
v-for="(item, index) in advSearchDataRow"
|
||||
:key="index"
|
||||
>
|
||||
<div class="row items-center" style="width: 45px; height: 45px">
|
||||
<div class="row content-center" style="width: 45px; height: 45px">
|
||||
<q-btn
|
||||
dense
|
||||
color="teal"
|
||||
color="teal-5"
|
||||
icon="mdi-plus"
|
||||
v-if="index === advSearchDataRow.length - 1"
|
||||
@click="addAdvSearchData"
|
||||
|
|
@ -135,6 +135,7 @@ function clearAdvSearchData() {
|
|||
@keydown.enter.prevent="searchSubmit()"
|
||||
><template v-slot:append>
|
||||
<q-icon
|
||||
v-if="item.value"
|
||||
name="close"
|
||||
@click="() => (item.value = '')"
|
||||
class="cursor-pointer"
|
||||
|
|
@ -142,25 +143,16 @@ function clearAdvSearchData() {
|
|||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div>
|
||||
<div class="row content-center" style="width: 45px; height: 45px">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
icon="mdi-trash-can-outline"
|
||||
v-if="advSearchDataRow.length > 1"
|
||||
color="red"
|
||||
@click="() => delAdvSearchData(index)"
|
||||
id="delAdvSearchData"
|
||||
>
|
||||
<q-tooltip
|
||||
class="bg-red"
|
||||
anchor="top middle"
|
||||
self="bottom middle"
|
||||
:offset="[10, 10]"
|
||||
v-if="advSearchDataRow.length === 1"
|
||||
>
|
||||
<strong>ไม่สามารถลบได้</strong>
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -172,24 +164,34 @@ function clearAdvSearchData() {
|
|||
outlined
|
||||
dense
|
||||
v-model="advSearchDataField.keyword"
|
||||
placeholder="คำสำคัญ"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
new-value-mode="add"
|
||||
/>
|
||||
new-value-mode="add-unique"
|
||||
><template v-slot:prepend
|
||||
><span class="text-subtitle2">คำสำคัญ:</span></template
|
||||
></q-select
|
||||
>
|
||||
</div>
|
||||
<div class="col-12 col-md-grow">
|
||||
<q-input
|
||||
id="advSearchDes"
|
||||
dense
|
||||
outlined
|
||||
placeholder="รายละเอียด"
|
||||
@keydown.enter.prevent="searchSubmit()"
|
||||
v-model="advSearchDataField.description"
|
||||
/>
|
||||
><template v-slot:prepend
|
||||
><span class="text-subtitle2">รายละเอียด:</span></template
|
||||
><template v-slot:append>
|
||||
<q-icon
|
||||
v-if="advSearchDataField.description"
|
||||
name="close"
|
||||
@click="() => (advSearchDataField.description = '')"
|
||||
class="cursor-pointer"
|
||||
/> </template
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import FileIcon from '@/components/FileIcon.vue'
|
|||
|
||||
const { isFilePreview, fileInfo } = storeToRefs(useFileInfoStore())
|
||||
const { getType, getFormatDate, getSize } = useFileInfoStore()
|
||||
const filePath =
|
||||
(fileInfo.value?.pathname || '').split('/').slice(0, -1).join(' / ') + ' / '
|
||||
|
||||
async function downloadSubmit(path: string | undefined) {
|
||||
if (path) {
|
||||
|
|
@ -66,6 +68,9 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</q-btn>
|
||||
{{ fileInfo?.title }}</span
|
||||
>
|
||||
<span class="q-ml-lg text-grey">
|
||||
{{ filePath }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row q-mt-lg justify-center">
|
||||
|
|
@ -86,8 +91,9 @@ async function downloadSubmit(path: string | undefined) {
|
|||
<div class="q-px-md flex items-center justify-center">
|
||||
<file-icon
|
||||
size="preview"
|
||||
:fileName="fileInfo?.fileName ? fileInfo.fileName : undefined"
|
||||
:fileMimeType="
|
||||
fileInfo?.fileType ? fileInfo?.fileType : 'unknow'
|
||||
fileInfo?.fileType ? fileInfo.fileType : undefined
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -113,7 +119,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
|
||||
<div class="col-grow q-px-lg q-gutter-md q-pt-md">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>ชื่อไฟล์</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -122,7 +128,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>ชื่อเรื่อง</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -131,7 +137,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>รายละเอียด</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -140,7 +146,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>กลุ่ม/หมวดหมู่</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -149,7 +155,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>คำสำคัญ</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -158,7 +164,7 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>ขนาดไฟล์</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
@ -167,16 +173,18 @@ async function downloadSubmit(path: string | undefined) {
|
|||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>ประเภทไฟล์</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
<span class="text-grey">{{ getType(fileInfo?.fileType) }}</span>
|
||||
<span class="text-grey">{{
|
||||
getType(fileInfo?.fileType, fileInfo?.fileName)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator />
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-2">
|
||||
<div class="col-12 col-md-3">
|
||||
<span>วันที่อัปโหลด</span>
|
||||
</div>
|
||||
<div class="col-grow">
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@
|
|||
import { ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import axiosClient from '@/services/HttpService'
|
||||
import mime from 'mime'
|
||||
|
||||
import type { EhrFile } from '@/stores/tree-data'
|
||||
import { useSearchDataStore } from '@/stores/searched-data'
|
||||
import { useLoader } from '@/stores/loader'
|
||||
|
||||
import AdvancedSearch from '@/modules/01_user/components/AdvancedSearch.vue'
|
||||
import { useFileInfoStore } from '@/stores/file-info-data'
|
||||
|
||||
const loaderStore = useLoader()
|
||||
const { isFilePreview } = storeToRefs(useFileInfoStore())
|
||||
const {
|
||||
isSearch,
|
||||
isAdvSearchCall,
|
||||
|
|
@ -37,6 +40,7 @@ const props = defineProps<{
|
|||
}>()
|
||||
|
||||
async function searchSubmit() {
|
||||
isFilePreview.value = false
|
||||
if (searchData.value.value.trim() !== '') {
|
||||
submitSearchData.value = { AND: [], OR: [] }
|
||||
if (props.mode === 'admin') {
|
||||
|
|
@ -46,12 +50,19 @@ async function searchSubmit() {
|
|||
value: searchData.value.value,
|
||||
})
|
||||
})
|
||||
submitSearchData.value.OR.push({
|
||||
field: 'fileName',
|
||||
value: searchData.value.value,
|
||||
})
|
||||
submitSearchData.value.OR.push({
|
||||
field: 'fileType',
|
||||
value: mime.getType(searchData.value.value) || '',
|
||||
})
|
||||
} else {
|
||||
submitSearchData.value.OR.push({
|
||||
field: searchData.value.field,
|
||||
value: searchData.value.value,
|
||||
})
|
||||
|
||||
if (isAdvSearchCall.value) {
|
||||
let advField = advSearchDataField.value
|
||||
let advRow = advSearchDataRow.value
|
||||
|
|
@ -106,6 +117,15 @@ watch(
|
|||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => searchData.value.value,
|
||||
(search) => {
|
||||
if (search.length === 0) {
|
||||
isSearch.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -115,11 +135,14 @@ watch(
|
|||
outlined
|
||||
dense
|
||||
label="ค้นหา"
|
||||
debounce="500"
|
||||
bg-color="white"
|
||||
v-model="searchData.value"
|
||||
id="inputSearch"
|
||||
@update:model-value="searchSubmit"
|
||||
@keydown.enter.prevent="searchSubmit"
|
||||
>
|
||||
<template v-slot:prepend />
|
||||
<template v-slot:append
|
||||
><q-icon name="search" class="pointer" @click="searchSubmit"
|
||||
/></template>
|
||||
|
|
@ -153,6 +176,7 @@ watch(
|
|||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
v-if="searchData.value"
|
||||
name="close"
|
||||
@click="() => ((searchData.value = ''), (isSearch = false))"
|
||||
class="cursor-pointer"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Keycloak from 'keycloak-js'
|
||||
|
||||
const keycloak = new Keycloak()
|
||||
const keycloak = new Keycloak('/keycloak.json')
|
||||
|
||||
export async function login(cb?: (...args: any[]) => void) {
|
||||
const auth = await keycloak
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import mime from 'mime'
|
||||
|
||||
import type { EhrFile } from '@/stores/tree-data'
|
||||
|
||||
export interface MimeMap {
|
||||
[key: string]: { icon: string; color: string; type: string }
|
||||
[key: string]: { icon: string; color: string }
|
||||
}
|
||||
export interface TypeSetting {
|
||||
[key: string]: { icon: string; color: string }
|
||||
|
|
@ -13,129 +14,118 @@ export interface TypeSetting {
|
|||
export const useFileInfoStore = defineStore('info', () => {
|
||||
const fileInfo = ref<EhrFile>()
|
||||
const isFilePreview = ref<Boolean>(false)
|
||||
const file: TypeSetting = {
|
||||
const fileIcon: TypeSetting = {
|
||||
word: { icon: 'mdi-file-word-outline', color: 'blue-11' },
|
||||
excel: { icon: 'mdi-file-excel-outline', color: 'green-4' },
|
||||
powerpoint: { icon: 'mdi-file-powerpoint-outline', color: 'orange-4' },
|
||||
pdf: { icon: 'mdi-file-document-outline', color: 'red-11' },
|
||||
pdf: { icon: 'mdi-file-pdf-outline', color: 'red-11' },
|
||||
txt: { icon: 'mdi-file-document-outline', color: 'blue-11' },
|
||||
image: { icon: 'mdi-file-image-outline', color: 'blue-11' },
|
||||
}
|
||||
const mimeFileMapping: MimeMap = {
|
||||
'application/msword': {
|
||||
...file.word,
|
||||
type: '.doc',
|
||||
...fileIcon.word,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': {
|
||||
...file.word,
|
||||
type: '.docx',
|
||||
...fileIcon.word,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': {
|
||||
...file.word,
|
||||
type: '.dotx',
|
||||
...fileIcon.word,
|
||||
},
|
||||
'application/vnd.ms-word.document.macroEnabled.12': {
|
||||
...file.word,
|
||||
type: '.docm',
|
||||
...fileIcon.word,
|
||||
},
|
||||
'application/vnd.ms-word.template.macroEnabled.12': {
|
||||
...file.word,
|
||||
type: '.dotm',
|
||||
...fileIcon.word,
|
||||
},
|
||||
|
||||
'application/vnd.ms-excel': {
|
||||
...file.excel,
|
||||
type: '.xls',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {
|
||||
...file.excel,
|
||||
type: '.xlsx',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template': {
|
||||
...file.excel,
|
||||
type: '.xltx',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12': {
|
||||
...file.excel,
|
||||
type: '.xlsm',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.ms-excel.template.macroEnabled.12': {
|
||||
...file.excel,
|
||||
type: '.xltm',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.ms-excel.addin.macroEnabled.12': {
|
||||
...file.excel,
|
||||
type: '.xlam',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
'application/vnd.ms-excel.sheet.binary.macroEnabled.12': {
|
||||
...file.excel,
|
||||
type: '.xlsb',
|
||||
...fileIcon.excel,
|
||||
},
|
||||
|
||||
'application/vnd.ms-powerpoint': {
|
||||
...file.powerpoint,
|
||||
type: '.ppt',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
||||
{
|
||||
...file.powerpoint,
|
||||
type: '.pptx',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template': {
|
||||
...file.powerpoint,
|
||||
type: '.potx',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow': {
|
||||
...file.powerpoint,
|
||||
type: '.ppsx',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.ms-powerpoint.addin.macroEnabled.12': {
|
||||
...file.powerpoint,
|
||||
type: '.ppam',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.ms-powerpoint.presentation.macroEnabled.12': {
|
||||
...file.powerpoint,
|
||||
type: '.pptm',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.ms-powerpoint.template.macroEnabled.12': {
|
||||
...file.powerpoint,
|
||||
type: '.potm',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12': {
|
||||
...file.powerpoint,
|
||||
type: '.ppsm',
|
||||
...fileIcon.powerpoint,
|
||||
},
|
||||
|
||||
'application/pdf': {
|
||||
...file.pdf,
|
||||
type: '.pdf',
|
||||
...fileIcon.pdf,
|
||||
},
|
||||
|
||||
'text/plain': {
|
||||
...file.txt,
|
||||
type: '.txt',
|
||||
...fileIcon.txt,
|
||||
},
|
||||
|
||||
'image/x-png': {
|
||||
...file.image,
|
||||
type: '.png',
|
||||
'image/png': {
|
||||
...fileIcon.image,
|
||||
},
|
||||
'image/x-citrix-jpeg': {
|
||||
...file.image,
|
||||
type: '.jpg, jpeg',
|
||||
'image/jpeg': {
|
||||
...fileIcon.image,
|
||||
},
|
||||
}
|
||||
|
||||
function getType(mimeType: string | undefined): string {
|
||||
if (!!mimeType && mimeFileMapping.hasOwnProperty(mimeType)) {
|
||||
return mimeFileMapping[mimeType].type
|
||||
function getType(
|
||||
mimeType: string | undefined,
|
||||
fileName: string | undefined,
|
||||
): string {
|
||||
if (mimeType === undefined) {
|
||||
return 'ไม่ทราบประเภท'
|
||||
}
|
||||
return 'unknown type'
|
||||
|
||||
const extFomMime = mime.getExtension(mimeType)
|
||||
if (extFomMime) {
|
||||
return '.' + extFomMime
|
||||
}
|
||||
if (fileName && fileName.includes('.')) {
|
||||
const dotIndex = fileName.lastIndexOf('.')
|
||||
const extension = fileName.substring(dotIndex)
|
||||
return extension
|
||||
}
|
||||
return 'ไม่ทราบประเภท'
|
||||
}
|
||||
|
||||
function getFormatDate(dateTime: string | undefined): string {
|
||||
if (dateTime === undefined) {
|
||||
return 'unknown date'
|
||||
return 'ไม่ทราบวันที่'
|
||||
}
|
||||
const date = new Date(dateTime)
|
||||
return date.toLocaleDateString('th-TH', {
|
||||
|
|
@ -145,19 +135,9 @@ export const useFileInfoStore = defineStore('info', () => {
|
|||
})
|
||||
}
|
||||
|
||||
function getFileNameFormat(fileName: string | undefined): string {
|
||||
if (fileName === undefined) {
|
||||
return 'unknow name'
|
||||
}
|
||||
const dotIndex = fileName.lastIndexOf('.')
|
||||
const fileNameOnly = fileName.substring(0, dotIndex)
|
||||
|
||||
return fileNameOnly
|
||||
}
|
||||
|
||||
function getSize(size: string | undefined): string {
|
||||
if (size === undefined) {
|
||||
return 'unknow size'
|
||||
return 'ไม่ทราบขนาด'
|
||||
}
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let i = 0
|
||||
|
|
@ -169,7 +149,7 @@ export const useFileInfoStore = defineStore('info', () => {
|
|||
return sizeNumber.toFixed(2) + ' ' + units[i]
|
||||
}
|
||||
|
||||
async function getFileInfo(data: EhrFile) {
|
||||
function getFileInfo(data: EhrFile) {
|
||||
isFilePreview.value = true
|
||||
fileInfo.value = data
|
||||
}
|
||||
|
|
@ -180,7 +160,6 @@ export const useFileInfoStore = defineStore('info', () => {
|
|||
fileInfo,
|
||||
getType,
|
||||
getFormatDate,
|
||||
getFileNameFormat,
|
||||
getSize,
|
||||
getFileInfo,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ export const useTreeDataStore = defineStore('changeCabinet', () => {
|
|||
async function getFolder(pathname: string, updateStatus = true) {
|
||||
loader.show()
|
||||
|
||||
sessionStorage.setItem('currentPath', pathname)
|
||||
|
||||
const pathArray: string[] = pathname.split('/').filter(Boolean)
|
||||
|
||||
currentDept.value = pathArray.length
|
||||
|
|
|
|||
|
|
@ -7,15 +7,13 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fullscreen bg-secondary text-white text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div class="fullscreen bg-secondary text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div class="text-h1">ไม่พบหน้าที่ต้องการ</div>
|
||||
<div class="text-h2">(404 Page Not Found)</div>
|
||||
<div class="text-h1 text-primary text-weight-bold q-my-md">404</div>
|
||||
<div class="text-h3 text-grey q-mb-md">ไม่พบหน้าที่ต้องการ</div>
|
||||
<q-btn
|
||||
class="q-mt-xl"
|
||||
color="white"
|
||||
color="primary"
|
||||
text-color="secondary"
|
||||
unelevated
|
||||
to="/"
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ const { loader } = storeToRefs(loaderStore)
|
|||
<template>
|
||||
<q-layout view="hHh lpR fFf">
|
||||
<q-header class="bg-white text-black" bordered>
|
||||
<q-toolbar class="q-py-sm">
|
||||
<q-toolbar class="q-py-sm q-pr-sm">
|
||||
<q-img
|
||||
class="pointer"
|
||||
src="@/assets/logo.png"
|
||||
src="@/assets/logo-edm.png"
|
||||
spinner-color="white"
|
||||
style="height: 32px; max-width: 32px"
|
||||
style="height: 45px; max-width: 45px"
|
||||
@click="
|
||||
() => {
|
||||
currentPath = ''
|
||||
|
|
|
|||
|
|
@ -1,437 +0,0 @@
|
|||
import { test, expect, type Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('https://demo.playwright.dev/todomvc');
|
||||
});
|
||||
|
||||
const TODO_ITEMS = [
|
||||
'buy some cheese',
|
||||
'feed the cat',
|
||||
'book a doctors appointment'
|
||||
];
|
||||
|
||||
test.describe('New Todo', () => {
|
||||
test('should allow me to add todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create 1st todo.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list only has one todo item.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([
|
||||
TODO_ITEMS[0]
|
||||
]);
|
||||
|
||||
// Create 2nd todo.
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list now has two todo items.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
TODO_ITEMS[1]
|
||||
]);
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
|
||||
test('should clear text input field when an item is added', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create one todo item.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Check that input is empty.
|
||||
await expect(newTodo).toBeEmpty();
|
||||
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
});
|
||||
|
||||
test('should append new items to the bottom of the list', async ({ page }) => {
|
||||
// Create 3 items.
|
||||
await createDefaultTodos(page);
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count')
|
||||
|
||||
// Check test using different methods.
|
||||
await expect(page.getByText('3 items left')).toBeVisible();
|
||||
await expect(todoCount).toHaveText('3 items left');
|
||||
await expect(todoCount).toContainText('3');
|
||||
await expect(todoCount).toHaveText(/3/);
|
||||
|
||||
// Check all items in one call.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mark all as completed', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to mark all items as completed', async ({ page }) => {
|
||||
// Complete all todos.
|
||||
await page.getByLabel('Mark all as complete').check();
|
||||
|
||||
// Ensure all todos have 'completed' class.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to clear the complete state of all items', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
// Check and then immediately uncheck.
|
||||
await toggleAll.check();
|
||||
await toggleAll.uncheck();
|
||||
|
||||
// Should be no completed classes.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
|
||||
});
|
||||
|
||||
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
await toggleAll.check();
|
||||
await expect(toggleAll).toBeChecked();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Uncheck first todo.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').uncheck();
|
||||
|
||||
// Reuse toggleAll locator and make sure its not checked.
|
||||
await expect(toggleAll).not.toBeChecked();
|
||||
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Assert the toggle all is checked again.
|
||||
await expect(toggleAll).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Item', () => {
|
||||
|
||||
test('should allow me to mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
// Check first item.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
|
||||
// Check second item.
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await secondTodo.getByRole('checkbox').check();
|
||||
|
||||
// Assert completed class.
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).toHaveClass('completed');
|
||||
});
|
||||
|
||||
test('should allow me to un-mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
|
||||
|
||||
await firstTodoCheckbox.check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await firstTodoCheckbox.uncheck();
|
||||
await expect(firstTodo).not.toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
|
||||
});
|
||||
|
||||
test('should allow me to edit an item', async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const secondTodo = todoItems.nth(1);
|
||||
await secondTodo.dblclick();
|
||||
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
// Explicitly assert the new text value.
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2]
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Editing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should hide other controls when editing', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item').nth(1);
|
||||
await todoItem.dblclick();
|
||||
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
|
||||
await expect(todoItem.locator('label', {
|
||||
hasText: TODO_ITEMS[1],
|
||||
})).not.toBeVisible();
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should save edits on blur', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should trim entered text', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should remove the item if an empty text string was entered', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
});
|
||||
|
||||
test('should cancel edits on escape', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
|
||||
await expect(todoItems).toHaveText(TODO_ITEMS);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Counter', () => {
|
||||
test('should display the current number of todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count')
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
await expect(todoCount).toContainText('1');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
await expect(todoCount).toContainText('2');
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Clear completed button', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
});
|
||||
|
||||
test('should display the correct text', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should remove completed items when clicked', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).getByRole('checkbox').check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(todoItems).toHaveCount(2);
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should be hidden when there are no items that are completed', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Persistence', () => {
|
||||
test('should persist its data', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
|
||||
await firstTodoCheck.check();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
|
||||
// Ensure there is 1 completed item.
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// Now reload.
|
||||
await page.reload();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Routing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
// make sure the app had a chance to save updated todos in storage
|
||||
// before navigating to a new view, otherwise the items can get lost :(
|
||||
// in some frameworks like Durandal
|
||||
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
|
||||
});
|
||||
|
||||
test('should allow me to display active items', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should respect the back button', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await test.step('Showing all items', async () => {
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
await test.step('Showing active items', async () => {
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
});
|
||||
|
||||
await test.step('Showing completed items', async () => {
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
});
|
||||
|
||||
await expect(todoItem).toHaveCount(1);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should allow me to display completed items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('should allow me to display all items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should highlight the currently applied filter', async ({ page }) => {
|
||||
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
|
||||
|
||||
//create locators for active and completed links
|
||||
const activeLink = page.getByRole('link', { name: 'Active' });
|
||||
const completedLink = page.getByRole('link', { name: 'Completed' });
|
||||
await activeLink.click();
|
||||
|
||||
// Page change - active items.
|
||||
await expect(activeLink).toHaveClass('selected');
|
||||
await completedLink.click();
|
||||
|
||||
// Page change - completed items.
|
||||
await expect(completedLink).toHaveClass('selected');
|
||||
});
|
||||
});
|
||||
|
||||
async function createDefaultTodos(page: Page) {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction(e => {
|
||||
return JSON.parse(localStorage['react-todos']).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction(e => {
|
||||
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||
return await page.waitForFunction(t => {
|
||||
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
|
||||
}, title);
|
||||
}
|
||||
35
Services/client/tests/README.md
Normal file
35
Services/client/tests/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Test by playwrigth
|
||||
|
||||
## คำสั่งต่างๆ
|
||||
|
||||
คำสั่งที่ใช้ในการ รัน เช่น
|
||||
|
||||
|
||||
```
|
||||
npx playwright test tests/folder-CRUD.spec.ts
|
||||
```
|
||||
playwrigth ui
|
||||
```
|
||||
npx playwright test --ui
|
||||
```
|
||||
|
||||
tests/folder-CRUD.spec.ts คือตำเเหน่งของไฟล์
|
||||
|
||||
## ไฟล์ test
|
||||
ไฟล์งานอยู่ใน Directory tests
|
||||
- delete-file-grid-view.spec.ts `ทดสอบการสร้างไฟล์ใน folder และลบ`
|
||||
```
|
||||
npx playwright test tests/delete-file-grid-view.spec.ts
|
||||
```
|
||||
- folder-CRUD.spec.ts `ทดสอบการสร้างตู้ ลิ้นชัก folder subfolder แล้วเข้าไปในสุดจากนั้นออกมกเเก้ไขเเล้วลบที่ละรำดับ`
|
||||
|
||||
```
|
||||
npx playwright test tests/folder-CRUD.spec.ts
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
101
Services/client/tests/delete-file-grid-view.spec.ts
Normal file
101
Services/client/tests/delete-file-grid-view.spec.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.reload()
|
||||
await page.click("//button[@data-testid='actiontest-delete-file/']")
|
||||
await page.click("(//div[@id='FileltemActiondelete']//div)[2]")
|
||||
await page.click("(//button[@id='dialogDeleteConfirm']//span)[2]")
|
||||
await page.close()
|
||||
})
|
||||
|
||||
test('Login', async ({}) => {
|
||||
await page.goto('http://localhost:3010/admin')
|
||||
|
||||
await expect(page).toHaveTitle('Sign in to EDM')
|
||||
await page.fill("input[name='username']", 'admin')
|
||||
await page.fill("input[name='password']", 'P@ssw0rd')
|
||||
await page.click("input[name='login']")
|
||||
})
|
||||
|
||||
test('Create Cabinet', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-delete-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-delete-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Cabinet', async () => {
|
||||
await page.click("//div[@data-pathname='test-delete-file/']")
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[1]"),
|
||||
).toContainText(/test-delete-file/)
|
||||
})
|
||||
|
||||
test('Create Drawer', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-delete-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-delete-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Drawer', async () => {
|
||||
await page.click("//div[@data-pathname='test-delete-file/test-delete-file/']")
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[3]"),
|
||||
).toContainText(/test-delete-file/)
|
||||
})
|
||||
|
||||
test('Create Folder', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-delete-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-delete-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Folder', async () => {
|
||||
await page.click(
|
||||
"//div[@data-pathname='test-delete-file/test-delete-file/test-delete-file/']",
|
||||
)
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[5]"),
|
||||
).toContainText(/test-delete-file/)
|
||||
})
|
||||
|
||||
test('Upload File', async () => {
|
||||
await page.click("//div[@id='triggerFileCreateFileItem']")
|
||||
await page.setInputFiles("//input[@type='file']", 'tests/file.txt')
|
||||
await page.fill("//input[@placeholder='กรอกชื่อเรื่อง']", 'test-delete-file')
|
||||
await page.click("(//form[@class='q-form']//button)[2]")
|
||||
await page.waitForTimeout(3000)
|
||||
await expect(
|
||||
page.locator(
|
||||
"//div[@data-pathname='test-delete-file/test-delete-file/test-delete-file/file.txt']",
|
||||
),
|
||||
).toContainText(/test-delete-file/)
|
||||
})
|
||||
|
||||
test('Delete File', async () => {
|
||||
await page.click(
|
||||
"//button[@data-testid='actiontest-delete-file/test-delete-file/test-delete-file/file.txt']",
|
||||
)
|
||||
await page.click("//div[@id='FileltemActiondelete']")
|
||||
await page.click("//button[@id='dialogDeleteConfirm']")
|
||||
await page.waitForTimeout(3000)
|
||||
await expect(
|
||||
page.locator("(//div[@class='grid q-mt-md'])[2]"),
|
||||
).not.toContainText(/test-delete-file/)
|
||||
})
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('has title', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Playwright/);
|
||||
});
|
||||
|
||||
test('get started link', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Click the get started link.
|
||||
await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
});
|
||||
1
Services/client/tests/file.txt
Normal file
1
Services/client/tests/file.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Test by Playwright!
|
||||
277
Services/client/tests/folder-CRUD.spec.ts
Normal file
277
Services/client/tests/folder-CRUD.spec.ts
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
import { nanoid } from 'nanoid'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
const cabinet = nanoid()
|
||||
const drawer = nanoid()
|
||||
const folder = nanoid()
|
||||
const subfolder = nanoid()
|
||||
const newName = nanoid()
|
||||
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
|
||||
test('Login', async () => {
|
||||
await page.goto('http://localhost:3010/admin')
|
||||
await expect(page).toHaveTitle('Sign in to EDM')
|
||||
await page.fill("input[name='username']", 'oom')
|
||||
await page.fill("input[name='password']", 'oom')
|
||||
await page.click("input[name='login']")
|
||||
|
||||
await page.waitForTimeout(2000)
|
||||
})
|
||||
|
||||
test('Create Cabinet', async () => {
|
||||
await page.waitForTimeout(2000)
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
|
||||
await page.fill(
|
||||
"//input[@placeholder='กรอกชื่อ']",
|
||||
cabinet,
|
||||
)
|
||||
await page.click("//button[@type='submit']")
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(cabinet)
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
})
|
||||
|
||||
test('Go to Cabinet', async () => {
|
||||
await page.click(`(//div[text()='${cabinet}'])[2]`)
|
||||
})
|
||||
|
||||
test('Create drawer', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill(
|
||||
"//input[@placeholder='กรอกชื่อ']",
|
||||
drawer,
|
||||
)
|
||||
await page.click("//button[@type='submit']")
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(drawer)
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
})
|
||||
|
||||
test('Go into drawer', async () => {
|
||||
await page.click(`(//div[text()='${drawer}'])[2]`)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(drawer)
|
||||
})
|
||||
|
||||
test('Create Folder', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill(
|
||||
"//input[@placeholder='กรอกชื่อ']",
|
||||
folder,
|
||||
)
|
||||
await page.click("//button[@type='submit']")
|
||||
await page.waitForTimeout(300)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(folder)
|
||||
})
|
||||
|
||||
test('Go into Folder', async () => {
|
||||
await page.click(`(//div[text()='${folder}'])[2]`)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(folder)
|
||||
})
|
||||
|
||||
test('Create Subfolder', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill(
|
||||
"//input[@placeholder='กรอกชื่อ']",
|
||||
subfolder,
|
||||
)
|
||||
await page.click("(//button[@type='submit'])[3]")
|
||||
await page.waitForTimeout(300)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(subfolder)
|
||||
})
|
||||
|
||||
test('Go into Subfolder', async () => {
|
||||
|
||||
await page.click(`//div[text()='${subfolder}']`)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(folder)
|
||||
})
|
||||
|
||||
test('Back to Folder', async () => {
|
||||
await page.click("//i[text()='arrow_back']")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(subfolder)
|
||||
})
|
||||
|
||||
test('Edit Subfolder', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${folder}/${subfolder}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[1]")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", newName)
|
||||
await page.click("(//button[@id='FoldeSubmit'])[1]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Subfolder Cancel', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${folder}/${newName}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click("(//div[@class='q-space']/following-sibling::button)[2]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Subfolder Confirm', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${folder}/${newName}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click(
|
||||
"(//div[contains(@class,'q-card__actions justify-end')]//button)[2]",
|
||||
)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(newName)
|
||||
})
|
||||
|
||||
test('Back to drawer', async () => {
|
||||
await page.click("//i[text()='arrow_back']")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(folder)
|
||||
})
|
||||
|
||||
test('Edit Folder', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${folder}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[1]")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", newName)
|
||||
await page.click("(//button[@id='FoldeSubmit'])[1]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Folder Cancel', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${newName}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click("(//div[@class='q-space']/following-sibling::button)[2]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Folder Confirm', async () => {
|
||||
await page.click(
|
||||
`//button[@data-testid='action${cabinet}/${drawer}/${newName}/']`,
|
||||
)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click(
|
||||
"(//div[contains(@class,'q-card__actions justify-end')]//button)[2]",
|
||||
)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(newName)
|
||||
})
|
||||
|
||||
test('Back to Cabinet', async () => {
|
||||
await page.click("//i[text()='arrow_back']")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(drawer)
|
||||
})
|
||||
|
||||
test('Edit drawer', async () => {
|
||||
await page.click(`//button[@data-testid='action${cabinet}/${drawer}/']`)
|
||||
await page.click("(//div[@role='listitem'])[1]")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", newName)
|
||||
await page.click("(//button[@id='FoldeSubmit'])[1]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Drawer Cancel', async () => {
|
||||
await page.click(`//button[@data-testid='action${cabinet}/${newName}/']`)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click("(//div[@class='q-space']/following-sibling::button)[2]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Drawer Confirm', async () => {
|
||||
await page.click(`//button[@data-testid='action${cabinet}/${newName}/']`)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click(
|
||||
"(//div[contains(@class,'q-card__actions justify-end')]//button)[2]",
|
||||
)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(newName)
|
||||
})
|
||||
|
||||
test('Back to Home', async () => {
|
||||
await page.click("//i[text()='arrow_back']")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(cabinet)
|
||||
})
|
||||
|
||||
test('Edit Cabinet', async () => {
|
||||
await page.click(`//button[@data-testid='action${cabinet}/']`)
|
||||
await page.click("(//div[@role='listitem'])[1]")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", newName)
|
||||
await page.click("(//button[@id='FoldeSubmit'])[1]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Cabinet Cancel', async () => {
|
||||
await page.click(`//button[@data-testid='action${newName}/']`)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click("(//div[@class='q-space']/following-sibling::button)[2]")
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).toContainText(newName)
|
||||
})
|
||||
|
||||
test('Delete Cabinet Confirm', async () => {
|
||||
await page.click(`//button[@data-testid='action${newName}/']`)
|
||||
await page.click("(//div[@role='listitem'])[2]")
|
||||
await page.click(
|
||||
"(//div[contains(@class,'q-card__actions justify-end')]//button)[2]",
|
||||
)
|
||||
await expect(
|
||||
page.locator("(//div[contains(@class,'bg-white rounded-borders')])[2]"),
|
||||
).not.toHaveText(newName)
|
||||
})
|
||||
96
Services/client/tests/listview.spec.ts
Normal file
96
Services/client/tests/listview.spec.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
|
||||
test('Login', async ({}) => {
|
||||
await page.goto('http://localhost:3010/admin')
|
||||
await expect(page).toHaveTitle('Sign in to EDM')
|
||||
await page.fill("input[name='username']", 'admin')
|
||||
await page.fill("input[name='password']", 'P@ssw0rd')
|
||||
await page.click("input[name='login']")
|
||||
})
|
||||
|
||||
test('listViewMode', async () => {
|
||||
await page.click("(//i[@role='img'])[2]")
|
||||
})
|
||||
|
||||
test('Create Cabinet', async () => {
|
||||
await page.click("//span[text()='สร้างตู้เก็บเอกสาร']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[2]", 'oat-test')
|
||||
await page.click("(//span[text()='บันทึก'])[3]")
|
||||
})
|
||||
|
||||
test('Go into Cabinet', async () => {
|
||||
await page.click('//td[contains(text(),"oat-test")]')
|
||||
})
|
||||
|
||||
test('Create Drawer', async () => {
|
||||
await page.click("//span[text()='สร้างลิ้นชัก']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'oat-drawer2')
|
||||
await page.click("(//button[@type='submit'])[3]")
|
||||
})
|
||||
|
||||
test('Go into Drawer', async () => {
|
||||
await page.click("//table[@class='q-table']/tbody[1]/tr[1]/td[1]")
|
||||
})
|
||||
|
||||
test('Create Folder', async () => {
|
||||
await page.click("//span[text()='สร้างแฟ้ม']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'oat-folder2')
|
||||
await page.click("(//span[text()='บันทึก'])[2]")
|
||||
})
|
||||
|
||||
test('Go into Folder', async () => {
|
||||
await page.click("//table[@class='q-table']")
|
||||
})
|
||||
|
||||
test('Create subFolder', async () => {
|
||||
await page.click("//span[text()='สร้างแฟ้มย่อย']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]","oat-subfolder2")
|
||||
await page.click("(//span[text()='บันทึก'])[2]")
|
||||
})
|
||||
|
||||
test('Go into SubFolder', async () => {
|
||||
await page.click("//table[@class='q-table']")
|
||||
})
|
||||
|
||||
test('Upload File', async () => {
|
||||
await page.click("//span[text()='สร้างเอกสาร']")
|
||||
await page.locator("//input[@type='file']").setInputFiles('tests/test.docx')
|
||||
await page.fill("//input[@placeholder='กรอกชื่อเรื่อง']",'oattest2')
|
||||
await page.click("(//button[@type='submit'])[2]")
|
||||
await page.waitForTimeout(3000)
|
||||
await page.click("//i[text()='refresh']")
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
test('Go into File', async () => {
|
||||
await page.click("//table[@class='q-table']/tbody[1]/tr[1]/td[1]")
|
||||
})
|
||||
|
||||
test('Download File', async () => {
|
||||
await page.click("//span[text()='ดาวน์โหลด']")
|
||||
await page.waitForTimeout(2000)
|
||||
})
|
||||
|
||||
test('Go Back to MainLayout', async () => {
|
||||
await page.click("//i[text()='arrow_back']")
|
||||
await page.click("//span[text()='ตู้จัดเก็บเอกสาร']")
|
||||
})
|
||||
|
||||
test('Delete Cabinet', async () => {
|
||||
await page.click("//button[@data-testid='oat-test']")
|
||||
await page.click("//span[text()='ลบ']")
|
||||
await page.waitForTimeout(2000)
|
||||
})
|
||||
|
||||
BIN
Services/client/tests/test.docx
Normal file
BIN
Services/client/tests/test.docx
Normal file
Binary file not shown.
89
Services/client/tests/upload-file-grid-view.spec.ts
Normal file
89
Services/client/tests/upload-file-grid-view.spec.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
let page: Page
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.reload()
|
||||
await page.click("//button[@data-testid='actiontest-upload-file/']")
|
||||
await page.click("(//div[@id='FileltemActiondelete']//div)[2]")
|
||||
await page.click("(//button[@id='dialogDeleteConfirm']//span)[2]")
|
||||
await page.close()
|
||||
})
|
||||
|
||||
test('Login', async ({}) => {
|
||||
await page.goto('http://localhost:3010/admin')
|
||||
|
||||
await expect(page).toHaveTitle('Sign in to EDM')
|
||||
await page.fill("input[name='username']", 'admin')
|
||||
await page.fill("input[name='password']", 'P@ssw0rd')
|
||||
await page.click("input[name='login']")
|
||||
})
|
||||
|
||||
test('Create Cabinet', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-upload-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-upload-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Cabinet', async () => {
|
||||
await page.click("//div[@data-pathname='test-upload-file/']")
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[1]"),
|
||||
).toContainText(/test-upload-file/)
|
||||
})
|
||||
|
||||
test('Create Drawer', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-upload-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-upload-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Drawer', async () => {
|
||||
await page.click("//div[@data-pathname='test-upload-file/test-upload-file/']")
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[3]"),
|
||||
).toContainText(/test-upload-file/)
|
||||
})
|
||||
|
||||
test('Create Folder', async () => {
|
||||
await page.click("//div[@id='triggerFolderCreateFileItem']")
|
||||
await page.fill("(//input[@placeholder='กรอกชื่อ'])[1]", 'test-upload-file')
|
||||
await page.click("//button[@type='submit']")
|
||||
await expect(page.locator("(//div[@class='col'])[3]")).toContainText(
|
||||
/test-upload-file/,
|
||||
)
|
||||
})
|
||||
|
||||
test('Go into Folder', async () => {
|
||||
await page.click(
|
||||
"//div[@data-pathname='test-upload-file/test-upload-file/test-upload-file/']",
|
||||
)
|
||||
await expect(
|
||||
page.locator("//div[contains(@class,'flex items-center')]//div[5]"),
|
||||
).toContainText(/test-upload-file/)
|
||||
})
|
||||
|
||||
test('Upload File', async () => {
|
||||
await page.click("//div[@id='triggerFileCreateFileItem']")
|
||||
await page.setInputFiles("//input[@type='file']", 'tests/file.txt')
|
||||
await page.fill("//input[@placeholder='กรอกชื่อเรื่อง']", 'test-upload-file')
|
||||
await page.click("(//form[@class='q-form']//button)[2]")
|
||||
await page.waitForTimeout(3000)
|
||||
await expect(
|
||||
page.locator(
|
||||
"//div[@data-pathname='test-upload-file/test-upload-file/test-upload-file/file.txt']",
|
||||
),
|
||||
).toContainText(/test-upload-file/)
|
||||
})
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
"minio": "^7.1.3",
|
||||
"prettier": "^3.1.0",
|
||||
"promise.any": "^2.0.6",
|
||||
"socket.io": "^4.7.2",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"tsoa": "^5.1.1"
|
||||
},
|
||||
|
|
|
|||
96
Services/server/pnpm-lock.yaml
generated
96
Services/server/pnpm-lock.yaml
generated
|
|
@ -38,6 +38,9 @@ dependencies:
|
|||
promise.any:
|
||||
specifier: ^2.0.6
|
||||
version: 2.0.6
|
||||
socket.io:
|
||||
specifier: ^4.7.2
|
||||
version: 4.7.2
|
||||
swagger-ui-express:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(express@4.18.2)
|
||||
|
|
@ -154,6 +157,10 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/@socket.io/component-emitter@3.1.0:
|
||||
resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
|
||||
dev: false
|
||||
|
||||
/@tsconfig/node10@1.0.9:
|
||||
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
|
||||
dev: true
|
||||
|
|
@ -215,11 +222,14 @@ packages:
|
|||
dependencies:
|
||||
'@types/node': 20.9.0
|
||||
|
||||
/@types/cookie@0.4.1:
|
||||
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
||||
dev: false
|
||||
|
||||
/@types/cors@2.8.17:
|
||||
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
|
||||
dependencies:
|
||||
'@types/node': 20.9.0
|
||||
dev: true
|
||||
|
||||
/@types/express-serve-static-core@4.17.41:
|
||||
resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==}
|
||||
|
|
@ -420,6 +430,11 @@ packages:
|
|||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
/base64id@2.0.0:
|
||||
resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
|
||||
engines: {node: ^4.5.0 || >= 5.9}
|
||||
dev: false
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -576,6 +591,11 @@ packages:
|
|||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
dev: false
|
||||
|
||||
/cookie@0.4.2:
|
||||
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -706,6 +726,31 @@ packages:
|
|||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/engine.io-parser@5.2.1:
|
||||
resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dev: false
|
||||
|
||||
/engine.io@6.5.4:
|
||||
resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==}
|
||||
engines: {node: '>=10.2.0'}
|
||||
dependencies:
|
||||
'@types/cookie': 0.4.1
|
||||
'@types/cors': 2.8.17
|
||||
'@types/node': 20.9.0
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.4.2
|
||||
cors: 2.8.5
|
||||
debug: 4.3.4
|
||||
engine.io-parser: 5.2.1
|
||||
ws: 8.11.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/es-abstract@1.22.3:
|
||||
resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -1786,6 +1831,42 @@ packages:
|
|||
semver: 7.5.4
|
||||
dev: true
|
||||
|
||||
/socket.io-adapter@2.5.2:
|
||||
resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==}
|
||||
dependencies:
|
||||
ws: 8.11.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/socket.io-parser@4.2.4:
|
||||
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.0
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/socket.io@4.7.2:
|
||||
resolution: {integrity: sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==}
|
||||
engines: {node: '>=10.2.0'}
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cors: 2.8.5
|
||||
debug: 4.3.4
|
||||
engine.io: 6.5.4
|
||||
socket.io-adapter: 2.5.2
|
||||
socket.io-parser: 4.2.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -2192,6 +2273,19 @@ packages:
|
|||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: false
|
||||
|
||||
/ws@8.11.0:
|
||||
resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: ^5.0.2
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/xml2js@0.5.0:
|
||||
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import "dotenv/config";
|
|||
import express from "express";
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
import cors from "cors";
|
||||
import { createServer } from "http";
|
||||
import { Server } from "socket.io";
|
||||
|
||||
import { RegisterRoutes } from "./routes";
|
||||
import errorHandler from "./middlewares/exception";
|
||||
|
|
@ -9,6 +11,7 @@ import rabbitmq from "./rabbitmq";
|
|||
|
||||
import swaggerSpecs from "./swagger.json";
|
||||
import { handler as amqHandler } from "./rabbitmq/handler";
|
||||
import { setInstance } from "./lib/websocket";
|
||||
|
||||
const PORT = +(process.env.PORT || 80);
|
||||
|
||||
|
|
@ -31,7 +34,24 @@ app.use((_req, res, _next) => {
|
|||
res.sendFile(`${process.cwd()}/static/index.html`);
|
||||
});
|
||||
|
||||
app.listen(PORT, "0.0.0.0", () =>
|
||||
const server = createServer(app);
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
},
|
||||
});
|
||||
|
||||
setInstance(io);
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log("User Connected");
|
||||
|
||||
socket.on("disconnected", () => {
|
||||
console.log("User Disconnected");
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PORT, "0.0.0.0", () =>
|
||||
console.log(`[APP] Application is running on http://localhost:${PORT}`),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/mi
|
|||
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
|
||||
import { getInstance } from "../lib/websocket";
|
||||
|
||||
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
|
@ -86,6 +87,9 @@ export class CabinetController extends Controller {
|
|||
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("CreateFolder", { pathname: `${replaceIllegalChars(body.name)}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +159,9 @@ export class CabinetController extends Controller {
|
|||
}),
|
||||
);
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("EditFolder", { from: `${cabinetName}/`, to: `${replaceIllegalChars(body.name)}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +187,26 @@ export class CabinetController extends Controller {
|
|||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("DeleteFolder", { pathname: `${cabinetName}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example cabinetName "ตู้เอกสาร 1"
|
||||
*/
|
||||
@Get("/{cabinetName}/size")
|
||||
@Tags("ตู้เอกสาร")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async calc(@Path() cabinetName: string) {
|
||||
const list = await listItem(DEFAULT_BUCKET!, `${cabinetName}/`, true).catch((e) =>
|
||||
console.error(`Error List Folder: ${e}`),
|
||||
);
|
||||
|
||||
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
|
||||
|
||||
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
|
|||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { getInstance } from "../lib/websocket";
|
||||
|
||||
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
|
@ -101,6 +102,9 @@ export class DrawerController extends Controller {
|
|||
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
|
|
@ -172,6 +176,12 @@ export class DrawerController extends Controller {
|
|||
}),
|
||||
);
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("EditFolder", {
|
||||
from: `${cabinetName}/${drawerName}/`,
|
||||
to: `${cabinetName}/${replaceIllegalChars(body.name)}/`,
|
||||
});
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
|
|
@ -201,6 +211,29 @@ export class DrawerController extends Controller {
|
|||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("DeleteFolder", {
|
||||
pathname: `${cabinetName}/${drawerName}/`,
|
||||
});
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example cabinetName "ตู้เอกสาร 1"
|
||||
* @example drawerName "ลิ้นชัก 1"
|
||||
*/
|
||||
@Get("/{drawerName}/size")
|
||||
@Tags("ลิ้นชัก")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async calc(@Path() cabinetName: string, @Path() drawerName: string) {
|
||||
const list = await listItem(DEFAULT_BUCKET!, `${cabinetName}/${drawerName}/`, true).catch((e) =>
|
||||
console.error(`Error List Folder: ${e}`),
|
||||
);
|
||||
|
||||
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
|
||||
|
||||
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
|
|||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { getInstance } from "../lib/websocket";
|
||||
|
||||
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
|
@ -106,6 +107,9 @@ export class FolderController extends Controller {
|
|||
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +183,12 @@ export class FolderController extends Controller {
|
|||
}),
|
||||
);
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("EditFolder", {
|
||||
from: `${cabinetName}/${drawerName}/${folderName}`,
|
||||
to: `${cabinetName}/${drawerName}/${replaceIllegalChars(body.name)}/`,
|
||||
});
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
|
|
@ -213,6 +223,34 @@ export class FolderController extends Controller {
|
|||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("DeleteFolder", { pathname: `${cabinetName}/${drawerName}/${folderName}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example cabinetName "ตู้เอกสาร 1"
|
||||
* @example drawerName "ลิ้นชัก 1"
|
||||
* @example folderName "แฟ้ม 1"
|
||||
*/
|
||||
@Get("/{folderName}/size")
|
||||
@Tags("แฟ้ม")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async calc(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
@Path() folderName: string,
|
||||
) {
|
||||
const list = await listItem(
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/${folderName}/`,
|
||||
true,
|
||||
).catch((e) => console.error(`Error List Folder: ${e}`));
|
||||
|
||||
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
|
||||
|
||||
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "
|
|||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { getInstance } from "../lib/websocket";
|
||||
|
||||
const DEFAULT_BUCKET = process.env.MINIO_BUCKET;
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
|
@ -105,6 +106,9 @@ export class SubFolderController extends Controller {
|
|||
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("CreateFolder", { pathname: `${basePath}${replaceIllegalChars(body.name)}/` });
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +186,12 @@ export class SubFolderController extends Controller {
|
|||
}),
|
||||
);
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("EditFolder", {
|
||||
from: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
|
||||
to: `${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(body.name)}/`,
|
||||
});
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +228,38 @@ export class SubFolderController extends Controller {
|
|||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const io = getInstance();
|
||||
io?.emit("DeleteFolder", {
|
||||
pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
|
||||
});
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example cabinetName "ตู้เอกสาร 1"
|
||||
* @example drawerName "ลิ้นชัก 1"
|
||||
* @example folderName "แฟ้ม 1"
|
||||
* @example subFolderName "แฟ้มย่อย 1"
|
||||
*/
|
||||
@Get("/{subFolderName}/size")
|
||||
@Tags("แฟ้มย่อย")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async calc(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
@Path() folderName: string,
|
||||
@Path() subFolderName: string,
|
||||
) {
|
||||
const list = await listItem(
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
|
||||
true,
|
||||
).catch((e) => console.error(`Error List Folder: ${e}`));
|
||||
|
||||
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
|
||||
|
||||
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
Services/server/src/lib/websocket.ts
Normal file
11
Services/server/src/lib/websocket.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { Server } from "socket.io";
|
||||
|
||||
let io: Server | null = null;
|
||||
|
||||
export function setInstance(server: Server) {
|
||||
io = server;
|
||||
}
|
||||
|
||||
export function getInstance() {
|
||||
return io;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { StorageFile } from "../interfaces/storage-fs";
|
||||
import esClient from "../elasticsearch";
|
||||
import minioClient from "../minio";
|
||||
import { getInstance } from "../lib/websocket";
|
||||
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ export async function handler(key: string, event: string): Promise<boolean> {
|
|||
cachedBuffer[key] = buffer;
|
||||
} catch (e: any) {
|
||||
if (e.code === "NoSuchKey") {
|
||||
console.info(`[AMQ] Key: ${key} received but cannot be found.`)
|
||||
console.info(`[AMQ] Key: ${key} received but cannot be found.`);
|
||||
delete cachedBuffer[key];
|
||||
delete cachedMetadata[key];
|
||||
await ensureDelete(pathname);
|
||||
|
|
@ -43,7 +44,7 @@ export async function handler(key: string, event: string): Promise<boolean> {
|
|||
|
||||
const rec = await popInfo(pathname);
|
||||
|
||||
console.info(`[AMQ] Key: ${key} - ${rec ?? 'Not Found.'}`)
|
||||
console.info(`[AMQ] Key: ${key} - ${rec ?? "Not Found."}`);
|
||||
|
||||
const result = rec
|
||||
? await handleFoundRecord(rec, cachedBuffer[key], cachedMetadata[key])
|
||||
|
|
@ -94,6 +95,10 @@ async function ensureDelete(pathname: string) {
|
|||
conflicts: "proceed",
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
const io = getInstance();
|
||||
io?.send("FileDelete", { pathname });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -132,12 +137,16 @@ async function handleNotFoundRecord(
|
|||
pipeline: "attachment",
|
||||
index: DEFAULT_INDEX!,
|
||||
document: { data: base64, ...metadata },
|
||||
refresh: "wait_for",
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
if (result) return true;
|
||||
if (!result) return false;
|
||||
|
||||
return false;
|
||||
const io = getInstance();
|
||||
io?.send("FileUpdate", metadata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleFoundRecord(
|
||||
|
|
@ -154,10 +163,14 @@ async function handleFoundRecord(
|
|||
pipeline: "attachment",
|
||||
index: DEFAULT_INDEX!,
|
||||
document: { data: Buffer.from(buffer).toString("base64"), ...metadata },
|
||||
refresh: "wait_for",
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
if (result) return true;
|
||||
if (!result) return false;
|
||||
|
||||
return false;
|
||||
const io = getInstance();
|
||||
io?.send("FileUpdate", metadata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,32 @@ export function RegisterRoutes(app: Router) {
|
|||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/size',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController)),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.calc)),
|
||||
|
||||
function CabinetController_calc(request: any, response: any, next: any) {
|
||||
const args = {
|
||||
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
|
||||
};
|
||||
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
|
||||
let validatedArgs: any[] = [];
|
||||
try {
|
||||
validatedArgs = getValidatedArgs(args, request, response);
|
||||
|
||||
const controller = new CabinetController();
|
||||
|
||||
|
||||
const promise = controller.calc.apply(controller, validatedArgs as any);
|
||||
promiseHandler(controller, promise, response, 200, next);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
|
|
@ -289,6 +315,33 @@ export function RegisterRoutes(app: Router) {
|
|||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer/:drawerName/size',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.calc)),
|
||||
|
||||
function DrawerController_calc(request: any, response: any, next: any) {
|
||||
const args = {
|
||||
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
|
||||
drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"},
|
||||
};
|
||||
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
|
||||
let validatedArgs: any[] = [];
|
||||
try {
|
||||
validatedArgs = getValidatedArgs(args, request, response);
|
||||
|
||||
const controller = new DrawerController();
|
||||
|
||||
|
||||
const promise = controller.calc.apply(controller, validatedArgs as any);
|
||||
promiseHandler(controller, promise, response, 200, next);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/file',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FileController)),
|
||||
|
|
@ -549,6 +602,34 @@ export function RegisterRoutes(app: Router) {
|
|||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/size',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.calc)),
|
||||
|
||||
function FolderController_calc(request: any, response: any, next: any) {
|
||||
const args = {
|
||||
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
|
||||
drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"},
|
||||
folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"},
|
||||
};
|
||||
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
|
||||
let validatedArgs: any[] = [];
|
||||
try {
|
||||
validatedArgs = getValidatedArgs(args, request, response);
|
||||
|
||||
const controller = new FolderController();
|
||||
|
||||
|
||||
const promise = controller.calc.apply(controller, validatedArgs as any);
|
||||
promiseHandler(controller, promise, response, 200, next);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.post('/search',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SearchController)),
|
||||
|
|
@ -692,6 +773,35 @@ export function RegisterRoutes(app: Router) {
|
|||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/size',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.calc)),
|
||||
|
||||
function SubFolderController_calc(request: any, response: any, next: any) {
|
||||
const args = {
|
||||
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
|
||||
drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"},
|
||||
folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"},
|
||||
subFolderName: {"in":"path","name":"subFolderName","required":true,"dataType":"string"},
|
||||
};
|
||||
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
|
||||
let validatedArgs: any[] = [];
|
||||
try {
|
||||
validatedArgs = getValidatedArgs(args, request, response);
|
||||
|
||||
const controller = new SubFolderController();
|
||||
|
||||
|
||||
const promise = controller.calc.apply(controller, validatedArgs as any);
|
||||
promiseHandler(controller, promise, response, 200, next);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
|
||||
app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/file',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderFileController)),
|
||||
|
|
|
|||
|
|
@ -375,6 +375,51 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/size": {
|
||||
"get": {
|
||||
"operationId": "Calc",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ตู้เอกสาร"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "cabinetName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ตู้เอกสาร 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer": {
|
||||
"get": {
|
||||
"operationId": "ListDrawer",
|
||||
|
|
@ -592,6 +637,60 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer/{drawerName}/size": {
|
||||
"get": {
|
||||
"operationId": "Calc",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"ลิ้นชัก"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "cabinetName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ตู้เอกสาร 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "drawerName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ลิ้นชัก 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/file": {
|
||||
"get": {
|
||||
"operationId": "GetFile",
|
||||
|
|
@ -1461,6 +1560,69 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/size": {
|
||||
"get": {
|
||||
"operationId": "Calc",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"แฟ้ม"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "cabinetName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ตู้เอกสาร 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "drawerName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ลิ้นชัก 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "folderName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "แฟ้ม 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search": {
|
||||
"post": {
|
||||
"operationId": "SearchFile",
|
||||
|
|
@ -1788,6 +1950,78 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder/{subFolderName}/size": {
|
||||
"get": {
|
||||
"operationId": "Calc",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"แฟ้มย่อย"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "cabinetName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ตู้เอกสาร 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "drawerName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "ลิ้นชัก 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "folderName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "แฟ้ม 1"
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "subFolderName",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "แฟ้มย่อย 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder/{subFolderName}/file": {
|
||||
"get": {
|
||||
"operationId": "GetFile",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import minioClient from "../minio";
|
|||
* @returns illegal character replaced path
|
||||
*/
|
||||
export function replaceIllegalChars(path: string, replace = "-") {
|
||||
return path.replace(/[/\\?%*:|"<>#]/g, replace);
|
||||
return path.replace(/[/\\?%*:|"<>#]/g, replace).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue