Эх сурвалжийг харах

formatted project with prettier

Warafear 1 жил өмнө
parent
commit
fc54809f87
78 өөрчлөгдсөн 1385 нэмэгдсэн , 1038 устгасан
  1. 0 3
      src/app/app.component.ts
  2. 20 16
      src/app/character/character-creator/character-creator.component.html
  3. 56 52
      src/app/character/character-creator/character-creator.component.ts
  4. 7 3
      src/app/character/character-picker/character-card/character-card.component.html
  5. 12 12
      src/app/character/character-picker/character-picker.component.html
  6. 2 0
      src/app/character/character-picker/character-picker.component.ts
  7. 3 0
      src/app/character/character.module.ts
  8. 1 1
      src/app/journal/journal-character/class/class.component.html
  9. 43 19
      src/app/journal/journal-character/general/general.component.html
  10. 17 12
      src/app/journal/journal-character/journal-character.component.html
  11. 0 3
      src/app/journal/journal-character/journal-character.component.ts
  12. 6 5
      src/app/journal/journal-character/species/species.component.html
  13. 14 8
      src/app/journal/journal-character/story/story.component.html
  14. 12 10
      src/app/journal/journal-character/subclass/subclass.component.html
  15. 6 5
      src/app/journal/journal-character/subclass/subclass.component.ts
  16. 14 14
      src/app/journal/journal-home/navigation-panel/navigation-panel.component.html
  17. 23 15
      src/app/journal/journal-inventory/journal-inventory.component.html
  18. 17 9
      src/app/journal/journal-inventory/simple-item-details/simple-item-details.component.html
  19. 29 15
      src/app/journal/journal-inventory/simple-item-modal/simple-item-modal.component.html
  20. 5 1
      src/app/journal/journal-settings/journal-settings.component.html
  21. 7 5
      src/app/journal/journal-spellcards/add-card/add-card.component.html
  22. 18 8
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.html
  23. 5 1
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.ts
  24. 15 5
      src/app/journal/journal-spellcards/journal-spellcards.component.html
  25. 0 32
      src/app/journal/journal-spellcards/journal-spellcards.component.ts
  26. 6 2
      src/app/journal/journal-spellcards/spellcard/spellcard.component.html
  27. 2 8
      src/app/journal/journal-spellcards/spellcard/spellcard.component.ts
  28. 12 4
      src/app/journal/journal-stats/ability-panel/ability-table/ability-details/ability-details.component.html
  29. 18 9
      src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.html
  30. 6 2
      src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.html
  31. 6 2
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html
  32. 12 4
      src/app/journal/journal-stats/ability-panel/proficiencies-table/tools-modal/tools-modal.component.html
  33. 12 4
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html
  34. 6 2
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.html
  35. 12 4
      src/app/journal/journal-stats/ability-panel/trait-table/trait-details/trait-details.component.html
  36. 18 9
      src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.html
  37. 6 2
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.html
  38. 2 2
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.html
  39. 1 1
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts
  40. 1 1
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.html
  41. 1 1
      src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.html
  42. 1 1
      src/app/journal/journal-stats/info-row/armor-class/armor-class-details/armor-class-details.component.html
  43. 12 4
      src/app/journal/journal-stats/info-row/conditions/conditions-details/conditions-details.component.html
  44. 4 1
      src/app/journal/journal-stats/info-row/conditions/exhaustion-details/exhaustion-details.component.html
  45. 1 1
      src/app/journal/journal-stats/info-row/initiative/initiative-details/initiative-details.component.html
  46. 4 4
      src/app/journal/journal-stats/info-row/movement/movement-details/movement-details.component.html
  47. 1 1
      src/app/journal/journal-stats/info-row/proficiency/proficiency-details/proficiency-details.component.html
  48. 12 4
      src/app/journal/journal-stats/life-container/life/life-details/life-details.component.html
  49. 12 4
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html
  50. 0 1
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.ts
  51. 0 75
      src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.html
  52. 0 4
      src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.scss
  53. 0 21
      src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.spec.ts
  54. 0 28
      src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.ts
  55. 6 2
      src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.html
  56. 0 4
      src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.ts
  57. 12 2
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component.html
  58. 18 6
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.html
  59. 6 1
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-table.component.html
  60. 3 3
      src/app/journal/journal.module.ts
  61. 131 242
      src/app/journal/spell-modal/spell-modal.component.html
  62. 16 1
      src/app/journal/spell-modal/spell-modal.component.scss
  63. 12 69
      src/app/journal/spell-modal/spell-modal.component.ts
  64. 4 1
      src/app/shared-components/full-spellcard/full-spellcard.component.html
  65. 4 0
      src/app/shared-components/full-spellcard/full-spellcard.component.ts
  66. 12 2
      src/app/shared-components/shared-components.module.ts
  67. 2 1
      src/app/shared-components/ui-button/ui-button.component.html
  68. 2 15
      src/app/shared-components/ui-button/ui-button.component.ts
  69. 1 1
      src/app/shared-components/value-box/value-box.component.html
  70. 300 2
      src/assets/i18n/de.json
  71. 0 1
      src/interfaces/class-data.ts
  72. 1 0
      src/interfaces/spell.ts
  73. 8 0
      src/pipes/duration/duration.pipe.spec.ts
  74. 26 0
      src/pipes/duration/duration.pipe.ts
  75. 24 36
      src/services/class/class.service.ts
  76. 136 80
      src/services/spells/spells.service.ts
  77. 34 30
      src/services/subclass/subclass.service.ts
  78. 127 79
      src/services/translator/translator.service.ts

+ 0 - 3
src/app/app.component.ts

@@ -15,10 +15,7 @@ export class AppComponent {
 
   private initTranslation(): void {
     this.translate.addLangs(['en', 'de']);
-    // this.translate.setDefaultLang('de');
     const userLang = localStorage.getItem('language');
-    console.log('userLang', userLang);
     this.translate.setDefaultLang(userLang || 'de');
-    // this.translate.use(userLang || 'de'); // TODO: probably use defaultLangugae
   }
 }

+ 20 - 16
src/app/character/character-creator/character-creator.component.html

@@ -1,43 +1,47 @@
 <div class="creation-container">
   <form class="form-box">
-    <h3 style="text-align: center">Neuen Charakter erstellen</h3>
+    <h3 style="text-align: center">{{ "creator.new" | translate }}</h3>
     <mat-form-field appearance="outline">
-      <mat-label>Name</mat-label>
+      <mat-label>{{ "creator.name" | translate }}</mat-label>
       <input matInput [(ngModel)]="characterName" name="name" />
     </mat-form-field>
     <mat-form-field appearance="outline">
-      <mat-label>Volk</mat-label>
+      <mat-label>{{ "creator.species" | translate }}</mat-label>
       <mat-select [(ngModel)]="characterSpecies" name="species">
