소스 검색

added spellslots and ki points.
started working on the proficiency component
will utilize an accordion for this

Christopher Giese 1 년 전
부모
커밋
f10c8bee6a
37개의 변경된 파일1271개의 추가작업 그리고 46개의 파일을 삭제
  1. 148 0
      .nx/cache/d/daemon.log
  2. 1 1
      .nx/cache/d/server-process.json
  3. 3 8
      src/app/journal/journal-stats/ability-panel/ability-panel.component.html
  4. 1 1
      src/app/journal/journal-stats/ability-panel/ability-panel.component.ts
  5. 1 4
      src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.html
  6. 5 4
      src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.ts
  7. 15 28
      src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.ts
  8. 52 0
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html
  9. 25 0
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.scss
  10. 21 0
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.spec.ts
  11. 31 0
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.ts
  12. 57 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html
  13. 0 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.scss
  14. 21 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.spec.ts
  15. 76 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.ts
  16. 50 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.html
  17. 0 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.scss
  18. 21 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.spec.ts
  19. 152 0
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.ts
  20. 56 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.html
  21. 5 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.scss
  22. 21 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.spec.ts
  23. 70 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.ts
  24. 23 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.html
  25. 58 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.scss
  26. 21 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.spec.ts
  27. 69 0
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.ts
  28. 13 0
      src/app/journal/journal.module.ts
  29. 10 0
      src/app/shared-components/shared-components.module.ts
  30. 4 0
      src/app/shared-components/switch/switch.component.html
  31. 87 0
      src/app/shared-components/switch/switch.component.scss
  32. 21 0
      src/app/shared-components/switch/switch.component.spec.ts
  33. 33 0
      src/app/shared-components/switch/switch.component.ts
  34. BIN
      src/assets/icons/UIIcons/minus.png
  35. BIN
      src/assets/icons/UIIcons/plus.png
  36. 6 0
      src/interfaces/traits.ts
  37. 94 0
      src/services/data/data.service.ts

+ 148 - 0
.nx/cache/d/daemon.log

@@ -458922,3 +458922,151 @@ To fix this, set a unique name for each project in a project.json inside the pro
 [NX Daemon Server] - 2023-11-06T16:28:26.932Z - Time taken for 'hash changed files from watcher' 129.30799984931946ms
 [NX Daemon Server] - 2023-11-06T16:28:26.932Z - [WATCHER]: Processing file changes in outputs
 [NX Daemon Server] - 2023-11-06T16:28:26.932Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-09T14:57:53.099Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\b99a3e5445b3962b8dd8\d.sock
+[NX Daemon Server] - 2023-11-09T14:57:53.111Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools (native)
+[NX Daemon Server] - 2023-11-09T14:57:53.112Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-09T14:57:53.114Z - Closed a connection. Number of open connections: 0
+[NX Daemon Server] - 2023-11-09T14:57:53.114Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-09T14:57:53.117Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-09T14:57:53.627Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-09T14:57:53.628Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-09T14:57:53.630Z - Time taken for 'hash changed files from watcher' 85.61879992485046ms
+[NX Daemon Server] - 2023-11-09T14:57:53.630Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-09T14:57:53.630Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-09T15:14:10.232Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\b99a3e5445b3962b8dd8\d.sock
+[NX Daemon Server] - 2023-11-09T15:14:10.234Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools (native)
+[NX Daemon Server] - 2023-11-09T15:14:10.238Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-09T15:14:10.241Z - Closed a connection. Number of open connections: 0
+[NX Daemon Server] - 2023-11-09T15:14:10.241Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-09T15:14:10.242Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-09T15:14:10.322Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-09T15:14:10.322Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-09T15:14:10.324Z - Time taken for 'hash changed files from watcher' 26.21239995956421ms
+[NX Daemon Server] - 2023-11-09T15:14:10.324Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-09T15:14:10.325Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-10T05:47:26.132Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\b99a3e5445b3962b8dd8\d.sock
+[NX Daemon Server] - 2023-11-10T05:47:26.143Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools (native)
+[NX Daemon Server] - 2023-11-10T05:47:26.145Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-10T05:47:26.145Z - Established a connection. Number of open connections: 2
+[NX Daemon Server] - 2023-11-10T05:47:26.147Z - Closed a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-10T05:47:26.148Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-10T05:47:26.692Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-10T05:47:26.692Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-10T05:47:26.694Z - Time taken for 'hash changed files from watcher' 82.81760001182556ms
+[NX Daemon Server] - 2023-11-10T05:47:26.694Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-10T05:47:26.694Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-10T06:52:21.902Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\b99a3e5445b3962b8dd8\d.sock
+[NX Daemon Server] - 2023-11-10T06:52:21.903Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools (native)
+[NX Daemon Server] - 2023-11-10T06:52:21.908Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-10T06:52:21.909Z - Closed a connection. Number of open connections: 0
+[NX Daemon Server] - 2023-11-10T06:52:21.910Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-10T06:52:21.911Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-10T06:52:22.006Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-10T06:52:22.006Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\CharacterJournal\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-10T06:52:22.007Z - Time taken for 'hash changed files from watcher' 43.397600173950195ms
+[NX Daemon Server] - 2023-11-10T06:52:22.008Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-10T06:52:22.008Z - Done responding to the client null

+ 1 - 1
.nx/cache/d/server-process.json

@@ -1 +1 @@
-{"processId":19856}
+{"processId":2884}

+ 3 - 8
src/app/journal/journal-stats/ability-panel/ability-panel.component.html

@@ -9,24 +9,19 @@
     <li [ngbNavItem]="2">
       <button ngbNavLink>Eigenschaften</button>
       <ng-template ngbNavContent>
-        <p>Fey Ancestry Great Weapon Mastery Darkvision R</p>
+        <trait-table></trait-table>
       </ng-template>
     </li>
     <li [ngbNavItem]="3">
       <button ngbNavLink>Zauberplätze</button>
       <ng-template ngbNavContent>
