Compare commits

..

29 Commits

Author SHA1 Message Date
e3c5e84bb7 feat(doc-full-view): init 2025-07-14 09:21:34 +02:00
c1c7d6baaa refactor(openModal): Umbenennung von openModal in openCreateFilterModal 2025-07-11 14:49:21 +02:00
15eb1e87e8 feat(doc-search-view): Mit openModal gebundenen Ladeeffekt hinzufügen 2025-07-11 14:41:10 +02:00
960c4db1ac feat(doc-item): Vereinfachung der Darstellung von Änderungsinformationen durch neue Utility-Methode
- Ersetzte die separate Verwendung von `changedWho` und `changedWhen` durch die neue Methode `getChangedInfo()` in der Klasse `Doc`.
- Aktualisiertes Symbol für `addedWho` von ‚gridicons:add‘ zu ‚mdi:user‘.
2025-07-11 14:31:16 +02:00
93fec2051b refactor(doc-item): Aktualisierung der Icons und Ausrichtung im DocItem-Infobereich
- Ersetzte Info-Symbole mit semantisch passenderen Symbolen
- Ändern von `justifyContent` von `flex-end` zu `flex-start` für bessere Ausrichtung
- Falsy-Werte herausgefiltert, um zu verhindern, dass leere Info-Elemente angezeigt werden
2025-07-11 14:12:26 +02:00
cbdb695ce5 refactor(icons\file): bearbeitete pdf.svg 2025-07-11 13:55:49 +02:00
da087d9a00 refactor(icons\file): bearbeitete docx.svg- und xlsx.svg-Symbole 2025-07-11 13:49:38 +02:00
383d2d5d34 Fix: Behandlung von Bildladefehlern für Dokument-Cover Fallback
- onError-Handler zur renderCover-Komponente hinzugefügt, um ein Fallback-Symbol
anzuzeigen, wenn das Dokumentbild nicht geladen werden kann. Verhindert eine fehlerhafte Bildanzeige durch
, indem die Quelle durch ein standardmäßiges „unbekanntes“ Symbol ersetzt wird.
2025-07-11 13:44:07 +02:00
9b03b39db0 refactor(doc-item): renderCover icon src durch doc.iconSrc ersetzen.
- pdf.svg bearbeiten
2025-07-11 13:04:12 +02:00
5e36a978a7 feat(doc): statische map-Methode und iconSrc getter hinzugefügt
- Methode `static map()` hinzugefügt, um eine `Doc`-Instanz aus einem Teilobjekt zu erzeugen.
- Umbenennung des Getters `Extension` in `extension`, um mit den Namenskonventionen übereinzustimmen.
- Getter `iconSrc` hinzugefügt, um einen Dateisymbolpfad basierend auf der Dateierweiterung bereitzustellen.
2025-07-11 12:43:43 +02:00
b9e6ff27db feat: Extension getter zur Doc-Klasse hinzufügen, um die Dateierweiterung aus der Eigenschaft name abzurufen 2025-07-11 11:22:31 +02:00
24c3bf0324 Refactor: Konvertierung von Doc-Typ-Alias in Klasse mit eindeutigen Zuweisungsbestätigungen 2025-07-11 11:00:51 +02:00
617ad611ed chore (assests\icons\file): docx, pdf und xlsx svg hinzufügen 2025-07-11 10:53:21 +02:00
a8e8bd9afd fix(doc-item): Avatar löschen 2025-07-11 10:22:59 +02:00
074378335b fix(doc-item): Formatierung von addedWhen und changedWhen mit toLocaleDateString
Ersetzte die Verwendung von `fDate()` und rohem `changedWhen` Rendering mit `toLocaleDateString('de-DE')`
für konsistente und lokalisierte Datumsformatierung in Dokumentinfo und Datumsanzeige.
2025-07-11 10:08:49 +02:00
3b8b9796fa refactor(doc-item): Unbekanntes Dateisymbol als Standard hinzufügen 2025-07-11 09:44:11 +02:00
9a819d1bd6 refactor: remove unnecessary async suffix from attribute functions 2025-07-10 12:09:25 +02:00
a5e32d0d39 refactor(doc-item): IDocItem-Typ durch Doc-Modell aus API ersetzen 2025-07-09 17:15:42 +02:00
8ad250f227 rename(doc-item): post als doc umbenennen 2025-07-09 16:36:11 +02:00
7f01597ea1 refactor: Umbenennung von latestDoc in long und latestDocLarge in large 2025-07-09 16:26:34 +02:00
d5f38cff85 refactor(Doc): Add addedWhen (Date), addedWho (string), changedWhen (Date) and changedWho (string) properties. 2025-07-09 13:51:22 +02:00
49452998cb Refactor: Dokumentenabfragefunktionen hinzufügen und Mock-Daten importieren
- _documents aus dem Mock-Modul importieren
 - DocQuery-Typ für optionale Abfrageparameter hinzufügen
 - Implementierung der Funktionen getDocuments, getDocumentById und getDocumentByName
 - Beibehaltung der ursprünglichen Doc-Typ-Definition für Konsistenz
2025-07-09 13:36:54 +02:00
0009ceae81 Create fake data for documents 2025-07-09 13:18:51 +02:00
480393743f feat: add sample document data array with typed document structure
- Define Doc type representing documents with id, name, and binary data
 - Export sample documents array with example PDF, DOCX, and XLSX files
 - Use Uint8Array to simulate file binary content in-memory
