Compare commits
24 Commits
e3c5e84bb7
...
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 |
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { Doc } from 'src/api/document-service';
|
||||
import { Product } from 'src/api/product-service';
|
||||
import { Filter } from 'src/api/attribute-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,
|
||||
@@ -232,7 +232,7 @@ 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' },
|
||||
@@ -249,6 +249,291 @@ export const _filters: Filter[] = [
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
@@ -261,44 +546,19 @@ function base64ToUint8Array(base64: string): Uint8Array {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export const _documents: Doc[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "example1.pdf",
|
||||
data: base64ToUint8Array(_base64.example1_pdf),
|
||||
addedWhen: new Date("2024-01-12T10:00:00Z"),
|
||||
addedWho: "TekH"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "example2.pdf",
|
||||
data: base64ToUint8Array(_base64.example2_pdf),
|
||||
addedWhen: new Date("2024-02-03T09:30:00Z"),
|
||||
addedWho: "bob",
|
||||
changedWhen: new Date("2024-03-15T12:00:00Z"),
|
||||
changedWho: "KammM"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "document1.docx",
|
||||
data: base64ToUint8Array(_base64.document1_docx),
|
||||
addedWhen: new Date("2023-12-20T14:45:00Z"),
|
||||
addedWho: "SchreiberM"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "spreadsheet1.xlsx",
|
||||
data: base64ToUint8Array(_base64.spreadsheet1_xlsx),
|
||||
addedWhen: new Date("2024-05-01T08:15:00Z"),
|
||||
addedWho: "KammM",
|
||||
changedWhen: new Date("2024-06-10T16:20:00Z"),
|
||||
changedWho: "OlgunR"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "report.docx",
|
||||
data: base64ToUint8Array(_base64.report_docx),
|
||||
addedWhen: new Date("2024-04-17T11:25:00Z"),
|
||||
addedWho: "SchreiberM"
|
||||
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;
|
||||
}
|
||||
].map(doc => Doc.map(doc));
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { _documents } from "src/_mock"
|
||||
|
||||
export class Doc {
|
||||
|
||||
static map(source?: Partial<Doc>): Doc {
|
||||
const doc = new Doc();
|
||||
Object.assign(doc, source);
|
||||
return doc;
|
||||
}
|
||||
|
||||
id!: number;
|
||||
name!: string;
|
||||
data!: Uint8Array;
|
||||
addedWhen!: Date;
|
||||
addedWho!: string;
|
||||
changedWhen?: Date;
|
||||
changedWho?: string;
|
||||
|
||||
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(): string | undefined {
|
||||
const parts = this.name.split('.');
|
||||
if (parts.length > 1 && parts[parts.length - 1].trim() !== '') {
|
||||
return parts[parts.length - 1].toLowerCase();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get iconSrc(): string {
|
||||
return `assets/icons/file/${this.extension ?? 'unknown'}.svg`;
|
||||
}
|
||||
}
|
||||
|
||||
export type DocQuery = {
|
||||
id?: number | undefined,
|
||||
name?: string | undefined
|
||||
}
|
||||
|
||||
export function getDocuments(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);
|
||||
|
||||
return Promise.resolve(documents);
|
||||
}
|
||||
|
||||
export function getDocumentById(id: number): Promise<Doc[]> {
|
||||
return getDocuments({ id: id });
|
||||
}
|
||||
|
||||
export function getDocumentByName(name: string): Promise<Doc[]> {
|
||||
return getDocuments({ name: name });
|
||||
}
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { _posts } from 'src/_mock';
|
||||
import { CONFIG } from 'src/config-global';
|
||||
import { Doc, getDocuments } from 'src/api/document-service';
|
||||
|
||||
import { DocSearchView } from 'src/sections/document/view';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function Page() {
|
||||
const [docs, setDocs] = useState<Doc[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getDocuments({}).then((res) => {
|
||||
setDocs(res);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<title>{`Document Search - ${CONFIG.appName}`}</title>
|
||||
|
||||
<DocSearchView docs={docs} />
|
||||
<DocSearchView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import Link from '@mui/material/Link';
|
||||
import Card from '@mui/material/Card';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import { Doc } from 'src/api/document-service';
|
||||
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 function DocItem({
|
||||
@@ -45,7 +47,7 @@ export function DocItem({
|
||||
//#endregion
|
||||
|
||||
const [openViewDoc, setOpenViewDoc] = useState(false);
|
||||
|
||||
|
||||
const renderTitle = (
|
||||
<Link
|
||||
color="inherit"
|
||||
@@ -151,49 +153,61 @@ export function DocItem({
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card sx={sx} {...other}>
|
||||
<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>
|
||||
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),
|
||||
...((large || long) && {
|
||||
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,7 +4,7 @@ import TextField from '@mui/material/TextField';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
|
||||
|
||||
import { Doc } from 'src/api/document-service';
|
||||
import { Doc } from 'src/services/document-service';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
|
||||
|
||||
@@ -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 { createAttributes, filterTypes, Type } from '../../../api/attribute-service';
|
||||
import { createAttributes, filterTypes, Type } from '../../../services/attribute-service';
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import { TextField } from '@mui/material';
|
||||
|
||||
import { Type } from 'src/api/attribute-service';
|
||||
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;
|
||||
data: Uint8Array | Promise<Uint8Array>;
|
||||
format: FileFormat;
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
}
|
||||
@@ -17,28 +37,86 @@ const style = {
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
width: '90%',
|
||||
height: '90%',
|
||||
bgcolor: 'transparent',
|
||||
};
|
||||
|
||||
export default function DocFullView({ data, open, handleClose }: DocFullViewProps) {
|
||||
const [name, setName] = useState<string | undefined>('');
|
||||
const [label, setLabel] = useState<string | undefined>('');
|
||||
const [selectedType, setSelectedType] = useState<Type | null>(null);
|
||||
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}>
|
||||
<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)} />
|
||||
{renderContent()}
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Pagination from '@mui/material/Pagination';
|
||||
|
||||
import { Doc } from 'src/api/document-service';
|
||||
import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { Filter, getAttributes } from 'src/api/attribute-service';
|
||||
import docService, { Doc } from 'src/services/document-service';
|
||||
import { Attribute, getAttributes } from 'src/services/attribute-service';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
|
||||
@@ -20,14 +20,10 @@ import { DecimalFilter, IntFilter } from '../num-filter';
|
||||
import { DateFilter, DateTimeFilter, TimeFilter } from '../date-filter';
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Props = {
|
||||
docs: Doc[];
|
||||
};
|
||||
|
||||
export function DocSearchView({ docs }: 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);
|
||||
@@ -41,6 +37,43 @@ export function DocSearchView({ docs }: Props) {
|
||||
|
||||
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
|
||||
// sx={{
|
||||
@@ -98,13 +131,13 @@ export function DocSearchView({ docs }: 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} />
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { _filters } from 'src/_mock/_data';
|
||||
import { _attributes } from 'src/_mock/_data';
|
||||
|
||||
export type Type = 'BOOLEAN' | 'DATE' | 'TIME' | 'DATETIME' | 'VARCHAR' | 'INTEGER' | 'DECIMAL';
|
||||
|
||||
@@ -18,19 +18,19 @@ export type AttributeCreateDto = {
|
||||
type: Type;
|
||||
};
|
||||
|
||||
export type Filter = AttributeCreateDto & {
|
||||
export type Attribute = AttributeCreateDto & {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export function getAttributes(): Promise<Filter[]> {
|
||||
return Promise.resolve(_filters);
|
||||
export function getAttributes(): Promise<Attribute[]> {
|
||||
return Promise.resolve(_attributes);
|
||||
}
|
||||
|
||||
export function createAttributes(filter: AttributeCreateDto): Promise<Filter> {
|
||||
const newFilter: Filter = {
|
||||
export function createAttributes(filter: AttributeCreateDto): Promise<Attribute> {
|
||||
const newFilter: Attribute = {
|
||||
...filter,
|
||||
id: _filters.length + 1
|
||||
id: _attributes.length + 1
|
||||
};
|
||||
_filters.push(newFilter);
|
||||
_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