-        @for (species of species; track species) {
-          <mat-option [value]="species.value">{{ species.view }}</mat-option>
+        @for (species of translator.races; track species) {
+          <mat-option [value]="species">{{
+            "species." + species | translate
+          }}</mat-option>
         }
       </mat-select>
     </mat-form-field>
     <mat-form-field appearance="outline">
-      <mat-label>Klasse</mat-label>
+      <mat-label>{{ "creator.class" | translate }}</mat-label>
       <mat-select [(ngModel)]="characterClass" name="class">
-        @for (characterClass of classes; track characterClass) {
-          <mat-option [value]="characterClass.value">{{
-            characterClass.view
+        @for (characterClass of translator.classes; track characterClass) {
+          <mat-option [value]="characterClass">{{
+            "classes." + characterClass | translate
           }}</mat-option>
         }
       </mat-select>
     </mat-form-field>
     <mat-form-field appearance="outline">
-      <mat-label>Hintergrund</mat-label>
+      <mat-label>{{ "creator.background" | translate }}</mat-label>
       <mat-select [(ngModel)]="characterBackground" name="background">
-        @for (background of backgrounds; track background) {
-          <mat-option [value]="background.value">{{
-            background.view
+        @for (background of translator.backgrounds; track background) {
+          <mat-option [value]="background">{{
+            "backgrounds." + background | translate
           }}</mat-option>
         }
       </mat-select>
     </mat-form-field>
     <mat-form-field appearance="outline">
-      <mat-label>Geschlecht</mat-label>
+      <mat-label>{{ "creator.gender" | translate }}</mat-label>
       <mat-select [(ngModel)]="characterGender" name="gender">
-        @for (gender of genders; track gender) {
-          <mat-option [value]="gender.value">{{ gender.view }}</mat-option>
+        @for (gender of translator.genders; track gender) {
+          <mat-option [value]="gender">{{
+            "genders." + gender | translate
+          }}</mat-option>
         }
       </mat-select>
     </mat-form-field>

+ 56 - 52
src/app/character/character-creator/character-creator.component.ts

@@ -1,6 +1,8 @@
 import { Component } from '@angular/core';
 import { DataService } from 'src/services/data/data.service';
 import { Router } from '@angular/router';
+import { TranslatorService } from 'src/services/translator/translator.service';
+import { TranslateService } from '@ngx-translate/core';
 
 interface characterData {
   view: string;
@@ -18,61 +20,61 @@ export class CharacterCreatorComponent {
   // Translators
 
   // TODO: Implement the species
-  public species: characterData[] = [
-    { view: 'Mensch', value: 'Human' },
-    { view: 'Zwerg', value: 'Dwarf' },
-    { view: 'Elf', value: 'Elf' },
-    { view: 'Eladrin', value: 'Eladrin' },
-    { view: 'Halbelf', value: 'HalfElf' },
-    { view: 'Halbelf (Mal des Entdeckens)', value: 'HalfElfDetection' },
-    { view: 'Halbling', value: 'Halfling' },
-    { view: 'Gnom', value: 'Gnome' },
-    { view: 'Halbork', value: 'HalfOrc' },
-    { view: 'Tiefling', value: 'Tiefling' },
-  ];
+  // public species: characterData[] = [
+  //   { view: 'Mensch', value: 'Human' },
+  //   { view: 'Zwerg', value: 'Dwarf' },
+  //   { view: 'Elf', value: 'Elf' },
+  //   { view: 'Eladrin', value: 'Eladrin' },
+  //   { view: 'Halbelf', value: 'HalfElf' },
+  //   { view: 'Halbelf (Mal des Entdeckens)', value: 'HalfElfDetection' },
+  //   { view: 'Halbling', value: 'Halfling' },
+  //   { view: 'Gnom', value: 'Gnome' },
+  //   { view: 'Halbork', value: 'HalfOrc' },
+  //   { view: 'Tiefling', value: 'Tiefling' },
+  // ];
 
-  // TODO: Implement the classes
-  public classes: characterData[] = [
-    { view: 'Test', value: 'Test' },
-    { view: 'Barbar', value: 'Barbarian' },
-    { view: 'Barde', value: 'Bard' },
-    { view: 'Druide', value: 'Druid' },
-    { view: 'Hexenmeister', value: 'Warlock' },
-    { view: 'Kämpfer', value: 'Fighter' },
-    { view: 'Kleriker', value: 'Cleric' },
-    { view: 'Magier', value: 'Wizard' },
-    { view: 'Mönch', value: 'Monk' },
-    { view: 'Paladin', value: 'Paladin' },
-    { view: 'Schurke', value: 'Rogue' },
-    { view: 'Waldläufer', value: 'Ranger' },
-    { view: 'Zauberer', value: 'Sorcerer' },
-  ];
+  // // TODO: Implement the classes
+  // public classes: characterData[] = [
+  //   { view: 'Test', value: 'Test' },
+  //   { view: 'Barbar', value: 'Barbarian' },
+  //   { view: 'Barde', value: 'Bard' },
+  //   { view: 'Druide', value: 'Druid' },
+  //   { view: 'Hexenmeister', value: 'Warlock' },
+  //   { view: 'Kämpfer', value: 'Fighter' },
+  //   { view: 'Kleriker', value: 'Cleric' },
+  //   { view: 'Magier', value: 'Wizard' },
+  //   { view: 'Mönch', value: 'Monk' },
+  //   { view: 'Paladin', value: 'Paladin' },
+  //   { view: 'Schurke', value: 'Rogue' },
+  //   { view: 'Waldläufer', value: 'Ranger' },
+  //   { view: 'Zauberer', value: 'Sorcerer' },
+  // ];
 
-  public backgrounds: characterData[] = [
-    { view: 'Akolyth', value: 'acolyte' },
-    { view: 'Scharlatan', value: 'charlatan' },
-    { view: 'Edelmann', value: 'noble' },
-    { view: 'Entertainer', value: 'entertainer' },
-    { view: 'Folk Held', value: 'folkHero' },
-    { view: 'Gelehrter', value: 'sage' },
-    { view: 'Gladiator', value: 'gladiator' },
-    { view: 'Gildenhändler', value: 'guildArtisan' },
-    { view: 'Gildehandwerker', value: 'guildMerchant' },
-    { view: 'Herumtreiber', value: 'outlander' },
-    { view: 'Krimineller', value: 'criminal' },
-    { view: 'Künstler', value: 'artist' },
-    { view: 'Marine', value: 'sailor' },
-    { view: 'Scharlatan', value: 'charlatan' },
-    { view: 'Soldat', value: 'soldier' },
-    { view: 'Stadtwache', value: 'cityWatch' },
-    { view: 'Urchin', value: 'urchin' },
-  ];
+  // public backgrounds: characterData[] = [
+  //   { view: 'Akolyth', value: 'acolyte' },
+  //   { view: 'Scharlatan', value: 'charlatan' },
+  //   { view: 'Edelmann', value: 'noble' },
+  //   { view: 'Entertainer', value: 'entertainer' },
+  //   { view: 'Folk Held', value: 'folkHero' },
+  //   { view: 'Gelehrter', value: 'sage' },
+  //   { view: 'Gladiator', value: 'gladiator' },
+  //   { view: 'Gildenhändler', value: 'guildArtisan' },
+  //   { view: 'Gildehandwerker', value: 'guildMerchant' },
+  //   { view: 'Herumtreiber', value: 'outlander' },
+  //   { view: 'Krimineller', value: 'criminal' },
+  //   { view: 'Künstler', value: 'artist' },
+  //   { view: 'Marine', value: 'sailor' },
+  //   { view: 'Scharlatan', value: 'charlatan' },
+  //   { view: 'Soldat', value: 'soldier' },
+  //   { view: 'Stadtwache', value: 'cityWatch' },
+  //   { view: 'Urchin', value: 'urchin' },
+  // ];
 
-  public genders: characterData[] = [
-    { view: 'Weiblich', value: 'Female' },
-    { view: 'Männlich', value: 'Male' },
-    { view: 'Divers', value: 'Diverse' },
-  ];
+  // public genders: characterData[] = [
+  //   { view: 'Weiblich', value: 'Female' },
+  //   { view: 'Männlich', value: 'Male' },
+  //   { view: 'Divers', value: 'Diverse' },
+  // ];
 
   // Predefined Data
 
@@ -115,6 +117,8 @@ export class CharacterCreatorComponent {
   public constructor(
     public dataAccessor: DataService,
     private Router: Router,
+    public translator: TranslatorService,
+    public translate: TranslateService,
   ) {}
 
   public async createCharacter(): Promise<void> {

+ 7 - 3
src/app/character/character-picker/character-card/character-card.component.html

@@ -22,10 +22,14 @@
     <div class="content">
       <!-- <div>{{ image }}</div> -->
       <div class="name">{{ character }}</div>
-      <div class="level">Stufe {{ characterData.level }}</div>
-      <div class="species">{{ races[characterData.race].display }}</div>
+      <div class="level">
+        {{ "general.level" | translate }} {{ characterData.level }}
+      </div>
+      <div class="species">
+        {{ "species." + characterData.race | translate }}
+      </div>
       <div class="class">
-        {{ classes[characterData.class].display }}
+        {{ "classes." + characterData.class | translate }}
       </div>
 
       <!-- <div class="subclass">{{ characterData.class }}</div> -->

+ 12 - 12
src/app/character/character-picker/character-picker.component.html

@@ -1,5 +1,5 @@
 <div class="header">
-  <h1>DND-TOOLS</h1>
+  <h1>{{ "picker.label" | translate }}</h1>
 </div>
 <div class="character-card-container">
   @for (card of [0, 1, 2, 3, 4, 5]; track card) {
@@ -28,7 +28,9 @@
 
 <ng-template #content let-modal>
   <div class="modal-header">
-    <h4 class="modal-title" id="modal-basic-title">Charakter löschen</h4>
+    <h4 class="modal-title" id="modal-basic-title">
+      {{ "picker.delete" | translate }}
+    </h4>
     <button
       type="button"
       class="btn-close"
@@ -37,12 +39,14 @@
     ></button>
   </div>
   <div class="modal-body">
-    Möchest du <b>{{ currentCharacter }}</b> unwiderruflich löschen?
+    <div
+      [innerHTML]="'picker.confirm' | translate: { name: currentCharacter }"
+    ></div>
   </div>
   <div class="modal-footer">
     <div class="button-row">
       <button class="delete-button" (click)="modal.dismiss('delete')">
-        Löschen
+        {{ "buttons.delete" | translate }}
       </button>
     </div>
   </div>
@@ -50,7 +54,7 @@
 
 <ng-template #warning let-warning>
   <div class="modal-header">
-    <h4 class="modal-title" id="modal-basic-title">v0.6.0</h4>
+    <h4 class="modal-title" id="modal-basic-title">v0.7.0</h4>
     <button
       type="button"
       class="btn-close"
@@ -60,23 +64,19 @@
   </div>
   <div class="modal-body">
     <h5>
-      Die App befindet sich immer noch in einem Entwicklungsstadium und es
-      können Fehler auftreten
+      {{ "picker.hint" | translate }}
     </h5>
 
     <br />
     <h5>
-      Fehler bitte an
-      <a href="mailto:giese-engineering@outlook.com?subject=Fehler"
-        >diese Mail
-      </a>
+      {{ "picker.issues" | translate }}
     </h5>
     <button
       style="display: block; margin: auto; margin-top: 32px"
       class="okay-button"
       (click)="warning.dismiss('dismiss'); acknowledgeWarning()"
     >
-      Verstanden!
+      {{ "picker.okay" | translate }}!
     </button>
   </div>
 </ng-template>

+ 2 - 0
src/app/character/character-picker/character-picker.component.ts

@@ -10,6 +10,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 import { DataService } from 'src/services/data/data.service';
 import { Router } from '@angular/router';
 import { CharacterCardComponent } from './character-card/character-card.component';
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'app-character-picker',
@@ -30,6 +31,7 @@ export class CharacterPickerComponent {
   public constructor(
     public dataService: DataService,
     private Router: Router,
+    public translate: TranslateService,
   ) {
     this.dataService.dataLoaded = false;
     this.dataService.getCollection('characters').then((characters: any) => {

+ 3 - 0
src/app/character/character.module.ts

@@ -12,6 +12,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
 import { MatSelectModule } from '@angular/material/select';
 import { MatAutocompleteModule } from '@angular/material/autocomplete';
 import { DeletionConfirmComponent } from './character-picker/deletion-confirm/deletion-confirm.component';
+import { TranslateModule, TranslatePipe } from '@ngx-translate/core';
 
 @NgModule({
   declarations: [
@@ -31,6 +32,8 @@ import { DeletionConfirmComponent } from './character-picker/deletion-confirm/de
     MatFormFieldModule,
     MatSelectModule,
     MatAutocompleteModule,
+    TranslateModule,
   ],
+  providers: [TranslatePipe],
 })
 export class CharacterModule {}

+ 1 - 1
src/app/journal/journal-character/class/class.component.html

@@ -1,5 +1,5 @@
 <div class="class-container">
-  <div class="title">{{ class.title }}</div>
+  <div class="title">{{ "classes." + class.title | translate }}</div>
 
   <div [innerHTML]="class.description"></div>
 

+ 43 - 19
src/app/journal/journal-character/general/general.component.html

@@ -23,55 +23,71 @@
     <!-- LOOKS -->
     <div class="looks">
       <div class="looks-entry">
-        <label class="looks-label">Volk</label>
+        <label class="looks-label">{{
+          "character.general.species" | translate
+        }}</label>
         <div class="looks-field">
-          {{ races[data.race].display }}
+          {{ "species." + data.race | translate }}
         </div>
       </div>
       <div class="looks-entry">
-        <label class="looks-label">Klasse</label>
+        <label class="looks-label">{{
+          "character.general.class" | translate
+        }}</label>
         <div class="looks-field">
-          {{ classes[data.class].display }}
+          {{ "classes." + data.class | translate }}
         </div>
       </div>
       <div class="looks-entry">
-        <label class="looks-label">Geschlecht</label>
+        <label class="looks-label">{{
+          "character.general.gender" | translate
+        }}</label>
         <div class="looks-field">
-          {{ genders[data.gender].display }}
+          {{ "genders." + data.gender | translate }}
         </div>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Alter</div>
+        <div class="looks-label">{{ "character.general.age" | translate }}</div>
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.age" (change)="updateData()" />
         </mat-form-field>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Größe</div>
+        <div class="looks-label">
+          {{ "character.general.height" | translate }}
+        </div>
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.height" (change)="updateData()" />
         </mat-form-field>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Gewicht</div>
+        <div class="looks-label">
+          {{ "character.general.weight" | translate }}
+        </div>
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.weight" (change)="updateData()" />
         </mat-form-field>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Haarfarbe</div>
+        <div class="looks-label">
+          {{ "character.general.hair" | translate }}
+        </div>
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.hair" (change)="updateData()" />
         </mat-form-field>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Augenfarbe</div>
+        <div class="looks-label">
+          {{ "character.general.eyes" | translate }}
+        </div>
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.eyes" (change)="updateData()" />
         </mat-form-field>
       </div>
       <div class="looks-entry">
-        <div class="looks-label">Hautfarbe</div>
+        <div class="looks-label">
+          {{ "character.general.skin" | translate }}
+        </div>
 
         <mat-form-field appearance="outline" class="looks-input">
           <input matInput [(ngModel)]="data.skin" (change)="updateData()" />
@@ -88,7 +104,9 @@
           (change)="updateData()"
         ></textarea>
       </mat-form-field>
-      <label class="description-label">Beschreibung</label>
+      <label class="description-label">{{
+        "character.general.description" | translate
+      }}</label>
     </div>
   </div>
   <!-- PERSONALITY -->
@@ -103,9 +121,9 @@
           (change)="updateData()"
         ></textarea>
       </mat-form-field>
-      <label for="personality" class="personality-label"
-        >Persönlichkeitsmerkmale</label
-      >
+      <label for="personality" class="personality-label">{{
+        "character.general.personality" | translate
+      }}</label>
     </div>
     <div class="personality-box">
       <mat-form-field appearance="outline" class="personality-wrapper">
@@ -117,7 +135,9 @@
           (change)="updateData()"
         ></textarea>
       </mat-form-field>
-      <label for="ideals" class="personality-label">Ideale</label>
+      <label for="ideals" class="personality-label">{{
+        "character.general.ideals" | translate
+      }}</label>
     </div>
     <div class="personality-box">
       <mat-form-field appearance="outline" class="personality-wrapper">
@@ -129,7 +149,9 @@
           (change)="updateData()"
         ></textarea>
       </mat-form-field>
-      <label for="bonds" class="personality-label">Bindungen</label>
+      <label for="bonds" class="personality-label">{{
+        "character.general.bonds" | translate
+      }}</label>
     </div>
     <div class="personality-box">
       <mat-form-field appearance="outline" class="personality-wrapper">
@@ -141,7 +163,9 @@
           (change)="updateData()"
         ></textarea>
       </mat-form-field>
-      <label for="flaws" class="personality-label">Makel</label>
+      <label for="flaws" class="personality-label">{{
+        "character.general.flaws" | translate
+      }}</label>
     </div>
   </div>
 </div>

+ 17 - 12
src/app/journal/journal-character/journal-character.component.html

@@ -2,7 +2,7 @@
   <div class="header-data">
     <input type="number" [(ngModel)]="data.level" (change)="updateData()" />
     <div class="horizontal-ruler"></div>
-    Stufe
+    {{ "character.level" | translate }}
   </div>
   <div class="name">{{ name }}</div>
   <div class="header-data">
@@ -12,7 +12,7 @@
       (change)="updateData()"
     />
     <div class="horizontal-ruler"></div>
-    Erfahrungspunkte
+    {{ "character.experience" | translate }}
   </div>
 </div>
 <div class="character-body">
@@ -25,26 +25,29 @@
       orientation="vertical"
     >
       <ng-container ngbNavItem="general">
-        <button ngbNavLink>Allgemeines</button>
+        <button ngbNavLink>{{ "character.general.label" | translate }}</button>
         <ng-template ngbNavContent>
           <general></general>
         </ng-template>
       </ng-container>
       <ng-container ngbNavItem="species">
-        <button ngbNavLink>Volk</button>
+        <button ngbNavLink>{{ "character.species.label" | translate }}</button>
         <ng-template ngbNavContent>
           <species></species>
         </ng-template>
       </ng-container>
       <ng-container ngbNavItem="class">
-        <button ngbNavLink>Klasse</button>
+        <button ngbNavLink>{{ "character.class.label" | translate }}</button>
         <ng-template ngbNavContent>
           <class></class>
         </ng-template>
       </ng-container>
       <ng-container ngbNavItem="subclass">
         @if (showSubclass) {
-          <button ngbNavLink>{{ subclassLabel }}</button>
+          {{ data.title }}
+          <button ngbNavLink>
+            {{ "character.subclass." + data.class | translate }}
+          </button>
           <ng-template ngbNavContent>
             <subclass></subclass>
           </ng-template>
@@ -55,26 +58,28 @@
               src="assets/icons/UIIcons/lock.svg"
               alt="locked"
             />
-            Unterklasse
+            {{ "character.subclass." + data.class | translate }}
           </button>
         }
       </ng-container>
       <ng-container ngbNavItem="combined">
-        <button ngbNavLink>Tabellen- ansicht</button>
+        <button ngbNavLink>{{ "character.complete.label" | translate }}</button>
         <ng-template ngbNavContent>
           <!-- <class></class> -->
-          combined works!
+          Coming soon!
         </ng-template>
       </ng-container>
       <ng-container ngbNavItem="background">
-        <button ngbNavLink>Hintergrund</button>
+        <button ngbNavLink>
+          {{ "character.background.label" | translate }}
+        </button>
         <ng-template ngbNavContent>
           <!-- <class></class> -->
-          background works!
+          Coming soon!
         </ng-template>
       </ng-container>
       <ng-container ngbNavItem="stroy">
-        <button ngbNavLink>Geschichte</button>
+        <button ngbNavLink>{{ "character.story.label" | translate }}</button>
         <ng-template ngbNavContent>
           <story></story>
         </ng-template>

+ 0 - 3
src/app/journal/journal-character/journal-character.component.ts

@@ -21,9 +21,6 @@ export class JournalCharacterComponent {
 
   ngOnInit(): void {
     this.data = this.dataAccessor.characterData;
-    this.subclassLabel = this.classAccessor.getClassDetails(
-      this.data.class,
-    ).subclassLabel;
     this.isLevelSufficientForSubclass();
   }
 

+ 6 - 5
src/app/journal/journal-character/species/species.component.html

@@ -1,18 +1,19 @@
 <div class="species-container">
-  <div class="title">{{ species.title }}</div>
-  <!-- <div class="description" markdown="species.description">hi</div> -->
+  <div class="title">{{ "species." + species.title | translate }}</div>
 
-  <markdown [data]="species.description"></markdown>
+  <!-- <markdown [data]="species.description"></markdown> -->
+  <div [innerHTML]="species.description"></div>
 
   <div class="abilities">
     @for (ability of species.abilities; track ability) {
       <div class="ability">
         <div class="ability-name">{{ ability.name }}</div>
         <div class="ability-level">{{ ability.level }}</div>
-        <markdown
+        <div [innerHTML]="ability.description"></div>
+        <!-- <markdown
           class="ability-description"
           [data]="ability.description"
-        ></markdown>
+        ></markdown> -->
       </div>
     }
   </div>

+ 14 - 8
src/app/journal/journal-character/story/story.component.html

@@ -19,15 +19,21 @@
       ></ngx-editor>
     </div>
     <div class="horizontal-buttons" style="justify-content: space-around">
-      <ui-button [color]="'green'" (click)="update()" [width]="'w15'"
-        >Anwenden</ui-button
-      >
-      <ui-button [color]="'red'" (click)="cancel()" [width]="'w15'"
-        >Verwerfen</ui-button
-      >
+      <ui-button
+        [color]="'green'"
+        [width]="'w15'"
+        [type]="'apply'"
+        (click)="update()"
+      ></ui-button>
+      <ui-button
+        [color]="'red'"
+        [width]="'w15'"
+        [type]="'discard'"
+        (click)="cancel()"
+      ></ui-button>
     </div>
   } @else {
-    Hintergrundgeschichte bearbeiten
-    <button (click)="edit()">Bearbeiten</button>
+    {{ "character.story.editStory" | translate }}
+    <button (click)="edit()">{{ "character.story.edit" | translate }}</button>
   }
 </div>

+ 12 - 10
src/app/journal/journal-character/subclass/subclass.component.html

@@ -1,29 +1,31 @@
 <div class="subclass-container">
   @if (!subclass) {
-    <div class="title">Noch keine Unterklasse gewählt</div>
+    <div class="title">{{ "subclasses.empty" | translate }}</div>
     <div class="description">
+      <p>{{ "subclasses.hint" | translate }}</p>
       <p>
-        Hier kannst du deine Unterklasse wählen. Die Unterklasse ist eine
-        spezielle Spezialisierung deiner Klasse. Sie gibt dir zusätzliche
-        Fähigkeiten und Eigenschaften.
-      </p>
-      <p>
-        <mat-form-field appearance="outline">
-          <mat-label>Unterklasse</mat-label>
+        <mat-form-field appearance="outline" class="t-1">
+          <mat-label>{{
+            "character.subclass." + className | translate
+          }}</mat-label>
           <mat-select
             (selectionChange)="updateData()"
             [(ngModel)]="data.subclass"
             name="species"
           >
             @for (subclass of availableSubclasses; track subclass) {
-              <mat-option [value]="subclass">{{ subclass }}</mat-option>
+              <mat-option [value]="subclass">{{
+                "subclasses." + className + "." + subclass | translate
+              }}</mat-option>
             }
           </mat-select>
         </mat-form-field>
       </p>
     </div>
   } @else {
-    <div class="title">{{ subclass.title }}</div>
+    <div class="title">
+      {{ "subclasses." + className + "." + subclassName | translate }}
+    </div>
 
     <div [innerHTML]="subclass.description"></div>
 

+ 6 - 5
src/app/journal/journal-character/subclass/subclass.component.ts

@@ -13,9 +13,9 @@ import { TraitModalComponent } from '../../journal-stats/ability-panel/trait-tab
   styleUrl: './subclass.component.scss',
 })
 export class SubclassComponent {
-  public subclassName: string = 'EchoKnight';
+  public subclassName: string = 'echoKnight';
   public subclass: any;
-  private className: string = '';
+  public className: string = '';
   public availableSubclasses: string[] = [];
   public data: any;
 
@@ -35,10 +35,11 @@ export class SubclassComponent {
       this.subclass = this.SubclassService.getSubclassDetails(
         this.subclassName,
       );
+    } else {
+      this.availableSubclasses = this.SubclassService.getAvailableSubclassNames(
+        this.className,
+      );
     }
-    this.availableSubclasses = this.SubclassService.getAvailableSubclassNames(
-      this.className,
-    );
   }
 
   /**

+ 14 - 14
src/app/journal/journal-home/navigation-panel/navigation-panel.component.html

@@ -1,6 +1,6 @@
 <div #navigationBackdrop class="backdrop" (click)="closePanel()"></div>
 <div #navigationPanel class="panel">
-  <h2 style="margin-bottom: 1.5rem">Menu</h2>
+  <h2 style="margin-bottom: 1.5rem">{{ "navigation.menu" | translate }}</h2>
   <ul>
     <li>
       <div
@@ -9,7 +9,7 @@
         (click)="setActiveProperty(1); closeAll()"
         [routerLink]="'./stats'"
       >
-        Übersicht
+        {{ "navigation.dashboard" | translate }}
       </div>
     </li>
     <li>
@@ -19,7 +19,7 @@
         (click)="setActiveProperty(2); closeAll()"
         [routerLink]="'./character'"
       >
-        Charakter
+        {{ "navigation.character" | translate }}
       </div>
     </li>
     <li>
@@ -29,7 +29,7 @@
         (click)="setActiveProperty(3); closeAll()"
         [routerLink]="'./inventory'"
       >
-        Inventar
+        {{ "navigation.inventory" | translate }}
       </div>
     </li>
     <li>
@@ -39,7 +39,7 @@
         (click)="setActiveProperty(4); closeAll()"
         [routerLink]="'./spellcards'"
       >
-        Zauber
+        {{ "navigation.spells" | translate }}
       </div>
     </li>
     <li>
@@ -49,7 +49,7 @@
         (click)="setActiveProperty(5); closeAll()"
         [routerLink]="'./notes'"
       >
-        Notizen
+        {{ "navigation.notes" | translate }}
       </div>
     </li>
     <li>
@@ -59,7 +59,7 @@
         (click)="setActiveProperty(6); closeAll()"
         [routerLink]="'./spellbook'"
       >
-        Zauberbuch
+        {{ "navigation.spellbook" | translate }}
       </div>
     </li>
     <li>
@@ -69,7 +69,7 @@
         (click)="setActiveProperty(7); closeAll()"
         [routerLink]="'./quests'"
       >
-        Aufträge
+        {{ "navigation.quests" | translate }}
       </div>
     </li>
     <li>
@@ -79,7 +79,7 @@
         (click)="setActiveProperty(8); closeAll()"
         [routerLink]="'./npcs'"
       >
-        NPCs
+        {{ "navigation.npcs" | translate }}
       </div>
     </li>
     <li>
@@ -89,7 +89,7 @@
         (click)="setActiveProperty(9); closeAll()"
         [routerLink]="'./places'"
       >
-        Orte
+        {{ "navigation.places" | translate }}
       </div>
     </li>
     <li>
@@ -99,7 +99,7 @@
         (click)="setActiveProperty(10); closeAll()"
         [routerLink]="'./maps'"
       >
-        Karten
+        {{ "navigation.maps" | translate }}
       </div>
     </li>
 
@@ -110,7 +110,7 @@
         (click)="setActiveProperty(11); closeAll()"
         [routerLink]="'./ruleset'"
       >
-        Regelwerk
+        {{ "navigation.rules" | translate }}
       </div>
     </li>
   </ul>
@@ -118,7 +118,7 @@
   <div class="settings-container">
     <button class="settings-button" (click)="closeAll()" [routerLink]="'../'">
       <icon [size]="'s'" [type]="'UI'" [icon]="'characters'"></icon>
-      <div>Charaktere</div>
+      <div>{{ "navigation.characters" | translate }}</div>
     </button>
 
     <button class="settings-button" [routerLink]="'./settings'">
@@ -128,7 +128,7 @@
         (click)="closeAll()"
         [icon]="'settings'"
       ></icon>
-      <div>Einstellungen</div>
+      <div>{{ "navigation.settings" | translate }}</div>
     </button>
   </div>
 </div>

+ 23 - 15
src/app/journal/journal-inventory/journal-inventory.component.html

@@ -7,22 +7,22 @@
         (click)="active = 1"
         [class]="active === 1 ? 'active' : ''"
       >
-        Waffen und Rüstungen
+        {{ "inventory.weapArm" | translate }}
       </button>
       <button
         class="tab-button"
         (click)="active = 2"
         [class]="active === 2 ? 'active' : ''"
       >
-        Sonstiges
+        {{ "inventory.misc" | translate }}
       </button>
     </div>
 
     <div class="heading-row">
-      <div class="table-heading">Name</div>
-      <div class="table-heading">Wert</div>
-      <div class="table-heading">Gewicht</div>
-      <div class="table-heading">Anzahl</div>
+      <div class="table-heading">{{ "inventory.name" | translate }}</div>
+      <div class="table-heading">{{ "inventory.value" | translate }}</div>
+      <div class="table-heading">{{ "inventory.weight" | translate }}</div>
+      <div class="table-heading">{{ "inventory.quantity" | translate }}</div>
     </div>
 
     @switch (active) {
@@ -35,8 +35,12 @@
     }
 
     <div class="footer">
-      <ui-button [color]="'green'" [width]="'w22'" (click)="addItem('items')">
-        Eintrag hinzufügen
+      <ui-button
+        [color]="'green'"
+        [width]="'w22'"
+        [type]="'addEntry'"
+        (click)="addItem('items')"
+      >
       </ui-button>
     </div>
   </div>
@@ -50,23 +54,27 @@
           (click)="foodActive = 1"
           [class]="foodActive === 1 ? 'active' : ''"
         >
-          Nahrung
+          {{ "inventory.food" | translate }}
         </button>
         <button
           class="tab-button"
           (click)="foodActive = 2"
           [class]="foodActive === 2 ? 'active' : ''"
         >
-          Verbrauchsgegenstände
+          {{ "inventory.usable" | translate }}
         </button>
       </div>
       <div class="heading-row">
-        <div class="table-heading">Name</div>
+        <div class="table-heading">{{ "inventory.name" | translate }}</div>
         <div class="table-heading">
-          {{ foodActive === 1 ? "Verzehrfertig" : "Wert" }}
+          {{
+            foodActive === 1
+              ? ("inventory.ready" | translate)
+              : ("inventory.value" | translate)
+          }}
         </div>
-        <div class="table-heading">Gewicht</div>
-        <div class="table-heading">Anzahl</div>
+        <div class="table-heading">{{ "inventory.weight" | translate }}</div>
+        <div class="table-heading">{{ "inventory.quantity" | translate }}</div>
       </div>
       @switch (foodActive) {
         @case (1) {
@@ -81,9 +89,9 @@
         <ui-button
           [color]="'green'"
           [width]="'w22'"
+          [type]="'addEntry'"
           (click)="addItem('consumables')"
         >
-          Eintrag hinzufügen
         </ui-button>
       </div>
     </div>

+ 17 - 9
src/app/journal/journal-inventory/simple-item-details/simple-item-details.component.html

@@ -3,19 +3,19 @@
 <div class="value-row t-2">
   <value-box
     [value]="isSimpleItem(item) ? item.value : item.isReady ? 'Ja' : 'Nein'"
-    [label]="isSimpleItem(item) ? 'Wert' : 'Verzehrbereit'"
+    [label]="isSimpleItem(item) ? 'inventory.value' : 'inventory.ready'"
   ></value-box>
-  <value-box [value]="item.weight" [label]="'Gewicht'"></value-box>
+  <value-box [value]="item.weight" [label]="'inventory.weight'"></value-box>
 </div>
 <div class="value-row t-2">
-  <value-box [value]="item.quantity" [label]="'Anzahl'"></value-box>
+  <value-box [value]="item.quantity" [label]="'inventory.quantity'"></value-box>
   <value-box
     [value]="item.quantity * item.weight"
-    [label]="'Gesamtgewicht'"
+    [label]="'inventory.total'"
   ></value-box>
 </div>
 
-<div class="input-label t-2">Beschreibung</div>
+<div class="input-label t-2">{{ "inventory.description" | translate }}</div>
 <div
   class="t-1"
   style="padding-left: 0.25rem"
@@ -23,10 +23,18 @@
 ></div>
 
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w22'" (click)="close('update')">
-    Bearbeiten
+  <ui-button
+    [color]="'green'"
+    [width]="'w22'"
+    [type]="'edit'"
+    (click)="close('update')"
+  >
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w22'" (click)="close('delete')">
-    Löschen
+  <ui-button
+    [color]="'red'"
+    [width]="'w22'"
+    [type]="'delete'"
+    (click)="close('delete')"
+  >
   </ui-button>
 </div>

+ 29 - 15
src/app/journal/journal-inventory/simple-item-modal/simple-item-modal.component.html

@@ -1,15 +1,15 @@
 <div class="dimensions">
   <div class="title">
     @if (!isUpdate) {
-      Gegenstand erstellen
+      {{ "inventory.modal.add" | translate }}
     } @else {
-      Gegenstand bearbeiten
+      {{ "inventory.modal.edit" | translate }}
     }
   </div>
 
   <div class="flex-form t-15">
     <div>
-      <div class="input-label">Name</div>
+      <div class="input-label">{{ "inventory.name" | translate }}</div>
       <mat-form-field appearance="outline" style="width: 18.9rem">
         <input matInput [(ngModel)]="name" />
       </mat-form-field>
@@ -17,22 +17,24 @@
 
     <div class="flex-row numbers">
       <div>
-        <div class="input-label">Gewicht</div>
+        <div class="input-label">{{ "inventory.weight" | translate }}</div>
         <mat-form-field appearance="outline" style="width: 7rem">
           <input type="number" class="right" matInput [(ngModel)]="weight" />
           <span class="input-value" matTextSuffix>Lb.</span>
         </mat-form-field>
       </div>
       <div>
-        <div class="input-label">Anzahl</div>
+        <div class="input-label">{{ "inventory.quantity" | translate }}</div>
         <mat-form-field appearance="outline" style="width: 7rem">
           <input type="number" class="right" matInput [(ngModel)]="quantity" />
-          <span class="input-value" matTextSuffix>Stk.</span>
+          <span class="input-value" matTextSuffix>{{
+            "inventory.modal.pieces" | translate
+          }}</span>
         </mat-form-field>
       </div>
       @if (isFood) {
         <div>
-          <div class="input-label">Verzehrfertig</div>
+          <div class="input-label">{{ "inventory.ready" | translate }}</div>
           <input
             type="checkbox"
             style="margin: 0.5rem 0 0 2.5rem"
@@ -41,7 +43,7 @@
         </div>
       } @else {
         <div>
-          <div class="input-label">Wert</div>
+          <div class="input-label">{{ "inventory.value" | translate }}</div>
           <mat-form-field appearance="outline" style="width: 7rem">
             <input type="number" class="right" matInput [(ngModel)]="value" />
             <span class="input-value" matTextSuffix>Gold</span>
@@ -56,22 +58,34 @@
     <ngx-editor
       [editor]="editor"
       [(ngModel)]="description"
-      placeholder="Beschreibung des Gegenstandes"
+      [placeholder]="'inventory.modal.placeholder' | translate"
     ></ngx-editor>
   </div>
 
   <div class="horizontal-buttons">
     @if (isUpdate) {
-      <ui-button [color]="'green'" style="width: 40%" (click)="update()">
-        Bestätigen
+      <ui-button
+        [color]="'green'"
+        [type]="'confirm'"
+        style="width: 40%"
+        (click)="update()"
+      >
       </ui-button>
     } @else {
-      <ui-button [color]="'green'" style="width: 40%" (click)="add()">
-        Bestätigen
+      <ui-button
+        [color]="'green'"
+        [type]="'confirm'"
+        style="width: 40%"
+        (click)="add()"
+      >
       </ui-button>
     }
-    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()">
-      Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 40%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>

+ 5 - 1
src/app/journal/journal-settings/journal-settings.component.html

@@ -27,6 +27,10 @@
   <hr />
   <div class="settings-entry">
     Aktuellen Charakter exportieren (coming soon)
-    <ui-button [color]="'neutral'" [size]="'w15'">Exportieren</ui-button>
+    <ui-button
+      [color]="'neutral'"
+      [type]="'export'"
+      [width]="'w15'"
+    ></ui-button>
   </div>
 </div>

+ 7 - 5
src/app/journal/journal-spellcards/add-card/add-card.component.html

@@ -6,17 +6,19 @@
   } @else if (state === 2) {
     <div class="button-card">
       <button (click)="continueToSpellSelection(false); state = 3">
-        Offizieller Zauber
+        {{ "spellcards.add.official" | translate }}
       </button>
 
       <hr />
       <button (click)="continueToSpellSelection(true); state = 3">
-        Offiziellen Zauber bearbeiten
+        {{ "spellcards.add.edit" | translate }}
       </button>
 
       <hr />
 
-      <button (click)="emitNewSpell()">Eigener Zauber</button>
+      <button (click)="emitNewSpell()">
+        {{ "spellcards.add.custom" | translate }}
+      </button>
     </div>
   } @else if (state === 3) {
     <div class="spell-selection">
@@ -24,7 +26,7 @@
         type="text"
         class="spell-name"
         [(ngModel)]="newSpellName"
-        placeholder="Zauber durchsuchen"
+        [placeholder]="'spellcards.add.official' | translate"
         (keyup)="
           isModification
             ? filterSpellArrayForModification()
@@ -46,7 +48,7 @@
               </button>
             </li>
           } @empty {
-            Keine Zauber gefunden
+            {{ "spellcards.add.empty" | translate }}
           }
         </ul>
       </div>

+ 18 - 8
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.html

@@ -1,6 +1,6 @@
 <div class="dimensions">
   <div class="content">
-    <div class="title">Eigene Zauber verwalten</div>
+    <div class="title">{{ "spellcards.manage" | translate }}</div>
     <div class="spell-list">
       @for (spell of spells; let index = $index; track spell) {
         <div
@@ -9,17 +9,27 @@
           [ngClass]="{ selected: indexList.includes(index) }"
           (click)="toggleSpellSelection(index)"
         >
-          {{ spell.german }}
+          @if (translate.getDefaultLang() == "de") {
+            {{ spell.german }}
+          } @else {
+            {{ spell.english }}
+          }
         </div>
       }
     </div>
   </div>
   <div class="horizontal-buttons" style="padding: 2rem 1.5rem">
-    <ui-button [color]="'red'" [width]="'w15'" (click)="delete()"
-      >Ausgewählte Löschen</ui-button
-    >
-    <ui-button [color]="'red'" [width]="'w15'" (click)="cancel()"
-      >Abbrechen</ui-button
-    >
+    <ui-button
+      [color]="'red'"
+      [width]="'w15'"
+      [type]="'deleteSelected'"
+      (click)="delete()"
+    ></ui-button>
+    <ui-button
+      [color]="'red'"
+      [width]="'w15'"
+      [type]="'cancel'"
+      (click)="cancel()"
+    ></ui-button>
   </div>
 </div>

+ 5 - 1
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.ts

@@ -1,6 +1,7 @@
 import { Component, Input } from '@angular/core';
 import { Spell } from 'src/interfaces/spell';
 import { ModalService } from 'src/services/modal/modal.service';
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'custom-spells-modal',
@@ -11,7 +12,10 @@ export class CustomSpellsModalComponent {
   @Input() public spells: Spell[] = [];
   public indexList: number[] = [];
 
-  public constructor(private modalAccessor: ModalService) {}
+  public constructor(
+    private modalAccessor: ModalService,
+    public translate: TranslateService,
+  ) {}
 
   public toggleSpellSelection(index: number): void {
     if (this.indexList.includes(index)) {

+ 15 - 5
src/app/journal/journal-spellcards/journal-spellcards.component.html

@@ -1,11 +1,15 @@
 <div class="spellcards-container">
   <button class="manage-spells" (click)="openManageCustomSpellsModal()">
-    <img src="assets/icons/UIIcons/settings.svg" /> Eigene Zauber verwalten
+    <img src="assets/icons/UIIcons/settings.svg" />
+    {{ "spellcards.manage" | translate }}
   </button>
 
   <div cdkDropListGroup>
-    <!-- TODO: revert array to 0-9 -->
-    @for (level of [0, 1, 2, 3]; track level; let index = $index) {
+    @for (
+      level of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+      track level;
+      let index = $index
+    ) {
       <div class="example-container">
         <div
           [class]="showSpellList[index] ? 'level-row' : 'level-row collapsed'"
@@ -19,7 +23,13 @@
               [class]="'pointer'"
               class="inline"
             ></icon>
-            <div class="heading-2 inline">{{ getSpellLevel(index) }}</div>
+            <div class="heading-2 inline">
+              @if (index === 0) {
+                {{ "spellcards.cantrips" | translate }}
+              } @else {
+                Level {{ index }}
+              }
+            </div>
           </div>
         </div>
 
@@ -47,7 +57,7 @@
           }
           @if (draggingIndex === index) {
             <div class="removal-card" [id]="'deletion' + index">
-              Hier zum Löschen ablegen
+              {{ "spellcards.delete" | translate }}
             </div>
           } @else {
             <add-card

+ 0 - 32
src/app/journal/journal-spellcards/journal-spellcards.component.ts

@@ -269,38 +269,6 @@ export class JournalSpellcardsComponent {
     }
   }
 
-  /**
-   * A lookup function to match spell levels from 0-9 to strings eg: 2 => "Level 2".
-   * @param level The level that is looked up.
-   * @returns Returns a string with the name to display in the view.
-   */
-  public getSpellLevel(level: number): string {
-    switch (level) {
-      case 0:
-        return 'Zaubertricks';
-      case 1:
-        return 'Level 1';
-      case 2:
-        return 'Level 2';
-      case 3:
-        return 'Level 3';
-      case 4:
-        return 'Level 4';
-      case 5:
-        return 'Level 5';
-      case 6:
-        return 'Level 6';
-      case 7:
-        return 'Level 7';
-      case 8:
-        return 'Level 8';
-      case 9:
-        return 'Level 9';
-      default:
-        return '';
-    }
-  }
-
   public toggleSpellList(index: number) {
     this.showSpellList[index] = !this.showSpellList[index];
   }

+ 6 - 2
src/app/journal/journal-spellcards/spellcard/spellcard.component.html

@@ -9,7 +9,11 @@
     class="title"
     [style.box-shadow]="'var(--' + spell.school.toLowerCase() + '-border)'"
   >
-    {{ spell.german }}
+    @if (translate.getDefaultLang() == "de") {
+      {{ spell.german }}
+    } @else {
+      {{ spell.english }}
+    }
   </div>
   <img
     class="spell-image"
@@ -22,7 +26,7 @@
     class="info"
     [style.box-shadow]="'var(--' + spell.school.toLowerCase() + '-border)'"
   >
-    <div class="school">{{ schools[spell.school].display }}</div>
+    <div class="school">{{ "schools." + spell.school | translate }}</div>
     <div
       class="level"
       [style.box-shadow]="'var(--' + spell.school.toLowerCase() + '-border)'"

+ 2 - 8
src/app/journal/journal-spellcards/spellcard/spellcard.component.ts

@@ -1,6 +1,7 @@
 import { Component, Input, Output, EventEmitter } from '@angular/core';
 import { Spell } from 'src/interfaces/spell';
 import { TranslatorService } from 'src/services/translator/translator.service';
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'spellcard',
@@ -9,15 +10,8 @@ import { TranslatorService } from 'src/services/translator/translator.service';
 })
 export class SpellcardComponent {
   @Input() public spell!: Spell;
-  public schools: any;
 
-  public constructor(public translator: TranslatorService) {
-    this.schools = translator.schools;
-  }
-
-  public ngOnInit(): void {
-    console.log(this.spell);
-  }
+  public constructor(public translate: TranslateService) {}
 
   public setBackupImage(event: any): void {
     event.target.src = 'assets/images/spells/backup.jpg';

+ 12 - 4
src/app/journal/journal-stats/ability-panel/ability-table/ability-details/ability-details.component.html

@@ -11,10 +11,18 @@
 </div>
 
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w20'" (click)="close('update')">
-    Anpassen
+  <ui-button
+    [color]="'green'"
+    [width]="'w20'"
+    [type]="'edit'"
+    (click)="close('update')"
+  >
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w20'" (click)="close('delete')">
-    Löschen
+  <ui-button
+    [color]="'red'"
+    [width]="'w20'"
+    [type]="'delete'"
+    (click)="close('delete')"
+  >
   </ui-button>
 </div>

+ 18 - 9
src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.html

@@ -76,17 +76,26 @@
 
     <div class="horizontal-buttons">
       @if (isUpdate && !isAddedFromCharacter) {
-        <ui-button [color]="'green'" style="width: 45%" (click)="update()"
-          >Aktualisieren</ui-button
-        >
+        <ui-button
+          [color]="'green'"
+          [type]="'apply'"
+          style="width: 45%"
+          (click)="update()"
+        ></ui-button>
       } @else {
-        <ui-button [color]="'green'" style="width: 45%" (click)="add()"
-          >Hinzufügen</ui-button
-        >
+        <ui-button
+          [color]="'green'"
+          [type]="'add'"
+          style="width: 45%"
+          (click)="add()"
+        ></ui-button>
       }
-      <ui-button [color]="'red'" style="width: 45%" (click)="cancel()"
-        >Abbrechen</ui-button
-      >
+      <ui-button
+        [color]="'red'"
+        [type]="'cancel'"
+        style="width: 45%"
+        (click)="cancel()"
+      ></ui-button>
     </div>
   </div>
 </div>

+ 6 - 2
src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.html

@@ -44,8 +44,12 @@
     }
   </div>
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal(false)">
-      Hinzufügen
+    <ui-button
+      [color]="'green'"
+      [type]="'add'"
+      style="width: 80%"
+      (click)="openModal(false)"
+    >
     </ui-button>
   </div>
 </div>

+ 6 - 2
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html

@@ -92,8 +92,12 @@
 
   <!-- MODAL BUTTON -->
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal()">
-      Anpassen
+    <ui-button
+      [color]="'green'"
+      [type]="'modify'"
+      style="width: 80%"
+      (click)="openModal()"
+    >
     </ui-button>
   </div>
 </div>

+ 12 - 4
src/app/journal/journal-stats/ability-panel/proficiencies-table/tools-modal/tools-modal.component.html

@@ -43,11 +43,19 @@
   </div>
 
   <div class="horizontal-buttons">
-    <ui-button [color]="'green'" style="width: 45%" (click)="update()">
-      Aktualisieren
+    <ui-button
+      [color]="'green'"
+      [type]="'confirm'"
+      style="width: 45%"
+      (click)="update()"
+    >
     </ui-button>
-    <ui-button [color]="'red'" style="width: 45%" (click)="cancel()">
-      Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 45%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>

+ 12 - 4
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html

@@ -57,11 +57,19 @@
   }
 
   <div class="horizontal-buttons">
-    <ui-button [color]="'green'" style="width: 45%" (click)="update()">
-      Aktualisieren
+    <ui-button
+      [color]="'green'"
+      [type]="'confirm'"
+      style="width: 45%"
+      (click)="update()"
+    >
     </ui-button>
-    <ui-button [color]="'red'" style="width: 45%" (click)="cancel()">
-      Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 45%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>

+ 6 - 2
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.html

@@ -89,8 +89,12 @@
   <!-- MODAL BUTTON -->
 
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal()">
-      Anpassen
+    <ui-button
+      [color]="'green'"
+      [type]="'modify'"
+      style="width: 80%"
+      (click)="openModal()"
+    >
     </ui-button>
   </div>
 </div>

+ 12 - 4
src/app/journal/journal-stats/ability-panel/trait-table/trait-details/trait-details.component.html

@@ -3,10 +3,18 @@
 <div class="content" [innerHTML]="trait?.longDescription"></div>
 
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w20'" (click)="close('update')">
-    Anpassen
+  <ui-button
+    [color]="'green'"
+    [type]="'edit'"
+    [width]="'w20'"
+    (click)="close('update')"
+  >
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w20'" (click)="close('delete')">
-    Löschen
+  <ui-button
+    [color]="'red'"
+    [type]="'delete'"
+    [width]="'w20'"
+    (click)="close('delete')"
+  >
   </ui-button>
 </div>

+ 18 - 9
src/app/journal/journal-stats/ability-panel/trait-table/trait-modal/trait-modal.component.html

@@ -44,17 +44,26 @@
 
     <div class="horizontal-buttons">
       @if (isUpdate && !isAddedFromCharacter) {
-        <ui-button [color]="'green'" style="width: 45%" (click)="update()"
-          >Aktualisieren</ui-button
-        >
+        <ui-button
+          [color]="'green'"
+          [type]="'apply'"
+          style="width: 45%"
+          (click)="update()"
+        ></ui-button>
       } @else {
-        <ui-button [color]="'green'" style="width: 45%" (click)="add()"
-          >Hinzufügen</ui-button
-        >
+        <ui-button
+          [color]="'green'"
+          [type]="'add'"
+          style="width: 45%"
+          (click)="add()"
+        ></ui-button>
       }
-      <ui-button [color]="'red'" style="width: 45%" (click)="cancel()"
-        >Abbrechen</ui-button
-      >
+      <ui-button
+        [color]="'red'"
+        [type]="'cancel'"
+        style="width: 45%"
+        (click)="cancel()"
+      ></ui-button>
     </div>
   </div>
 </div>

+ 6 - 2
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.html

@@ -11,8 +11,12 @@
     }
   </div>
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal(false)">
-      Hinzufügen
+    <ui-button
+      [color]="'green'"
+      [type]="'add'"
+      style="width: 80%"
+      (click)="openModal(false)"
+    >
     </ui-button>
   </div>
 </div>

+ 2 - 2
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.html

@@ -5,8 +5,8 @@
   </div>
 </div>
 <div class="attribute-container">
-  <value-box [value]="attribute.value" [label]="'attributeValue'"></value-box>
-  <value-box [value]="modifier" [label]="'modifier'"></value-box>
+  <value-box [value]="attribute.value" [label]="'attributes.value'"></value-box>
+  <value-box [value]="modifier" [label]="'general.modifier'"></value-box>
 </div>
 
 @if (attribute.name !== "constitution") {

+ 1 - 1
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts

@@ -19,7 +19,7 @@ export class AttributeDetailsComponent {
 
   public skillsTable: any = {
     strength: ['athletics'],
-    dexterity: ['acrobatics', 'sleightOfHands', 'stealth'],
+    dexterity: ['acrobatics', 'sleightOfHand', 'stealth'],
     constitution: [],
     intelligence: ['arcana', 'history', 'investigation', 'nature', 'religion'],
     wisdom: ['animalHandling', 'insight', 'medicine', 'perception', 'survival'],

+ 1 - 1
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.html

@@ -7,4 +7,4 @@
   {{ "savingThrow.content" | translate }}
 </div>
 
-<value-box [value]="saveModifier" [label]="'modifier'"></value-box>
+<value-box [value]="saveModifier" [label]="'general.modifier'"></value-box>

+ 1 - 1
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.html

@@ -1,3 +1,3 @@
 <div class="title">{{ "skills." + skillName | translate }}</div>
 <div class="content">{{ "skillsDescription." + skillName | translate }}</div>
-<value-box [value]="skillModifier" [label]="'modifier'"></value-box>
+<value-box [value]="skillModifier" [label]="'general.modifier'"></value-box>

+ 1 - 1
src/app/journal/journal-stats/info-row/armor-class/armor-class-details/armor-class-details.component.html

@@ -4,4 +4,4 @@
   {{ "armorClass.description" | translate }}
 </div>
 
-<value-box [value]="armorClass" [label]="'value'"></value-box>
+<value-box [value]="armorClass" [label]="'general.value'"></value-box>

+ 12 - 4
src/app/journal/journal-stats/info-row/conditions/conditions-details/conditions-details.component.html

@@ -80,10 +80,18 @@
 
 <!-- NAVIGATION BUTTONS -->
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w20'" (click)="close('update')">
-    Anpassen
+  <ui-button
+    [color]="'green'"
+    [type]="'edit'"
+    [width]="'w20'"
+    (click)="close('update')"
+  >
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w20'" (click)="close('cancel')">
-    Löschen
+  <ui-button
+    [color]="'red'"
+    [type]="'delete'"
+    [width]="'w20'"
+    (click)="close('cancel')"
+  >
   </ui-button>
 </div>

+ 4 - 1
src/app/journal/journal-stats/info-row/conditions/exhaustion-details/exhaustion-details.component.html

@@ -2,7 +2,10 @@
 
 <div class="content">{{ "exhaustion.description" | translate }}</div>
 
-<value-box [value]="exhaustion" [label]="'currentExhaustion'"></value-box>
+<value-box
+  [value]="exhaustion"
+  [label]="'exhaustion.currentExhaustion'"
+></value-box>
 
 <div class="t-2">
   <table class="table table-striped">

+ 1 - 1
src/app/journal/journal-stats/info-row/initiative/initiative-details/initiative-details.component.html

@@ -2,4 +2,4 @@
 
 <div class="content">{{ "initiative.description" | translate }}</div>
 
-<value-box [value]="initiative" [label]="'Wert'"></value-box>
+<value-box [value]="initiative" [label]="'general.value'"></value-box>

+ 4 - 4
src/app/journal/journal-stats/info-row/movement/movement-details/movement-details.component.html

@@ -6,8 +6,8 @@
 </div>
 
 <div class="value-row">
-  <value-box [value]="movement" [label]="'feet'"></value-box>
-  <value-box [value]="movement / 5" [label]="'fields'"></value-box>
+  <value-box [value]="movement" [label]="'general.feet'"></value-box>
+  <value-box [value]="movement / 5" [label]="'general.fields'"></value-box>
 </div>
 
 <div class="heading left">{{ "movement.jumping" | translate }}</div>
@@ -15,8 +15,8 @@
   {{ "movement.jumpingDescription" | translate }}
 </div>
 <div class="value-row">
-  <value-box [value]="strength / 2" [label]="'feet'"></value-box>
-  <value-box [value]="strength / 10" [label]="'fields'"></value-box>
+  <value-box [value]="strength / 2" [label]="'general.feet'"></value-box>
+  <value-box [value]="strength / 10" [label]="'general.fields'"></value-box>
 </div>
 
 <mat-expansion-panel>

+ 1 - 1
src/app/journal/journal-stats/info-row/proficiency/proficiency-details/proficiency-details.component.html

@@ -2,4 +2,4 @@
 
 <div class="content">{{ "proficiency.description" | translate }}</div>
 
-<value-box [value]="proficiency" [label]="'value'"></value-box>
+<value-box [value]="proficiency" [label]="'general.value'"></value-box>

+ 12 - 4
src/app/journal/journal-stats/life-container/life/life-details/life-details.component.html

@@ -22,10 +22,18 @@
 <hit-dice style="margin-top: 5rem" (setHitDice)="setHitDice($event)"></hit-dice>
 
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w20'" (click)="close('update')">
-    Anpassen
+  <ui-button
+    [color]="'green'"
+    [type]="'apply'"
+    [width]="'w20'"
+    (click)="close('update')"
+  >
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w20'" (click)="close('cancel')">
-    Löschen
+  <ui-button
+    [color]="'red'"
+    [type]="'discard'"
+    [width]="'w20'"
+    (click)="close('cancel')"
+  >
   </ui-button>
 </div>

+ 12 - 4
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html

@@ -77,11 +77,19 @@
   </div>
 
   <div class="horizontal-buttons">
-    <ui-button [color]="'green'" style="width: 40%" (click)="update()">
-      Bestätigen
+    <ui-button
+      [color]="'green'"
+      [type]="'confirm'"
+      style="width: 40%"
+      (click)="update()"
+    >
     </ui-button>
-    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()">
-      Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 40%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>

+ 0 - 1
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.ts

@@ -1,5 +1,4 @@
 import { Component, Input } from '@angular/core';
-import { CommonModule } from '@angular/common';
 import { Spell } from 'src/interfaces/spell';
 import { ModalService } from 'src/services/modal/modal.service';
 

+ 0 - 75
src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.html

@@ -1,75 +0,0 @@
-<div class="details-title">{{ spell.german }}</div>
-
-<div class="details-scrollcontainer">
-  <div class="details-subheading">Beschreibung</div>
-  <div class="details-content" [innerHTML]="spell.description_de"></div>
-
-  <div class="details-subheading">Komponenten:</div>
-  <div class="details-content-small">
-    <span *ngIf="spell.needsVerbal"> Verbal, </span>
-    <span *ngIf="spell.needsSomatic"> Geste, </span>
-    <span *ngIf="spell.needsMaterial"> Material </span>
-  </div>
-
-  <div class="details-subheading">Eigenschaften</div>
-  <div class="details-content-small">
-    <div *ngIf="spell.canRitual">Kann als Ritual gewirkt werden</div>
-    <div *ngIf="spell.needsConcentration">Benötigt Konzentration</div>
-    <div>Schule: {{ spell.school }}</div>
-  </div>
-
-  <div class="details-subheading">Reichweite</div>
-  <div class="details-content-small">
-    <div>Reichweite: {{ spell.range }} ft.</div>
-    <div *ngIf="spell.hasAreaOfEffect">
-      Flächeneffekt: {{ spell.diameter }} ft. {{ spell.areaOfEffectType }}
-    </div>
-  </div>
-
-  <!-- Angriff /Rettungswurf -->
-
-  <div class="details-subheading">Effekte</div>
-  <div class="details-content-small">
-    <div *ngIf="spell.needsSavingThrow">
-      <div class="details-value">{{ modifiers.saveDC }}</div>
-      <div class="details-label">
-        {{
-          spell.savingThrowAttribute
-            ? attributeTranslator[spell.savingThrowAttribute]
-            : ""
-        }}
-      </div>
-    </div>
-    <div *ngIf="spell.needsAttackRoll">
-      Anfgriffsmodifikator: {{ modifiers.attackBonus }}
-    </div>
-    <ng-container *ngIf="spell.doesDamage">
-      Schaden:
-      <div *ngFor="let damage of spell.damage">
-        {{ damage.diceNumber }} {{ damage.diceType }}
-        {{ damage.additionalDamage }} {{ damage.damageType }}
-      </div>
-    </ng-container>
-  </div>
-</div>
-
-<div class="vertical-button-wrapper-3">
-  <ui-button
-    [type]="'edit'"
-    [size]="'xlarge'"
-    [color]="'primary'"
-    (click)="close('update')"
-  ></ui-button>
-  <ui-button
-    [type]="'delete'"
-    [size]="'xlarge'"
-    [color]="'primary'"
-    (click)="close('delete')"
-  ></ui-button>
-  <ui-button
-    [type]="'cancel'"
-    [size]="'xlarge'"
-    [color]="'primary'"
-    (click)="close('cancel')"
-  ></ui-button>
-</div>

+ 0 - 4
src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.scss

@@ -1,4 +0,0 @@
-.details-scrollcontainer {
-  height: calc(100% - 16rem);
-  overflow-y: auto;
-}

+ 0 - 21
src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.spec.ts

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

+ 0 - 28
src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.ts

@@ -1,28 +0,0 @@
-import { Component, Input } from '@angular/core';
-import { DetailsService } from 'src/services/details/details.service';
-import { Spell } from 'src/interfaces/spell';
-
-@Component({
-  selector: 'app-spell-details',
-  templateUrl: './spell-details.component.html',
-  styleUrls: ['./spell-details.component.scss'],
-})
-export class SpellDetailsComponent {
-  public constructor(public detailsAccessor: DetailsService) {}
-
-  @Input() spell!: Spell;
-  @Input() modifiers: any = {};
-
-  public attributeTranslator: any = {
-    strength: 'Stärke-Rettungswurf',
-    dexterity: 'Geschicklichkeits-Rettungswurf',
-    constitution: 'Konstitutions-Rettungswurf',
-    intelligence: 'Intelligenz-Rettungswurf',
-    wisdom: 'Weisheits-Rettungswurf',
-    charisma: 'Charisma-Rettungswurf',
-  };
-
-  public close(result: string): void {
-    this.detailsAccessor.closePanel(result);
-  }
-}

+ 6 - 2
src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.html

@@ -70,8 +70,12 @@
     }
   </div>
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal()">
-      Bearbeiten
+    <ui-button
+      [color]="'green'"
+      [type]="'edit'"
+      style="width: 80%"
+      (click)="openModal()"
+    >
     </ui-button>
   </div>
 

+ 0 - 4
src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.ts

@@ -4,11 +4,7 @@ import { ModalService } from 'src/services/modal/modal.service';
 import { DetailsService } from 'src/services/details/details.service';
 import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
 import { Spell } from 'src/interfaces/spell';
-import { SpellDetailsComponent } from './spell-details/spell-details.component';
-import { SpellModalComponent } from 'src/app/journal/spell-modal/spell-modal.component';
 import { FullSpellcardComponent } from 'src/app/shared-components/full-spellcard/full-spellcard.component';
-import { Observable, OperatorFunction } from 'rxjs';
-import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
 import { FavoriteSpellsModalComponent } from './favorite-spells-modal/favorite-spells-modal.component';
 import { TranslateService } from '@ngx-translate/core';
 

+ 12 - 2
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component.html

@@ -81,10 +81,20 @@
   <p class="content" [innerHTML]="weapon?.description"></p>
 }
 <div class="vertical-buttons bottom">
-  <ui-button [color]="'green'" [width]="'w20'" (click)="close('update')">
+  <ui-button
+    [color]="'green'"
+    [type]="'edit'"
+    [width]="'w20'"
+    (click)="close('update')"
+  >
     Anpassen
   </ui-button>
-  <ui-button [color]="'red'" [width]="'w20'" (click)="close('delete')">
+  <ui-button
+    [color]="'red'"
+    [type]="'delete'"
+    [width]="'w20'"
+    (click)="close('delete')"
+  >
     Löschen
   </ui-button>
 </div>

+ 18 - 6
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.html

@@ -337,16 +337,28 @@
 
   <div class="horizontal-buttons">
     @if (isUpdate) {
-      <ui-button [color]="'green'" style="width: 40%" (click)="update()">
-        Anpassen
+      <ui-button
+        [color]="'green'"
+        [type]="'confirm'"
+        style="width: 40%"
+        (click)="update()"
+      >
       </ui-button>
     } @else {
-      <ui-button [color]="'green'" style="width: 40%" (click)="add()">
-        Erstellen
+      <ui-button
+        [color]="'green'"
+        [type]="'add'"
+        style="width: 40%"
+        (click)="add()"
+      >
       </ui-button>
     }
-    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()">
-      Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 40%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>

+ 6 - 1
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-table.component.html

@@ -54,7 +54,12 @@
     }
   </div>
   <div class="footer">
-    <ui-button [color]="'green'" style="width: 80%" (click)="openModal(false)">
+    <ui-button
+      [color]="'green'"
+      [type]="'add'"
+      style="width: 80%"
+      (click)="openModal(false)"
+    >
       Hinzufügen
     </ui-button>
   </div>

+ 3 - 3
src/app/journal/journal.module.ts

@@ -64,7 +64,6 @@ import { DetailsPanelComponent } from './journal-home/details-panel/details-pane
 import { AbilityDetailsComponent } from './journal-stats/ability-panel/ability-table/ability-details/ability-details.component';
 import { LifeDetailsComponent } from './journal-stats/life-container/life/life-details/life-details.component';
 import { WeaponDetailsComponent } from './journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component';
-import { SpellDetailsComponent } from './journal-stats/weapons-container/spell-table/spell-details/spell-details.component';
 import { AttributePanelComponent } from './journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component';
 import { SkillPanelComponent } from './journal-stats/attribute-skill-container/skill-panel/skill-panel.component';
 import { SaveThrowPanelComponent } from './journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component';
@@ -98,6 +97,7 @@ import { StoryComponent } from './journal-character/story/story.component';
 import { FavoriteSpellsModalComponent } from './journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component';
 import { MatRippleModule } from '@angular/material/core';
 import { CustomSpellsModalComponent } from './journal-spellcards/custom-spells-modal/custom-spells-modal.component';
+import { DurationPipe } from '../../pipes/duration/duration.pipe';
 
 @NgModule({
   declarations: [
@@ -136,7 +136,6 @@ import { CustomSpellsModalComponent } from './journal-spellcards/custom-spells-m
     AbilityDetailsComponent,
     LifeDetailsComponent,
     WeaponDetailsComponent,
-    SpellDetailsComponent,
     AttributePanelComponent,
     SkillPanelComponent,
     SaveThrowPanelComponent,
@@ -177,6 +176,7 @@ import { CustomSpellsModalComponent } from './journal-spellcards/custom-spells-m
     FavoriteSpellsModalComponent,
     CustomSpellsModalComponent,
   ],
+  providers: [TranslatePipe, DurationPipe],
   imports: [
     CommonModule,
     JournalRoutingModule,
@@ -199,7 +199,7 @@ import { CustomSpellsModalComponent } from './journal-spellcards/custom-spells-m
     TranslateModule,
     MatRadioModule,
     MatDividerModule,
+    DurationPipe,
   ],
-  providers: [TranslatePipe],
 })
 export class JournalModule {}

+ 131 - 242
src/app/journal/spell-modal/spell-modal.component.html

@@ -1,14 +1,14 @@
 <div class="dimensions">
   <div class="title">
     @if (isModification) {
-      Zauber bearbeiten
+      {{ "spellmodal.edit" | translate }}
     } @else {
-      Zauber erstellen
+      {{ "spellmodal.add" | translate }}
     }
   </div>
 
   <div class="content b-0">
-    <div class="input-label">Name</div>
+    <div class="input-label">{{ "spellmodal.name" | translate }}</div>
     <mat-form-field appearance="outline" class="w-100">
       <input matInput [(ngModel)]="name" />
     </mat-form-field>
@@ -18,15 +18,15 @@
       <div class="checkbox-column">
         <div class="checkbox-row">
           <input id="verbal" type="checkbox" [(ngModel)]="needsVerbal" />
-          <label for="verbal">Verbal</label>
+          <label for="verbal">{{ "spellmodal.verbal" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input id="somatic" type="checkbox" [(ngModel)]="needsSomatic" />
-          <label for="somatic">Geste</label>
+          <label for="somatic">{{ "spellmodal.somatic" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input id="material" type="checkbox" [(ngModel)]="needsMaterial" />
-          <label for="material">Material</label>
+          <label for="material">{{ "spellmodal.material" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input
@@ -34,7 +34,9 @@
             type="checkbox"
             [(ngModel)]="needsConcentration"
           />
-          <label for="concentration">Konzentration</label>
+          <label for="concentration">{{
+            "spellmodal.concentration" | translate
+          }}</label>
         </div>
 
         <div class="checkbox-row">
@@ -44,7 +46,9 @@
             (change)="unsetRitual($event)"
             [(ngModel)]="canRitual"
           />
-          <label for="canRitual">Ritual möglich</label>
+          <label for="canRitual">{{
+            "spellmodal.canRitual" | translate
+          }}</label>
         </div>
         <div class="checkbox-row">
           <input
@@ -53,21 +57,21 @@
             (change)="setRitual($event)"
             [(ngModel)]="isRitual"
           />
-          <label for="isRitual">Ist Ritual</label>
+          <label for="isRitual">{{ "spellmodal.isRitual" | translate }}</label>
         </div>
       </div>
       <div class="checkbox-column">
         <div class="checkbox-row">
           <input id="doesDamage" type="checkbox" [(ngModel)]="doesDamage" />
-          <label for="doesDamage">Schaden</label>
+          <label for="doesDamage">{{ "spellmodal.damage" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input id="doesHeal" type="checkbox" [(ngModel)]="doesHeal" />
-          <label for="doesHeal">Heilung</label>
+          <label for="doesHeal">{{ "spellmodal.heal" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input id="isRanged" type="checkbox" [(ngModel)]="isRanged" />
-          <label for="isRanged">Hat Reichweite</label>
+          <label for="isRanged">{{ "spellmodal.hasRange" | translate }}</label>
         </div>
         <div class="checkbox-row">
           <input
@@ -75,7 +79,9 @@
             type="checkbox"
             [(ngModel)]="hasAreaOfEffect"
           />
-          <label for="hasAreaOfEffect">Hat Flächeneffekt</label>
+          <label for="hasAreaOfEffect">{{
+            "spellmodal.hasAreaOfEffect" | translate
+          }}</label>
         </div>
 
         <div class="checkbox-row">
@@ -84,7 +90,9 @@
             type="checkbox"
             [(ngModel)]="needsSavingThrow"
           />
-          <label for="needsSavingThrow">Erfordert Rettungswurf</label>
+          <label for="needsSavingThrow">{{
+            "spellmodal.needsSavingThrow" | translate
+          }}</label>
         </div>
         <div class="checkbox-row">
           @if (!needsSavingThrow) {
@@ -93,15 +101,17 @@
               type="checkbox"
               [(ngModel)]="needsAttackRoll"
             />
-            <label for="needsAttackRoll">Erfordert Angriffswurf</label>
+            <label for="needsAttackRoll">{{
+              "spellmodal.needsAttackRoll" | translate
+            }}</label>
           } @else {
             <label>Attribut</label>
             <select [(ngModel)]="savingThrowAttribute" *ngIf="needsSavingThrow">
               <option
-                *ngFor="let attribute of savingThrowAttributes"
-                [value]="attribute.value"
+                *ngFor="let attribute of translator.attributes"
+                [value]="attribute"
               >
-                {{ attribute.display }}
+                {{ "attributes." + attribute | translate }}
               </option>
             </select>
           }
@@ -110,26 +120,40 @@
 
       <div class="checkbox-column">
         <div class="input-row">
-          <label>Kosten</label>
+          <label>{{ "spellmodal.cost" | translate }}</label>
           <mat-form-field appearance="outline">
             <mat-select [(ngModel)]="cost">
               @for (cost of costs; track cost) {
-                <mat-option [value]="cost.value">{{ cost.display }}</mat-option>
+                <mat-option [value]="cost">{{
+                  "cost." + cost | translate
+                }}</mat-option>
               }
             </mat-select>
           </mat-form-field>
         </div>
 
-        <div class="input-row duration">
-          <label>Dauer</label>
-          <mat-form-field appearance="outline">
-            <input type="number" matInput [(ngModel)]="duration" />
-            <span matSuffix class="suffix">Runden</span>
-          </mat-form-field>
+        <div class="input-row duration" display="display: flex">
+          <label>{{ "spellmodal.duration" | translate }}</label>
+          <div
+            style="display: flex; justify-content: space-between; width: 11rem"
+          >
+            <mat-form-field class="time" appearance="outline">
+              <input type="number" matInput [(ngModel)]="duration" />
+            </mat-form-field>
+            <mat-form-field class="unit" appearance="outline">
+              <mat-select [(ngModel)]="durationtype">
+                @for (type of translator.durationTypes; track type) {
+                  <mat-option [value]="type">{{
+                    "duration." + type | translate
+                  }}</mat-option>
+                }
+              </mat-select>
+            </mat-form-field>
+          </div>
         </div>
 
         <div class="input-row">
-          <label>Stufe</label>
+          <label>{{ "spellmodal.level" | translate }}</label>
           <mat-form-field appearance="outline">
             <mat-select [(ngModel)]="level">
               @for (level of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; track level) {
@@ -139,12 +163,12 @@
           </mat-form-field>
         </div>
         <div class="input-row">
-          <label>Schule</label>
+          <label>{{ "spellmodal.school" | translate }}</label>
           <mat-form-field appearance="outline">
             <mat-select [(ngModel)]="school">
-              @for (school of schools; track school) {
-                <mat-option [value]="school.value">{{
-                  school.display
+              @for (school of translator.schools; track school) {
+                <mat-option [value]="school">{{
+                  "schools." + school | translate
                 }}</mat-option>
               }
             </mat-select>
@@ -167,9 +191,11 @@
         <!-- DAMAGE -->
         <ng-container ngbNavItem="damage">
           @if (doesDamage) {
-            <button ngbNavLink>Schaden</button>
+            <button ngbNavLink>{{ "spellmodal.damage" | translate }}</button>
           } @else {
-            <button class="disabled-button" disabled>Schaden</button>
+            <button class="disabled-button" disabled>
+              {{ "spellmodal.damage" | translate }}
+            </button>
           }
           <ng-template ngbNavContent>
             <div class="tab-content flex-row t-05">
@@ -181,35 +207,46 @@
                 <div class="damage-box">
                   <div class="subheading left t-025">
                     @if (index == 0) {
-                      Schaden
+                      {{ "spellmodal.damage" | translate }}
                     } @else {
-                      Weiterer Schaden
+                      {{ "spellmodal.additionalDamage" | translate }}
                     }
                   </div>
-                  <div class="input-label">Anzahl Würfel</div>
+                  <div class="input-label">
+                    {{ "general.diceNumber" | translate }}
+                  </div>
                   <mat-form-field appearance="outline">
                     <mat-select [(ngModel)]="damageEntry.diceNumber">
-                      @for (number of numbers; track number) {
+                      @for (
+                        number of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+                        track number
+                      ) {
                         <mat-option [value]="number"> {{ number }} </mat-option>
                       }
                     </mat-select>
                   </mat-form-field>
 
-                  <div class="input-label t-05">Würfelart</div>
+                  <div class="input-label t-05">
+                    {{ "general.diceType" | translate }}
+                  </div>
                   <mat-form-field appearance="outline">
                     <mat-select [(ngModel)]="damageEntry.diceType">
-                      @for (die of dice; track die) {
-                        <mat-option [value]="die"> {{ die }} </mat-option>
+                      @for (die of [4, 6, 8, 10, 12, 20, 100]; track die) {
+                        <mat-option [value]="die">
+                          {{ "general.dice" | translate }}{{ die }}
+                        </mat-option>
                       }
                     </mat-select>
                   </mat-form-field>
 
-                  <div class="input-label t-05">Schadensart</div>
+                  <div class="input-label t-05">
+                    {{ "general.damageType" | translate }}
+                  </div>
                   <mat-form-field appearance="outline">
                     <mat-select [(ngModel)]="damageEntry.damageType">
-                      @for (type of damageTypes; track type) {
-                        <mat-option [value]="type.value">
-                          {{ type.display }}
+                      @for (type of translator.damageTypes; track type) {
+                        <mat-option [value]="type">
+                          {{ "damageTypes." + type | translate }}
                         </mat-option>
                       }
                     </mat-select>
@@ -235,26 +272,37 @@
         <!-- HEAL -->
         <ng-container ngbNavItem="heal">
           @if (doesHeal) {
-            <button ngbNavLink>Heilung</button>
+            <button ngbNavLink>{{ "spellmodal.heal" | translate }}</button>
           } @else {
-            <button class="disabled-button" disabled>Heilung</button>
+            <button class="disabled-button" disabled>
+              {{ "spellmodal.heal" | translate }}
+            </button>
           }
           <ng-template ngbNavContent>
             <div class="tab-content t-05">
-              <div class="input-label">Würfelanzahl</div>
+              <div class="input-label">
+                {{ "general.diceNumber" | translate }}
+              </div>
               <mat-form-field appearance="outline">
                 <mat-select [(ngModel)]="heal.diceNumber">
-                  @for (number of numbers; track number) {
+                  @for (
+                    number of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+                    track number
+                  ) {
                     <mat-option [value]="number">{{ number }}</mat-option>
                   }
                 </mat-select>
               </mat-form-field>
 
-              <div class="input-label">Würfelart</div>
+              <div class="input-label">
+                {{ "general.diceType" | translate }}
+              </div>
               <mat-form-field appearance="outline">
                 <mat-select [(ngModel)]="heal.diceType">
-                  @for (die of dice; track die) {
-                    <mat-option [value]="die">{{ die }}</mat-option>
+                  @for (die of [4, 6, 8, 10, 12, 20, 100]; track die) {
+                    <mat-option [value]="die"
+                      >{{ "general.dice" | translate }}{{ die }}</mat-option
+                    >
                   }
                 </mat-select>
               </mat-form-field>
@@ -264,32 +312,44 @@
         <!-- RANGE -->
         <ng-container ngbNavItem="range">
           @if (isRanged || hasAreaOfEffect) {
-            <button ngbNavLink>Reichweite</button>
+            <button ngbNavLink>{{ "spellmodal.range" | translate }}</button>
           } @else {
-            <button class="disabled-button" disabled>Reichweite</button>
+            <button class="disabled-button" disabled>
+              {{ "spellmodal.range" | translate }}
+            </button>
           }
           <ng-template ngbNavContent>
             <div class="range-container">
-              <div class="input-label">Reichweite</div>
+              <div class="input-label">
+                {{ "spellmodal.range" | translate }}
+              </div>
               <mat-form-field appearance="outline">
                 <input type="number" matInput [(ngModel)]="range" />
-                <span class="suffix" matTextSuffix>Fuß</span>
+                <span class="suffix" matTextSuffix>{{
+                  "general.feet" | translate
+                }}</span>
               </mat-form-field>
               @if (hasAreaOfEffect) {
-                <div class="input-label">Flächenart</div>
+                <div class="input-label">
+                  {{ "areaTypes.areaType" | translate }}
+                </div>
                 <mat-form-field appearance="outline">
                   <mat-select [(ngModel)]="areaOfEffectType">
-                    @for (areaType of areaTypes; track areaType) {
-                      <mat-option [value]="areaType.value">{{
-                        areaType.display
-                      }}</mat-option>
+                    @for (areaType of translator.areaTypes; track areaType) {
+                      <mat-option [value]="areaType">
+                        {{ "areaTypes." + areaType | translate }}
+                      </mat-option>
                     }
                   </mat-select>
                 </mat-form-field>
-                <div class="input-label">Durchmesser</div>
+                <div class="input-label">
+                  {{ "spellmodal.diameter" | translate }}
+                </div>
                 <mat-form-field appearance="outline">
                   <input type="number" matInput [(ngModel)]="diameter" />
-                  <span class="suffix" matTextSuffix>Fuß</span>
+                  <span class="suffix" matTextSuffix>{{
+                    "general.feet" | translate
+                  }}</span>
                 </mat-form-field>
               }
             </div>
@@ -297,7 +357,7 @@
         </ng-container>
         <!-- DESCRIPTION -->
         <ng-container ngbNavItem="description">
-          <button ngbNavLink>Beschreibung</button>
+          <button ngbNavLink>{{ "spellmodal.description" | translate }}</button>
           <ng-template ngbNavContent>
             <div class="NgxEditor__Wrapper">
               <ngx-editor-menu [editor]="editor" [toolbar]="toolbar">
@@ -305,7 +365,7 @@
               <ngx-editor
                 [editor]="editor"
                 [(ngModel)]="description_de"
-                placeholder="Beschreibung des Zaubers"
+                [placeholder]="'spellmodal.placeholder' | translate"
               ></ngx-editor>
             </div>
           </ng-template>
@@ -328,192 +388,21 @@
     <hr class="b-0" />
   </div>
 
-  <!-- <div class="horizontal-buttons">
-    @if(isModification){
-    <ui-button [color]="'green'" style="width: 40%" (click)="update()">
-      Anpassen
-    </ui-button>
-    }@else{
-    <ui-button [color]="'green'" style="width: 40%" (click)="add()">
-      Erstellen
-    </ui-button>
-    }
-    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()">
-      Abbrechen
-    </ui-button> -->
-
-  <!-- <div class="range-area-container">
-      <div class="range-box">
-        <div class="checkbox-element">
-          <input type="checkbox" [(ngModel)]="isRanged" />
-          <label>Fernkampf</label>
-        </div>
-        <div class="checkbox-element" *ngIf="isRanged">
-          <input type="text" [(ngModel)]="range" />
-          <label>Reichweite</label>
-        </div>
-      </div>
-
-      <div class="area-box">
-        <div class="checkbox-element">
-          <input type="checkbox" [(ngModel)]="hasAreaOfEffect" />
-          <label>Flächeneffekt</label>
-        </div>
-        <div class="input-element" *ngIf="hasAreaOfEffect">
-          <input type="text" class="add-input" [(ngModel)]="diameter" />
-          <label>diameter</label>
-        </div>
-        <div class="checkbox-element" *ngIf="hasAreaOfEffect">
-          <select [(ngModel)]="areaOfEffectType">
-            <option *ngFor="let areaType of areaTypes" [value]="areaType.value">
-              {{ areaType.display }}
-            </option>
-          </select>
-          <label>Flächenart</label>
-        </div>
-      </div>
-    </div>
-
-    <div style="display: flex">
-      <div>
-        <input type="checkbox" [(ngModel)]="needsSavingThrow" />
-        <label>Rettungswurf?</label>
-      </div>
-
-      <div>
-        <input type="checkbox" [(ngModel)]="needsAttackRoll" />
-        <label>Angriff?</label>
-      </div>
-    </div> -->
-
-  <!-- <ng-container
-      *ngIf="needsSavingThrow"
-      [ngTemplateOutlet]="attackTabContent"
-    ></ng-container>
-
-    <ng-container
-      *ngIf="doesDamage"
-      [ngTemplateOutlet]="damageTabContent"
-    ></ng-container>
-
-    <ng-container
-      *ngIf="doesHeal"
-      [ngTemplateOutlet]="healTabContent"
-    ></ng-container>
-
-    <ng-container [ngTemplateOutlet]="descriptionTabContent"></ng-container> -->
-
   <!-- Button section -->
   <div class="horizontal-buttons">
     <ui-button
       [color]="'green'"
+      [type]="isModification ? 'apply' : 'add'"
       style="width: 40%"
       (click)="isModification ? update() : add()"
     >
-      @if (isModification) {
-        Anwenden
-      } @else {
-        Erstellen
-      }
     </ui-button>
-    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()"
-      >Abbrechen
+    <ui-button
+      [color]="'red'"
+      [type]="'cancel'"
+      style="width: 40%"
+      (click)="cancel()"
+    >
     </ui-button>
   </div>
 </div>
-<!-- </div> -->
-
-<!-- templates -->
-
-<!-- <ng-template #attackTabContent>
-  <div>
-    <select [(ngModel)]="savingThrowAttribute" *ngIf="needsSavingThrow">
-      <option
-        *ngFor="let attribute of savingThrowAttributes"
-        [value]="attribute.value"
-      >
-        {{ attribute.display }}
-      </option>
-    </select>
-    <label *ngIf="needsSavingThrow">Attribut</label>
-  </div>
-</ng-template> -->
-
-<!-- <ng-template #damageTabContent>
-  <div class="damage-container">
-    <div class="damage-box" *ngFor="let damage of damage; let index = index">
-      <div class="dice-row">
-        <div class="flex-column">
-          <label>Anzahl</label>
-          <select [(ngModel)]="damage.diceNumber">
-            <option *ngFor="let number of numbers" [value]="number">
-              {{ number }}
-            </option>
-          </select>
-        </div>
-
-        <div class="flex-column">
-          <label>Würfel</label>
-          <select [(ngModel)]="damage.diceType">
-            <option *ngFor="let die of dice" [value]="die">
-              {{ die }}
-            </option>
-          </select>
-        </div>
-      </div>
-
-      <label for="damageType">Schadensart</label>
-      <select [(ngModel)]="damage.damageType">
-        <option *ngFor="let type of damageTypes" [value]="type.value">
-          {{ type.display }}
-        </option>
-      </select>
-
-      <icon
-        *ngIf="index > 0"
-        (click)="removeDamage(index)"
-        [size]="'m'"
-        [type]="'UI'"
-        [icon]="'remove'"
-        [class]="'pointer'"
-      ></icon>
-    </div>
-    <icon
-      *ngIf="damage.length < 3"
-      (click)="addDamage()"
-      [size]="'m'"
-      [type]="'UI'"
-      [icon]="'add'"
-      [class]="'pointer'"
-    ></icon>
-  </div>
-</ng-template> -->
-
-<!-- <ng-template #healTabContent>
-  <div class="heal-container">
-    <div class="dice-row">
-      <div class="flex-column">
-        <label>Anzahl</label>
-        <select [(ngModel)]="heal.diceNumber">
-          <option *ngFor="let number of numbers" [value]="number">
-            {{ number }}
-          </option>
-        </select>
-      </div>
-
-      <div class="flex-column">
-        <label>Würfel</label>
-        <select [(ngModel)]="heal.diceType">
-          <option *ngFor="let die of dice" [value]="die">
-            {{ die }}
-          </option>
-        </select>
-      </div>
-    </div>
-    <label>Heilung</label>
-  </div>
-</ng-template> -->
-
-<!-- <ng-template #descriptionTabContent>
-  <textarea style="height: 10rem" [(ngModel)]="description_de"></textarea>
-</ng-template> -->

+ 16 - 1
src/app/journal/spell-modal/spell-modal.component.scss

@@ -35,7 +35,22 @@
 .flex-row,
 .range-container {
   ::ng-deep .mat-mdc-text-field-wrapper {
-    width: 10rem !important;
+    width: 11rem !important;
+  }
+  .time {
+    ::ng-deep .mat-mdc-text-field-wrapper {
+      width: 2.5rem !important;
+      padding-left: 0 !important;
+      padding-right: 0 !important;
+    }
+    input {
+      text-align: center;
+    }
+  }
+  .unit {
+    ::ng-deep .mat-mdc-text-field-wrapper {
+      width: 8rem !important;
+    }
   }
   ::ng-deep .mat-mdc-form-field-infix {
     height: 32px !important;

+ 12 - 69
src/app/journal/spell-modal/spell-modal.component.ts

@@ -1,5 +1,6 @@
 import { Component, Input } from '@angular/core';
 import { ModalService } from 'src/services/modal/modal.service';
+import { TranslatorService } from 'src/services/translator/translator.service';
 import { Damage } from 'src/interfaces/damage';
 import { Heal } from 'src/interfaces/heal';
 import { Spell } from 'src/interfaces/spell';
@@ -11,7 +12,6 @@ import { Editor } from 'ngx-editor';
   styleUrls: ['./spell-modal.component.scss'],
 })
 export class SpellModalComponent {
-  public constructor(private modalAccessor: ModalService) {}
   @Input() public spell: any;
   @Input() public level: number = 0;
   @Input() classes: string[] = [];
@@ -26,6 +26,7 @@ export class SpellModalComponent {
   public image: string = '';
   public cost: string = 'action';
   public duration: number = 0;
+  public durationtype: string = 'rounds';
   public timeToCast: number = 0;
   public canRitual: boolean = false;
   public isRitual: boolean = false;
@@ -33,7 +34,7 @@ export class SpellModalComponent {
   public needsVerbal: boolean = false;
   public needsSomatic: boolean = false;
   public needsMaterial: boolean = false;
-  public school: string = 'Evocation';
+  public school: string = 'evocation';
   public description_de: string = '';
   public description_en: string = '';
   public doesDamage: boolean = false;
@@ -65,74 +66,13 @@ export class SpellModalComponent {
   ];
   // #endregion
 
-  // #region OPTIONS
-  public areaTypes: any[] = [
-    { display: 'Kreis', value: 'circle' },
-    { display: 'Kegel', value: 'cone' },
-    { display: 'Kugel', value: 'sphere' },
-    { display: 'Linie', value: 'line' },
-    { display: 'Quadrat', value: 'square' },
-    { display: 'Würfel', value: 'cube' },
-  ];
-
-  public schools: any[] = [
-    { display: 'Verwandlung', value: 'Transmutation' },
-    { display: 'Verzauberung', value: 'Enchantment' },
-    { display: 'Illusion', value: 'Illusion' },
-    { display: 'Nekromantie', value: 'Necromancy' },
-    { display: 'Beschwörung', value: 'Conjuration' },
-    { display: 'Hervorrufung', value: 'Evocation' },
-    { display: 'Bannmagie', value: 'Abjuration' },
-    { display: 'Wahrsagerei', value: 'Divination' },
-  ];
-
-  public savingThrowAttributes: any[] = [
-    { display: 'Stärke', value: 'strength' },
-    { display: 'Geschicklichkeit', value: 'dexterity' },
-    { display: 'Konstitution', value: 'constitution' },
-    { display: 'Intelligenz', value: 'intelligence' },
-    { display: 'Weisheit', value: 'wisdom' },
-    { display: 'Charisma', value: 'charisma' },
-  ];
-
-  public damageTypes: any[] = [
-    { display: 'Wucht', value: 'bludgeoning' },
-    { display: 'Stich', value: 'piercing' },
-    { display: 'Hieb', value: 'slashing' },
-    { display: 'Feuer', value: 'fire' },
-    { display: 'Kälte', value: 'cold' },
-    { display: 'Blitz', value: 'lightning' },
-    { display: 'Gift', value: 'poison' },
-    { display: 'Säure', value: 'acid' },
-    { display: 'Nekrotisch', value: 'necrotic' },
-    { display: 'Psychisch', value: 'psychic' },
-    { display: 'Heilig', value: 'holy' },
-    { display: 'Göttlich', value: 'divine' },
-    { display: 'Kraft', value: 'force' },
-  ];
-
-  public dice: string[] = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'];
-
-  public numbers: string[] = [
-    '1',
-    '2',
-    '3',
-    '4',
-    '5',
-    '6',
-    '7',
-    '8',
-    '9',
-    '10',
-  ];
-
-  public costs: any[] = [
-    { display: 'Aktion', value: 'action' },
-    { display: 'Bonusaktion', value: 'bonus action' },
-    { display: 'Reaktion', value: 'reaction' },
-  ];
+  public costs: string[] = ['action', 'bonus', 'reaction'];
 
   // #endregion
+  public constructor(
+    private modalAccessor: ModalService,
+    public translator: TranslatorService,
+  ) {}
 
   public ngOnInit(): void {
     if (this.isModification || this.isBasedOnOfficialSpell) {
@@ -181,6 +121,7 @@ export class SpellModalComponent {
     this.canRitual = this.spell.canRitual;
     this.isRitual = this.spell.isRitual;
     this.duration = this.spell.duration;
+    this.durationtype = this.spell.durationType;
     this.timeToCast = this.spell.timeToCast;
     this.needsConcentration = this.spell.needsConcentration;
     this.needsVerbal = this.spell.needsVerbal;
@@ -212,7 +153,8 @@ export class SpellModalComponent {
       german: this.name,
       image: this.image,
       classes: this.classes,
-      duration: 0, // FIXME: only mocked
+      duration: this.duration,
+      durationType: this.durationtype,
       timeToCast: 0, // FIXME: only mocked
       damage: this.damage,
       heal: this.heal,
@@ -253,6 +195,7 @@ export class SpellModalComponent {
     this.canRitual = false;
     this.isRitual = false;
     this.duration = 0;
+    this.durationtype = 'rounds';
     this.timeToCast = 0;
     this.needsConcentration = false;
     this.needsVerbal = false;

+ 4 - 1
src/app/shared-components/full-spellcard/full-spellcard.component.html

@@ -47,7 +47,10 @@
         </tr>
         <tr>
           <td>Wirkungsdauer</td>
-          <td>{{ spell.duration }}</td>
+          <td>
+            {{ spell.duration }}
+            {{ "duration." + spell.durationType | translate }}
+          </td>
         </tr>
 
         <tr></tr>

+ 4 - 0
src/app/shared-components/full-spellcard/full-spellcard.component.ts

@@ -14,6 +14,10 @@ export class FullSpellcardComponent {
 
   public constructor(private modalAccessor: ModalService) {}
 
+  ngOnInit(): void {
+    console.log('Der zauber: ', this.spell);
+  }
+
   public delete(): void {
     this.modalAccessor.handleModalClosing('delete', undefined);
   }

+ 12 - 2
src/app/shared-components/shared-components.module.ts

@@ -7,7 +7,8 @@ import { IconButtonComponent } from './icon-button/icon-button.component';
 import { ValueBoxComponent } from './value-box/value-box.component';
 import { FormsModule } from '@angular/forms';
 import { MatRippleModule } from '@angular/material/core';
-import { TranslateService, TranslatePipe } from '@ngx-translate/core';
+import { TranslateModule } from '@ngx-translate/core';
+import { DurationPipe } from '../../pipes/duration/duration.pipe';
 
 @NgModule({
   declarations: [
@@ -17,7 +18,6 @@ import { TranslateService, TranslatePipe } from '@ngx-translate/core';
     IconButtonComponent,
     ValueBoxComponent,
   ],
-  imports: [CommonModule, FormsModule, MatRippleModule],
   exports: [
     SwitchComponent,
     UiButtonComponent,
@@ -25,6 +25,16 @@ import { TranslateService, TranslatePipe } from '@ngx-translate/core';
     IconButtonComponent,
     ValueBoxComponent,
     FormsModule,
+    MatRippleModule,
+    TranslateModule,
   ],
+  imports: [
+    CommonModule,
+    FormsModule,
+    MatRippleModule,
+    TranslateModule,
+    DurationPipe,
+  ],
+  providers: [DurationPipe],
 })
 export class SharedComponentsModule {}

+ 2 - 1
src/app/shared-components/ui-button/ui-button.component.html

@@ -1,3 +1,4 @@
 <button [class]="width + ' ' + color">
-  <ng-content></ng-content>
+  <!-- <ng-content></ng-content> -->
+  {{ "buttons." + type | translate }}
 </button>

+ 2 - 15
src/app/shared-components/ui-button/ui-button.component.ts

@@ -6,25 +6,12 @@ import { Component, Input } from '@angular/core';
   styleUrls: ['./ui-button.component.scss'],
 })
 export class UiButtonComponent {
-  /** Available are w1 - w20 in rem. Defaulted to 100% */
-  @Input() size: string = '';
   /** Available colors: green | red | blue | neutral */
   @Input() type: string = 'default';
-  //
 
-  /**
-   *  The width of the button, defaulted to 100%.
-   *  Available are w1 - w20 in rem.
-   * @type {string}
-   * @memberof UiButtonComponent
-   */
+  /** The width of the button from w1 to w25 in rem, defaulted to 100%. */
   @Input() width: string = 'default';
 
-  /**
-   * The color of the button, default is neutral.
-   * Available are green, red, blue, neutral.
-   * @type {string}
-   * @memberof UiButtonComponent
-   */
+  /** Available colors are green | red | blue | neutral. defaulted to neutral. */
   @Input() color: string = 'neutral';
 }

+ 1 - 1
src/app/shared-components/value-box/value-box.component.html

@@ -10,5 +10,5 @@
   } @else {
     <div class="value" id="input">{{ value }}</div>
   }
-  <label for="input">{{ label }}</label>
+  <label for="input">{{ label | translate }}</label>
 </div>

+ 300 - 2
src/assets/i18n/de.json

@@ -9,7 +9,7 @@
     "meter": "Meter",
     "level": "Stufe",
     "effect": "Auswirkung",
-    "diceNumber": "Anzahl Würfel",
+    "diceNumber": "Würfelanzahl",
     "diceType": "Würfelart",
     "dice": "W",
     "weapons": "Waffen",
@@ -22,7 +22,29 @@
     "english": "Englisch",
     "light": "Hell",
     "dark": "Dunkel",
-    "round": "Runde(n)"
+    "round": "Runde(n)",
+    "yes": "Ja",
+    "no": "Nein"
+  },
+  "genders": {
+    "male": "Männlich",
+    "female": "Weiblich",
+    "diverse": "Divers"
+  },
+  "backgrounds": {
+    "acolyte": "Adept",
+    "charlatan": "Scharlatan",
+    "criminal": "Krimineller",
+    "entertainer": "Unterhalter",
+    "folkHero": "Volksheld",
+    "guildArtisan": "Gildenhandwerker",
+    "hermit": "Einsiedler",
+    "noble": "Adliger",
+    "outlander": "Ausgestoßener",
+    "sage": "Gelehrter",
+    "sailor": "Seefahrer",
+    "soldier": "Soldat",
+    "urchin": "Straßenkind"
   },
   "modal": {
     "name": "Name",
@@ -33,6 +55,29 @@
     "cost": "Kosten:",
     "uses": "Nutzungen"
   },
+  "buttons": {
+    "add": "Hinzufügen",
+    "apply": "Anwenden",
+    "addEntry": "Eintrag hinzufügen",
+    "confirm": "Bestätigen",
+    "modify": "Anpassen",
+    "save": "Speichern",
+    "edit": "Bearbeiten",
+    "delete": "Löschen",
+    "remove": "Entfernen",
+    "cancel": "Abbrechen",
+    "close": "Schließen",
+    "discard": "Verwerfen",
+    "export": "Exportieren",
+    "deleteSelected": "Ausgewählte löschen"
+  },
+  "duration": {
+    "rounds": "Runde(n)",
+    "minutes": "Minute(n)",
+    "hours": "Stunde(n)",
+    "days": "Tag(e)",
+    "instantaneous": "Augenblicklich"
+  },
   "cost": {
     "none": "Keine",
     "action": "Aktion",
@@ -69,6 +114,7 @@
     "thunder": "Blitz"
   },
   "areaTypes": {
+    "areaType": "Flächentyp",
     "cone": "Kegel",
     "cube": "Würfel",
     "cylinder": "Zylinder",
@@ -77,7 +123,116 @@
     "sphere": "Kugel",
     "circle": "Kreis"
   },
+  "schools": {
+    "abjuration": "Bannmagie",
+    "conjuration": "Beschwörung",
+    "divination": "Erkenntnismagie",
+    "enchantment": "Verzauberung",
+    "evocation": "Hervorrufung",
+    "illusion": "Illusion",
+    "necromancy": "Nekromantie",
+    "transmutation": "Verwandlung"
+  },
+  "species": {
+    "human": "Mensch",
+    "elf": "Elf",
+    "dwarf": "Zwerg",
+    "halfling": "Halbling",
+    "gnome": "Gnom",
+    "halfelf": "Halbelf",
+    "halforc": "Halbork",
+    "tiefling": "Tiefling",
+    "dragonborn": "Drachenblütiger"
+  },
+  "classes": {
+    "barbarian": "Barbar",
+    "bard": "Barde",
+    "cleric": "Kleriker",
+    "druid": "Druide",
+    "fighter": "Kämpfer",
+    "monk": "Mönch",
+    "paladin": "Paladin",
+    "ranger": "Waldläufer",
+    "rogue": "Schurke",
+    "sorcerer": "Zauberer",
+    "warlock": "Hexenmeister",
+    "wizard": "Magier",
+    "test": "Test"
+  },
+  "subclasses": {
+    "empty": "Noch keine Unterklasse gewählt",
+    "hint": " Hier kannst du deine Unterklasse wählen. Die Unterklasse ist eine spezielle Spezialisierung deiner Klasse. Sie gibt dir zusätzliche Fähigkeiten und Eigenschaften.",
+    "barbarian": {
+      "berserker": "Berserker"
+    },
+    "bard": {
+      "lore": "Wissensbarde",
+      "valor": "Kampfbarde"
+    },
+    "cleric": {
+      "peaceDomain": "Domäne des Friedens"
+    },
+    "druid": {
+      "land": "Zirkel des Landes",
+      "moon": "Zirkel des Mondes"
+    },
+    "fighter": {
+      "champion": "Champion",
+      "battleMaster": "Kampfmeister",
+      "eldritchKnight": "Arkaner Ritter",
+      "echoKnight": "Echo Knight"
+    },
+    "monk": {
+      "openHand": "Weg des offenen Händen",
+      "wayOfShadows": "Weg des Schattens",
+      "fourElements": "Weg der vier Elemente"
+    },
+    "paladin": {
+      "devotion": "Schwur der Hingabe",
+      "ancients": "Schwur der Uralten",
+      "vengeance": "Schwur der Vergeltung",
+      "crown": "Schwur der Krone",
+      "conquest": "Schwur der Eroberung",
+      "redemption": "Schwur der Erlösung"
+    },
+    "ranger": {
+      "hunter": "Jäger",
+      "beastMaster": "Bestienmeister",
+      "gloomStalker": "Düsterpirscher"
+    },
+    "rogue": {
+      "thief": "Dieb",
+      "assassin": "Attentäter",
+      "arcaneTrickster": "Arkaner Trickser",
+      "mastermind": "Meister des Spiels",
+      "swashbuckler": "Schwertschwingender"
+    },
+    "sorcerer": {
+      "draconic": "Drachenblut",
+      "wildMagic": "Chaostalente",
+      "phoenixSorcery": "Phönixzauberei",
+      "shadowMagic": "Schattenzauberei"
+    },
+    "warlock": {
+      "fiend": "Dämonenpakt",
+      "archfey": "Feenpakt",
+      "greatOldOne": "Pakt des Großen Alten",
+      "celestial": "Pakt des Himmels",
+      "hexblade": "Hexblade"
+    },
+    "wizard": {
+      "abjuration": "Abjuration",
+      "conjuration": "Beschwörung",
+      "divination": "Weissagung",
+      "enchantment": "Verzauberung",
+      "evocation": "Verwandlung",
+      "illusion": "Illusion",
+      "necromancy": "Nekrom"
+    }
+  },
   "attributes": {
+    "value": "Attributswert",
+    "modifier": "Modifikator",
     "strength": "Stärke",
     "dexterity": "Geschicklichkeit",
     "constitution": "Konstitution",
@@ -419,5 +574,148 @@
     "languages": "Sprachen",
     "tools": "Werkzeuge",
     "modalHeader": "Sprachen und Werkzeuge"
+  },
+  "navigation": {
+    "menu": "Menü",
+    "dashboard": "Übersicht",
+    "character": "Charakter",
+    "inventory": "Inventar",
+    "spells": "Zauber",
+    "notes": "Notizen",
+    "spellbook": "Zauberbuch",
+    "quests": "Aufträge",
+    "npcs": "NPCs",
+    "places": "Orte",
+    "maps": "Karten",
+    "rules": "Regeln",
+    "settings": "Einstellungen",
+    "characters": "Charaktere"
+  },
+  "character": {
+    "level": "Stufe",
+    "experience": "Erfahrungspunkte",
+    "general": {
+      "label": "Allgemeines",
+      "species": "Volk",
+      "class": "Klasse",
+      "gender": "Geschlecht",
+      "age": "Alter",
+      "height": "Größe",
+      "weight": "Gewicht",
+      "eyes": "Augenfarbe",
+      "skin": "Hautfarbe",
+      "hair": "Haarfarbe",
+      "description": "Beschreibung",
+      "personality": "Persönlichkeitsmerkmale",
+      "ideals": "Ideale",
+      "bonds": "Bindungen",
+      "flaws": "Makel"
+    },
+    "species": {
+      "label": "Volk"
+    },
+    "class": {
+      "label": "Klasse"
+    },
+    "subclass": {
+      "barabarian": "Pfad",
+      "bard": "Kolleg",
+      "druid": "Zirkel",
+      "monk": "Klostertradition",
+      "fighter": "Archetyp",
+      "ranger": "Konklave",
+      "cleric": "Domäne",
+      "paladin": "Schwur",
+      "rogue": "Archetyp",
+      "wizard": "Tradition",
+      "warlock": "Pakt",
+      "sorcerer": "Herkunft"
+    },
+    "complete": {
+      "label": "Vollständige Übersicht"
+    },
+    "background": {
+      "label": "Hintergrund"
+    },
+    "story": {
+      "label": "Geschichte",
+      "editStory": "Hintergrundgeschichte bearbeiten",
+      "edit": "Bearbeiten"
+    }
+  },
+  "inventory": {
+    "weapArm": "Waffen und Rüstungen",
+    "misc": "Sonstiges",
+    "food": "Nahrung",
+    "usable": "Verbrauchsgegenstände",
+    "name": "Name",
+    "weight": "Gewicht",
+    "value": "Wert",
+    "quantity": "Anzahl",
+    "ready": "Verzehrfertig",
+    "total": "Gesamtgewicht",
+    "description": "Beschreibung",
+    "modal": {
+      "edit": "Gegenstand bearbeiten",
+      "add": "Gegenstand erstellen",
+      "placeholder": "Beschreibung des Gegenstandes",
+      "pieces": "Stk."
+    }
+  },
+  "spellcards": {
+    "manage": "Eigene Zauber verwalten",
+    "cantrips": "Zaubertricks",
+    "delete": "Zum Löschen hier ablegen",
+    "add": {
+      "official": "Offizieller Zauber",
+      "custom": "Eigener Zauber",
+      "edit": "Offizieller Zauber bearbeiten",
+      "lookup": "Zauber durchsuchen",
+      "empty": "Keine Zauber gefunden",
+      "cancel": "Abbrechen"
+    }
+  },
+  "spellmodal": {
+    "edit": "Zauber bearbeiten",
+    "add": "Zauber erstellen",
+    "name": "Name",
+    "level": "Stufe",
+    "school": "Schule",
+    "verbal": "Verbal",
+    "somatic": "Geste",
+    "material": "Material",
+    "concentration": "Konzentration",
+    "canRitual": "Ritual möglich",
+    "isRitual": "Ist Ritual",
+    "damage": "Schaden",
+    "heal": "Heilung",
+    "range": "Reichweite",
+    "hasRange": "Hat Reichweite",
+    "hasAreaOfEffect": "Hat Flächeneffekt",
+    "needsSavingThrow": "Erfordert Rettungswurf",
+    "needsAttackRoll": "Erfordert Angriffswurf",
+    "cost": "Kosten",
+    "duration": "Dauer",
+    "rounds": "Runden",
+    "additionalDamage": "Weiterer Schaden",
+    "description": "Beschreibung",
+    "placeholder": "Beschreibung des Zaubers",
+    "diameter": "Durchmesser"
+  },
+  "creator": {
+    "new": "Neuen Charakter erstellen",
+    "name": "Name",
+    "species": "Volk",
+    "class": "Klasse",
+    "background": "Hintergrund",
+    "gender": "Geschlecht"
+  },
+  "picker": {
+    "label": "DND-Tools",
+    "delete": "Charakter löschen",
+    "confirm": "Möchtest du <b>{{name}}</b> unwiederruflich löschen?",
+    "hint": "Die App befindet sich immer noch in einem Entwicklungsstadium und es können Fehler auftreten",
+    "issues": "Fehler bitte auf LINK_MISSING melden",
+    "okay": "Verstanden"
   }
 }

+ 0 - 1
src/interfaces/class-data.ts

@@ -1,7 +1,6 @@
 export interface ClassData {
   title: string;
   subclassLevel: number;
-  subclassLabel: string;
   description: string;
   features: any[];
 }

+ 1 - 0
src/interfaces/spell.ts

@@ -9,6 +9,7 @@ export interface Spell {
   cost: string;
   timeToCast: number;
   duration: number;
+  durationType: string;
   canRitual: boolean;
   isRitual: boolean;
   needsConcentration: boolean;

+ 8 - 0
src/pipes/duration/duration.pipe.spec.ts

@@ -0,0 +1,8 @@
+import { DurationPipe } from './duration.pipe';
+
+describe('DurationPipe', () => {
+  it('create an instance', () => {
+    const pipe = new DurationPipe();
+    expect(pipe).toBeTruthy();
+  });
+});

+ 26 - 0
src/pipes/duration/duration.pipe.ts

@@ -0,0 +1,26 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+
+@Pipe({
+  name: 'duration',
+  standalone: true,
+})
+export class DurationPipe implements PipeTransform {
+  public constructor(public translate: TranslateService) {}
+  transform(value: number): string {
+    console.log('value', value);
+    if (value < 10) {
+      var suffix = this.translate.instant('duration.rounds');
+      return value + ' ' + suffix;
+    } else if (value < 600) {
+      var suffix = this.translate.instant('duration.minutes');
+      return value / 10 + ' ' + suffix;
+    } else if (value < 1440) {
+      var suffix = this.translate.instant('duration.hours');
+      return value / 600 + ' ' + suffix;
+    } else {
+      var suffix = this.translate.instant('duration.days');
+      return value / 1440 + ' ' + suffix;
+    }
+  }
+}

+ 24 - 36
src/services/class/class.service.ts

@@ -11,29 +11,29 @@ export class ClassService {
 
   public getClassDetails(className: string): ClassData {
     switch (className) {
-      case 'Bard':
+      case 'bard':
         return this.bard;
-      case 'Barbarian':
+      case 'barbarian':
         return this.barbarian;
-      case 'Cleric':
+      case 'cleric':
         return this.cleric;
-      case 'Druid':
+      case 'druid':
         return this.druid;
-      case 'Fighter':
+      case 'fighter':
         return this.fighter;
-      case 'Monk':
+      case 'monk':
         return this.monk;
-      case 'Paladin':
+      case 'paladin':
         return this.paladin;
-      case 'Ranger':
+      case 'ranger':
         return this.ranger;
-      case 'Rogue':
+      case 'rogue':
         return this.rogue;
-      case 'Sorcerer':
+      case 'orcerer':
         return this.sorcerer;
-      case 'Warlock':
+      case 'warlock':
         return this.warlock;
-      case 'Wizard':
+      case 'wizard':
         return this.wizard;
       default:
         return this.notImplementedYet;
@@ -43,9 +43,8 @@ export class ClassService {
   // CLASS DETAILS
 
   public monk: ClassData = {
-    title: 'Mönch',
+    title: 'monk',
     subclassLevel: 3,
-    subclassLabel: 'Kloster',
     description: `
       <p>Monks are united in their ability to magically harness the energy that flows in their bodies. Whether channeled as a striking display of combat prowess or a subtler focus of defensive ability and speed, this energy infuses all that a monk does.</p>
       <h4>Class Features</h4>
@@ -276,9 +275,8 @@ export class ClassService {
   };
 
   public fighter: ClassData = {
-    title: 'Kämpfer',
+    title: 'fighter',
     subclassLevel: 3,
-    subclassLabel: 'Archetyp',
     description: `
     <p>Fighters share an unparalleled mastery with weapons and armor, and a thorough knowledge of the skills of combat. They are well acquainted with death, both meting it out and staring it defiantly in the face.</p>
     <h4>Class Features</h4>
@@ -458,9 +456,8 @@ export class ClassService {
   };
 
   public barbarian: ClassData = {
-    title: 'Barbar',
+    title: 'barbarian',
     subclassLevel: 3,
-    subclassLabel: 'Pfad',
     description: `
     <p>Barbarians are savage warriors who deal with their opponents through a combination of sheer brute force and terrifying rage. Their strength and ferocity make them well suited for melee combat. Barbarians are also able to wreak havoc on their enemies using unorthodox methods, such as throwing improvised weapons or even their own bodies.</p>
     <h4>Class Features</h4>
@@ -609,9 +606,8 @@ export class ClassService {
   };
 
   public ranger: ClassData = {
-    title: 'Waldläufer',
+    title: 'ranger',
     subclassLevel: 3,
-    subclassLabel: 'Konklave',
     description: `
     <p>Rangers are skilled stalkers and hunters who make their home in the woods. Their martial skill is nearly the equal of the fighter, but they lack the latter's dedication to the craft of fighting. Instead, the ranger focuses his skills and training on a specific enemy—a type of creature he bears a vengeful grudge against and hunts above all others.</p>
     <h4>Class Features</h4>
@@ -850,9 +846,8 @@ export class ClassService {
   };
 
   public cleric: ClassData = {
-    title: 'Cleric',
+    title: 'cleric',
     subclassLevel: 1,
-    subclassLabel: 'Domäne',
     description: `
     <p>Clerics are intermediaries between the mortal world and the distant planes of the gods. As varied as the gods they serve, clerics strive to embody the handiwork of their deities. No ordinary priest, a cleric is imbued with divine magic.</p>
     <h4>Class Features</h4>
@@ -993,9 +988,8 @@ export class ClassService {
   };
 
   public paladin: ClassData = {
-    title: 'Paladin',
+    title: 'paladin',
     subclassLevel: 1,
-    subclassLabel: 'Schwur',
     description: `
     <p>Paladins are the ultimate champions of good, using their holy magic to protect the innocent and spread justice. They are often found in the thick of battle, smiting evil and saving the lives of their allies.</p>
     <h4>Class Features</h4>
@@ -1220,9 +1214,8 @@ export class ClassService {
   };
 
   public wizard: ClassData = {
-    title: 'Wizard',
+    title: 'wizard',
     subclassLevel: 2,
-    subclassLabel: 'Tradition',
     description: `
     Wizards are supreme magic-users, defined and united as a class by the spells they cast. Drawing on the subtle weave of magic that permeates the cosmos, wizards cast spells of explosive fire, arcing lightning, subtle deception, brute-force mind control, and much more.
     <h4>Class Features</h4>
@@ -1357,9 +1350,8 @@ export class ClassService {
   };
 
   public warlock: ClassData = {
-    title: 'Hexenmeister',
+    title: 'warlock',
     subclassLevel: 1,
-    subclassLabel: 'Pakt',
     description: `
     <p>Warlocks are seekers of the knowledge that lies hidden in the fabric of the multiverse. Through pacts made with mysterious beings of supernatural power, warlocks unlock magical effects both subtle and spectacular. Drawing on the ancient knowledge of beings such as fey nobles, demons, devils, hags, and alien entities of the Far Realm, warlocks piece together arcane secrets to bolster their own power.</p>
     <h4>Class Features</h4>
@@ -1546,9 +1538,8 @@ export class ClassService {
   };
 
   public bard: ClassData = {
-    title: 'Barde',
+    title: 'bard',
     subclassLevel: 3,
-    subclassLabel: 'Kolleg',
     description: `
     <p>Entertainers and leaders, bards use their artistic talents to induce magical effects. They can soothe the wounded, inspire the weary, and turn the tide of battle with a well-chosen word.</p>
     <h4>Class Features</h4>
@@ -1704,9 +1695,8 @@ export class ClassService {
   };
 
   public sorcerer: ClassData = {
-    title: 'Zauberer',
+    title: 'sorcerer',
     subclassLevel: 1,
-    subclassLabel: 'Herkunft',
     description: `
     <p>Sorcerers carry a magical birthright conferred upon them by an exotic bloodline, some otherworldly influence, or exposure to unknown cosmic forces. No one chooses sorcery; the power chooses the sorcerer.</p>
     <h4>Class Features</h4>
@@ -1920,9 +1910,8 @@ export class ClassService {
   };
 
   public druid: ClassData = {
-    title: 'Druide',
+    title: 'druid',
     subclassLevel: 2,
-    subclassLabel: 'Zirkel',
     description: `
     <p>Druids revere nature above all, gaining their spells and other magical powers either from the force of nature itself or from a nature deity. Many druids pursue a mystic spirituality of transcendent union with nature rather than devotion to a divine entity, while others serve gods of wild nature, animals, or elemental forces. The ancient druidic traditions are sometimes called the Old Faith, in contrast to the worship of gods in temples and shrines.</p>
     <h4>Class Features</h4>
@@ -2110,9 +2099,8 @@ export class ClassService {
   };
 
   public rogue: ClassData = {
-    title: 'Schurke',
+    title: 'rogue',
     subclassLevel: 3,
-    subclassLabel: 'Archetyp',
     description: `
     <p>Rogues rely on skill, stealth, and their foes' vulnerabilities to get the upper hand in any situation. They have a knack for finding the solution to just about any problem, demonstrating a resourcefulness and versatility that is the cornerstone of any successful adventuring party.</p>
     <h4>Class Features</h4>

+ 136 - 80
src/services/spells/spells.service.ts

@@ -69,7 +69,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric', 'Druid'],
       level: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       timeToCast: 0,
       canRitual: false,
       isRitual: false,
@@ -89,7 +90,7 @@ export class SpellsService {
       description_en: `
         You touch one willing creature. Once before the spell ends, the target can roll a d4 and add the number rolled to one ability check of its choice. It can roll the die before or after making the ability check. The spell then ends.
       `,
-      school: 'Divination',
+      school: 'divination',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -106,6 +107,7 @@ export class SpellsService {
       level: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       timeToCast: 0,
       canRitual: false,
       isRitual: false,
@@ -130,7 +132,7 @@ export class SpellsService {
 
         <p> <b>At Higher Levels:</b> The spell’s damage increases by one die when you reach 5th level (2d8 or 2d12), 11th level (3d8 or 3d12), and 17th level (4d8 or 4d12).<p>
       `,
-      school: 'Necromancy',
+      school: 'necromancy',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -146,7 +148,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric'],
       level: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       timeToCast: 0,
       canRitual: false,
       isRitual: false,
@@ -183,7 +186,7 @@ export class SpellsService {
           <li>You alter the appearance of your eyes for 1 minute.</li>
         <p>If you cast this spell multiple times, you can have up to three of its 1-minute effects active at a time, and you can dismiss such an effect as an action.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -201,6 +204,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -218,7 +222,7 @@ export class SpellsService {
       description_en: `
         <p>You extend your hand toward a creature you can see within range and project a puff of noxious gas from your palm. The creature must succeed on a Constitution saving throw or take 1d12 poison damage.</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 10,
       hasAreaOfEffect: false,
@@ -236,6 +240,7 @@ export class SpellsService {
       timeToCast: 10,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -255,7 +260,7 @@ export class SpellsService {
         <p>This spell repairs a single break or tear in an object you touch, such as broken chain link, two halves of a broken key, a torn clack, or a leaking wineskin. As long as the break or tear is no larger than 1 foot in any dimension, you mend it, leaving no trace of the former damage.</p>
         <p>This spell can physically repair a magic item or construct, but the spell can't restore magic to such an object.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -273,6 +278,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -308,7 +314,7 @@ export class SpellsService {
         <p>This movement doesn’t provoke opportunity attacks, and if the direction rolled is blocked, the target doesn’t move.</p>
         <p>The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -325,6 +331,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -347,7 +354,7 @@ export class SpellsService {
         <p><b>At Higher Levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
 
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 15,
       hasAreaOfEffect: false,
@@ -364,6 +371,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 1,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -383,7 +391,7 @@ export class SpellsService {
       <p>You point your finger toward a creature within range and whisper a message. The target (and only the target) hears the message and can reply in a whisper that only you can hear.</p>
       <p>You can cast this spell through solid objects if you are familiar with the target and know it is beyond the barrier. Magical silence, 1 foot of stone, 1 inch of common metal, a thin sheet of lead, or 3 feet of wood blocks the spell. The spell doesn’t have to follow a straight line and can travel freely around corners or through openings.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
@@ -401,6 +409,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -422,7 +431,7 @@ export class SpellsService {
         <p>You create a burst of thunderous sound that can be heard up to 100 feet away. Each creature within range, other than you, must succeed on a Constitution saving throw or take 1d6 thunder damage.</p>
         <p>The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: true,
@@ -440,6 +449,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -459,7 +469,7 @@ export class SpellsService {
         <p>You create a long, vine-like whip covered in thorns that lashes out at your command toward a creature in range. Make a melee spell attack against the target. If the attack hits, the creature takes 1d6 piercing damage, and if the creature is Large or smaller, you pull the creature up to 10 feet closer to you.</p>
         <p>This spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -478,6 +488,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -508,7 +519,7 @@ export class SpellsService {
           <li>You instantly light or snuff out a candle, a torch, or a small campfire.</li>
         </ul>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -526,6 +537,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 1,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -546,7 +558,7 @@ export class SpellsService {
       <p>You brandish the weapon used in the spell’s casting and make a melee attack with it against one creature within 5 feet of you. On a hit, the target suffers the weapon attack’s normal effects and then becomes sheathed in booming energy until the start of your next turn. If the target willingly moves 5 feet or more before then, the target takes 1d8 thunder damage, and the spell ends.</p>
       <p>At 5th level, the melee attack deals an extra 1d8 thunder damage to the target on a hit, and the damage the target takes for moving increases to 2d8. Both damage rolls increase by 1d8 at 11th level (2d8 and 3d8) and again at 17th level (3d8 and 4d8).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -564,6 +576,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 1,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -585,7 +598,7 @@ export class SpellsService {
       <p>If you create a sound, its volume can range from a whisper to a scream. It can be your voice, someone else’s voice, a lion’s roar, a beating of drums, or any other sound you choose. The sound continues unabated throughout the duration, or you can make discrete sounds at different times before the spell ends.</p>
       <p>If you create an image of an object - such as a chair, muddy footprints, or a small chest - it must be no larger than a 5-foot cube. The image can’t create sound, light, smell, or any other sensory effect. Physical interaction with the image reveals it to be an illusion, because things can pass through it. A creature that uses its action to examine the image can determine that it is an illusion with a successful Intelligence (Investigation) check against your spell save DC. If a creature discerns the illusion for what it is, the illusion becomes faint to the creature.</p>
       `,
-      school: 'Illusion',
+      school: 'illusion',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -602,7 +615,8 @@ export class SpellsService {
       classes: ['Test', 'Druid', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
-      duration: 600,
+      duration: 1,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -616,7 +630,7 @@ export class SpellsService {
       doesHeal: true,
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de: `
-        <p>Du wählst ein Stück Erde oder Stein in Reichweite, das du sehen kannst und das in einen Würfel von 5 Fußn passt. Du kannst es auf eine der folgenden Arten manipulieren:</p>
+        <p>Du wählst ein Stück Erde oder Stein in Reichweite, das du sehen kannst und das in einen Würfel von 5 Fuß passt. Du kannst es auf eine der folgenden Arten manipulieren:</p>
         <ul>
           <li>Wenn du einen Bereich mit loser Erde anvisierst, kannst du sie augenblicklich ausheben, über den Boden bewegen und bis zu 5 Fuß entfernt ablegen. Diese Bewegung hat nicht genug Kraft, um Schaden zu verursachen.</li>
           <li>Du lässt Formen, Farben oder beides auf der Erde oder den Steinen erscheinen, indem du Wörter schreibst, Bilder kreierst oder Muster formst. Die Veränderungen halten 1 Stunde lang an.</li>
@@ -633,7 +647,7 @@ export class SpellsService {
         </ul>
         <p>If you cast this spell multiple times, you can have no more than two of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: true,
@@ -651,6 +665,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -672,7 +687,7 @@ export class SpellsService {
         <p>You cause numbing frost to form on one creature that you can see within range. The target must make a Constitution saving throw. On a failed save, the target takes 1d6 cold damage, and it has disadvantage on the next weapon attack roll it makes before the end of its next turn.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -690,6 +705,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -710,7 +726,7 @@ export class SpellsService {
         <p>You hurl a mote of fire at a creature or object within range. Make a ranged spell attack against the target. On a hit, the target takes 1d10 fire damage. A flammable object hit by this spell ignites if it isn’t being worn or carried.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d10 when you reach 5th level (2d10), 11th level (3d10), and 17th level (4d10).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
@@ -727,7 +743,8 @@ export class SpellsService {
       classes: ['Test', 'Druid'],
       timeToCast: 0,
       cost: 'action',
-      duration: 100,
+      duration: 10,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -750,7 +767,7 @@ export class SpellsService {
         <p>You can also attack with the flame, although doing so ends the spell. When you cast this spell, or as an action on a later turn, you can hurl the flame at a creature within 30 feet of you. Make a ranged spell attack. On a hit, the target takes 1d8 fire damage.</p>
         <p>The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -767,7 +784,8 @@ export class SpellsService {
       classes: ['Test', 'Druid', 'Artificer', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
-      duration: 600,
+      duration: 1,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -800,7 +818,7 @@ export class SpellsService {
         </ul>
         <p>If you cast this spell multiple times, you can have up to three of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: true,
@@ -817,7 +835,8 @@ export class SpellsService {
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -836,7 +855,7 @@ export class SpellsService {
       description_en: `
         <p>For the duration, you have advantage on all Charisma checks directed at one creature of your choice that isn’t hostile toward you. When the spell ends, the creature realizes that you used magic to influence its mood and becomes hostile toward you. A creature prone to violence might attack you. Another creature might seek retribution in other ways (at the DM’s discretion), depending on the nature of your interaction with it.</p>
       `,
-      school: 'Enchantment',
+      school: 'enchantment',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -854,6 +873,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -876,7 +896,7 @@ export class SpellsService {
         <p>You drive a disorienting spike of psychic energy into the mind of one creature you can see within range. The target must succeed on an Intelligence saving throw or take 1d6 psychic damage and subtract 1d4 from the next saving throw it makes before the end of your next turn.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Enchantment',
+      school: 'enchantment',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -894,6 +914,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -914,7 +935,7 @@ export class SpellsService {
         <p>You unleash a string of insults laced with subtle enchantments at a creature you can see within range. If the target can hear you (though it need not understand you), it must succeed on a Wisdom saving throw or take 1d4 psychic damage and have disadvantage on the next attack roll it makes before the end of its next turn.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d4 when you reach 5th level (2d4), 11th level (3d4), and 17th level (4d4).</p>
       `,
-      school: 'Enchantment',
+      school: 'enchantment',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -932,6 +953,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -953,7 +975,7 @@ export class SpellsService {
         <p>You extend your hand toward a creature you can see within range and project a puff of noxious gas from your palm. The creature must succeed on a Constitution saving throw or take 1d12 poison damage.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d12 when you reach 5th level (2d12), 11th level (3d12), and 17th level (4d12).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 10,
       hasAreaOfEffect: false,
@@ -971,6 +993,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -991,7 +1014,7 @@ export class SpellsService {
       <p>As part of the action used to cast this spell, you must make a melee attack with a weapon against one creature within the spell’s range, otherwise the spell fails. On a hit, the target suffers the weapon attack’s normal effects, and green fire leaps from the target to a different creature of your choice that you can see within 5 feet of it. The second creature takes fire damage equal to your spellcasting ability modifier.</p>
       <p><b>At higher levels:</b> At 5th level, the melee attack deals an extra 1d8 fire damage to the target, and the fire damage to the second creature increases to 1d8 + your spellcasting ability modifier. Both damage rolls increase by 1d8 at 11th level and 17th level.</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1009,6 +1032,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1030,7 +1054,7 @@ export class SpellsService {
       <p>Flame-like radiance descends on a creature that you can see within range. The target must succeed on a Dexterity saving throw or take 1d8 radiant damage. The target gains no benefit from cover for this saving throw.</p>
       <p><b>At higher levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -1048,6 +1072,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1070,7 +1095,7 @@ export class SpellsService {
       <p>If you hit an undead target, it also has disadvantage on attack rolls against you until the end of your next turn.</p>
       <p><b>At higher levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Necromancy',
+      school: 'necromancy',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
@@ -1087,7 +1112,8 @@ export class SpellsService {
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
-      duration: 100,
+      duration: 10,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1106,7 +1132,7 @@ export class SpellsService {
       description_en: `
       <p>You extend your hand and trace a sigil of warding in the air. Until the end of your next turn, you have resistance against bludgeoning, piercing, and slashing damage dealt by weapon attacks.</p>
       `,
-      school: 'Abjuration',
+      school: 'abjuration',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1124,6 +1150,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1144,7 +1171,7 @@ export class SpellsService {
       <p>A frigid beam of blue-white light streaks toward a creature within range. Make a ranged spell attack against the target. On a hit, it takes 1d8 cold damage, and its speed is reduced by 10 feet until the start of your next turn.</p>
       <p><b>At higher levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -1161,7 +1188,8 @@ export class SpellsService {
       classes: ['Test', 'Druid', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1183,7 +1211,7 @@ export class SpellsService {
       <p>You create a bonfire on ground that you can see within range. Until the spell ends, the magic bonfire fills a 5-foot cube. Any creature in the bonfire’s space when you cast the spell must succeed on a Dexterity saving throw or take 1d8 fire damage. A creature must also make the saving throw when it enters the bonfire’s space for the first time on a turn or ends its turn there.</p>
       <p><b>At higher levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: true,
@@ -1208,7 +1236,8 @@ export class SpellsService {
       ],
       timeToCast: 0,
       cost: 'action',
-      duration: 600,
+      duration: 1,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1229,7 +1258,7 @@ export class SpellsService {
       <p>You touch one object that is no larger than 10 feet in any dimension. Until the spell ends, the object sheds bright light in a 20-foot radius and dim light for an additional 20 feet. The light can be colored as you like. Completely covering the object with something opaque blocks the light. The spell ends if you cast it again or dismiss it as an action.</p>
       <p>If you target an object held or worn by a hostile creature, that creature must succeed on a Dexterity saving throw to avoid the spell.</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1246,7 +1275,8 @@ export class SpellsService {
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1269,7 +1299,7 @@ export class SpellsService {
       <p>You can use your action to control the hand. You can use the hand to manipulate an object, open an unlocked door or container, stow or retrieve an item from an open container, or pour the contents out of a vial. You can move the hand up to 30 feet each time you use it.</p>
       <p>The hand can’t attack, activate magic items, or carry more than 10 pounds.</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -1286,7 +1316,8 @@ export class SpellsService {
       classes: ['Test', 'Druid', 'Hexenmeister'],
       timeToCast: 0,
       cost: 'bonus',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1307,7 +1338,7 @@ export class SpellsService {
       <p>You touch one to three pebbles and imbue them with magic. You or someone else can make a ranged spell attack with one of the pebbles by throwing it or hurling it with a sling. If thrown, it has a range of 60 feet. If someone else attacks with the pebble, that attacker adds your spellcasting ability modifier, not the attacker’s, to the attack roll. On a hit, the target takes bludgeoning damage equal to 1d6 + your spellcasting ability modifier. Hit or miss, the spell then ends on the stone.</p>
       <p>If you cast this spell again, the spell ends on any pebbles still affected by your previous casting.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1324,7 +1355,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric', 'Druid', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1343,7 +1375,7 @@ export class SpellsService {
       description_en: `
       <p>You touch one willing creature. Once before the spell ends, the target can roll a d4 and add the number rolled to one saving throw of its choice. It can roll the die before or after making the saving throw. The spell then ends.</p>
       `,
-      school: 'Abjuration',
+      school: 'abjuration',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1361,6 +1393,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1381,7 +1414,7 @@ export class SpellsService {
         <p>A beam of crackling energy streaks toward a creature within range. Make a ranged spell attack against the target. On a hit, the target takes 1d10 force damage.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d10 when you reach 5th level (2d10), 11th level (3d10), and 17th level (4d10).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
@@ -1399,6 +1432,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1419,7 +1453,7 @@ export class SpellsService {
         <p>Lightning springs from your hand to deliver a shock to a creature you try to touch. Make a melee spell attack against the target. You have advantage on the attack roll if the target is wearing armor made of metal. On a hit, the target takes 1d8 lightning damage, and it can’t take reactions until the start of its next turn.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d8 when you reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1437,6 +1471,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -1458,7 +1493,7 @@ export class SpellsService {
         <p>You create a momentary circle of spectral blades that sweep around you. Each creature within range, other than you, must succeed on a Dexterity saving throw or take 1d6 force damage.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: true,
@@ -1475,7 +1510,8 @@ export class SpellsService {
       classes: ['Test', 'Druid'],
       timeToCast: 0,
       cost: 'bonus',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1494,7 +1530,7 @@ export class SpellsService {
       description_en: `
         <p>The wood of a club or quarterstaff you are holding is imbued with nature’s power. For the duration, you can use your spellcasting ability instead of Strength for the attack and damage rolls of melee attacks using that weapon, and the weapon’s damage die becomes a d8. The weapon also becomes magical, if it isn’t already. The spell ends if you cast it again or if you let go of the weapon.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1512,6 +1548,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1533,7 +1570,7 @@ export class SpellsService {
         <p>You hurl a bubble of acid. Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. A target must succeed on a Dexterity saving throw or take 1d6 acid damage.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Conjuration',
+      school: 'conjuration',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: true,
@@ -1550,7 +1587,8 @@ export class SpellsService {
       classes: ['Test', 'Bard', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1570,7 +1608,7 @@ export class SpellsService {
         <p>You create up to four torch-sized lights within range, making them appear as torches, lanterns, or glowing orbs that hover in the air for the duration. You can also combine the four lights into one glowing vaguely humanoid form of Medium size. Whichever form you choose, each light sheds dim light in a 10-foot radius.</p>
         <p>As a bonus action on your turn, you can move the lights up to 60 feet to a new spot within range. A light must be within 20 feet of another light created by this spell, and a light winks out if it exceeds the spell’s range.</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -1587,7 +1625,8 @@ export class SpellsService {
       classes: ['Test', 'Bard', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
-      duration: 600,
+      duration: 1,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1624,7 +1663,7 @@ export class SpellsService {
         </ul>
         <p>If you cast this spell multiple times, you can have up to three of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 10,
       hasAreaOfEffect: false,
@@ -1642,6 +1681,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1660,7 +1700,7 @@ export class SpellsService {
       description_en: `
         <p>You touch a living creature that has 0 hit points. The creature becomes stable. This spell has no effect on undead or constructs.</p>
       `,
-      school: 'Necromancy',
+      school: 'necromancy',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1678,6 +1718,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -1698,7 +1739,7 @@ export class SpellsService {
         <p>You channel primal magic to cause your teeth or fingernails to sharpen, ready to deliver a corrosive attack. Make a melee spell attack against one creature within 5 feet of you. On a hit, the target takes 1d10 acid damage.</p>
         <p><b>At higher levels:</b> The spell’s damage increases by 1d10 when you reach 5th level (2d10), 11th level (3d10), and 17th level (4d10).</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1715,7 +1756,8 @@ export class SpellsService {
       classes: ['Test', 'Druid', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
-      duration: 600,
+      duration: 1,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -1748,7 +1790,7 @@ export class SpellsService {
       </ul>
       <p>If you cast this spell multiple times, you can have no more than two of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action.</p>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: true,
@@ -1766,6 +1808,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1795,7 +1838,7 @@ export class SpellsService {
         <li>You create a harmless sensory effect using air, such as causing leaves to rustle, wind to slam shutters shut, or your clothing to ripple in a breeze.</li>
       </ul>
       `,
-      school: 'Transmutation',
+      school: 'transmutation',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -1813,6 +1856,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -1834,7 +1878,7 @@ export class SpellsService {
       <p>You utter a divine word, and burning radiance erupts from you. Each creature of your choice that you can see within range must succeed on a Constitution saving throw or take 1d6 radiant damage.</p>
       <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: true,
@@ -1852,6 +1896,7 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       canRitual: false,
       isRitual: false,
       needsVerbal: false,
@@ -1870,7 +1915,7 @@ export class SpellsService {
       description_en: `
         <p>You extend your hand and point a finger at a target in range. Your magic grants you a brief insight into the target’s defenses. On your next turn, you gain advantage on your first attack roll against the target, provided that this spell hasn’t ended.</p>
       `,
-      school: 'Divination',
+      school: 'divination',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -1888,6 +1933,7 @@ export class SpellsService {
       isCustom: false,
       classes: ['Test', 'Cleric'],
       duration: 0,
+      durationType: 'rounds',
       timeToCast: 0,
       canRitual: false,
       isRitual: false,
@@ -1905,7 +1951,7 @@ export class SpellsService {
         'Mach einen Nahkampf-Zauberangriff gegen eine Kreatur deiner Wahl. Bei einem Erfolg erleidet das Ziel 3W10 Nekrotischen Schaden. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt der Schaden für jeden Grad über dem 1. um 1W10.',
       description_en:
         'Make a melee spell attack against a creature you can reach. On a hit, the target takes 3d10 necrotic damage. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d10 for each slot level above 1st.',
-      school: 'Necromancy',
+      school: 'necromancy',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1920,6 +1966,7 @@ export class SpellsService {
       level: 1,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       isCustom: false,
       classes: ['Test', 'Bard', 'Cleric', 'Druid', 'Paladin', 'Ranger'],
       timeToCast: 0,
@@ -1939,7 +1986,7 @@ export class SpellsService {
         'Eine Kreatur, die du berührst, gewinnt Trefferpunkte in Höhe von 1W8 + deinem Attributsmodifikator im Zauberwirken zurück. Dieser Zauber wirkt nicht auf Untote oder Konstrukte. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt die Heilung für jeden Grad über dem 1. um 1W8.',
       description_en:
         'A creature you touch regains a number of hit points equal to 1d8 + your spellcasting ability modifier. This spell has no effect on undead or constructs. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d8 for each slot level above 1st.',
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -1953,7 +2000,8 @@ export class SpellsService {
       image: 'detectMagic',
       level: 1,
       cost: 'action',
-      duration: 100,
+      duration: 10,
+      durationType: 'minutes',
       isCustom: false,
       classes: [
         'Test',
@@ -1982,7 +2030,7 @@ export class SpellsService {
         'Während der Wirkungsdauer nimmst du die Gegenwart von Magie im Abstand von bis zu 30 Fuß von dir wahr. Wenn du auf diese Weise Magie wahrnimmst, kannst du deine Aktion verwenden, um schwache Auren um sichtbare magische Kreaturen oder Objekte sowie ihre magische Schule zu erkennen, falls vorhanden. Dieser Zauber durchdringt die meisten Barrieren, wird aber von 30 Zentimeter Stein, 2,5 Zentimetern gewöhnlichem Metall, dünnem Bleiblech sowie von einem Meter Holz oder Erde blockiert.',
       description_en:
         'For the duration, you sense the presence of magic within 30 feet of you. If you sense magic in this way, you can use your action to see a faint aura around any visible creature or object in the area that bears magic, and you learn its school of magic, if any. The spell can penetrate most barriers, but it is blocked by 1 foot of stone, 1 inch of common metal, a thin sheet of lead, or 3 feet of wood or dirt.',
-      school: 'Divination',
+      school: 'divination',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -1996,7 +2044,8 @@ export class SpellsService {
       image: 'bless',
       level: 1,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       isCustom: false,
       classes: ['Test', 'Cleric', 'Paladin'],
       timeToCast: 0,
@@ -2016,7 +2065,7 @@ export class SpellsService {
         'Du segnest bis zu drei Kreaturen deiner Wahl in Reichweite. Wenn ein Ziel während der Wirkungsdauer einen Angriffs- oder Rettungswurf ausführt, darf es mit einem W4 würfeln und das Würfelergebnis zu seinem Angriffs- oder Rettungswurf addieren. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, kannst du für jeden Grad über dem 1. eine zusätzliche Kreatur als Ziel wählen.',
       description_en:
         'You bless up to three creatures of your choice within range. Whenever a target makes an attack roll or a saving throw before the spell ends, the target can roll a d4 and add the number rolled to the attack roll or saving throw. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, you can target one additional creature for each slot level above 1st.',
-      school: 'Enchantment',
+      school: 'enchantment',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -2033,6 +2082,7 @@ export class SpellsService {
       level: 1,
       cost: 'bonus action',
       duration: 0,
+      durationType: 'rounds',
       timeToCast: 0,
       canRitual: false,
       isRitual: false,
@@ -2050,7 +2100,7 @@ export class SpellsService {
         'Eine Kreatur deiner Wahl in Reichweite, die du sehen kannst, gewinnt Trefferpunkte in Höhe von 1W4 + deinem Zauberwirken-Attributsmodifikator zurück. Dieser Zauber wirkt nicht auf Untote oder Konstrukte. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt die Heilung für jeden Grad über dem 1. um 1W4.',
       description_en:
         'A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier. This spell has no effect on undead or constructs. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d4 for each slot level above 1st.',
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
@@ -2065,6 +2115,7 @@ export class SpellsService {
       level: 1,
       cost: 'action',
       duration: 0,
+      durationType: 'rounds',
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
@@ -2084,7 +2135,7 @@ export class SpellsService {
         'Ein Lichtblitz schießt auf eine Kreatur deiner Wahl in Reichweite zu. Führe einen Fernkampf-Zauberangriff gegen das Ziel aus. Bei einem Erfolg erleidet das Ziel 4W6 gleißenden Schaden. Dank des mystischen dämmrigen Lichts, das auf dem Ziel glitzert, ist der nächste Angriffswurf gegen das Ziel vor Ende deines nächsten Zugs im Vorteil. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt der Schaden für jeden Grad über dem 1. um 1W6.',
       description_en:
         'A flash of light streaks toward a creature of your choice within range. Make a ranged spell attack against the target. On a hit, the target takes 4d6 radiant damage, and the next attack roll made against this target before the end of your next turn has advantage, thanks to the mystical dim light glittering on the target until then. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d6 for each slot level above 1st.',
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
@@ -2098,7 +2149,8 @@ export class SpellsService {
       image: 'detectPoisonAndDisease',
       level: 1,
       cost: 'action',
-      duration: 100,
+      duration: 10,
+      durationType: 'minutes',
       isCustom: false,
       classes: ['Test', 'Cleric', 'Druid', 'Paladin', 'Ranger'],
       timeToCast: 0,
@@ -2118,7 +2170,7 @@ export class SpellsService {
         'Während der Wirkungsdauer nimmst du die Gegenwart und die Position von Giften, giftigen Kreaturen und Krankheiten im Abstand von bis zu neun Metern von dir wahr. Du kannst auch die Art des Gifts, der giftigen Kreatur oder der Krankheit bestimmen. Dieser Zauber durchdringt die meisten Barrieren, wird aber von 1 Fußn Stein, 2,5 Zentimetern gewöhnlichem Metall, dünnem Bleiblech sowie von einem Meter Holz oder Erde blockiert.',
       description_en:
         'For the duration, you can sense the presence and location of poisons, poisonous creatures, and diseases within 30 feet of you. You also identify the kind of poison, poisonous creature, or disease in each case. The spell can penetrate most barriers, but it is blocked by 1 foot of stone, 1 inch of common metal, a thin sheet of lead, or 3 feet of wood or dirt.',
-      school: 'Divination',
+      school: 'divination',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -2132,7 +2184,8 @@ export class SpellsService {
       image: 'heroism',
       level: 1,
       cost: 'action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       isCustom: false,
       classes: ['Test', 'Bard', 'Paladin'],
       timeToCast: 0,
@@ -2152,7 +2205,7 @@ export class SpellsService {
         'Du berührst eine bereitwillige Kreatur und erfüllst sie mit Tapferkeit. Bis der Zauber endet, kann die Kreatur nicht verängstigt werden, und sie erhält zu Beginn jedes ihrer Züge temporäre Trefferpunkte in Höhe deines Zauberwirken-Attributsmodifikators. Wenn der Zauber endet, verliert das Ziel alle verbleibenden temporären Trefferpunkte dieses Zaubers. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, kannst du für jeden Grad über dem 1. eine zusätzliche Kreatur als Ziel wählen.',
       description_en:
         'A willing creature you touch is imbued with bravery. Until the spell ends, the creature is immune to being frightened and gains temporary hit points equal to your spellcasting ability modifier at the start of each of its turns. When the spell ends, the target loses any remaining temporary hit points from this spell. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, you can target one additional creature for each slot level above 1st.',
-      school: 'Enchantment',
+      school: 'enchantment',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
@@ -2169,7 +2222,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'bonus action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -2186,7 +2240,7 @@ export class SpellsService {
         'Du schützt eine Kreatur in Reichweite vor Angriffen. Während der Wirkungsdauer muss jede Kreatur, die bei einem Angriff oder schädlichen Zauber die geschützte Kreatur als Ziel hat, zuerst einen Weisheitsrettungswurf bestehen. Scheitert der Wurf, muss die Kreatur ein neues Ziel wählen, oder sie verliert den Angriff oder Zauber. Dieser Zauber schützt die entsprechende Kreatur nicht vor Flächeneffekten wie der Explosion eines Feuerballs. Greift die geschützte Kreatur an oder wirkt sie einen Zauber auf eine feindliche Kreatur, endet der Effekt dieses Zaubers.',
       description_en:
         'You ward a creature within range against attack. Until the spell ends, any creature who targets the warded creature with an attack or a harmful spell must first make a Wisdom saving throw. On a failed save, the creature must choose a new target or lose the attack or spell. This spell doesn’t protect the warded creature from area effects, such as the explosion of a fireball. If the warded creature makes an attack or casts a spell that affects an enemy creature, this spell ends.',
-      school: 'Abjuration',
+      school: 'abjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -2205,7 +2259,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric', 'Paladin'],
       timeToCast: 0,
       cost: 'action',
-      duration: 4800,
+      duration: 8,
+      durationType: 'hours',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -2226,7 +2281,7 @@ export class SpellsService {
         <p>Your spell bolsters your allies with toughness and resolve. Choose up to three creatures within range. Each target’s hit point maximum and current hit points increase by 5 for the duration.</p>
         <p><b>At higher levels:</b> When you cast this spell using a spell slot of 3rd level or higher, a target’s hit points increase by an additional 5 for each slot level above 2nd.</p>
       `,
-      school: 'Abjuration',
+      school: 'abjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
@@ -2243,7 +2298,8 @@ export class SpellsService {
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'bonus action',
-      duration: 10,
+      duration: 1,
+      durationType: 'minutes',
       canRitual: false,
       isRitual: false,
       needsVerbal: true,
@@ -2266,7 +2322,7 @@ export class SpellsService {
         <p>You create a floating, spectral weapon within range that lasts for the duration or until you cast this spell again. When you cast the spell, you can make a melee spell attack against a creature within 5 feet of the weapon. On a hit, the target takes force damage equal to 1d8 + your spellcasting ability modifier. As a bonus action on your turn, you can move the weapon up to 20 feet and repeat the attack against a creature within 5 feet of it. The weapon can take whatever form you choose. Clerics of deities who are associated with a particular weapon (as St. Cuthbert is known for his mace and Thor for his hammer) make this spell’s effect resemble that weapon.</p>
         <p><b>At higher levels:</b> When you cast this spell using a spell slot of 3rd level or higher, the damage increases by 1d8 for every two slot levels above 2nd.</p>
       `,
-      school: 'Evocation',
+      school: 'evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,

+ 34 - 30
src/services/subclass/subclass.service.ts

@@ -7,15 +7,20 @@ import { SubclassData } from 'src/interfaces/subclass-data';
 export class SubclassService {
   public getSubclassDetails(subclass: string): any {
     switch (subclass) {
-      case 'EchoKnight':
+      // FIGHTER
+      case 'echoKnight':
         return this.echoKnight;
-      case 'PeaceDomain':
+      // CLERIC
+      case 'peaceDomain':
         return this.peaceDomain;
-      case 'Vengance':
+      // PALADIN
+      case 'vengance':
         return this.vengeance;
-      case 'WayOfShadows':
+      // MONK
+      case 'wayOfShadows':
         return this.wayOfShadows;
-      case 'BeastMaster':
+      // RANGER
+      case 'beastMaster':
         return this.beastMaster;
       default:
         return this.notImplementedYet;
@@ -24,29 +29,29 @@ export class SubclassService {
 
   public getAvailableSubclassNames(className: string): string[] {
     switch (className) {
-      case 'Barbarian':
+      case 'barbarian':
         return [];
-      case 'Bard':
+      case 'bard':
         return [];
-      case 'Cleric':
-        return ['PeaceDomain'];
-      case 'Druid':
+      case 'cleric':
+        return ['peaceDomain'];
+      case 'druid':
         return [];
-      case 'Fighter':
-        return ['EchoKnight'];
-      case 'Monk':
-        return ['WayOfShadows'];
-      case 'Paladin':
-        return ['Vengance'];
-      case 'Ranger':
-        return ['BeastMaster'];
-      case 'Rogue':
+      case 'fighter':
+        return ['echoKnight'];
+      case 'monk':
+        return ['wayOfShadows'];
+      case 'paladin':
+        return ['vengance'];
+      case 'ranger':
+        return ['beastMaster'];
+      case 'rogue':
         return [];
-      case 'Sorcerer':
+      case 'sorcerer':
         return [];
-      case 'Warlock':
+      case 'warlock':
         return [];
-      case 'Wizard':
+      case 'wizard':
         return [];
       default:
         return [];
@@ -69,16 +74,15 @@ export class SubclassService {
         name: 'Implement of Peace',
         level: 1,
         description: `
-          When you choose this domain at 1st level, you gain proficiency in the Insight, Performance, or Persuasion skill (your choice).
+          <p>When you choose this domain at 1st level, you gain proficiency in the Insight, Performance, or Persuasion skill (your choice).</p>
         `,
       },
       {
         name: 'Emboldening Bond',
         level: 1,
         description: `
-          Starting at 1st level, you can forge an empowering bond among people who are at peace with one another. As an action, you choose a number of willing creatures within 30 feet of you (this can include yourself) equal to your proficiency bonus. You create a magical bond among them for 10 minutes or until you use this feature again. While any bonded creature is within 30 feet of another, the creature can roll a d4 and add the number rolled to an attack roll, an ability check, or a saving throw it makes. Each creature can add the d4 no more than once per turn.
-
-          You can use this feature a number of times equal to your proficiency bonus, and you regain all expended uses when you finish a long rest.
+          <p>Starting at 1st level, you can forge an empowering bond among people who are at peace with one another. As an action, you choose a number of willing creatures within 30 feet of you (this can include yourself) equal to your proficiency bonus. You create a magical bond among them for 10 minutes or until you use this feature again. While any bonded creature is within 30 feet of another, the creature can roll a d4 and add the number rolled to an attack roll, an ability check, or a saving throw it makes. Each creature can add the d4 no more than once per turn.</p>
+          <p>You can use this feature a number of times equal to your proficiency bonus, and you regain all expended uses when you finish a long rest. </p>
         `,
         ability: '',
       },
@@ -86,7 +90,7 @@ export class SubclassService {
         name: 'Channel Divinity: Balm of Peace',
         level: 2,
         description: `
-          Starting at 2nd level, you can use your Channel Divinity to make your very presence a soothing balm. As an action, you can move up to your speed, without provoking opportunity attacks, and when you move within 5 feet of any other creature during this action, you can restore a number of hit points to that creature equal to 2d6 + your Wisdom modifier (minimum of 1 hit point). A creature can receive this healing only once whenever you take this action.
+          <p>Starting at 2nd level, you can use your Channel Divinity to make your very presence a soothing balm. As an action, you can move up to your speed, without provoking opportunity attacks, and when you move within 5 feet of any other creature during this action, you can restore a number of hit points to that creature equal to 2d6 + your Wisdom modifier (minimum of 1 hit point). A creature can receive this healing only once whenever you take this action.</p>
         `,
         ability: '',
       },
@@ -94,7 +98,7 @@ export class SubclassService {
         name: 'Protective Bond',
         level: 6,
         description: `
-          Beginning at 6th level, the bond you forge between people helps them protect each other. When a creature affected by your Emboldening Bond feature is about to take damage, a second bonded creature within 30 feet of the first can use its reaction to teleport to an unoccupied space within 5 feet of the first creature. The second creature then takes all the damage instead.
+          <p>Beginning at 6th level, the bond you forge between people helps them protect each other. When a creature affected by your Emboldening Bond feature is about to take damage, a second bonded creature within 30 feet of the first can use its reaction to teleport to an unoccupied space within 5 feet of the first creature. The second creature then takes all the damage instead.</p>
         `,
         ability: '',
       },
@@ -102,14 +106,14 @@ export class SubclassService {
         name: 'Potent Spellcasting',
         level: 8,
         description: `
-          At 8th level, you add your Wisdom modifier to the damage you deal with any cleric cantrip.
+          <p>At 8th level, you add your Wisdom modifier to the damage you deal with any cleric cantrip.</p>
         `,
       },
       {
         name: 'Expansive Bond',
         level: 17,
         description: `
-          At 17th level, the benefits of your Emboldening Bond and Protective Bond features now work when the creatures are within 60 feet of each other. Moreover, when a creature uses Protective Bond to take someone else's damage, the creature has resistance to that damage.
+          <p>At 17th level, the benefits of your Emboldening Bond and Protective Bond features now work when the creatures are within 60 feet of each other. Moreover, when a creature uses Protective Bond to take someone else's damage, the creature has resistance to that damage.</p>
         `,
       },
     ],

+ 127 - 79
src/services/translator/translator.service.ts

@@ -6,86 +6,134 @@ import { Injectable } from '@angular/core';
 export class TranslatorService {
   constructor() {}
 
-  public races: any = {
-    Aarakocra: { display: 'Aarakocra', value: 'Aarakocra' },
-    Aasimar: { display: 'Aasimar', value: 'Aasimar' },
-    Autognome: { display: 'Autognom', value: 'Autognome' },
-    Bugbear: { display: 'Grottenschrat', value: 'Bugbear' },
-    Centaur: { display: 'Zentaur', value: 'Centaur' },
-    Changeling: { display: 'Wechselbalg ', value: 'Changeling' },
-    Dragonborn: { display: 'Drachenblütiger', value: 'Dragonborn' },
-    Duergar: { display: 'Duergar', value: 'Duergar' },
-    Dwarf: { display: 'Zwerg', value: 'Dwarf' },
-    Elf: { display: 'Elf', value: 'Elf' },
-    Eladrin: { display: 'Eladrin', value: 'Eladrin' },
-    Fairy: { display: 'Fee', value: 'Fairy' },
-    Firbolg: { display: 'Firbolg', value: 'Firbolg' },
-    Genasi: { display: 'Genasi', value: 'Genasi' },
-    Gith: { display: 'Gedankenschinder', value: 'Gith' },
-    Gnome: { display: 'Gnom', value: 'Gnome' },
-    Goblin: { display: 'Goblin', value: 'Goblin' },
-    Goliath: { display: 'Goliath', value: 'Goliath' },
-    HalfElf: { display: 'Halbelf', value: 'HalfElf' },
-    HalfElfDetection: {
-      display: 'Halbelf (Mal des Entdeckens)',
-      value: 'HalfElfDetection',
-    },
-    HalfOrc: { display: 'Halbork', value: 'HalfOrc' },
-    Halfling: { display: 'Halbling', value: 'Halfling' },
-    Hobgoblin: { display: 'Hobgoblin', value: 'Hobgoblin' },
-    Human: { display: 'Mensch', value: 'Human' },
-    Kalashtar: { display: 'Kalashtar', value: 'Kalashtar' },
-    Kenku: { display: 'Kenku', value: 'Kenku' },
-    Kobold: { display: 'Kobold', value: 'Kobold' },
-    Leonin: { display: 'Leonin', value: 'Leonin' },
-    Lizardfolk: { display: 'Echsenmensch', value: 'Lizardfolk' },
-    Locathah: { display: 'Locathah', value: 'Locathah' },
-    Loxodon: { display: 'Loxodon', value: 'Loxodon' },
-    Minotaur: { display: 'Minotaurus', value: 'Minotaur' },
-    Orc: { display: 'Ork', value: 'Orc' },
-    Owlin: { display: 'Eulenmensch', value: 'Owlin' },
-    Satyr: { display: 'Satyr', value: 'Satyr' },
-    Shifter: { display: 'Wandler', value: 'Shifter' },
-    Tabaxi: { display: 'Tabaxi', value: 'Tabaxi' },
-    Tiefling: { display: 'Tiefling', value: 'Tiefling' },
-    Tortle: { display: 'Tortle', value: 'Tortle' },
-    Triton: { display: 'Triton', value: 'Triton' },
-    Vedalken: { display: 'Vedalken', value: 'Vedalken' },
-    Verdan: { display: 'Verdan', value: 'Verdan' },
-    Warforged: { display: 'Kriegsgeschmiedeter', value: 'Warforged' },
-    YuanTi: { display: 'Yuan-Ti', value: 'YuanTi' },
-  };
+  // TODO: REMOVE THIS SERVICE AND CHECK WHERE IT IS USED
 
-  public classes: any = {
-    Barbarian: { display: 'Barbar', value: 'Barbarian' },
-    Bard: { display: 'Barde', value: 'Bard' },
-    Cleric: { display: 'Kleriker', value: 'Cleric' },
-    Druid: { display: 'Druide', value: 'Druid' },
-    Fighter: { display: 'Kämpfer', value: 'Fighter' },
-    Monk: { display: 'Mönch', value: 'Monk' },
-    Paladin: { display: 'Paladin', value: 'Paladin' },
-    Ranger: { display: 'Waldläufer', value: 'Ranger' },
-    Rogue: { display: 'Schurke', value: 'Rogue' },
-    Sorcerer: { display: 'Zauberer', value: 'Sorcerer' },
-    Warlock: { display: 'Hexenmeister', value: 'Warlock' },
-    Wizard: { display: 'Magier', value: 'Wizard' },
-    Test: { display: 'Test', value: 'Test' },
-  };
+  public races: string[] = [
+    'aarakocra',
+    'aasimar',
+    'autognome',
+    'bugbear',
+    'centaur',
+    'changeling',
+    'dragonborn',
+    'duergar',
+    'dwarf',
+    'elf',
+    'eladrin',
+    'fairy',
+    'firbolg',
+    'genasi',
+    'gith',
+    'gnome',
+    'goblin',
+    'goliath',
+    'halfElf',
+    'halfElfDetection',
+    'halfOrc',
+    'halfling',
+    'hobgoblin',
+    'human',
+    'kalashtar',
+    'kenku',
+    'kobold',
+    'leonin',
+    'lizardfolk',
+    'locathah',
+    'loxodon',
+    'minotaur',
+    'orc',
+    'owlin',
+    'satyr',
+    'shifter',
+    'tabaxi',
+    'tiefling',
+    'tortle',
+    'triton',
+    'vedalken',
+    'verdan',
+    'warforged',
+    'yuanTi',
+  ];
 
-  public genders: any = {
-    Male: { display: 'Männlich', value: 'Male' },
-    Female: { display: 'Weiblich', value: 'Female' },
-    Diverse: { display: 'Divers', value: 'Diverse' },
-  };
+  public classes: string[] = [
+    'barbarian',
+    'bard',
+    'cleric',
+    'druid',
+    'fighter',
+    'monk',
+    'paladin',
+    'ranger',
+    'rogue',
+    'sorcerer',
+    'warlock',
+    'wizard',
+    'test',
+  ];
 
-  public schools: any = {
-    Abjuration: { display: 'Bannmagie', value: 'Abjuration' },
-    Conjuration: { display: 'Beschwörung', value: 'Conjuration' },
-    Divination: { display: 'Erkenntnismagie', value: 'Divination' },
-    Enchantment: { display: 'Verzauberung', value: 'Enchantment' },
-    Evocation: { display: 'Hervorrufung', value: 'Evocation' },
-    Illusion: { display: 'Illusion', value: 'Illusion' },
-    Necromancy: { display: 'Nekromantie', value: 'Necromancy' },
-    Transmutation: { display: 'Verwandlung', value: 'Transmutation' },
-  };
+  public genders: string[] = ['male', 'female', 'diverse'];
+
+  public schools: string[] = [
+    'abjuration',
+    'conjuration',
+    'divination',
+    'enchantment',
+    'evocation',
+    'illusion',
+    'necromancy',
+    'transmutation',
+  ];
+
+  public attributes: string[] = [
+    'strength',
+    'dexterity',
+    'constitution',
+    'intelligence',
+    'wisdom',
+    'charisma',
+  ];
+
+  damageTypes: string[] = [
+    'slashing',
+    'bludgeoning',
+    'piercing',
+    'fire',
+    'force',
+    'lightning',
+    'necrotic',
+    'cold',
+    'poison',
+    'psychic',
+    'radiant',
+    'acid',
+    'thunder',
+  ];
+
+  areaTypes: string[] = [
+    'circle',
+    'line',
+    'cone',
+    'cube',
+    'sphere',
+    'cylinder',
+    'square',
+  ];
+
+  backgrounds: string[] = [
+    'acolyte',
+    'charlatan',
+    'criminal',
+    'entertainer',
+    'folkHero',
+    'guildArtisan',
+    'hermit',
+    'noble',
+    'outlander',
+    'sage',
+    'sailor',
+    'soldier',
+    'urchin',
+  ];
+
+  durationTypes: string[] = ['rounds', 'minutes', 'hours', 'days'];
 }