-        <p>
-          Level 1 O O O <br />Level 2 O O <br />
-          Level 3 O
-        </p>
+        <spellslots></spellslots>
       </ng-template>
     </li>
     <li [ngbNavItem]="4">
       <button ngbNavLink>Proficiencies and Languages</button>
       <ng-template ngbNavContent>
-        <p>Sprachen</p>
-        <p>Tools</p>
-        <p>Rüstung und Waffen</p>
+        <proficiencies-table></proficiencies-table>
       </ng-template>
     </li>
   </ul>

+ 1 - 1
src/app/journal/journal-stats/ability-panel/ability-panel.component.ts

@@ -6,5 +6,5 @@ import { Component } from '@angular/core';
   styleUrls: ['./ability-panel.component.scss'],
 })
 export class AbilityPanelComponent {
-  active = 1;
+  active = 3;
 }

+ 1 - 4
src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.html

@@ -1,7 +1,7 @@
 <ngx-smart-modal
   #abilityModal
   identifier="abilityModal"
-  (onOpenFinished)="checkIfUpdate()"
+  (onOpenFinished)="checkIfAbilityIsToUpdate()"
 >
   <div class="modal-title">
     <h3 *ngIf="!isToUpdate">Fähigkeit erstellen</h3>
@@ -76,6 +76,3 @@
     Fähigkeit aktualisieren
   </button>
 </ngx-smart-modal>
-
-<!-- (onAnyCloseEventFinished)="removeData()"
-  (onOpenFinished)="checkIfUpdate()" -->

+ 5 - 4
src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.ts

@@ -1,8 +1,6 @@
 import { Component, Output, EventEmitter, Input } from '@angular/core';
 import { NgxSmartModalService } from 'ngx-smart-modal';
 import { Ability } from 'src/interfaces/ability';