2025-07-09 11:50:05 +02:00
e47bb4f35c refactor(getFiltersAsync): umbenannt in getAttributesAsync 2025-07-09 11:33:30 +02:00
3b2dba5317 refactor(FilterCreateDto): umbenannt in AttributeCreateDto 2025-07-09 11:30:29 +02:00
48be96a28b refactor(filter-service.ts): umbenannt in attribute-service.ts 2025-07-09 11:28:41 +02:00
15fe1dfec6 fix(create-filter-modal): Verhindert unkontrollierte bis kontrollierte Eingabewarnungen in CreateFilterModal 2025-07-09 11:10:47 +02:00
d7e3e500ab fix: Zurücksetzen des Formularstatus beim Schließen von CreateFilterModal
- Funktion `closeReset` hinzugefügt, um `name`, `label`, und `selectedType` beim Schließen des Modals zurückzusetzen
- Stellt sicher, dass das Formular nach der Filtererstellung oder dem manuellen Schließen gelöscht wird
- Modaler `onClose` Handler und `createFiltersAsync` Callback aktualisiert, um `closeReset` zu verwenden
2025-07-09 11:05:07 +02:00
14 changed files with 518 additions and 245 deletions

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,7 @@
import { Filter } from 'src/api/filter-service';
import { Doc } from 'src/api/document-service';
import { Product } from 'src/api/product-service';
import { Filter } from 'src/api/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,6 +230,8 @@ export const _products: Product[] = [
}
];
// ----------------------------------------------------------------------
export const _filters: Filter[] = [
{ id: 1, label: 'Rechnungsnummer', name: 'invoiceNumber', type: 'VARCHAR' },
{ id: 2, label: 'Kundenname', name: 'customerName', type: 'INTEGER' },
@ -239,3 +246,59 @@ export const _filters: Filter[] = [
{ id: 11, label: 'Lieferzeit', name: 'deliveryTime', type: 'TIME' },
{ id: 12, label: 'Letzte Aktualisierung', name: 'lastUpdated', type: 'DATETIME' }
];
// ----------------------------------------------------------------------
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 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"
}
].map(doc => Doc.map(doc));

File diff suppressed because one or more lines are too long

View File

@ -12,21 +12,21 @@ export const filterTypes: Type[] = [
'DECIMAL'
];
export type FilterCreateDto = {
export type AttributeCreateDto = {
label?: string | undefined;
name: string;
type: Type;
};
export type Filter = FilterCreateDto & {
export type Filter = AttributeCreateDto & {
id: number;
};
export function getFiltersAsync(): Promise<Filter[]> {
export function getAttributes(): Promise<Filter[]> {
return Promise.resolve(_filters);
}
export function createFiltersAsync(filter: FilterCreateDto): Promise<Filter> {
export function createAttributes(filter: AttributeCreateDto): Promise<Filter> {
const newFilter: Filter = {
...filter,
id: _filters.length + 1

View File

@ -0,0 +1,66 @@
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 });
}

View File

@ -1,16 +1,27 @@
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 posts={_posts} />
<DocSearchView docs={docs} />
</>
);
}

View File

@ -1,211 +1,199 @@
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/api/document-service';
import { Iconify } from 'src/components/iconify';
import { SvgColor } from 'src/components/svg-color';
// ----------------------------------------------------------------------
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>
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>
<Box
sx={(theme) => ({
p: theme.spacing(6, 3, 3, 3),
...((latestDocLarge || latestDoc) && {
width: 1,
bottom: 0,
position: 'absolute',
}),
})}
>
{renderDate}
{renderTitle}
{renderInfo}
</Box>
</Card>
);
<Box
sx={(theme) => ({
p: theme.spacing(6, 3, 3, 3),
...((large || long) && {
width: 1,
bottom: 0,
position: 'absolute',
}),
})}
>
{renderDate}
{renderTitle}
{renderInfo}
</Box>
</Card>
);
}

View File

@ -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/api/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

View File

@ -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 '../../../api/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>
);
}

View File

@ -0,0 +1,44 @@
import { 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';
type DocFullViewProps = {
data: Uint8Array;
open: boolean;
handleClose: () => void;
}
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
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);
return (
<Modal
open={open}
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)} />
</Box>
</Modal>
);
}

View File

@ -6,8 +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, getFiltersAsync } from 'src/api/filter-service';
import { Filter, getAttributes } from 'src/api/attribute-service';
import { Iconify } from 'src/components/iconify';
@ -17,15 +18,13 @@ 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[];
docs: Doc[];
};
export function DocSearchView({ posts }: Props) {
export function DocSearchView({ docs }: Props) {
const [sortBy, setSortBy] = useState('latest');
const [filters, setFilters] = useState<Filter[]>([])
@ -35,12 +34,12 @@ export function DocSearchView({ posts }: Props) {
}, []);
useEffect(() => {
getFiltersAsync().then((res) => {
getAttributes().then((res) => {
setFilters(res);
});
}, []);
const [openModal, setOpenModal] = useState(false);
const [openCreateFilterModal, setOpenCreateFilterModal] = useState(false);
//#region example components
// <Box
@ -51,7 +50,7 @@ export function DocSearchView({ posts }: Props) {
// justifyContent: 'space-between',
// }}
// >
// <DocSearch posts={posts} />
// <DocSearch docs={docs} />
// <DocSort
// sortBy={sortBy}
// onSort={handleSort}
@ -81,13 +80,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) => {
@ -145,20 +145,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>
);
})}