Compare commits
53 Commits
fdd093b8aa
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 364c67f3ad | |||
| 2adcc217af | |||
| 8c7e18637a | |||
| a88238f209 | |||
| a34270cfc3 | |||
| c022b96a12 | |||
| bbb55e9746 | |||
| d82e12759e | |||
| 3cef34bd1e | |||
| 1fbcdd82c8 | |||
| f58fa0dd80 | |||
| 8a43e88f24 | |||
| cfadae4df3 | |||
| 4e1ab7e9c2 | |||
| 9a03c3ef85 | |||
| d5d82a5a1e | |||
| 8d7615fb37 | |||
| 1af2ee3890 | |||
| 8bfcd65ad1 | |||
| 75ecbba71d | |||
| 1a55bd7748 | |||
| 3e7a22f9e2 | |||
| 49667d7fe9 | |||
| b15b4dc3b1 | |||
| e3c5e84bb7 | |||
| c1c7d6baaa | |||
| 15eb1e87e8 | |||
| 960c4db1ac | |||
| 93fec2051b | |||
| cbdb695ce5 | |||
| da087d9a00 | |||
| 383d2d5d34 | |||
| 9b03b39db0 | |||
| 5e36a978a7 | |||
| b9e6ff27db | |||
| 24c3bf0324 | |||
| 617ad611ed | |||
| a8e8bd9afd | |||
| 074378335b | |||
| 3b8b9796fa | |||
| 9a819d1bd6 | |||
| a5e32d0d39 | |||
| 8ad250f227 | |||
| 7f01597ea1 | |||
| d5f38cff85 | |||
| 49452998cb | |||
| 0009ceae81 | |||
| 480393743f | |||
| e47bb4f35c | |||
| 3b2dba5317 | |||
| 48be96a28b | |||
| 15fe1dfec6 | |||
| d7e3e500ab |
25
src/client/dd-hub-react/package-lock.json
generated
25
src/client/dd-hub-react/package-lock.json
generated
@@ -21,6 +21,7 @@
|
||||
"apexcharts": "^4.5.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"es-toolkit": "^1.34.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"minimal-shared": "^1.0.7",
|
||||
"react": "^19.1.0",
|
||||
"react-apexcharts": "^1.7.0",
|
||||
@@ -30,6 +31,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.1",
|
||||
@@ -2195,6 +2197,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash.debounce": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz",
|
||||
"integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
|
||||
@@ -5125,6 +5144,12 @@
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"apexcharts": "^4.5.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"es-toolkit": "^1.34.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"minimal-shared": "^1.0.7",
|
||||
"react": "^19.1.0",
|
||||
"react-apexcharts": "^1.7.0",
|
||||
@@ -48,6 +49,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.1",
|
||||
|
||||
230
src/client/dd-hub-react/public/api/document.json
Normal file
230
src/client/dd-hub-react/public/api/document.json
Normal file
@@ -0,0 +1,230 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "example1.pdf",
|
||||
"addedWhen": "2024-01-12T10:00:00.000Z",
|
||||
"addedWho": "TekH",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "invoiceNumber",
|
||||
"serilizedValue": "INV-20250714-001"
|
||||
},
|
||||
{
|
||||
"name": "customerName",
|
||||
"serilizedValue": "John Doe"
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"serilizedValue": "2025-07-01"
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"serilizedValue": "2025-07-14"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"serilizedValue": "100.50"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"serilizedValue": "1000.00"
|
||||
},
|
||||
{
|
||||
"name": "taxIncluded",
|
||||
"serilizedValue": "true"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"serilizedValue": "2025-07-10"
|
||||
},
|
||||
{
|
||||
"name": "deliveryTime",
|
||||
"serilizedValue": "15:30:00"
|
||||
},
|
||||
{
|
||||
"name": "lastUpdated",
|
||||
"serilizedValue": "2025-07-14T10:45:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "example2.pdf",
|
||||
"addedWhen": "2024-02-03T09:30:00.000Z",
|
||||
"addedWho": "bob",
|
||||
"changedWhen": "2024-03-15T12:00:00.000Z",
|
||||
"changedWho": "KammM",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "invoiceNumber",
|
||||
"serilizedValue": "INV-20250701-007"
|
||||
},
|
||||
{
|
||||
"name": "customerName",
|
||||
"serilizedValue": "Jane Smith"
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"serilizedValue": "2025-06-01"
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"serilizedValue": "2025-06-30"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"serilizedValue": "250.00"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"serilizedValue": "850.75"
|
||||
},
|
||||
{
|
||||
"name": "taxIncluded",
|
||||
"serilizedValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"serilizedValue": "2025-06-15"
|
||||
},
|
||||
{
|
||||
"name": "deliveryTime",
|
||||
"serilizedValue": "09:00:00"
|
||||
},
|
||||
{
|
||||
"name": "lastUpdated",
|
||||
"serilizedValue": "2025-06-30T14:15:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "document1.docx",
|
||||
"addedWhen": "2023-12-20T14:45:00.000Z",
|
||||
"addedWho": "SchreiberM",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "invoiceNumber",
|
||||
"serilizedValue": "INV-20250620-043"
|
||||
},
|
||||
{
|
||||
"name": "customerName",
|
||||
"serilizedValue": "Max Mustermann"
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"serilizedValue": "2025-05-01"
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"serilizedValue": "2025-05-15"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"serilizedValue": "600.00"
|
||||
},
|
||||
{
|
||||
"name": "taxIncluded",
|
||||
"serilizedValue": "true"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"serilizedValue": "2025-05-02"
|
||||
},
|
||||
{
|
||||
"name": "lastUpdated",
|
||||
"serilizedValue": "2025-05-15T11:00:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "spreadsheet1.xlsx",
|
||||
"addedWhen": "2024-05-01T08:15:00.000Z",
|
||||
"addedWho": "KammM",
|
||||
"changedWhen": "2024-06-10T16:20:00.000Z",
|
||||
"changedWho": "OlgunR",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "invoiceNumber",
|
||||
"serilizedValue": "INV-20250410-021"
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"serilizedValue": "2025-04-01"
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"serilizedValue": "2025-04-30"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"serilizedValue": "150.99"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"serilizedValue": "999.99"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"serilizedValue": "2025-04-15"
|
||||
},
|
||||
{
|
||||
"name": "deliveryTime",
|
||||
"serilizedValue": "17:45:00"
|
||||
},
|
||||
{
|
||||
"name": "lastUpdated",
|
||||
"serilizedValue": "2025-04-30T18:30:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "report.docx",
|
||||
"addedWhen": "2024-04-17T11:25:00.000Z",
|
||||
"addedWho": "SchreiberM",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "invoiceNumber",
|
||||
"serilizedValue": "INV-20250305-099"
|
||||
},
|
||||
{
|
||||
"name": "customerName",
|
||||
"serilizedValue": "Ali Veli"
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"serilizedValue": "2025-03-01"
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"serilizedValue": "2025-03-20"
|
||||
},
|
||||
{
|
||||
"name": "minAmount",
|
||||
"serilizedValue": "75.00"
|
||||
},
|
||||
{
|
||||
"name": "maxAmount",
|
||||
"serilizedValue": "500.00"
|
||||
},
|
||||
{
|
||||
"name": "taxIncluded",
|
||||
"serilizedValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"serilizedValue": "2025-03-02"
|
||||
},
|
||||
{
|
||||
"name": "deliveryTime",
|
||||
"serilizedValue": "08:20:00"
|
||||
},
|
||||
{
|
||||
"name": "lastUpdated",
|
||||
"serilizedValue": "2025-03-20T09:30:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-10 -10 52 52" xmlns="http://www.w3.org/2000/svg"><title>file_type_word2</title><path d="M18.536,2.323V4.868c3.4.019,7.12-.035,10.521.019a.783.783,0,0,1,.912.861c.054,6.266-.013,12.89.032,19.157-.02.4.009,1.118-.053,1.517-.079.509-.306.607-.817.676-.286.039-.764.034-1.045.047-2.792-.014-5.582-.011-8.374-.01l-1.175,0v2.547L2,27.133Q2,16,2,4.873L18.536,2.322" style="fill:#283c82"/><path d="M18.536,5.822h10.5V26.18h-10.5V23.635h8.27V22.363h-8.27v-1.59h8.27V19.5h-8.27v-1.59h8.27V16.637h-8.27v-1.59h8.27V13.774h-8.27v-1.59h8.27V10.911h-8.27V9.321h8.27V8.048h-8.27V5.822" style="fill:#fff"/><path d="M8.573,11.443c.6-.035,1.209-.06,1.813-.092.423,2.147.856,4.291,1.314,6.429.359-2.208.757-4.409,1.142-6.613.636-.022,1.272-.057,1.905-.1-.719,3.082-1.349,6.19-2.134,9.254-.531.277-1.326-.013-1.956.032-.423-2.106-.916-4.2-1.295-6.314C8.99,16.1,8.506,18.133,8.08,20.175q-.916-.048-1.839-.111c-.528-2.8-1.148-5.579-1.641-8.385.544-.025,1.091-.048,1.635-.067.328,2.026.7,4.043.986,6.072.448-2.08.907-4.161,1.352-6.241" style="fill:#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
66
src/client/dd-hub-react/public/assets/icons/file/pdf.svg
Normal file
66
src/client/dd-hub-react/public/assets/icons/file/pdf.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="_x35_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="-300 -300 1112 1112" xml:space="preserve">
|
||||
<g>
|
||||
<polygon style="fill:#B12A27;" points="475.435,117.825 475.435,512 47.791,512 47.791,0.002 357.613,0.002 412.491,54.881 "/>
|
||||
<rect x="36.565" y="34.295" style="fill:#F2F2F2;" width="205.097" height="91.768"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#B12A27;" d="M110.132,64.379c-0.905-2.186-2.111-4.146-3.769-5.804c-1.658-1.658-3.694-3.015-6.031-3.92
|
||||
c-2.412-0.98-5.126-1.432-8.141-1.432H69.651v58.195h11.383V89.481h11.157c3.015,0,5.729-0.452,8.141-1.432
|
||||
c2.337-0.905,4.372-2.261,6.031-3.92c1.659-1.658,2.865-3.543,3.769-5.804c0.829-2.186,1.282-4.523,1.282-6.935
|
||||
C111.413,68.902,110.961,66.565,110.132,64.379z M97.844,77.118c-1.508,1.432-3.618,2.186-6.181,2.186H81.034V63.323h10.629
|
||||
c2.563,0,4.674,0.754,6.181,2.261c1.432,1.432,2.186,3.392,2.186,5.804C100.031,73.726,99.277,75.686,97.844,77.118z"/>
|
||||
<path style="fill:#B12A27;" d="M164.558,75.761c-0.075-2.035-0.151-3.844-0.377-5.503c-0.226-1.659-0.603-3.166-1.131-4.598
|
||||
c-0.528-1.357-1.206-2.714-2.111-3.92c-2.035-2.94-4.523-5.126-7.312-6.483c-2.865-1.357-6.257-2.035-10.252-2.035h-20.956
|
||||
v58.195h20.956c3.995,0,7.387-0.678,10.252-2.035c2.789-1.357,5.277-3.543,7.312-6.483c0.905-1.206,1.583-2.563,2.111-3.92
|
||||
c0.528-1.432,0.905-2.94,1.131-4.598c0.226-1.658,0.301-3.468,0.377-5.503c0.075-1.96,0.075-4.146,0.075-6.558
|
||||
C164.633,79.908,164.633,77.721,164.558,75.761z M153.175,88.2c0,1.734-0.151,3.091-0.302,4.297
|
||||
c-0.151,1.131-0.377,2.186-0.678,2.94c-0.301,0.829-0.754,1.583-1.281,2.261c-1.885,2.412-4.749,3.543-8.518,3.543h-8.669V63.323
|
||||
h8.669c3.769,0,6.634,1.206,8.518,3.618c0.528,0.678,0.98,1.357,1.281,2.186s0.528,1.809,0.678,3.015
|
||||
c0.151,1.131,0.302,2.563,0.302,4.221c0.075,1.659,0.075,3.694,0.075,5.955C153.251,84.581,153.251,86.541,153.175,88.2z"/>
|
||||
<path style="fill:#B12A27;" d="M213.18,63.323V53.222h-38.37v58.195h11.383V87.823h22.992V77.646h-22.992V63.323H213.18z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#B12A27;" d="M110.132,64.379c-0.905-2.186-2.111-4.146-3.769-5.804c-1.658-1.658-3.694-3.015-6.031-3.92
|
||||
c-2.412-0.98-5.126-1.432-8.141-1.432H69.651v58.195h11.383V89.481h11.157c3.015,0,5.729-0.452,8.141-1.432
|
||||
c2.337-0.905,4.372-2.261,6.031-3.92c1.659-1.658,2.865-3.543,3.769-5.804c0.829-2.186,1.282-4.523,1.282-6.935
|
||||
C111.413,68.902,110.961,66.565,110.132,64.379z M97.844,77.118c-1.508,1.432-3.618,2.186-6.181,2.186H81.034V63.323h10.629
|
||||
c2.563,0,4.674,0.754,6.181,2.261c1.432,1.432,2.186,3.392,2.186,5.804C100.031,73.726,99.277,75.686,97.844,77.118z"/>
|
||||
</g>
|
||||
</g>
|
||||
<polygon style="opacity:0.08;fill:#040000;" points="475.435,117.825 475.435,512 47.791,512 47.791,419.581 247.705,219.667
|
||||
259.54,207.832 266.098,201.273 277.029,190.343 289.995,177.377 412.491,54.881 "/>
|
||||
<polygon style="fill:#771B1B;" points="475.435,117.836 357.599,117.836 357.599,0 "/>
|
||||
<g>
|
||||
<path style="fill:#F2F2F2;" d="M414.376,370.658c-2.488-4.372-5.88-8.518-10.101-12.287c-3.467-3.166-7.538-6.106-12.137-8.82
|
||||
c-18.544-10.93-45.003-16.207-80.961-16.207h-3.618c-1.96-1.809-3.995-3.618-6.106-5.503
|
||||
c-13.644-12.287-24.499-25.63-32.942-40.48c16.584-36.561,24.499-69.126,23.519-96.867c-0.151-4.674-0.829-9.046-2.035-13.117
|
||||
c-1.809-6.558-4.824-12.363-9.046-17.112c-0.075-0.075-0.075-0.075-0.151-0.151c-6.709-7.538-16.056-11.835-25.555-11.835
|
||||
c-9.574,0-18.393,4.146-24.801,11.76c-6.332,7.538-9.724,17.866-9.875,30.002c-0.226,18.544,1.281,36.108,4.448,52.315
|
||||
c0.301,1.282,0.528,2.563,0.829,3.844c3.166,14.7,7.84,28.645,13.87,41.611c-7.086,14.398-14.247,26.836-19.223,35.279
|
||||
c-3.769,6.408-7.915,13.117-12.212,19.826c-19.373,3.468-35.807,7.689-50.129,12.966c-19.373,7.011-34.902,16.056-46.059,26.836
|
||||
c-7.237,6.935-12.137,14.323-14.549,22.012c-2.563,7.915-2.412,15.83,0.452,22.916c2.638,6.558,7.387,12.061,13.72,15.83
|
||||
c1.508,0.905,3.091,1.658,4.749,2.337c4.825,1.96,10.101,3.015,15.604,3.015c12.74,0,25.856-5.503,36.937-15.378
|
||||
c20.655-18.469,41.988-48.169,54.577-66.94c10.327-1.583,21.559-2.94,34.224-4.297c14.926-1.508,28.118-2.412,40.104-2.865
|
||||
c3.694,3.317,7.237,6.483,10.629,9.498c18.846,16.81,33.168,28.947,46.134,37.465c0,0.075,0.075,0.075,0.151,0.075
|
||||
c5.126,3.392,10.026,6.181,14.926,8.443c5.503,2.563,11.081,3.92,16.81,3.92c7.237,0,14.021-2.186,19.675-6.181
|
||||
c5.729-4.146,9.875-10.101,11.76-16.81C420.18,387.694,418.899,378.724,414.376,370.658z M247.705,219.667
|
||||
c-1.055-9.348-1.508-19.072-1.357-29.324c0.151-9.724,3.694-16.283,8.895-16.283c3.92,0,8.066,3.543,9.95,10.327
|
||||
c0.528,2.035,0.905,4.372,0.98,7.01c0.151,3.166,0.075,6.483-0.075,9.875c-0.452,9.574-2.111,19.75-4.975,30.681
|
||||
c-1.734,7.011-3.995,14.323-6.784,21.936C251.173,243.186,248.911,231.803,247.705,219.667z M121.967,418.073
|
||||
c-1.282-3.166,0.151-9.272,7.991-16.81c11.986-11.458,30.756-20.504,56.914-27.364c-4.975,6.784-9.875,12.966-14.624,18.619
|
||||
c-7.237,8.744-14.172,16.132-20.429,21.71c-5.352,4.824-11.232,7.84-16.81,8.594c-0.98,0.151-1.96,0.226-2.94,0.226
|
||||
C127.168,423.049,123.173,421.089,121.967,418.073z M242.428,337.942l0.528-0.829l-0.829,0.151
|
||||
c0.151-0.377,0.377-0.754,0.603-1.055c3.166-5.352,7.161-12.212,11.458-20.127l0.377,0.829l0.98-2.035
|
||||
c3.166,4.523,6.634,8.971,10.252,13.267c1.734,2.035,3.543,3.995,5.352,5.955l-1.206,0.075l1.055,0.98
|
||||
c-3.091,0.226-6.332,0.528-9.574,0.829c-2.035,0.226-4.146,0.377-6.257,0.603C250.796,337.037,246.499,337.49,242.428,337.942z
|
||||
M369.297,384.98c-8.971-5.729-18.996-13.795-31.359-24.575c17.564,1.809,31.359,5.654,41.159,11.383
|
||||
c4.297,2.488,7.538,5.051,9.724,7.538c3.618,3.844,4.9,7.312,4.221,9.649c-0.603,2.337-3.241,3.92-6.483,3.92
|
||||
c-1.885,0-3.844-0.452-5.88-1.432c-3.468-1.658-7.086-3.694-10.93-6.181C369.598,385.282,369.448,385.131,369.297,384.98z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-6 -6 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 3L13.7071 2.29289C13.5196 2.10536 13.2652 2 13 2V3ZM14 22C14.5523 22 15 21.5523 15 21C15 20.4477 14.5523 20 14 20V22ZM19 9H20C20 8.73478 19.8946 8.48043 19.7071 8.29289L19 9ZM18 10C18 10.5523 18.4477 11 19 11C19.5523 11 20 10.5523 20 10H18ZM5.21799 19.908L4.32698 20.362H4.32698L5.21799 19.908ZM6.09202 20.782L6.54601 19.891L6.54601 19.891L6.09202 20.782ZM6.09202 3.21799L5.63803 2.32698L5.63803 2.32698L6.09202 3.21799ZM5.21799 4.09202L4.32698 3.63803L4.32698 3.63803L5.21799 4.09202ZM13.109 8.45399L14 8V8L13.109 8.45399ZM13.546 8.89101L14 8L13.546 8.89101ZM17.2299 17.7929C16.8394 18.1834 16.8394 18.8166 17.2299 19.2071C17.6204 19.5976 18.2536 19.5976 18.6441 19.2071L17.2299 17.7929ZM15.0316 15.2507C14.8939 15.7856 15.2159 16.3308 15.7507 16.4684C16.2856 16.6061 16.8308 16.2841 16.9684 15.7493L15.0316 15.2507ZM17.9375 20C17.3852 20 16.9375 20.4477 16.9375 21C16.9375 21.5523 17.3852 22 17.9375 22V20ZM17.9475 22C18.4998 22 18.9475 21.5523 18.9475 21C18.9475 20.4477 18.4998 20 17.9475 20V22ZM13 2H8.2V4H13V2ZM4 6.2V17.8H6V6.2H4ZM8.2 22H14V20H8.2V22ZM19.7071 8.29289L13.7071 2.29289L12.2929 3.70711L18.2929 9.70711L19.7071 8.29289ZM20 10V9H18V10H20ZM4 17.8C4 18.3436 3.99922 18.8114 4.03057 19.195C4.06287 19.5904 4.13419 19.9836 4.32698 20.362L6.10899 19.454C6.0838 19.4045 6.04612 19.3038 6.02393 19.0322C6.00078 18.7488 6 18.3766 6 17.8H4ZM8.2 20C7.62345 20 7.25117 19.9992 6.96784 19.9761C6.69617 19.9539 6.59545 19.9162 6.54601 19.891L5.63803 21.673C6.01641 21.8658 6.40963 21.9371 6.80497 21.9694C7.18864 22.0008 7.65645 22 8.2 22V20ZM4.32698 20.362C4.6146 20.9265 5.07354 21.3854 5.63803 21.673L6.54601 19.891C6.35785 19.7951 6.20487 19.6422 6.10899 19.454L4.32698 20.362ZM8.2 2C7.65645 2 7.18864 1.99922 6.80497 2.03057C6.40963 2.06287 6.01641 2.13419 5.63803 2.32698L6.54601 4.10899C6.59545 4.0838 6.69617 4.04612 6.96784 4.02393C7.25117 4.00078 7.62345 4 8.2 4V2ZM6 6.2C6 5.62345 6.00078 5.25117 6.02393 4.96784C6.04612 4.69617 6.0838 4.59545 6.10899 4.54601L4.32698 3.63803C4.13419 4.01641 4.06287 4.40963 4.03057 4.80497C3.99922 5.18864 4 5.65645 4 6.2H6ZM5.63803 2.32698C5.07354 2.6146 4.6146 3.07354 4.32698 3.63803L6.10899 4.54601C6.20487 4.35785 6.35785 4.20487 6.54601 4.10899L5.63803 2.32698ZM12 3V7.4H14V3H12ZM14.6 10H19V8H14.6V10ZM12 7.4C12 7.66353 11.9992 7.92131 12.0169 8.13823C12.0356 8.36682 12.0797 8.63656 12.218 8.90798L14 8C14.0293 8.05751 14.0189 8.08028 14.0103 7.97537C14.0008 7.85878 14 7.69653 14 7.4H12ZM14.6 8C14.3035 8 14.1412 7.99922 14.0246 7.9897C13.9197 7.98113 13.9425 7.9707 14 8L13.092 9.78201C13.3634 9.92031 13.6332 9.96438 13.8618 9.98305C14.0787 10.0008 14.3365 10 14.6 10V8ZM12.218 8.90798C12.4097 9.2843 12.7157 9.59027 13.092 9.78201L14 8V8L12.218 8.90798ZM18.937 16C18.937 16.1732 18.8915 16.3053 18.6175 16.5697C18.4638 16.718 18.2828 16.8653 18.0319 17.074C17.7936 17.2723 17.5141 17.5087 17.2299 17.7929L18.6441 19.2071C18.86 18.9913 19.0805 18.8033 19.3109 18.6116C19.5287 18.4305 19.7852 18.2223 20.0065 18.0087C20.4825 17.5493 20.937 16.9314 20.937 16H18.937ZM17.937 15C18.4893 15 18.937 15.4477 18.937 16H20.937C20.937 14.3431 19.5938 13 17.937 13V15ZM16.9684 15.7493C17.0795 15.3177 17.4724 15 17.937 15V13C16.5377 13 15.3645 13.957 15.0316 15.2507L16.9684 15.7493ZM17.9375 22H17.9475V20H17.9375V22Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-8 -8 48 48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="a" x1="4.494" y1="-2092.086" x2="13.832" y2="-2075.914" gradientTransform="translate(0 2100)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#18884f"/><stop offset="0.5" stop-color="#117e43"/><stop offset="1" stop-color="#0b6631"/></linearGradient></defs><title>file_type_excel</title><path d="M19.581,15.35,8.512,13.4V27.809A1.192,1.192,0,0,0,9.705,29h19.1A1.192,1.192,0,0,0,30,27.809h0V22.5Z" style="fill:#185c37"/><path d="M19.581,3H9.705A1.192,1.192,0,0,0,8.512,4.191h0V9.5L19.581,16l5.861,1.95L30,16V9.5Z" style="fill:#21a366"/><path d="M8.512,9.5H19.581V16H8.512Z" style="fill:#107c41"/><path d="M16.434,8.2H8.512V24.45h7.922a1.2,1.2,0,0,0,1.194-1.191V9.391A1.2,1.2,0,0,0,16.434,8.2Z" style="opacity:0.10000000149011612;isolation:isolate"/><path d="M15.783,8.85H8.512V25.1h7.271a1.2,1.2,0,0,0,1.194-1.191V10.041A1.2,1.2,0,0,0,15.783,8.85Z" style="opacity:0.20000000298023224;isolation:isolate"/><path d="M15.783,8.85H8.512V23.8h7.271a1.2,1.2,0,0,0,1.194-1.191V10.041A1.2,1.2,0,0,0,15.783,8.85Z" style="opacity:0.20000000298023224;isolation:isolate"/><path d="M15.132,8.85H8.512V23.8h6.62a1.2,1.2,0,0,0,1.194-1.191V10.041A1.2,1.2,0,0,0,15.132,8.85Z" style="opacity:0.20000000298023224;isolation:isolate"/><path d="M3.194,8.85H15.132a1.193,1.193,0,0,1,1.194,1.191V21.959a1.193,1.193,0,0,1-1.194,1.191H3.194A1.192,1.192,0,0,1,2,21.959V10.041A1.192,1.192,0,0,1,3.194,8.85Z" style="fill:url(#a)"/><path d="M5.7,19.873l2.511-3.884-2.3-3.862H7.758L9.013,14.6c.116.234.2.408.238.524h.017c.082-.188.169-.369.26-.546l1.342-2.447h1.7l-2.359,3.84,2.419,3.905H10.821l-1.45-2.711A2.355,2.355,0,0,1,9.2,16.8H9.176a1.688,1.688,0,0,1-.168.351L7.515,19.873Z" style="fill:#fff"/><path d="M28.806,3H19.581V9.5H30V4.191A1.192,1.192,0,0,0,28.806,3Z" style="fill:#33c481"/><path d="M19.581,16H30v6.5H19.581Z" style="fill:#107c41"/></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,5 +1,7 @@
|
||||
import { Filter } from 'src/api/filter-service';
|
||||
import { Product } from 'src/api/product-service';
|
||||
|
||||
import { Doc } from 'src/services/document-service';
|
||||
import { Product } from 'src/services/product-service';
|
||||
import { Attribute, Type } from 'src/services/attribute-service';
|
||||
|
||||
import {
|
||||
_id,
|
||||
@@ -12,6 +14,7 @@ import {
|
||||
_postTitles,
|
||||
_description,
|
||||
_productNames,
|
||||
_base64,
|
||||
} from './_mock';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -212,6 +215,8 @@ export const _notifications = [
|
||||
},
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const _products: Product[] = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -225,7 +230,9 @@ export const _products: Product[] = [
|
||||
}
|
||||
];
|
||||
|
||||
export const _filters: Filter[] = [
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const _attributes: Attribute[] = [
|
||||
{ id: 1, label: 'Rechnungsnummer', name: 'invoiceNumber', type: 'VARCHAR' },
|
||||
{ id: 2, label: 'Kundenname', name: 'customerName', type: 'INTEGER' },
|
||||
{ id: 3, label: 'Startdatum', name: 'startDate', type: 'DATE' },
|
||||
@@ -238,4 +245,320 @@ export const _filters: Filter[] = [
|
||||
{ id: 10, label: 'Erstellungsdatum', name: 'createdAt', type: 'DATE' },
|
||||
{ id: 11, label: 'Lieferzeit', name: 'deliveryTime', type: 'TIME' },
|
||||
{ id: 12, label: 'Letzte Aktualisierung', name: 'lastUpdated', type: 'DATETIME' }
|
||||
];
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const _documents: Doc[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "example1.pdf",
|
||||
addedWhen: new Date("2024-01-12T10:00:00Z"),
|
||||
addedWho: "TekH",
|
||||
attributes: [
|
||||
{
|
||||
name: "invoiceNumber",
|
||||
serilizedValue: "INV-20250714-001",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "customerName",
|
||||
serilizedValue: "John Doe",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "startDate",
|
||||
serilizedValue: "2025-07-01",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "endDate",
|
||||
serilizedValue: "2025-07-14",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "minAmount",
|
||||
serilizedValue: "100.50",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "maxAmount",
|
||||
serilizedValue: "1000.00",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "taxIncluded",
|
||||
serilizedValue: "true",
|
||||
type: 'BOOLEAN'
|
||||
},
|
||||
{
|
||||
name: "createdAt",
|
||||
serilizedValue: "2025-07-10",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "deliveryTime",
|
||||
serilizedValue: "15:30:00",
|
||||
type: 'TIME'
|
||||
},
|
||||
{
|
||||
name: "lastUpdated",
|
||||
serilizedValue: "2025-07-14T10:45:00",
|
||||
type: 'DATETIME'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "example2.pdf",
|
||||
addedWhen: new Date("2024-02-03T09:30:00Z"),
|
||||
addedWho: "bob",
|
||||
changedWhen: new Date("2024-03-15T12:00:00Z"),
|
||||
changedWho: "KammM",
|
||||
attributes: [
|
||||
{
|
||||
name: "invoiceNumber",
|
||||
serilizedValue: "INV-20250701-007",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "customerName",
|
||||
serilizedValue: "Jane Smith",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "startDate",
|
||||
serilizedValue: "2025-06-01",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "endDate",
|
||||
serilizedValue: "2025-06-30",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "minAmount",
|
||||
serilizedValue: "250.00",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "maxAmount",
|
||||
serilizedValue: "850.75",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "taxIncluded",
|
||||
serilizedValue: "false",
|
||||
type: 'BOOLEAN'
|
||||
},
|
||||
{
|
||||
name: "createdAt",
|
||||
serilizedValue: "2025-06-15",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "deliveryTime",
|
||||
serilizedValue: "09:00:00",
|
||||
type: 'TIME'
|
||||
},
|
||||
{
|
||||
name: "lastUpdated",
|
||||
serilizedValue: "2025-06-30T14:15:00",
|
||||
type: 'DATETIME'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "document1.docx",
|
||||
addedWhen: new Date("2023-12-20T14:45:00Z"),
|
||||
addedWho: "SchreiberM",
|
||||
attributes: [
|
||||
{
|
||||
name: "invoiceNumber",
|
||||
serilizedValue: "INV-20250620-043",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "customerName",
|
||||
serilizedValue: "Max Mustermann",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "startDate",
|
||||
serilizedValue: "2025-05-01",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "endDate",
|
||||
serilizedValue: "2025-05-15",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "maxAmount",
|
||||
serilizedValue: "600.00",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "taxIncluded",
|
||||
serilizedValue: "true",
|
||||
type: 'BOOLEAN'
|
||||
},
|
||||
{
|
||||
name: "createdAt",
|
||||
serilizedValue: "2025-05-02",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "lastUpdated",
|
||||
serilizedValue: "2025-05-15T11:00:00",
|
||||
type: 'DATETIME'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "spreadsheet1.xlsx",
|
||||
addedWhen: new Date("2024-05-01T08:15:00Z"),
|
||||
addedWho: "KammM",
|
||||
changedWhen: new Date("2024-06-10T16:20:00Z"),
|
||||
changedWho: "OlgunR",
|
||||
attributes: [
|
||||
{
|
||||
name: "invoiceNumber",
|
||||
serilizedValue: "INV-20250410-021",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "startDate",
|
||||
serilizedValue: "2025-04-01",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "endDate",
|
||||
serilizedValue: "2025-04-30",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "minAmount",
|
||||
serilizedValue: "150.99",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "maxAmount",
|
||||
serilizedValue: "999.99",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "createdAt",
|
||||
serilizedValue: "2025-04-15",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "deliveryTime",
|
||||
serilizedValue: "17:45:00",
|
||||
type: 'TIME'
|
||||
},
|
||||
{
|
||||
name: "lastUpdated",
|
||||
serilizedValue: "2025-04-30T18:30:00",
|
||||
type: 'DATETIME'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "report.docx",
|
||||
addedWhen: new Date("2024-04-17T11:25:00Z"),
|
||||
addedWho: "SchreiberM",
|
||||
attributes: [
|
||||
{
|
||||
name: "invoiceNumber",
|
||||
serilizedValue: "INV-20250305-099",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "customerName",
|
||||
serilizedValue: "Ali Veli",
|
||||
type: 'VARCHAR'
|
||||
},
|
||||
{
|
||||
name: "startDate",
|
||||
serilizedValue: "2025-03-01",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "endDate",
|
||||
serilizedValue: "2025-03-20",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "minAmount",
|
||||
serilizedValue: "75.00",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "maxAmount",
|
||||
serilizedValue: "500.00",
|
||||
type: 'DECIMAL'
|
||||
},
|
||||
{
|
||||
name: "taxIncluded",
|
||||
serilizedValue: "false",
|
||||
type: 'BOOLEAN'
|
||||
},
|
||||
{
|
||||
name: "createdAt",
|
||||
serilizedValue: "2025-03-02",
|
||||
type: 'DATE'
|
||||
},
|
||||
{
|
||||
name: "deliveryTime",
|
||||
serilizedValue: "08:20:00",
|
||||
type: 'TIME'
|
||||
},
|
||||
{
|
||||
name: "lastUpdated",
|
||||
serilizedValue: "2025-03-20T09:30:00",
|
||||
type: 'DATETIME'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
.map(doc => ({
|
||||
...doc,
|
||||
attributes: doc.attributes?.map(attr => ({
|
||||
...attr,
|
||||
type: attr.type as Type
|
||||
}))
|
||||
}))
|
||||
.map(doc => Doc.map(doc));
|
||||
|
||||
function base64ToUint8Array(base64: string): Uint8Array {
|
||||
const binaryString = atob(base64); // Decode base64 to binary string
|
||||
const len = binaryString.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function _genFile(name: string): Uint8Array | undefined {
|
||||
switch (name) {
|
||||
case "example1.pdf":
|
||||
return base64ToUint8Array(_base64.example1_pdf);
|
||||
case "example2.pdf":
|
||||
return base64ToUint8Array(_base64.example2_pdf);
|
||||
case "document1.docx":
|
||||
return base64ToUint8Array(_base64.document1_docx);
|
||||
case "spreadsheet1.xlsx":
|
||||
return base64ToUint8Array(_base64.spreadsheet1_xlsx);
|
||||
case "report.docx":
|
||||
return base64ToUint8Array(_base64.report_docx);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,36 +0,0 @@
|
||||
import { _filters } from 'src/_mock/_data';
|
||||
|
||||
export type Type = 'BOOLEAN' | 'DATE' | 'TIME' | 'DATETIME' | 'VARCHAR' | 'INTEGER' | 'DECIMAL';
|
||||
|
||||
export const filterTypes: Type[] = [
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
'TIME',
|
||||
'DATETIME',
|
||||
'VARCHAR',
|
||||
'INTEGER',
|
||||
'DECIMAL'
|
||||
];
|
||||
|
||||
export type FilterCreateDto = {
|
||||
label?: string | undefined;
|
||||
name: string;
|
||||
type: Type;
|
||||
};
|
||||
|
||||
export type Filter = FilterCreateDto & {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export function getFiltersAsync(): Promise<Filter[]> {
|
||||
return Promise.resolve(_filters);
|
||||
}
|
||||
|
||||
export function createFiltersAsync(filter: FilterCreateDto): Promise<Filter> {
|
||||
const newFilter: Filter = {
|
||||
...filter,
|
||||
id: _filters.length + 1
|
||||
};
|
||||
_filters.push(newFilter);
|
||||
return Promise.resolve(newFilter);
|
||||
}
|
||||
17
src/client/dd-hub-react/src/common/json-viewer.tsx
Normal file
17
src/client/dd-hub-react/src/common/json-viewer.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
type JsonViewerProps = {
|
||||
data: any;
|
||||
};
|
||||
|
||||
export function JsonViewer({ data }: JsonViewerProps) {
|
||||
return (
|
||||
<pre style={{
|
||||
backgroundColor: '#f4f4f4',
|
||||
padding: '1rem',
|
||||
borderRadius: '5px',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}>
|
||||
{JSON.stringify(data)}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
@@ -9,8 +9,7 @@ export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<title>{`Document Search - ${CONFIG.appName}`}</title>
|
||||
|
||||
<DocSearchView posts={_posts} />
|
||||
<DocSearchView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,211 +1,213 @@
|
||||
import type { CardProps } from '@mui/material/Card';
|
||||
import type { IconifyName } from 'src/components/iconify';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { varAlpha } from 'minimal-shared/utils';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Link from '@mui/material/Link';
|
||||
import Card from '@mui/material/Card';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import { fDate } from 'src/utils/format-time';
|
||||
import { fShortenNumber } from 'src/utils/format-number';
|
||||
import { Doc } from 'src/services/document-service';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { SvgColor } from 'src/components/svg-color';
|
||||
|
||||
import DocFullView from './view/doc-full-view';
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type IDocItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
coverUrl: string;
|
||||
totalViews: number;
|
||||
description: string;
|
||||
totalShares: number;
|
||||
totalComments: number;
|
||||
totalFavorites: number;
|
||||
postedAt: string | number | null;
|
||||
author: {
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function DocItem({
|
||||
sx,
|
||||
post,
|
||||
latestDoc,
|
||||
latestDocLarge,
|
||||
...other
|
||||
sx,
|
||||
doc,
|
||||
long,
|
||||
large,
|
||||
...other
|
||||
}: CardProps & {
|
||||
post: IDocItem;
|
||||
latestDoc: boolean;
|
||||
latestDocLarge: boolean;
|
||||
doc: Doc;
|
||||
long: boolean;
|
||||
large: boolean;
|
||||
}) {
|
||||
const renderAvatar = (
|
||||
<Avatar
|
||||
alt={post.author.name}
|
||||
src={post.author.avatarUrl}
|
||||
sx={{
|
||||
left: 24,
|
||||
zIndex: 9,
|
||||
bottom: -24,
|
||||
position: 'absolute',
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
top: 24,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
//#region render Avatar
|
||||
// const renderAvatar = (
|
||||
// <Avatar
|
||||
// alt={doc.addedWho}
|
||||
// src={doc.addedWho}
|
||||
// sx={{
|
||||
// left: 24,
|
||||
// zIndex: 9,
|
||||
// bottom: -24,
|
||||
// position: 'absolute',
|
||||
// ...((large || long) && {
|
||||
// top: 24,
|
||||
// }),
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
//#endregion
|
||||
|
||||
const renderTitle = (
|
||||
<Link
|
||||
color="inherit"
|
||||
variant="subtitle2"
|
||||
underline="hover"
|
||||
sx={{
|
||||
height: 44,
|
||||
overflow: 'hidden',
|
||||
WebkitLineClamp: 2,
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
...(latestDocLarge && { typography: 'h5', height: 60 }),
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{post.title}
|
||||
</Link>
|
||||
);
|
||||
const [openViewDoc, setOpenViewDoc] = useState(false);
|
||||
|
||||
const renderInfo = (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 3,
|
||||
gap: 1.5,
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
color: 'text.disabled',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{ number: post.totalComments, icon: 'solar:chat-round-dots-bold' },
|
||||
{ number: post.totalViews, icon: 'solar:eye-bold' },
|
||||
{ number: post.totalShares, icon: 'solar:share-bold' },
|
||||
].map((info, _index) => (
|
||||
<Box
|
||||
key={_index}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
opacity: 0.64,
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
const renderTitle = (
|
||||
<Link
|
||||
color="inherit"
|
||||
variant="subtitle2"
|
||||
underline="hover"
|
||||
sx={{
|
||||
height: 44,
|
||||
overflow: 'hidden',
|
||||
WebkitLineClamp: 2,
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
...(large && { typography: 'h5', height: 60 }),
|
||||
...((large || long) && {
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Iconify width={16} icon={info.icon as IconifyName} sx={{ mr: 0.5 }} />
|
||||
<Typography variant="caption">{fShortenNumber(info.number)}</Typography>
|
||||
{doc.name}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const renderInfo = (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 3,
|
||||
gap: 1.5,
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
color: 'text.disabled',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{ data: doc.addedWho, icon: 'mdi:user' },
|
||||
{ data: doc.getChangedInfo(', '), icon: 'material-symbols:change-circle-rounded' }
|
||||
].filter(info => info.data).map((info, _index) => (
|
||||
<Box
|
||||
key={_index}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...((large || long) && {
|
||||
opacity: 0.64,
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Iconify width={16} icon={info.icon as IconifyName} sx={{ mr: 0.5 }} />
|
||||
<Typography variant="caption">{info.data?.toString()}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
);
|
||||
|
||||
const renderCover = (
|
||||
<Box
|
||||
component="img"
|
||||
alt={post.title}
|
||||
src={post.coverUrl}
|
||||
sx={{
|
||||
top: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
objectFit: 'cover',
|
||||
position: 'absolute',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const renderCover = (
|
||||
<Box
|
||||
component="img"
|
||||
alt={doc.name}
|
||||
src={doc.iconSrc}
|
||||
onError={(e) => {
|
||||
e.currentTarget.onerror = null;
|
||||
e.currentTarget.src = 'assets/icons/file/unknown.svg';
|
||||
}}
|
||||
sx={{
|
||||
top: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
objectFit: 'cover',
|
||||
position: 'absolute',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderDate = (
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{
|
||||
mb: 1,
|
||||
color: 'text.disabled',
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
opacity: 0.48,
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{fDate(post.postedAt)}
|
||||
</Typography>
|
||||
);
|
||||
const renderDate = (
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{
|
||||
mb: 1,
|
||||
color: 'text.disabled',
|
||||
...((large || long) && {
|
||||
opacity: 0.48,
|
||||
color: 'common.white',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{doc.addedWhen.toLocaleDateString('de-DE')}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
const renderShape = (
|
||||
<SvgColor
|
||||
src="/assets/icons/shape-avatar.svg"
|
||||
sx={{
|
||||
left: 0,
|
||||
width: 88,
|
||||
zIndex: 9,
|
||||
height: 36,
|
||||
bottom: -16,
|
||||
position: 'absolute',
|
||||
color: 'background.paper',
|
||||
...((latestDocLarge || latestDoc) && { display: 'none' }),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const renderShape = (
|
||||
<SvgColor
|
||||
src="/assets/icons/shape-avatar.svg"
|
||||
sx={{
|
||||
left: 0,
|
||||
width: 88,
|
||||
zIndex: 9,
|
||||
height: 36,
|
||||
bottom: -16,
|
||||
position: 'absolute',
|
||||
color: 'background.paper',
|
||||
...((large || long) && { display: 'none' }),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card sx={sx} {...other}>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
position: 'relative',
|
||||
pt: 'calc(100% * 3 / 4)',
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
pt: 'calc(100% * 4 / 3)',
|
||||
'&:after': {
|
||||
top: 0,
|
||||
content: "''",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
bgcolor: varAlpha(theme.palette.grey['900Channel'], 0.72),
|
||||
},
|
||||
}),
|
||||
...(latestDocLarge && {
|
||||
pt: {
|
||||
xs: 'calc(100% * 4 / 3)',
|
||||
sm: 'calc(100% * 3 / 4.66)',
|
||||
},
|
||||
}),
|
||||
})}
|
||||
>
|
||||
{renderShape}
|
||||
{renderAvatar}
|
||||
{renderCover}
|
||||
</Box>
|
||||
sx ??= {
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.03)',
|
||||
boxShadow: 6,
|
||||
}
|
||||
}
|
||||
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
p: theme.spacing(6, 3, 3, 3),
|
||||
...((latestDocLarge || latestDoc) && {
|
||||
width: 1,
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
}),
|
||||
})}
|
||||
>
|
||||
{renderDate}
|
||||
{renderTitle}
|
||||
{renderInfo}
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<DocFullView data={doc.data} open={openViewDoc} handleClose={() => setOpenViewDoc(false)} format={doc.extension} />
|
||||
<Card sx={sx} {...other} onClick={() => setOpenViewDoc(true)}>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
position: 'relative',
|
||||
pt: 'calc(100% * 3 / 4)',
|
||||
...((large || long) && {
|
||||
pt: 'calc(100% * 4 / 3)',
|
||||
'&:after': {
|
||||
top: 0,
|
||||
content: "''",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
bgcolor: varAlpha(theme.palette.grey['900Channel'], 0.72),
|
||||
},
|
||||
}),
|
||||
...(large && {
|
||||
pt: {
|
||||
xs: 'calc(100% * 4 / 3)',
|
||||
sm: 'calc(100% * 3 / 4.66)',
|
||||
},
|
||||
}),
|
||||
})}
|
||||
>
|
||||
{renderShape}
|
||||
{renderCover}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
p: theme.spacing(6, 3, 3, 3),
|
||||
...((large || long) && {
|
||||
width: 1,
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
}),
|
||||
})}
|
||||
>
|
||||
{renderDate}
|
||||
{renderTitle}
|
||||
{renderInfo}
|
||||
</Box>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ import TextField from '@mui/material/TextField';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { Doc } from 'src/services/document-service';
|
||||
|
||||
import type { IDocItem } from './doc-item';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type DocSearchProps = {
|
||||
posts: IDocItem[];
|
||||
posts: Doc[];
|
||||
sx?: SxProps<Theme>;
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ export function DocSearch({ posts, sx }: DocSearchProps) {
|
||||
},
|
||||
}}
|
||||
options={posts}
|
||||
getOptionLabel={(post) => post.title}
|
||||
getOptionLabel={(post) => post.name}
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
import { useState } from 'react';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { useState, useMemo } from 'react';
|
||||
|
||||
import TextField from '@mui/material/TextField';
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type BoolFilterProps = {
|
||||
type NumFilterProps = {
|
||||
label: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const isNumbers = (str: string) => /^[0-9]*$/.test(str);
|
||||
|
||||
export function IntFilter({ label }: BoolFilterProps) {
|
||||
export function IntFilter({ label, onChange }: NumFilterProps) {
|
||||
const [val, setVal] = useState("");
|
||||
|
||||
const onInputChange = (event: any) => {
|
||||
const value = event.target.value;
|
||||
if (isNumbers(value)) {
|
||||
setVal(value);
|
||||
debouncedChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedChange = useMemo(() =>
|
||||
debounce((value: string) => {
|
||||
onChange?.(value);
|
||||
}, 1000)
|
||||
, [onChange]);
|
||||
|
||||
return <TextField label={label} value={val} onChange={onInputChange} variant="filled" />;
|
||||
}
|
||||
|
||||
export function DecimalFilter({ label }: BoolFilterProps) {
|
||||
return <TextField type="number" label={label} variant="filled" />;
|
||||
export function DecimalFilter({ label, onChange }: NumFilterProps) {
|
||||
const debouncedChange = useMemo(() =>
|
||||
debounce((value: string) => {
|
||||
onChange?.(value);
|
||||
}, 1000)
|
||||
, [onChange]);
|
||||
|
||||
return <TextField type="number" label={label} variant="filled" onChange={e => debouncedChange(e.target.value)} />;
|
||||
}
|
||||
@@ -1,10 +1,20 @@
|
||||
import { useMemo } from 'react';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import TextField from '@mui/material/TextField';
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type TextFilterProps = {
|
||||
label: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export function TextFilter({ label }: TextFilterProps) {
|
||||
return <TextField label={label} variant="filled" />;
|
||||
export function TextFilter({ label, onChange }: TextFilterProps) {
|
||||
const debouncedChange = useMemo(() =>
|
||||
debounce((value: string) => {
|
||||
onChange?.(value);
|
||||
}, 1000)
|
||||
, [onChange]);
|
||||
|
||||
return <TextField label={label} variant="filled" onChange={e => debouncedChange(e.target.value)} />;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import { Iconify } from 'src/components/iconify/iconify';
|
||||
|
||||
import { createFiltersAsync, FilterCreateDto, filterTypes, Type } from '../../../api/filter-service';
|
||||
import { createAttributes, filterTypes, Type } from '../../../services/attribute-service';
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
@@ -27,9 +27,16 @@ type ModalProps = {
|
||||
}
|
||||
|
||||
export default function CreateFilterModal({ open, handleClose }: ModalProps) {
|
||||
const [name, setName] = useState<string | undefined>(undefined);
|
||||
const [label, setLabel] = useState<string | undefined>(undefined);
|
||||
const [selectedType, setSelectedType] = useState<Type | undefined>(undefined);
|
||||
const [name, setName] = useState<string | undefined>('');
|
||||
const [label, setLabel] = useState<string | undefined>('');
|
||||
const [selectedType, setSelectedType] = useState<Type | null>(null);
|
||||
|
||||
function closeReset() {
|
||||
handleClose();
|
||||
setName('');
|
||||
setLabel('')
|
||||
setSelectedType(null)
|
||||
}
|
||||
|
||||
async function tryCreateFilter(): Promise<any> {
|
||||
if (!name) {
|
||||
@@ -39,38 +46,36 @@ export default function CreateFilterModal({ open, handleClose }: ModalProps) {
|
||||
alert('No type.');
|
||||
}
|
||||
else {
|
||||
await createFiltersAsync({ name: name, type: selectedType, label: label }).then(() => handleClose());
|
||||
await createAttributes({ name: name, type: selectedType, label: label }).then(closeReset);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<TextField label="Label" variant="filled" value={name} onChange={e => setName(e.target.value)} />
|
||||
<TextField label="Name" variant="filled" value={label} onChange={e => setLabel(e.target.value)} />
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
options={filterTypes}
|
||||
value={selectedType}
|
||||
onChange={(event, newValue) => setSelectedType(newValue ?? undefined)}
|
||||
renderInput={params => <TextField {...params} label="Type" variant="filled" />}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="inherit"
|
||||
startIcon={<Iconify icon="mingcute:add-line" />}
|
||||
onClick={() => tryCreateFilter()}
|
||||
>
|
||||
Add filter
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={closeReset}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<TextField label="Label" variant="filled" value={name} onChange={e => setName(e.target.value)} />
|
||||
<TextField label="Name" variant="filled" value={label} onChange={e => setLabel(e.target.value)} />
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
options={filterTypes}
|
||||
value={selectedType}
|
||||
onChange={(event, newValue) => setSelectedType(newValue)}
|
||||
renderInput={params => <TextField {...params} label="Type" variant="filled" />}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="inherit"
|
||||
startIcon={<Iconify icon="mingcute:add-line" />}
|
||||
onClick={() => tryCreateFilter()}
|
||||
>
|
||||
Add filter
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Modal from '@mui/material/Modal';
|
||||
|
||||
import { FileFormat } from 'src/services/document-service';
|
||||
|
||||
const getMimeType = (format: FileFormat): string => {
|
||||
switch (format) {
|
||||
case 'pdf': return 'application/pdf';
|
||||
case 'docx': return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
||||
case 'xlsx': return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
case 'csv': return 'text/csv';
|
||||
case 'pptx': return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
||||
case 'txt': return 'text/plain';
|
||||
case 'json': return 'application/json';
|
||||
case 'xml': return 'application/xml';
|
||||
case 'html': return 'text/html';
|
||||
case 'jpg': return 'image/jpeg';
|
||||
case 'png': return 'image/png';
|
||||
case 'svg': return 'image/svg+xml';
|
||||
case 'zip': return 'application/zip';
|
||||
case 'md': return 'text/markdown';
|
||||
default: return 'application/octet-stream';
|
||||
}
|
||||
};
|
||||
|
||||
type DocFullViewProps = {
|
||||
data: Uint8Array | Promise<Uint8Array>;
|
||||
format: FileFormat;
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: '90%',
|
||||
height: '90%',
|
||||
bgcolor: 'transparent',
|
||||
};
|
||||
|
||||
export default function DocFullView({ data, format, open, handleClose }: DocFullViewProps) {
|
||||
const [objectUrl, setObjectUrl] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
let url: string | null = null;
|
||||
|
||||
const processData = async () => {
|
||||
try {
|
||||
const resolvedData = await data;
|
||||
const mimeType = getMimeType(format);
|
||||
const blob = new Blob([resolvedData], { type: mimeType });
|
||||
url = URL.createObjectURL(blob);
|
||||
|
||||
if (isMounted) {
|
||||
setObjectUrl(url);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Data resolution error:', err);
|
||||
}
|
||||
};
|
||||
|
||||
processData();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
if (url) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
}, [data, format]);
|
||||
|
||||
const renderContent = () => {
|
||||
if (!objectUrl || !format) return null;
|
||||
|
||||
if (['pdf', 'html', 'txt', 'json', 'xml', 'md'].includes(format)) {
|
||||
return (
|
||||
<iframe
|
||||
src={objectUrl}
|
||||
style={{ width: '100%', height: '100%', border: 'none' }}
|
||||
title={`Viewer for ${format}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (['jpg', 'png', 'svg'].includes(format)) {
|
||||
return (
|
||||
<img
|
||||
src={objectUrl}
|
||||
alt="Document Preview"
|
||||
style={{ maxWidth: '100%', maxHeight: '100%' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Download link for unsupported preview formats
|
||||
style.bgcolor = 'lightgray'
|
||||
return (
|
||||
<Box textAlign="center">
|
||||
<p>Dieser Dateityp kann nicht angezeigt werden. Sie können es unten herunterladen:</p>
|
||||
<a href={objectUrl} download={`document.${format}`}>Datei herunterladen</a>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
{renderContent()}
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import Typography from '@mui/material/Typography';
|
||||
import Pagination from '@mui/material/Pagination';
|
||||
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { Filter, getFiltersAsync } from 'src/api/filter-service';
|
||||
import docService, { Doc } from 'src/services/document-service';
|
||||
import { Attribute, getAttributes } from 'src/services/attribute-service';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
|
||||
@@ -17,30 +18,61 @@ import { TextFilter } from '../text-filter';
|
||||
import CreateFilterModal from './create-filter-modal';
|
||||
import { DecimalFilter, IntFilter } from '../num-filter';
|
||||
import { DateFilter, DateTimeFilter, TimeFilter } from '../date-filter';
|
||||
|
||||
import type { IDocItem } from '../doc-item';
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Props = {
|
||||
posts: IDocItem[];
|
||||
};
|
||||
|
||||
export function DocSearchView({ posts }: Props) {
|
||||
export function DocSearchView() {
|
||||
const [sortBy, setSortBy] = useState('latest');
|
||||
|
||||
const [filters, setFilters] = useState<Filter[]>([])
|
||||
const [filters, setFilters] = useState<Attribute[]>([])
|
||||
|
||||
const handleSort = useCallback((newSort: string) => {
|
||||
setSortBy(newSort);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getFiltersAsync().then((res) => {
|
||||
getAttributes().then((res) => {
|
||||
setFilters(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [openCreateFilterModal, setOpenCreateFilterModal] = useState(false);
|
||||
|
||||
const [docs, setDocs] = useState<Doc[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
docService.get({}).then((res) => {
|
||||
setDocs(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
//#region attributes
|
||||
const [attributes, setAttributes] = useState<Record<string, string>>({});
|
||||
|
||||
useEffect(() => {
|
||||
docService.getByAttribute(attributes).then(setDocs);
|
||||
}, [attributes]);
|
||||
|
||||
function setAttribute(name: string, serilizedValue: string) {
|
||||
setAttributes(prev => ({
|
||||
...prev,
|
||||
[name]: serilizedValue
|
||||
}));
|
||||
}
|
||||
|
||||
function removeAttribute(name: string) {
|
||||
setAttributes(prev => {
|
||||
const { [name]: _, ...rest } = prev;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
|
||||
function updateAttribute(name: string, serilizedValue?: string | null) {
|
||||
if (serilizedValue)
|
||||
setAttribute(name, serilizedValue);
|
||||
else
|
||||
removeAttribute(name);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region example components
|
||||
// <Box
|
||||
@@ -51,7 +83,7 @@ export function DocSearchView({ posts }: Props) {
|
||||
// justifyContent: 'space-between',
|
||||
// }}
|
||||
// >
|
||||
// <DocSearch posts={posts} />
|
||||
// <DocSearch docs={docs} />
|
||||
// <DocSort
|
||||
// sortBy={sortBy}
|
||||
// onSort={handleSort}
|
||||
@@ -81,13 +113,14 @@ export function DocSearchView({ posts }: Props) {
|
||||
variant="contained"
|
||||
color="inherit"
|
||||
startIcon={<Iconify icon="mingcute:add-line" />}
|
||||
onClick={() => setOpenModal(true)}
|
||||
onClick={() => setOpenCreateFilterModal(true)}
|
||||
loading={openCreateFilterModal}
|
||||
>
|
||||
New filter
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<CreateFilterModal open={openModal} handleClose={() => setOpenModal(false)} />
|
||||
<CreateFilterModal open={openCreateFilterModal} handleClose={() => setOpenCreateFilterModal(false)} />
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{filters.map((filter, index) => {
|
||||
@@ -98,13 +131,13 @@ export function DocSearchView({ posts }: Props) {
|
||||
filterComp = <BoolFilter label={filter.label ?? filter.name} />
|
||||
break;
|
||||
case 'INTEGER':
|
||||
filterComp = <IntFilter label={filter.label ?? filter.name} />
|
||||
filterComp = <IntFilter label={filter.label ?? filter.name} onChange={value => updateAttribute(filter.name, value)} />
|
||||
break;
|
||||
case 'DECIMAL':
|
||||
filterComp = <DecimalFilter label={filter.label ?? filter.name} />
|
||||
filterComp = <DecimalFilter label={filter.label ?? filter.name} onChange={value => updateAttribute(filter.name, value)} />
|
||||
break;
|
||||
case 'VARCHAR':
|
||||
filterComp = <TextFilter label={filter.label ?? filter.name} />
|
||||
filterComp = <TextFilter label={filter.label ?? filter.name} onChange={value => updateAttribute(filter.name, value)} />
|
||||
break;
|
||||
case 'DATE':
|
||||
filterComp = <DateFilter label={filter.label ?? filter.name} />
|
||||
@@ -145,20 +178,20 @@ export function DocSearchView({ posts }: Props) {
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{posts.map((post, index) => {
|
||||
const latestDocLarge = index === 0;
|
||||
const latestDoc = index === 1 || index === 2;
|
||||
{docs.map((doc, index) => {
|
||||
const large = false;
|
||||
const long = false;
|
||||
|
||||
return (
|
||||
<Grid
|
||||
key={post.id}
|
||||
key={doc.id}
|
||||
size={{
|
||||
xs: 12,
|
||||
sm: latestDocLarge ? 12 : 6,
|
||||
md: latestDocLarge ? 6 : 3,
|
||||
sm: large ? 12 : 6,
|
||||
md: large ? 6 : 3,
|
||||
}}
|
||||
>
|
||||
<DocItem post={post} latestDoc={latestDoc} latestDocLarge={latestDocLarge} />
|
||||
<DocItem doc={doc} long={long} large={large} />
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -8,7 +8,7 @@ import Typography from '@mui/material/Typography';
|
||||
|
||||
import { fCurrency } from 'src/utils/format-number';
|
||||
|
||||
import { Product } from 'src/api/product-service';
|
||||
import { Product } from 'src/services/product-service';
|
||||
|
||||
import { Label } from 'src/components/label';
|
||||
import { ColorPreview } from 'src/components/color-utils';
|
||||
|
||||
@@ -7,7 +7,7 @@ import Typography from '@mui/material/Typography';
|
||||
|
||||
import { _products } from 'src/_mock';
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { getProductsAsync, Product } from 'src/api/product-service';
|
||||
import { getProductsAsync, Product } from 'src/services/product-service';
|
||||
|
||||
import { ProductSort } from '../product-sort';
|
||||
import { ProductItem } from '../product-item';
|
||||
|
||||
36
src/client/dd-hub-react/src/services/attribute-service.ts
Normal file
36
src/client/dd-hub-react/src/services/attribute-service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { _attributes } from 'src/_mock/_data';
|
||||
|
||||
export type Type = 'BOOLEAN' | 'DATE' | 'TIME' | 'DATETIME' | 'VARCHAR' | 'INTEGER' | 'DECIMAL';
|
||||
|
||||
export const filterTypes: Type[] = [
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
'TIME',
|
||||
'DATETIME',
|
||||
'VARCHAR',
|
||||
'INTEGER',
|
||||
'DECIMAL'
|
||||
];
|
||||
|
||||
export type AttributeCreateDto = {
|
||||
label?: string | undefined;
|
||||
name: string;
|
||||
type: Type;
|
||||
};
|
||||
|
||||
export type Attribute = AttributeCreateDto & {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export function getAttributes(): Promise<Attribute[]> {
|
||||
return Promise.resolve(_attributes);
|
||||
}
|
||||
|
||||
export function createAttributes(filter: AttributeCreateDto): Promise<Attribute> {
|
||||
const newFilter: Attribute = {
|
||||
...filter,
|
||||
id: _attributes.length + 1
|
||||
};
|
||||
_attributes.push(newFilter);
|
||||
return Promise.resolve(newFilter);
|
||||
}
|
||||
114
src/client/dd-hub-react/src/services/document-service.ts
Normal file
114
src/client/dd-hub-react/src/services/document-service.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { _documents, _genFile } from "src/_mock"
|
||||
|
||||
export type FileFormat =
|
||||
| 'pdf'
|
||||
| 'docx'
|
||||
| 'xlsx'
|
||||
| 'csv'
|
||||
| 'pptx'
|
||||
| 'txt'
|
||||
| 'json'
|
||||
| 'xml'
|
||||
| 'html'
|
||||
| 'jpg'
|
||||
| 'png'
|
||||
| 'svg'
|
||||
| 'zip'
|
||||
| 'md';
|
||||
|
||||
const validExtensions: FileFormat[] = [
|
||||
'pdf', 'docx', 'xlsx', 'csv', 'pptx',
|
||||
'txt', 'json', 'xml', 'html', 'jpg',
|
||||
'png', 'svg', 'zip', 'md'
|
||||
];
|
||||
|
||||
type DocAttribute = {
|
||||
name: string;
|
||||
serilizedValue: string;
|
||||
}
|
||||
|
||||
export class Doc {
|
||||
|
||||
static map(source?: Partial<Doc>): Doc {
|
||||
const doc = new Doc();
|
||||
Object.assign(doc, source);
|
||||
return doc;
|
||||
}
|
||||
|
||||
id!: number;
|
||||
name!: string;
|
||||
addedWhen!: Date;
|
||||
addedWho!: string;
|
||||
changedWhen?: Date;
|
||||
changedWho?: string;
|
||||
attributes: Array<DocAttribute> = [];
|
||||
|
||||
get data(): Promise<Uint8Array> {
|
||||
return Promise.resolve(_genFile(this.name)!);
|
||||
}
|
||||
|
||||
getChangedInfo(separator: string = " | "): string | null {
|
||||
const who = this.changedWho?.trim();
|
||||
const when = this.changedWhen?.toLocaleDateString('de-DE');
|
||||
|
||||
if (!who && !when) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [who, when].filter(Boolean).join(separator);
|
||||
}
|
||||
|
||||
get extension(): FileFormat {
|
||||
const parts = this.name.split('.');
|
||||
if (parts.length > 1 && parts[parts.length - 1].trim() !== '') {
|
||||
const ext = parts[parts.length - 1].toLowerCase();
|
||||
|
||||
if (validExtensions.includes(ext as FileFormat))
|
||||
return ext as FileFormat;
|
||||
}
|
||||
throw new Error(`Invalid or missing file extension in filename: "${this.name}". Supported extensions are: ${validExtensions.join(', ')}.`);
|
||||
}
|
||||
|
||||
get iconSrc(): string {
|
||||
return `assets/icons/file/${this.extension ?? 'unknown'}.svg`;
|
||||
}
|
||||
}
|
||||
|
||||
export type DocQuery = {
|
||||
id?: number | undefined,
|
||||
name?: string | undefined,
|
||||
attributes?: Record<string, string>
|
||||
}
|
||||
|
||||
class DocService {
|
||||
get(query: DocQuery | undefined = undefined): Promise<Doc[]> {
|
||||
let documents = _documents;
|
||||
|
||||
if (query?.id)
|
||||
documents = documents.filter(d => d.id === query.id);
|
||||
|
||||
if (query?.name)
|
||||
documents = documents.filter(d => d.name === query.name);
|
||||
|
||||
for (const name in query?.attributes) {
|
||||
const attr = query.attributes[name];
|
||||
documents = documents.filter(d => d.attributes.find(a => a.name === name)?.serilizedValue.toLowerCase().includes(attr.toLowerCase()));
|
||||
}
|
||||
|
||||
return Promise.resolve(documents);
|
||||
}
|
||||
|
||||
getById(id: number): Promise<Doc[]> {
|
||||
return this.get({ id: id });
|
||||
}
|
||||
|
||||
getByName(name: string): Promise<Doc[]> {
|
||||
return this.get({ name: name });
|
||||
}
|
||||
|
||||
getByAttribute(attributes: Record<string, string>): Promise<Doc[]> {
|
||||
return this.get({ attributes: attributes });
|
||||
}
|
||||
}
|
||||
|
||||
export default new DocService();
|
||||
@@ -586,6 +586,18 @@
|
||||
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
|
||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||
|
||||
"@types/lodash.debounce@^4.0.9":
|
||||
version "4.0.9"
|
||||
resolved "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz"
|
||||
integrity sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
version "4.17.20"
|
||||
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz"
|
||||
integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==
|
||||
|
||||
"@types/node@^18.0.0 || ^20.0.0 || >=22.0.0", "@types/node@^22.14.0":
|
||||
version "22.14.0"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz"
|
||||
@@ -2021,6 +2033,11 @@ locate-path@^6.0.0:
|
||||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
|
||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
||||
|
||||
Reference in New Issue
Block a user