-import { Damage } from 'src/interfaces/damage';
-import { Heal } from 'src/interfaces/heal';
 
 @Component({
   selector: 'ability-modal',
@@ -65,7 +63,7 @@ export class AbilityModalComponent {
     this.resetModalData();
   }
 
-  public checkIfUpdate(): void {
+  public checkIfAbilityIsToUpdate(): void {
     if (this.isToUpdate) {
       this.newAbilityName = this.abilityToUpdate?.name || '';
       this.newAbilityCharges = this.abilityToUpdate?.charges || 0;
@@ -86,7 +84,10 @@ export class AbilityModalComponent {
       longDescription: this.newAbilityLongDescription,
       cost: this.newAbilityCost,
       charges: this.newAbilityCharges,
-      currentlyUsedCharges: this.newAbilityCurrentlyUsedCharges,
+      currentlyUsedCharges: Math.min(
+        this.newAbilityCurrentlyUsedCharges,
+        this.newAbilityCharges
+      ),
     };
     this.abilityUpdated.emit(updatedAbility);
     this.ngxSmartModalService.closeLatestModal();

+ 15 - 28
src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.ts

@@ -1,4 +1,4 @@
-import { Component, signal } from '@angular/core';
+import { Component } from '@angular/core';
 import {
   CdkDragDrop,
   CdkDropList,
@@ -36,7 +36,6 @@ export class AbilityTableComponent {
 
   public ngOnInit(): void {
     this.abilities = this.dataAccessor.getAbilities();
-    console.log(this.abilities);
   }
 
   public ngAfterViewInit(): void {
@@ -79,44 +78,34 @@ export class AbilityTableComponent {
   }
 
   public correctChargesView(abilityIndex: number): void {
-    console.log('correcting charges view');
-    console.log(this.abilities);
-    console.log(abilityIndex);
-
     const charges = this.abilities[abilityIndex].charges;
     const currentlyUsedCharges =
       this.abilities[abilityIndex].currentlyUsedCharges;
-
-    console.log('charges: ', charges);
-    console.log('currentlyUsedCharges: ', currentlyUsedCharges);
-
     for (
       let chargeIndex = 0;
       chargeIndex < currentlyUsedCharges;
       chargeIndex++
     ) {
-      console.log(
-        'setting checkbox ' + abilityIndex + '-' + chargeIndex + ' to true'
-      );
-      (
-        document.getElementById(
-          'checkbox' + abilityIndex + '-' + chargeIndex
-        ) as HTMLInputElement
-      ).checked = true;
+      setTimeout(() => {
+        (
+          document.getElementById(
+            'checkbox' + abilityIndex + '-' + chargeIndex
+          ) as HTMLInputElement
+        ).checked = true;
+      });
     }
     for (
       let chargeIndex = currentlyUsedCharges;
       chargeIndex < charges;
       chargeIndex++
     ) {
-      console.log(
-        'setting checkbox ' + abilityIndex + '-' + chargeIndex + ' to false'
-      );
-      (
-        document.getElementById(
-          'checkbox' + abilityIndex + '-' + chargeIndex
-        ) as HTMLInputElement
-      ).checked = false;
+      setTimeout(() => {
+        (
+          document.getElementById(
+            'checkbox' + abilityIndex + '-' + chargeIndex
+          ) as HTMLInputElement
+        ).checked = false;
+      });
     }
   }
 
@@ -132,8 +121,6 @@ export class AbilityTableComponent {
 
   //  update
   public updateAbility(ability: Ability): void {
-    console.log(ability);
-    console.log(this.updateAbilityIndex);
     this.abilities[this.updateAbilityIndex!] = ability;
     this.updateDatabase();
     this.correctChargesView(this.updateAbilityIndex!);

+ 52 - 0
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html

@@ -0,0 +1,52 @@
+<!-- <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
+  <div
+    class="example-box"
+    [class]="ability.currentlyUsedCharges === ability.charges ? 'used' : ''"
+
+    cdkDrag
+  >
+    <div *ngIf="ability.cost != 'none'" class="cost-box">
+      {{ costTranslator[ability.cost] }}
+    </div>
+    <div class="ability-name">{{ ability.name }}</div>
+    <br />
+
+    <p>{{ ability.shortDescription }}</p>
+
+    <div class="charges-box" *ngIf="ability.charges != 0">
+      <span>Aufladungen: </span>
+      <span
+        *ngFor="let _ of getArray(ability.charges); let chargeIndex = index"
+      >
+        <input
+          [id]="'checkbox' + abilityIndex + '-' + chargeIndex"
+          type="checkbox"
+          (click)="$event.stopPropagation()"
+          (change)="
+            $event.stopPropagation();
+            handleChangedCharges(abilityIndex, $event.target)
+          "
+        />
+      </span>
+    </div>
+  </div> -->
+
+<button class="accordion">Section 1</button>
+<div class="panel">
+  <p>Lorem ipsum... 1</p>
+</div>
+
+<button class="accordion">Section 2</button>
+<div class="panel">
+  <p>Lorem ipsum... 2</p>
+</div>
+
+<button class="accordion">Section 3</button>
+<div class="panel">
+  <p>Lorem ipsum... 3</p>
+</div>
+
+<button class="accordion">Section 4</button>
+<div class="panel">
+  <p>Lorem ipsum... 4</p>
+</div>

+ 25 - 0
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.scss

@@ -0,0 +1,25 @@
+/* Style the buttons that are used to open and close the accordion panel */
+.accordion {
+  background-color: #eee;
+  color: #444;
+  cursor: pointer;
+  padding: 18px;
+  width: 100%;
+  text-align: left;
+  border: none;
+  outline: none;
+  transition: 0.4s;
+}
+
+/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
+.active, .accordion:hover {
+  background-color: #ccc;
+}
+
+/* Style the accordion panel. Note: hidden by default */
+.panel {
+  padding: 0 18px;
+  background-color: white;
+  display: none;
+  overflow: hidden;
+}

+ 21 - 0
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProficienciesTableComponent } from './proficiencies-table.component';
+
+describe('ProficienciesTableComponent', () => {
+  let component: ProficienciesTableComponent;
+  let fixture: ComponentFixture<ProficienciesTableComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [ProficienciesTableComponent]
+    });
+    fixture = TestBed.createComponent(ProficienciesTableComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.ts

@@ -0,0 +1,31 @@
+import { Component } from '@angular/core';
+import {
+  CdkDragDrop,
+  CdkDropList,
+  CdkDrag,
+  moveItemInArray,
+} from '@angular/cdk/drag-drop';
+import { DataService } from 'src/services/data/data.service';
+import { Ability } from 'src/interfaces/ability';
+import { NgxSmartModalService } from 'ngx-smart-modal';
+
+@Component({
+  selector: 'proficiencies-table',
+  templateUrl: './proficiencies-table.component.html',
+  styleUrls: ['./proficiencies-table.component.scss'],
+})
+export class ProficienciesTableComponent {
+  public constructor(
+    public dataAccessor: DataService,
+    public ngxSmartModalService: NgxSmartModalService
+  ) {}
+
+  public proficiencies!: any;
+
+  ngOnInit(): void {}
+
+  // public drop(event: CdkDragDrop<string[]>): void {
+  //   moveItemInArray(this.abilities, event.previousIndex, event.currentIndex);
+  //   this.updateDatabase();
+  // }
+}

+ 57 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html

@@ -0,0 +1,57 @@
+<ngx-smart-modal #spellslotsModal identifier="spellslotsModal">
+  <h3>Spellslots</h3>
+  <switch
+    [size]="'s'"
+    [checked]="showSpellslots"
+    (change)="onSpellslotsSwitchChanged($event)"
+  ></switch>
+  Zauberplätze in der Übersicht anzeigen
+  <div *ngIf="showSpellslots">
+    <div *ngFor="let level of spellslots; let levelIndex = index">
+      <div class="level-row">
+        Level {{ levelIndex + 1 }}
+        <select [(ngModel)]="spellslots[levelIndex].totalSlots">
+          <option *ngFor="let number of spellNumbersArray" [value]="number">
+            {{ number }}
+          </option>
+        </select>
+        <span>
+          <icon
+            [icon]="'remove'"
+            [size]="'s'"
+            [type]="'UI'"
+            [class]="'pointer'"
+            (click)="removeSpellLevel(levelIndex)"
+          ></icon>
+        </span>
+      </div>
+    </div>
+    <icon
+      [icon]="'add'"
+      [size]="'s'"
+      [type]="'UI'"
+      [class]="'pointer'"
+      (click)="addSpellLevel()"
+    ></icon>
+  </div>
+
+  <h3>KI-Punkte</h3>
+  <switch
+    [size]="'s'"
+    [checked]="kiPoints.showKiPoints"
+    (change)="onKiPointsSwitchChanged($event)"
+  ></switch
+  >KI Punkte in der Übersicht anzeigen
+  <div *ngIf="kiPoints.showKiPoints">
+    Verfügbare KI Punkte
+    <div>
+      <select [(ngModel)]="kiPoints.totalPoints">
+        <option *ngFor="let number of kiNumbersArray" [value]="number">
+          {{ number }}
+        </option>
+      </select>
+    </div>
+  </div>
+  <button (click)="updateSlotsandPoints()">Aktualisieren</button>
+  <button (click)="abortUpdate()">Verwerfen</button>
+</ngx-smart-modal>

+ 0 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.scss


+ 21 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SpellslotsModalComponent } from './spellslots-modal.component';
+
+describe('SpellslotsModalComponent', () => {
+  let component: SpellslotsModalComponent;
+  let fixture: ComponentFixture<SpellslotsModalComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [SpellslotsModalComponent]
+    });
+    fixture = TestBed.createComponent(SpellslotsModalComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 76 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.ts

@@ -0,0 +1,76 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { NgxSmartModalService } from 'ngx-smart-modal';
+
+@Component({
+  selector: 'spellslots-modal',
+  templateUrl: './spellslots-modal.component.html',
+  styleUrls: ['./spellslots-modal.component.scss'],
+})
+export class SpellslotsModalComponent {
+  public constructor(public ngxSmartModalService: NgxSmartModalService) {}
+
+  @Input() public spellslots: any[] = [];
+  @Input() public kiPoints: any;
+  @Input() public showSpellslots: boolean = true;
+
+  @Output() public slotsAndPointsUpdated = new EventEmitter<any>();
+
+  public spellNumbersArray: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+  public kiNumbersArray: number[] = [
+    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+  ];
+
+  ngOnInit(): void {
+    // Create a deep copy of the spellslots and kiPoints objects so they are not modified in the parent component
+    this.kiPoints = JSON.parse(JSON.stringify(this.kiPoints));
+    this.spellslots = JSON.parse(JSON.stringify(this.spellslots));
+  }
+
+  // Shows the spellslot container
+  public onSpellslotsSwitchChanged(event: any): void {
+    this.showSpellslots = event.target.checked;
+  }
+
+  // Shows the ki points container
+  public onKiPointsSwitchChanged(event: any): void {
+    this.kiPoints.showKiPoints = event.target.checked;
+  }
+
+  // Removes the specified spellslot level
+  public removeSpellLevel(levelIndex: number): void {
+    this.spellslots.splice(levelIndex, 1);
+  }
+
+  // adds a new spellslot level
+  public addSpellLevel(): void {
+    this.spellslots.push({
+      totalSlots: 1,
+      usedSlots: 0,
+    });
+  }
+
+  public updateSlotsandPoints(): void {
+    this.spellslots.forEach((level) => {
+      if (level.usedSlots > level.totalSlots) {
+        level.usedSlots = level.totalSlots;
+      }
+    });
+    if (this.kiPoints.usedPoints > this.kiPoints.totalPoints) {
+      this.kiPoints.usedPoints = this.kiPoints.totalPoints;
+    }
+    const updatedSlotsAndPoints = {
+      spellslots: {
+        spellslots: this.spellslots,
+        showSpellslots: this.showSpellslots,
+      },
+      kiPoints: { kiPoints: this.kiPoints },
+    };
+
+    this.slotsAndPointsUpdated.emit(updatedSlotsAndPoints);
+    this.ngxSmartModalService.close('spellslotsModal');
+  }
+
+  public abortUpdate(): void {
+    this.ngxSmartModalService.close('spellslotsModal');
+  }
+}

+ 50 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.html

@@ -0,0 +1,50 @@
+<div class="slots-container" style="position: relative">
+  <div *ngIf="kiPoints.showKiPoints" class="ki-container">
+    KI-Punkte
+    <ng-container
+      *ngFor="let _ of getArray(kiPoints.totalPoints); let kiIndex = index"
+    >
+      <input
+        [id]="'checkbox' + kiIndex"
+        type="checkbox"
+        (change)="handleUsedKiPoints(kiIndex, $event.target)"
+      />
+    </ng-container>
+    <div class="ki-add-buttons"></div>
+  </div>
+
+  <div *ngIf="showSpellslots" class="spellslot-sontainer">
+    <div class="level-row" *ngFor="let _ of spellslots; let levelIndex = index">
+      Level {{ levelIndex + 1 }}
+      <ng-container
+        *ngFor="
+          let _ of getArray(spellslots[levelIndex].totalSlots);
+          let slotIndex = index
+        "
+      >
+        <input
+          [id]="'checkbox' + levelIndex + '-' + slotIndex"
+          type="checkbox"
+          (change)="handleUsedSlots(levelIndex, slotIndex, $event.target)"
+        />
+      </ng-container>
+    </div>
+  </div>
+
+  <icon
+    style="position: absolute; right: 0; top: 0"
+    [icon]="'edit'"
+    [size]="'s'"
+    [type]="'UI'"
+    [class]="'pointer'"
+    (click)="initiateModification()"
+  ></icon>
+</div>
+
+<spellslots-modal
+  [kiPoints]="kiPoints"
+  [spellslots]="spellslots"
+  [showSpellslots]="showSpellslots"
+  (slotsAndPointsUpdated)="updateSlotsAndPoints($event)"
+>
+</spellslots-modal>

+ 0 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.scss


+ 21 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SpellslotsComponent } from './spellslots.component';
+
+describe('SpellslotsComponent', () => {
+  let component: SpellslotsComponent;
+  let fixture: ComponentFixture<SpellslotsComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [SpellslotsComponent]
+    });
+    fixture = TestBed.createComponent(SpellslotsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 152 - 0
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.ts

@@ -0,0 +1,152 @@
+import { Component } from '@angular/core';
+import { DataService } from 'src/services/data/data.service';
+import { NgxSmartModalService } from 'ngx-smart-modal';
+
+@Component({
+  selector: 'spellslots',
+  templateUrl: './spellslots.component.html',
+  styleUrls: ['./spellslots.component.scss'],
+})
+export class SpellslotsComponent {
+  public constructor(
+    public dataAccessor: DataService,
+    public ngxSmartModalService: NgxSmartModalService
+  ) {}
+
+  public spellslots: any[] = [];
+  public showSpellslots: boolean = false;
+  public kiPoints: any;
+
+  public slotNumber: number = 1;
+
+  public ngOnInit(): void {
+    const spells = this.dataAccessor.getSpellslots();
+    const kiPoints = this.dataAccessor.getKiPoints();
+    this.spellslots = spells.spellslots;
+    this.showSpellslots = spells.showSpellslots;
+    this.kiPoints = kiPoints;
+  }
+
+  public ngAfterViewInit(): void {
+    setTimeout(() => {
+      this.spellslots.forEach((_, levelIndex) => {
+        this.correctSpellslotsView(levelIndex);
+      });
+      this.correctKiPointsView();
+    }, 200);
+  }
+
+  //////////////
+
+  // ki points
+
+  public handleUsedKiPoints(pointIndex: number, eventTarget: any): void {
+    if (eventTarget.checked) {
+      this.kiPoints.usedPoints++;
+      if (pointIndex + 1 !== this.kiPoints.usedPoints) {
+        this.correctKiPointsView();
+      }
+    } else {
+      this.kiPoints.usedPoints--;
+      if (pointIndex !== this.kiPoints.usedPoints) {
+        this.correctKiPointsView();
+      }
+    }
+    this.updateKiPointsDatabase();
+  }
+
+  private correctKiPointsView(): void {
+    const totalKiPoints = this.kiPoints.totalPoints;
+    const usedKiPoints = this.kiPoints.usedPoints;
+    for (let kiIndex = 0; kiIndex < usedKiPoints; kiIndex++) {
+      setTimeout(() => {
+        (
+          document.getElementById('checkbox' + kiIndex) as HTMLInputElement
+        ).checked = true;
+      });
+    }
+    for (let kiIndex = usedKiPoints; kiIndex < totalKiPoints; kiIndex++) {
+      setTimeout(() => {
+        (
+          document.getElementById('checkbox' + kiIndex) as HTMLInputElement
+        ).checked = false;
+      });
+    }
+  }
+
+  // spellslots
+
+  public handleUsedSlots(
+    levelIndex: number,
+    slotIndex: number,
+    eventTarget: any
+  ) {
+    // if now checked, it means the slot was just used.
+    if (eventTarget.checked) {
+      this.spellslots[levelIndex].usedSlots++;
+      if (slotIndex + 1 !== this.spellslots[levelIndex].usedSlots) {
+        this.correctSpellslotsView(levelIndex);
+      }
+    } else {
+      this.spellslots[levelIndex].usedSlots--;
+      if (slotIndex !== this.spellslots[levelIndex].usedSlots) {
+        this.correctSpellslotsView(levelIndex);
+      }
+    }
+    this.updateSpellslotDatabase();
+  }
+
+  public correctSpellslotsView(levelIndex: number): void {
+    const totalSlots = this.spellslots[levelIndex].totalSlots;
+    const usedSlots = this.spellslots[levelIndex].usedSlots;
+    for (let slotIndex = 0; slotIndex < usedSlots; slotIndex++) {
+      setTimeout(() => {
+        (
+          document.getElementById(
+            'checkbox' + levelIndex + '-' + slotIndex
+          ) as HTMLInputElement
+        ).checked = true;
+      });
+    }
+    for (let slotIndex = usedSlots; slotIndex < totalSlots; slotIndex++) {
+      setTimeout(() => {
+        (
+          document.getElementById(
+            'checkbox' + levelIndex + '-' + slotIndex
+          ) as HTMLInputElement
+        ).checked = false;
+      });
+    }
+  }
+
+  // general
+
+  public getArray(length: number): any[] {
+    return Array.from({ length: length });
+  }
+
+  public isNotEmptyObject(obj: object): boolean {
+    return Object.keys(obj).length !== 0;
+  }
+
+  public updateSpellslotDatabase(): void {
+    this.dataAccessor.setSpellslots(this.spellslots);
+  }
+
+  public updateKiPointsDatabase(): void {
+    this.dataAccessor.setKiPoints(this.kiPoints);
+  }
+
+  public initiateModification(): void {
+    this.ngxSmartModalService.getModal('spellslotsModal').open();
+  }
+
+  public updateSlotsAndPoints(data: any): void {
+    this.spellslots = data.spellslots.spellslots;
+    this.showSpellslots = data.spellslots.showSpellslots;
+    this.kiPoints = data.kiPoints.kiPoints;
+
+    this.updateSpellslotDatabase();
+    this.updateKiPointsDatabase();
+  }
+}

+ 56 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.html

@@ -0,0 +1,56 @@
+<ngx-smart-modal
+  #traitModal
+  identifier="traitModal"
+  (onOpenFinished)="checkIfTraitIsToUpdate()"
+>
+  <div class="modal-title">
+    <h3 *ngIf="!isToUpdate">Eigenschaft erstellen</h3>
+    <h3 *ngIf="isToUpdate">Eigenschaft anpassen</h3>
+  </div>
+  <div class="modal-body">
+    <div class="modal-input">
+      <label for="traitName">Name</label>
+      <input
+        type="text"
+        class="modal-input"
+        id="traitName"
+        [(ngModel)]="newTraitName"
+      />
+    </div>
+
+    <div class="modal-input">
+      <label for="traitShortDescription">Kurzbeschreibung</label>
+      <textarea
+        id="traitShortDescription"
+        [(ngModel)]="newTraitShortDescription"
+      ></textarea>
+    </div>
+
+    <div class="modal-input">
+      <label for="traitLongDescription">Ausführliche Beschreibung</label>
+      <textarea
+        id="traitLongDescription"
+        [(ngModel)]="newTraitLongDescription"
+      ></textarea>
+    </div>
+  </div>
+  <button
+    *ngIf="!isToUpdate"
+    class="modal-button"
+    (click)="createTrait()"
+    [disabled]="
+      !newTraitName || !newTraitShortDescription || !newTraitLongDescription
+    "
+  >
+    Eigenschaft hinzufügen
+  </button>
+  <button
+    *ngIf="isToUpdate"
+    (click)="updateTrait()"
+    [disabled]="
+      !newTraitName || !newTraitShortDescription || !newTraitLongDescription
+    "
+  >
+    Eigenschaft aktualisieren
+  </button>
+</ngx-smart-modal>

+ 5 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.scss

@@ -0,0 +1,5 @@
+.modal-input{
+    display: flex;
+    flex-direction: column;
+    gap: 0.5rem;
+}

+ 21 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TraitModalComponent } from './trait-modal.component';
+
+describe('TraitModalComponent', () => {
+  let component: TraitModalComponent;
+  let fixture: ComponentFixture<TraitModalComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [TraitModalComponent]
+    });
+    fixture = TestBed.createComponent(TraitModalComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 70 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.ts

@@ -0,0 +1,70 @@
+import { Component, Output, EventEmitter, Input } from '@angular/core';
+import { NgxSmartModalService } from 'ngx-smart-modal';
+import { Trait } from 'src/interfaces/traits';
+
+@Component({
+  selector: 'trait-modal',
+  templateUrl: './trait-modal.component.html',
+  styleUrls: ['./trait-modal.component.scss'],
+})
+export class TraitModalComponent {
+  public constructor(public ngxSmartModalService: NgxSmartModalService) {}
+
+  @Output() public traitCreated: EventEmitter<Trait> =
+    new EventEmitter<Trait>();
+
+  @Output() public traitUpdated: EventEmitter<any> = new EventEmitter<any>();
+
+  @Output() public traitDelete: EventEmitter<number> =
+    new EventEmitter<number>();
+
+  @Input() public isToUpdate: boolean = false;
+  @Input() public traitToUpdate: Trait | undefined;
+
+  public newTraitName: string = '';
+  public newTraitShortDescription: string = '';
+  public newTraitLongDescription: string = '';
+  public newTraitOrigin: string = '';
+
+  public createTrait(): void {
+    const newTrait: Trait = {
+      name: this.newTraitName,
+      shortDescription: this.newTraitShortDescription,
+      longDescription: this.newTraitLongDescription,
+      origin: this.newTraitOrigin,
+    };
+
+    this.traitCreated.emit(newTrait);
+    this.ngxSmartModalService.getModal('traitModal').close();
+    this.resetData();
+  }
+
+  public checkIfTraitIsToUpdate(): void {
+    if (this.isToUpdate) {
+      this.newTraitName = this.traitToUpdate!.name;
+      this.newTraitShortDescription = this.traitToUpdate!.shortDescription;
+      this.newTraitLongDescription = this.traitToUpdate!.longDescription;
+      this.newTraitOrigin = this.traitToUpdate!.origin;
+    }
+  }
+
+  public updateTrait(): void {
+    const updatedTrait: Trait = {
+      name: this.newTraitName,
+      shortDescription: this.newTraitShortDescription,
+      longDescription: this.newTraitLongDescription,
+      origin: this.newTraitOrigin,
+    };
+
+    this.traitUpdated.emit(updatedTrait);
+    this.ngxSmartModalService.getModal('traitModal').close();
+    this.resetData();
+  }
+
+  public resetData(): void {
+    this.newTraitName = '';
+    this.newTraitShortDescription = '';
+    this.newTraitLongDescription = '';
+    this.newTraitOrigin = '';
+  }
+}

+ 23 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.html

@@ -0,0 +1,23 @@
+<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
+  <div
+    class="example-box"
+    *ngFor="let trait of traits; let abilityIndex = index"
+    (click)="openTraitModal(abilityIndex)"
+    cdkDrag
+  >
+    <!-- Eventuell Symbol für den Ursprung des Traits -->
+    <div class="trait-name">{{ trait.name }}</div>
+    <br />
+
+    <p>{{ trait.shortDescription }}</p>
+  </div>
+
+  <button (click)="openTraitModal()">+</button>
+</div>
+
+<trait-modal
+  (traitCreated)="addNewlyCreatedTrait($event)"
+  (traitUpdated)="updateTrait($event)"
+  [isToUpdate]="isToUpdate"
+  [traitToUpdate]="traitToUpdate"
+></trait-modal>

+ 58 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.scss

@@ -0,0 +1,58 @@
+.trait-name{
+  font-size: 1.25rem;
+  font-weight: 600;
+}
+
+// Table
+.used{
+  opacity: 0.5;
+}
+
+.example-list {
+  width: 100%;
+  border: solid 1px #ccc;
+  min-height: 60px;
+  display: block;
+  background: white;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.example-box {
+  padding: 20px 10px;
+  border-bottom: solid 1px #ccc;
+  color: rgba(0, 0, 0, 0.87);
+  display: flex;
+  position: relative;
+  flex-direction: column;
+  // align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  cursor: move;
+  background: white;
+  font-size: 14px;
+}
+
+.cdk-drag-preview {
+  box-sizing: border-box;
+  border-radius: 4px;
+  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
+              0 8px 10px 1px rgba(0, 0, 0, 0.14),
+              0 3px 14px 2px rgba(0, 0, 0, 0.12);
+}
+
+.cdk-drag-placeholder {
+  opacity: 0;
+}
+
+.cdk-drag-animating {
+  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}
+
+.example-box:last-child {
+  border: none;
+}
+
+.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
+  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}

+ 21 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TraitTableComponent } from './trait-table.component';
+
+describe('TraitTableComponent', () => {
+  let component: TraitTableComponent;
+  let fixture: ComponentFixture<TraitTableComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [TraitTableComponent]
+    });
+    fixture = TestBed.createComponent(TraitTableComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 69 - 0
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.ts

@@ -0,0 +1,69 @@
+import { Component } from '@angular/core';
+import {
+  CdkDragDrop,
+  CdkDropList,
+  CdkDrag,
+  moveItemInArray,
+} from '@angular/cdk/drag-drop';
+import { DataService } from 'src/services/data/data.service';
+import { NgxSmartModalService } from 'ngx-smart-modal';
+import { Trait } from 'src/interfaces/traits';
+
+@Component({
+  selector: 'trait-table',
+  templateUrl: './trait-table.component.html',
+  styleUrls: ['./trait-table.component.scss'],
+})
+export class TraitTableComponent {
+  public traits!: Trait[];
+
+  public isToUpdate: boolean = false;
+  public traitToUpdate: Trait | undefined;
+  public updateTraitIndex: number | undefined;
+
+  public constructor(
+    public dataAccessor: DataService,
+    public ngxSmartModalService: NgxSmartModalService
+  ) {}
+
+  public ngOnInit(): void {
+    this.traits = this.dataAccessor.getTraits();
+  }
+
+  public drop(event: CdkDragDrop<string[]>): void {
+    moveItemInArray(this.traits, event.previousIndex, event.currentIndex);
+    this.updateDatabase();
+  }
+
+  public openTraitModal(traitIndex?: number): void {
+    if (traitIndex !== undefined) {
+      this.isToUpdate = true;
+      this.traitToUpdate = this.traits[traitIndex];
+      this.updateTraitIndex = traitIndex;
+    }
+    this.ngxSmartModalService.getModal('traitModal').open();
+  }
+
+  // add
+  public addNewlyCreatedTrait(trait: Trait): void {
+    this.traits.push(trait);
+    this.updateDatabase();
+  }
+
+  //  update
+  public updateTrait(ability: Trait): void {
+    this.traits[this.updateTraitIndex!] = ability;
+    this.updateDatabase();
+    this.resetUpdateData();
+  }
+
+  public updateDatabase(): void {
+    this.dataAccessor.setTraits(this.traits);
+  }
+
+  private resetUpdateData(): void {
+    this.isToUpdate = false;
+    this.traitToUpdate = undefined;
+    this.updateTraitIndex = undefined;
+  }
+}

+ 13 - 0
src/app/journal/journal.module.ts

@@ -37,6 +37,13 @@ import { SpellModalComponent } from './spell-modal/spell-modal.component';
 import { AbilityPanelComponent } from './journal-stats/ability-panel/ability-panel.component';
 import { AbilityTableComponent } from './journal-stats/ability-panel/ability-table/ability-table.component';
 import { AbilityModalComponent } from './journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component';
+import { TraitTableComponent } from './journal-stats/ability-panel/trait-table/trait-table.component';
+import { TraitModalComponent } from './journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component';
+import { SpellslotsComponent } from './journal-stats/ability-panel/spellslots/spellslots.component';
+import { SpellslotsModalComponent } from './journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component';
+
+import { SharedComponentsModule } from '../shared-components/shared-components.module';
+import { ProficienciesTableComponent } from './journal-stats/ability-panel/proficiencies-table/proficiencies-table.component';
 
 @NgModule({
   declarations: [
@@ -70,6 +77,11 @@ import { AbilityModalComponent } from './journal-stats/ability-panel/ability-tab
     AbilityPanelComponent,
     AbilityTableComponent,
     AbilityModalComponent,
+    TraitTableComponent,
+    TraitModalComponent,
+    SpellslotsComponent,
+    SpellslotsModalComponent,
+    ProficienciesTableComponent,
   ],
   imports: [
     CommonModule,
@@ -79,6 +91,7 @@ import { AbilityModalComponent } from './journal-stats/ability-panel/ability-tab
     CdkTableModule,
     DragDropModule,
     NgxSmartModalModule.forChild(),
+    SharedComponentsModule,
   ],
 })
 export class JournalModule {}

+ 10 - 0
src/app/shared-components/shared-components.module.ts

@@ -0,0 +1,10 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SwitchComponent } from './switch/switch.component';
+
+@NgModule({
+  declarations: [SwitchComponent],
+  imports: [CommonModule],
+  exports: [SwitchComponent],
+})
+export class SharedComponentsModule {}

+ 4 - 0
src/app/shared-components/switch/switch.component.html

@@ -0,0 +1,4 @@
+<label class="switch">
+  <input #inputRef type="checkbox" />
+  <span class="slider round"></span>
+</label>

+ 87 - 0
src/app/shared-components/switch/switch.component.scss

@@ -0,0 +1,87 @@
+/* The switch - the box around the slider */
+.switch {
+  position: relative;
+  display: inline-block;
+  width: calc(var(--base-size) * 6.0); // 60px
+  height: calc(var(--base-size) * 3.4); // 34px
+}
+
+
+// .switch-small{
+//   --base-size: 0.3rem;
+//   position: relative;
+//   display: inline-block;
+//   width: calc(var(--base-size) * 6.0); // 60px
+//   height: calc(var(--base-size) * 3.4); // 34px
+// }
+
+// .switch-medium{
+//   --base-size: 0.4rem;
+//   position: relative;
+//   display: inline-block;
+//   width: calc(var(--base-size) * 6.0); // 60px
+//   height: calc(var(--base-size) * 3.4); // 34px
+// }
+
+// .switch-large{
+//   --base-size: 0.6rem;
+//   position: relative;
+//   display: inline-block;
+//   width: calc(var(--base-size) * 6.0); // 60px
+//   height: calc(var(--base-size) * 3.4); // 34px
+// }
+
+/* Hide default HTML checkbox */
+.switch input {
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+/* The slider */
+.slider {
+  position: absolute;
+  cursor: pointer;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: #ccc;
+  -webkit-transition: .4s;
+  transition: .4s;
+}
+
+.slider:before {
+  position: absolute;
+  content: "";
+  height: calc(var(--base-size) * 2.6); // 26px
+  width: calc(var(--base-size) * 2.6); // 26px
+  left: calc(var(--base-size) * .4); // 4px
+  bottom: calc(var(--base-size) * .4); // 4px
+  background-color: white;
+  -webkit-transition: .4s;
+  transition: .4s;
+}
+
+input:checked + .slider {
+  background-color: #2196F3;
+}
+
+input:focus + .slider {
+  box-shadow: 0 0 1px #2196F3;
+}
+
+input:checked + .slider:before {
+  -webkit-transform: translateX(calc(var(--base-size) * 2.6));
+  -ms-transform: translateX(calc(var(--base-size) * 2.6));
+  transform: translateX(calc(var(--base-size) * 2.6));
+}
+
+/* Rounded sliders */
+.slider.round {
+  border-radius: 1rem; // 34px
+}
+
+.slider.round:before {
+  border-radius: 50%; // 50%
+}

+ 21 - 0
src/app/shared-components/switch/switch.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SwitchComponent } from './switch.component';
+
+describe('SwitchComponent', () => {
+  let component: SwitchComponent;
+  let fixture: ComponentFixture<SwitchComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [SwitchComponent]
+    });
+    fixture = TestBed.createComponent(SwitchComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 33 - 0
src/app/shared-components/switch/switch.component.ts

@@ -0,0 +1,33 @@
+import { Component, Input, ElementRef, ViewChild } from '@angular/core';
+
+@Component({
+  selector: 'switch',
+  templateUrl: './switch.component.html',
+  styleUrls: ['./switch.component.scss'],
+})
+export class SwitchComponent {
+  public constructor(private el: ElementRef) {}
+  @ViewChild('inputRef') public input!: any;
+  @Input() public size: string = 's';
+  @Input() public checked: boolean = false;
+
+  ngOnInit(): void {
+    this.setBaseSize();
+  }
+
+  ngAfterViewInit(): void {
+    this.input.nativeElement.checked = this.checked;
+  }
+
+  private setBaseSize(): void {
+    if (this.size === 's') {
+      this.el.nativeElement.style.setProperty('--base-size', '0.3rem');
+    } else if (this.size === 'm') {
+      this.el.nativeElement.style.setProperty('--base-size', '0.4rem');
+    } else if (this.size === 'l') {
+      this.el.nativeElement.style.setProperty('--base-size', '0.5rem');
+    } else {
+      this.el.nativeElement.style.setProperty('--base-size', '0.3rem');
+    }
+  }
+}

BIN
src/assets/icons/UIIcons/minus.png


BIN
src/assets/icons/UIIcons/plus.png


+ 6 - 0
src/interfaces/traits.ts

@@ -0,0 +1,6 @@
+export interface Trait {
+  name: string;
+  shortDescription: string;
+  longDescription: string;
+  origin: string;
+}

+ 94 - 0
src/services/data/data.service.ts

@@ -8,6 +8,7 @@ import { Skill } from 'src/interfaces/skill';
 import { Weapon } from 'src/interfaces/weapon';
 import { Spell } from 'src/interfaces/spell';
 import { Ability } from 'src/interfaces/ability';
+import { Trait } from 'src/interfaces/traits';
 
 @Injectable({
   providedIn: 'root',
@@ -299,6 +300,99 @@ export class DataService {
     console.log('abilities updated from data service: ', this.abilities);
   }
 
+  private traits: Trait[] = [
+    {
+      name: 'Feenblut',
+      shortDescription:
+        'Du hast Vorteil auf alle Rettungswürfe gegen Zauber und andere magische Effekte.',
+      longDescription:
+        'Du hast Vorteil auf alle Rettungswürfe gegen Zauber und andere magische Effekte.',
+      origin: 'Elf',
+    },
+    {
+      name: 'Dunkelsicht',
+      shortDescription: 'Du hast 60 ft. Dunkelsicht.',
+      longDescription:
+        'Du hast 60 ft. Dunkelsicht. Du kannst in völliger Dunkelheit so klar sehen wie am Tag.',
+      origin: 'Elf',
+    },
+    {
+      name: 'Fey Ancestry',
+      shortDescription:
+        'Du meditierst und musst nicht schlafen. Du kannst nicht durch Magie in Schlaf versetzt werden.',
+      longDescription:
+        'Du meditierst und musst nicht schlafen. Du kannst nicht durch Magie in Schlaf versetzt werden.',
+      origin: 'Elf',
+    },
+  ];
+
+  public getTraits(): Trait[] {
+    return this.traits;
+  }
+
+  public setTraits(traits: Trait[]): void {
+    this.traits = traits;
+    console.log('traits updated from data service: ', this.traits);
+  }
+
+  private spellslots: any = {
+    spellslots: [
+      { totalSlots: 4, usedSlots: 2 },
+      { totalSlots: 2, usedSlots: 1 },
+    ],
+    showSpellslots: true,
+  };
+
+  public getSpellslots(): any {
+    return this.spellslots;
+  }
+
+  public setSpellslots(spellslots: any): void {
+    this.spellslots = spellslots;
+    console.log('spellslots updated from data service: ', this.spellslots);
+  }
+
+  private kiPoints: any = {
+    totalPoints: 4,
+    usedPoints: 2,
+    showKiPoints: true,
+  };
+
+  public getKiPoints(): any {
+    return this.kiPoints;
+  }
+
+  public setKiPoints(kiPoints: any): void {
+    this.kiPoints = kiPoints;
+    console.log('kiPoints updated from data service: ', this.kiPoints);
+  }
+
+  private proficiencies: any = {
+    armor: {
+      light: true,
+      medium: true,
+      heavy: false,
+    },
+    weapons: {
+      simple: true,
+      martial: true,
+    },
+    tools: ['Schmiedewerkzeuge', 'Würfel'],
+    languages: ['Gemeinsprache', 'Elfisch', 'Zwergisch'],
+  };
+
+  public getProficiencies(): any {
+    return this.proficiencies;
+  }
+
+  public setProficiencies(proficiencies: any): void {
+    this.proficiencies = proficiencies;
+    console.log(
+      'proficiencies updated from data service: ',
+      this.proficiencies
+    );
+  }
+
   // #region Character Data observables
 
   /**