瀏覽代碼

internationalization - english and polish, footer, cleanup

mateuszsudra 2 年之前
父節點
當前提交
12d3a5205a
共有 38 個文件被更改,包括 660 次插入197 次删除
  1. 7 8
      boat-reservation-logic/src/main/java/pl/sudra/controller/ReservationController.java
  2. 2 1
      boat-reservation-logic/src/main/java/pl/sudra/securityController/CustomInterceptor.java
  3. 29 0
      boat-reservation-view/package-lock.json
  4. 2 0
      boat-reservation-view/package.json
  5. 5 3
      boat-reservation-view/src/app/add-boat-component/add-boat.component.html
  6. 6 6
      boat-reservation-view/src/app/admin-panel/admin-panel.component.html
  7. 9 0
      boat-reservation-view/src/app/app.component.css
  8. 3 0
      boat-reservation-view/src/app/app.component.html
  9. 12 2
      boat-reservation-view/src/app/app.component.ts
  10. 20 10
      boat-reservation-view/src/app/app.module.ts
  11. 8 6
      boat-reservation-view/src/app/boats-component/boats-view.component.css
  12. 14 9
      boat-reservation-view/src/app/boats-component/boats-view.component.html
  13. 9 0
      boat-reservation-view/src/app/boats-component/boats-view.component.ts
  14. 5 0
      boat-reservation-view/src/app/footer/footer.component.css
  15. 15 0
      boat-reservation-view/src/app/footer/footer.component.html
  16. 32 0
      boat-reservation-view/src/app/footer/footer.component.ts
  17. 3 0
      boat-reservation-view/src/app/home-view/home-view.component.css
  18. 4 1
      boat-reservation-view/src/app/home-view/home-view.component.html
  19. 27 2
      boat-reservation-view/src/app/home-view/home-view.component.ts
  20. 17 6
      boat-reservation-view/src/app/login-view/login-view.component.html
  21. 3 1
      boat-reservation-view/src/app/login-view/login-view.component.ts
  22. 45 0
      boat-reservation-view/src/app/manager-panel/manager-panel.component.css
  23. 43 1
      boat-reservation-view/src/app/manager-panel/manager-panel.component.html
  24. 64 1
      boat-reservation-view/src/app/manager-panel/manager-panel.component.ts
  25. 0 0
      boat-reservation-view/src/app/map-view/map-view.component.css
  26. 0 1
      boat-reservation-view/src/app/map-view/map-view.component.html
  27. 0 10
      boat-reservation-view/src/app/map-view/map-view.component.ts
  28. 7 63
      boat-reservation-view/src/app/navbar/navbar.component.html
  29. 1 1
      boat-reservation-view/src/app/not-found/not-found.component.html
  30. 0 0
      boat-reservation-view/src/app/page-not-found/page-not-found.component.css
  31. 0 2
      boat-reservation-view/src/app/page-not-found/page-not-found.component.html
  32. 0 10
      boat-reservation-view/src/app/page-not-found/page-not-found.component.ts
  33. 20 18
      boat-reservation-view/src/app/profile-page/profile-page.component.html
  34. 22 16
      boat-reservation-view/src/app/register-view/register-view.component.html
  35. 18 17
      boat-reservation-view/src/app/reservation-view/reservation-view.component.html
  36. 6 2
      boat-reservation-view/src/app/services/reservation.service.ts
  37. 101 0
      boat-reservation-view/src/assets/i18n/en.json
  38. 101 0
      boat-reservation-view/src/assets/i18n/pl.json

+ 7 - 8
boat-reservation-logic/src/main/java/pl/sudra/controller/ReservationController.java

@@ -84,12 +84,11 @@ public class ReservationController {
         return this.reservationService.findReservationsByUserId(user_id);
     }
 
-//    @RequestMapping(
-//            value = "/getReservation",
-//            method = RequestMethod.GET,
-//            produces = MediaType.APPLICATION_JSON_VALUE)
-//    public Reservation findReservations(@RequestParam("id") Long id) {
-//        System.out.println("Looking for reservations");
-//        return this.reservationService.findById(id);
-//    }
+    @RequestMapping(
+            value = "/getAllReservations",
+            method = RequestMethod.GET,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public List<Reservation> findAllReservations() {
+        return this.reservationService.getReservations();
+    }
 }

+ 2 - 1
boat-reservation-logic/src/main/java/pl/sudra/securityController/CustomInterceptor.java

@@ -33,7 +33,8 @@ public class CustomInterceptor implements HandlerInterceptor {
     );
     List<String> manager_and_above = Stream.concat(customer_and_above.stream(),
                     List.of(
-                            "/addBoat"
+                            "/addBoat",
+                            "/getAllReservations"
                     ).stream())
             .collect(Collectors.toList());
 

+ 29 - 0
boat-reservation-view/package-lock.json

@@ -17,6 +17,8 @@
         "@angular/platform-browser-dynamic": "^16.0.0",
         "@angular/router": "^16.0.0",
         "@auth0/angular-jwt": "^5.1.2",
+        "@ngx-translate/core": "^15.0.0",
+        "@ngx-translate/http-loader": "^8.0.0",
         "jwt-decode": "^3.1.2",
         "ng-recaptcha": "^12.0.1",
         "node": "^20.2.0",
@@ -2841,6 +2843,33 @@
         "webpack": "^5.54.0"
       }
     },
+    "node_modules/@ngx-translate/core": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-15.0.0.tgz",
+      "integrity": "sha512-Am5uiuR0bOOxyoercDnAA3rJVizo4RRqJHo8N3RqJ+XfzVP/I845yEnMADykOHvM6HkVm4SZSnJBOiz0Anx5BA==",
+      "engines": {
+        "node": "^16.13.0 || >=18.10.0"
+      },
+      "peerDependencies": {
+        "@angular/common": ">=16.0.0",
+        "@angular/core": ">=16.0.0",
+        "rxjs": "^6.5.5 || ^7.4.0"
+      }
+    },
+    "node_modules/@ngx-translate/http-loader": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-8.0.0.tgz",
+      "integrity": "sha512-SFMsdUcmHF5OdZkL1CHEoSAwbP5EbAOPTLLboOCRRoOg21P4GJx+51jxGdJeGve6LSKLf4Pay7BkTwmE6vxYlg==",
+      "engines": {
+        "node": "^16.13.0 || >=18.10.0"
+      },
+      "peerDependencies": {
+        "@angular/common": ">=16.0.0",
+        "@angular/core": ">=16.0.0",
+        "@ngx-translate/core": ">=15.0.0",
+        "rxjs": "^6.5.5 || ^7.4.0"
+      }
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

+ 2 - 0
boat-reservation-view/package.json

@@ -21,6 +21,8 @@
     "@angular/platform-browser-dynamic": "^16.0.0",
     "@angular/router": "^16.0.0",
     "@auth0/angular-jwt": "^5.1.2",
+    "@ngx-translate/core": "^15.0.0",
+    "@ngx-translate/http-loader": "^8.0.0",
     "jwt-decode": "^3.1.2",
     "ng-recaptcha": "^12.0.1",
     "node": "^20.2.0",

+ 5 - 3
boat-reservation-view/src/app/add-boat-component/add-boat.component.html

@@ -1,7 +1,7 @@
 <div style="text-align: center;">
   <h2>REGISTER NEW BOAT</h2>
 </div>
-<form (submit)="addBoat()" #myForm="ngForm">
+<form (submit)="addBoat()" #myForm="ngForm" style="font-family: Courier New, monospace;">
   <label for="name">Name:</label>
   <input type="text"
          id="name"
@@ -17,6 +17,8 @@
          id="cost"
          name="cost" [(ngModel)]="formData.cost" required>
 
-  <input type="submit" value="Submit">
+  <div style="text-align: center;font-family: Courier New, monospace;">
+    <input type="submit" value="Submit">
+  </div>
 </form>
-
+<br>

+ 6 - 6
boat-reservation-view/src/app/admin-panel/admin-panel.component.html

@@ -1,13 +1,13 @@
 <div>
-  <br>
+  <b><h2 translate="admin.title"></h2></b>
   <table>
     <tr>
       <th>ID</th>
-      <th>Username</th>
+      <th translate="admin.username"></th>
       <th>Email</th>
       <th>A</th>
       <th>M</th>
-      <th>Actions</th>
+      <th translate="admin.actions"></th>
     </tr>
     <tr *ngFor="let user of users">
       <td>{{user.id}}</td>
@@ -19,9 +19,9 @@
       <td *ngIf="!user.isManager">❌</td>
       <td>
         <div class="action-cell">
-          <button class="button change-role" (click)="updateAdminRole(user.id)">Switch Admin</button>
-          <button class="button change-role" (click)="updateManagerRole(user.id)">Switch Manager</button>
-          <button class="button delete"  (click)="deleteUser(user.id)">Delete</button>
+          <button class="button change-role" (click)="updateAdminRole(user.id)" translate="admin.switch_a"></button>
+          <button class="button change-role" (click)="updateManagerRole(user.id)" translate="admin.switch_m"></button>
+          <button class="button delete"  (click)="deleteUser(user.id)" translate="admin.delete"></button>
         </div>
       </td>
     </tr>

+ 9 - 0
boat-reservation-view/src/app/app.component.css

@@ -35,12 +35,21 @@ body {
   box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.4);
 }
 
+.footer {
+  position: fixed;
+  bottom: 0;
+  width: 100%;
+  height: 2em;
+  box-shadow: 0px -5px 10px rgba(0, 0, 0, 0.4);
+}
+
 .main {
   overflow: auto;
   background-color: darksalmon;
   margin-left: 15%;
   margin-right: 15%;
   margin-top: 4em;
+  margin-bottom: 4em;
   /*height: calc(100% - 4em);*/
   /*position: absolute;*/
   box-shadow: -5px 0 20px rgba(0, 0, 0, 0.4), 5px 0 20px rgba(0, 0, 0, 0.4);

+ 3 - 0
boat-reservation-view/src/app/app.component.html

@@ -7,4 +7,7 @@
     <router-outlet></router-outlet>
   </div>
 </div>
+<div class="footer">
+  <app-footer></app-footer>
+</div>
 </body>

+ 12 - 2
boat-reservation-view/src/app/app.component.ts

@@ -1,5 +1,6 @@
 import {Component} from '@angular/core';
-import {TranslateService} from "./translate/translate.service";
+// import {TranslateService} from "./translate/translate.service";
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'app-root',
@@ -17,6 +18,15 @@ export class AppComponent {
   // constructor(translateService: TranslateService) {
   //   console.log(translateService.data)
   // }
-  constructor() {
+  // constructor() {
+  // }
+
+  constructor(translate: TranslateService) {
+    translate.addLangs(['en', 'klingon', 'pl']);
+    translate.setDefaultLang('en');
+    translate.use('en');
+  }
+
+  ngOnInit(){
   }
 }

+ 20 - 10
boat-reservation-view/src/app/app.module.ts

@@ -4,24 +4,28 @@ import {BrowserModule} from '@angular/platform-browser';
 import {RouterModule, Routes} from '@angular/router';
 import {AppComponent} from "./app.component";
 import {BoatsViewComponent} from "./boats-component/boats-view.component";
-import {PageNotFoundComponent} from './page-not-found/page-not-found.component';
 import {HomeViewComponent} from './home-view/home-view.component';
 import {TranslatePipe} from './translate/translate.pipe';
 import {TranslateService} from "./translate/translate.service";
-import {HttpClientModule} from "@angular/common/http";
+import {HttpClient, HttpClientModule} from "@angular/common/http";
 import {NavbarComponent} from './navbar/navbar.component';
 import {LoginViewComponent} from './login-view/login-view.component';
 import {RegisterViewComponent} from './register-view/register-view.component';
 import {ReservationViewComponent} from './reservation-view/reservation-view.component';
-import {MapViewComponent} from './map-view/map-view.component';
 import {AddBoatComponent} from './add-boat-component/add-boat.component';
 import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 import {NotFoundComponent} from './not-found/not-found.component';
 import {RecaptchaModule} from "ng-recaptcha";
-import { AdminPanelComponent } from './admin-panel/admin-panel.component';
-import { ManagerPanelComponent } from './manager-panel/manager-panel.component';
-import { ProfilePageComponent } from './profile-page/profile-page.component';
+import {AdminPanelComponent} from './admin-panel/admin-panel.component';
+import {ManagerPanelComponent} from './manager-panel/manager-panel.component';
+import {ProfilePageComponent} from './profile-page/profile-page.component';
+import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { FooterComponent } from './footer/footer.component';
 
+export function HttpLoaderFactory(http: HttpClient) {
+  return new TranslateHttpLoader(http);
+}
 export function setupTranslateServiceFactory(
   service: TranslateService): Function {
   return () => service.use('en');
@@ -64,19 +68,18 @@ export function setRoutes() {
   declarations: [
     AppComponent,
     BoatsViewComponent,
-    PageNotFoundComponent,
     HomeViewComponent,
     TranslatePipe,
     NavbarComponent,
     LoginViewComponent,
     RegisterViewComponent,
     ReservationViewComponent,
-    MapViewComponent,
     AddBoatComponent,
     NotFoundComponent,
     AdminPanelComponent,
     ManagerPanelComponent,
-    ProfilePageComponent
+    ProfilePageComponent,
+    FooterComponent
   ],
   imports: [
     BrowserModule,
@@ -84,7 +87,14 @@ export function setRoutes() {
     ReactiveFormsModule,
     FormsModule,
     HttpClientModule,
-    RecaptchaModule
+    RecaptchaModule,
+    TranslateModule.forRoot({
+      loader: {
+        provide: TranslateLoader,
+        useFactory: HttpLoaderFactory,
+        deps: [HttpClient]
+      }
+    })
   ],
   providers: [TranslateService,
     {

+ 8 - 6
boat-reservation-view/src/app/boats-component/boats-view.component.css

@@ -7,11 +7,14 @@
 .boat-list-item {
   width: 100%;
   padding: 20px;
+  padding-top: 0;
+  padding-bottom: 0;
   border: 1px solid #ccc;
   border-radius: 6px;
   margin: 1% 4% 5px;
   background-color: #fff;
   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  display: flex; justify-content: center;
 }
 
 .boat-list-item:hover {
@@ -37,7 +40,7 @@
 .item-name {
   font-style: normal;
   font-weight: bold;
-  font-family: Georgia, serif;
+  /*font-family: Georgia, serif;*/
 }
 
 /*.item-name:before {*/
@@ -52,20 +55,19 @@
 }
 
 .item-capacity:before {
-  content: "Max people capacity: ";
+  /*content: "Max people capacity: ";*/
   margin-right: 2px;
   font-style: normal;
 }
 
 .item-cost {
-  font-size: 24px;
-  font-weight: bold;
+  font-size: 18px;
   color: #333;
   font-style: normal;
 }
 
 .item-cost:before {
-  content: "cost per hour:   €";
+  /*content: "cost per hour:   €";*/
   margin-right: 2px;
   font-style: normal;
   font-size: 18px;
@@ -73,7 +75,7 @@
 }
 
 .item-cost:after {
-  content: "EUR";
+  /*content: "EUR";*/
   font-size: 12px;
   color: #999;
   margin-left: 2px;

+ 14 - 9
boat-reservation-view/src/app/boats-component/boats-view.component.html

@@ -1,19 +1,24 @@
-<!--<p>boats-view works!</p>-->
 <div style="text-align: center;">
-  <h2>OUR BOATS</h2>
+  <h2 translate="boats.title"></h2>
 </div>
 <div class="boat-list">
 
   <div class="boat-list-item" *ngFor="let item of boats">
     <table>
       <tr>
-        <td>
+        <td style="width: 40%">
           <img src="../../assets/leisure.png">
         </td>
-        <td>
+        <td style="text-align: center; font-family: Courier New, monospace;">
           <h3 class="item-name">{{item.name}}</h3>
-          <p class="item-capacity">{{item.capacity}}</p>
-          <p class="item-cost">{{item.cost}}</p>
+          <div class="item-capacity" style="display: flex; justify-content: space-evenly; align-items: center;">
+            <p translate="boats.capacity"></p>
+            <p>{{item.capacity}}</p>
+          </div>
+          <div class="item-cost" style="display: flex; justify-content: space-evenly; align-items: center;">
+            <p translate="boats.cost"></p>
+            <p>{{item.cost}}</p>
+          </div>
         </td>
       </tr>
     </table>
@@ -21,11 +26,11 @@
 </div>
 
 <br>
-<div style="text-align: center">
+<div *ngIf="isManager" style="text-align: center">
   <a class="navbar-button"
      routerLink="/add-boat"
-     routerLinkActive="activebutton">
-    ADD NEW BOAT
+     routerLinkActive="activebutton"
+     translate="boats.add">
   </a>
 </div>
 <br>

+ 9 - 0
boat-reservation-view/src/app/boats-component/boats-view.component.ts

@@ -10,8 +10,17 @@ import {Boat} from "../domain/boat";
 export class BoatsViewComponent {
 
   boats: Boat[] = [];
+  isAdmin: boolean = false;
+  isManager: boolean = false;
 
   constructor(private boatService: BoatService) {
+    let roles = sessionStorage.getItem('role')
+    if (roles?.includes("ADMIN")) {
+      this.isAdmin = true
+    }
+    if (roles?.includes("MANAGER")) {
+      this.isManager = true
+    }
   }
 
   async ngOnInit() {

+ 5 - 0
boat-reservation-view/src/app/footer/footer.component.css

@@ -0,0 +1,5 @@
+body {
+  background-color: #3c8aa7;
+  height: 100%;
+  font-family: Courier New, monospace;
+}

+ 15 - 0
boat-reservation-view/src/app/footer/footer.component.html

@@ -0,0 +1,15 @@
+<body style="justify-content: center;display: flex;">
+<table style="width: 95%;height: 100%;">
+  <tr>
+    <td colspan="10" style="text-align: left">
+      <a *ngIf="username !== null" translate="footer.log_info"><b>{{username}}</b></a>
+    </td>
+    <td style="text-align: right" translate="footer.langs">
+    </td>
+    <td *ngIf="lang == 'en'" style="width: 3%;text-align: center" (click)="change_lang('en')"><b>ENG</b></td>
+    <td *ngIf="lang != 'en'" style="width: 3%;text-align: center" (click)="change_lang('en')">ENG</td>
+    <td *ngIf="lang == 'pl'" style="width: 3%;text-align: center" (click)="change_lang('pl')"><b>PL</b></td>
+    <td *ngIf="lang != 'pl'" style="width: 3%;text-align: center" (click)="change_lang('pl')">PL</td>
+  </tr>
+</table>
+</body>

+ 32 - 0
boat-reservation-view/src/app/footer/footer.component.ts

@@ -0,0 +1,32 @@
+import {Component} from '@angular/core';
+import {TranslateService} from "@ngx-translate/core";
+
+@Component({
+  selector: 'app-footer',
+  templateUrl: './footer.component.html',
+  styleUrls: ['./footer.component.css']
+})
+export class FooterComponent {
+  username: string = '';
+  lang: string = '';
+
+  constructor(private translate: TranslateService) {
+    translate.addLangs(['en', 'pl']);
+    translate.setDefaultLang('en');
+    translate.use('en');
+    // if (this.lang == '') {
+    //   sessionStorage.setItem('lang', 'en')
+    //   this.lang = 'en'
+    // }
+    this.lang = sessionStorage.getItem("lang")!
+    this.username = sessionStorage.getItem('username')!
+    console.log(this.username)
+  }
+
+  change_lang(lang: string) {
+    sessionStorage.setItem('lang', lang)
+    this.translate.use(lang)
+    this.lang = lang
+    // console.log("zmiana na ", lang)
+  }
+}

+ 3 - 0
boat-reservation-view/src/app/home-view/home-view.component.css

@@ -0,0 +1,3 @@
+p {
+  text-align: justify;
+}

+ 4 - 1
boat-reservation-view/src/app/home-view/home-view.component.html

@@ -1 +1,4 @@
-<p>Home page and stuff</p>
+<p translate="homePage1"></p>
+<p translate="homePage2"></p>
+<p translate="homePage3"></p>
+<p translate="homePage4"></p>

+ 27 - 2
boat-reservation-view/src/app/home-view/home-view.component.ts

@@ -1,10 +1,35 @@
-import { Component } from '@angular/core';
+import {Component, OnInit} from '@angular/core';
+import {TranslateService} from "@ngx-translate/core";
 
 @Component({
   selector: 'app-home-view',
   templateUrl: './home-view.component.html',
   styleUrls: ['./home-view.component.css']
 })
-export class HomeViewComponent {
+export class HomeViewComponent implements OnInit{
+  user!: { firstName: string; lastName: string; };
+  welcome!: string;
+  usernameLabel!: string;
+  passwordLabel!: string;
 
+  constructor(private translate: TranslateService) {
+    translate.addLangs(['en', 'pl']);
+    translate.setDefaultLang('en');
+    translate.use('en');
+  }
+
+  ngOnInit() {
+    // hardcoded example
+    this.user = {firstName: 'Sammy', lastName: 'Shark'};
+
+    // synchronous. Also interpolate the 'firstName' parameter with a value.
+    this.welcome = this.translate.instant('welcomeMessage', {firstName: this.user.firstName});
+
+    // asynchronous - gets translations then completes.
+    this.translate.get(['login.username', 'login.password'])
+      .subscribe(translations => {
+        this.usernameLabel = translations['login.username'];
+        this.passwordLabel = translations['login.password'];
+      });
+  }
 }

+ 17 - 6
boat-reservation-view/src/app/login-view/login-view.component.html

@@ -1,10 +1,10 @@
 <div style="text-align: center;font-family: Courier New, monospace;">
   <div style="text-align: center;">
-    <h2>LOGIN</h2>
+    <h2 translate="login.title"></h2>
   </div>
   <form (submit)="login()" [formGroup]="formData">
     <div class="form-group">
-      <label for="username">Username:</label>
+      <label for="username" translate="login.username"></label>
       <input type="text"
              id="username"
              name="name" formControlName="username"
@@ -12,14 +12,17 @@
     </div>
 
     <div class="form-group">
-      <label for="password">Password:</label>
+      <label for="password" translate="login.password"></label>
       <input type="text"
              id="password"
              name="name" formControlName="password"
              minlength="5">
     </div>
 
-    <input type="submit" value="Login"
+    <input *ngIf="lang == 'en'" type="submit" value="Login"
+           [ngClass]="{'disabled': formData.invalid, 'enabled': !(formData.invalid)}"
+           [disabled]="formData.invalid">
+    <input *ngIf="lang == 'pl'" type="submit" value="Zaloguj"
            [ngClass]="{'disabled': formData.invalid, 'enabled': !(formData.invalid)}"
            [disabled]="formData.invalid">
     <br>
@@ -28,12 +31,20 @@
     </div>
   </form>
   <br>
-  <p>Don't have an account?</p>
-  <input class="enabled"
+  <p translate="login.question"></p>
+  <input *ngIf="lang == 'en'"
+         class="enabled"
          type="submit"
          value="Register"
          routerLink="/register"
          routerLinkActive="activebutton"
          style="width: 210px">
+  <input *ngIf="lang == 'pl'"
+         class="enabled"
+         type="submit"
+         value="Rejestracja"
+         routerLink="/register"
+         routerLinkActive="activebutton"
+         style="width: 210px">
   <br><br>
 </div>

+ 3 - 1
boat-reservation-view/src/app/login-view/login-view.component.ts

@@ -1,4 +1,4 @@
-import {Component} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
 import {AuthService} from "../services/auth.service";
 import {Router} from "@angular/router";
 import {FormBuilder, FormGroup, Validators} from "@angular/forms";
@@ -12,10 +12,12 @@ import jwt_decode from "jwt-decode";
 export class LoginViewComponent {
   formData: FormGroup;
   errors = ''
+  lang: string = ''
 
   constructor(private authService: AuthService,
               private router: Router,
               private formBuilder: FormBuilder) {
+    this.lang = sessionStorage.getItem('lang')!.toString()
     this.formData = this.formBuilder.group(
       {
         username: ['', Validators.required],

+ 45 - 0
boat-reservation-view/src/app/manager-panel/manager-panel.component.css

@@ -0,0 +1,45 @@
+table {
+  border-collapse: collapse;
+  width: 100%;
+  background-color: white;
+}
+
+th, td {
+  border: 1px solid black;
+  padding: 8px;
+}
+
+div {
+  text-align: center;
+  font-family: Courier New, monospace;
+}
+
+.action-cell {
+  display: flex;
+  justify-content: space-evenly;
+}
+
+.button {
+  padding: 6px 10px;
+  text-decoration: none;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-weight: bold;
+  transition: background-color 0.3s ease;
+}
+
+.button.change-role {
+  background-color: #4CAF50;
+  color: white;
+}
+
+.button.delete {
+  background-color: #f44336;
+  color: white;
+}
+
+.button:hover {
+  background-color: #555;
+}
+

+ 43 - 1
boat-reservation-view/src/app/manager-panel/manager-panel.component.html

@@ -1 +1,43 @@
-<p>manager-panel works!</p>
+<body style="font-family: Courier New, monospace;">
+<b><h2 style="text-align: center" translate="manager.title"></h2></b>
+<table style="text-align: center">
+  <tr>
+    <th translate="manager.client"></th>
+    <th translate="manager.boat"></th>
+    <th translate="manager.date"></th>
+    <th translate="manager.from"></th>
+    <th translate="manager.to"></th>
+    <th translate="manager.cost"></th>
+    <th translate="manager.status"></th>
+    <th translate="manager.actions"></th>
+  </tr>
+
+  <tr *ngFor="let res of reservations_info">
+    <td>{{res.userId}}</td>
+    <td *ngIf="isANumber(res.boatId)">⏳⌛</td>
+    <td *ngIf="!isANumber(res.boatId)">{{res.boatId}}</td>
+    <td>{{res.date}}</td>
+    <td>{{res.startHour}}:00</td>
+    <td>{{res.endHour}}:00</td>
+    <td>{{res.bill.totalCost}}</td>
+    <td translate="status.{{res.bill.status}}"></td>
+    <td>
+      <button
+        *ngIf="res.bill.status === 'Pending'"
+        class="button change-role"
+        (click)="confirmReservation(res.bill.id)"
+        style="margin-left:2px; margin-right: 2px"
+        translate="manager.confirm">
+      </button>
+      <button
+        *ngIf="res.bill.status !== 'Cancelled' && res.bill.status !== 'Completed' && res.bill.status !== 'Active'"
+        class="button change-role"
+        (click)="cancelReservation(res.bill.id)"
+        style="margin-left:2px; margin-right: 2px"
+        translate="manager.cancel">
+      </button>
+    </td>
+  </tr>
+</table>
+<br>
+</body>

+ 64 - 1
boat-reservation-view/src/app/manager-panel/manager-panel.component.ts

@@ -1,4 +1,7 @@
-import { Component } from '@angular/core';
+import {Component} from '@angular/core';
+import {UserService} from "../services/user.service";
+import {ReservationService} from "../services/reservation.service";
+import {BoatService} from "../services/boat.service";
 
 @Component({
   selector: 'app-manager-panel',
@@ -6,5 +9,65 @@ import { Component } from '@angular/core';
   styleUrls: ['./manager-panel.component.css']
 })
 export class ManagerPanelComponent {
+  reservations_info: any[] = [];
 
+
+  compareFn = (a: any, b: any): number => {
+    // Sort by date
+    const dateComparison = a.date.localeCompare(b.date);
+    if (dateComparison !== 0) {
+      return dateComparison;
+    }
+
+    // Sort by hour
+    return a.startHour - b.startHour;
+  };
+
+  isANumber(value: any): boolean {
+    return typeof value === 'number'
+  }
+
+  constructor(private userService: UserService,
+              private reservationService: ReservationService,
+              private boatService: BoatService) {
+    this.reservationService.getReservations().subscribe(
+      reservations => {
+        console.log(reservations)
+        this.reservations_info = reservations.map(r => {
+          r.date = new Date(r.date).toISOString().split("T")[0]
+          return r;
+        });
+        this.boatService.getBoats().subscribe(
+          boats => {
+            console.log(boats)
+            this.reservations_info.map(r => {
+              r.boatId = boats.find(b => b.id == r.boatId)!.name
+            })
+            this.reservations_info.sort(this.compareFn)
+          }
+        )
+        this.userService.getAllUsers().subscribe(
+          users => {
+            console.log(users)
+            this.reservations_info.map(r => {
+              r.userId = users.find(u => u.id == r.userId)!.username
+            })
+          }
+        )
+
+        console.log(reservations[0])
+      })
+  }
+
+  cancelReservation(id: number) {
+    this.reservationService.cancelReservation(id)
+    this.reservations_info.find(r => r.bill.id === id).bill.status = "Cancelled"
+  }
+
+  confirmReservation(id: number) {
+    this.reservationService.confirmReservation(id)
+    console.log(id)
+    console.log(this.reservations_info)
+    this.reservations_info.find(r => r.bill.id === id).bill.status = "Confirmed"
+  }
 }

+ 0 - 0
boat-reservation-view/src/app/map-view/map-view.component.css


+ 0 - 1
boat-reservation-view/src/app/map-view/map-view.component.html

@@ -1 +0,0 @@
-<p>map-view works!</p>

+ 0 - 10
boat-reservation-view/src/app/map-view/map-view.component.ts

@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'app-map-view',
-  templateUrl: './map-view.component.html',
-  styleUrls: ['./map-view.component.css']
-})
-export class MapViewComponent {
-
-}

+ 7 - 63
boat-reservation-view/src/app/navbar/navbar.component.html

@@ -12,105 +12,49 @@
          routerLink="/admin"
          routerLinkActive="activebutton"
          *ngIf="isAdmin">
-      <a>
-        ADMIN PANEL
+      <a translate="navbar.admin">
       </a>
     </div>
     <div class="navbar-button"
          routerLink="/manager"
          routerLinkActive="activebutton"
          *ngIf="isManager">
-      <a>
-        MANAGER PANEL
+      <a translate="navbar.manager">
       </a>
     </div>
     <div class="navbar-button"
          routerLink="/boats"
          routerLinkActive="activebutton">
-      <a>
-        BOATS
+      <a translate="navbar.boats">
       </a>
     </div>
     <div class="navbar-button"
          routerLink="/reservation"
          routerLinkActive="activebutton">
-      <a>
-        RESERVATION
+      <a translate="navbar.reservations">
       </a>
     </div>
     <div class="navbar-button"
          routerLink="/profile"
          routerLinkActive="activebutton"
          *ngIf="isLogged">
-      <a>
-        PROFILE PAGE
+      <a translate="navbar.profile">
       </a>
     </div>
     <div class="navbar-button"
          routerLink="/login"
          routerLinkActive="activebutton"
          *ngIf="!isLogged">
-      <a>
-        LOGIN
+      <a translate="navbar.login">
       </a>
     </div>
     <div class="navbar-button"
          (click)="logout()"
          routerLinkActive="activebutton"
          *ngIf="isLogged">
-      <a>
-        LOGOUT
+      <a translate="navbar.logout">
       </a>
     </div>
   </div>
 </div>
 </body>
-
-<!--<table>-->
-<!--  <tr>-->
-<!--    <td>-->
-<!--      <a class="logo"-->
-<!--         routerLink="/"-->
-<!--         routerLinkActive="activebutton">-->
-<!--        Oslo Boating Service-->
-<!--      </a>-->
-<!--    </td>-->
-<!--    <td>-->
-<!--      <a class="navbar-button"-->
-<!--         routerLink="/boats"-->
-<!--         routerLinkActive="activebutton">-->
-<!--        BOATS-->
-<!--      </a>-->
-<!--    </td>-->
-<!--    &lt;!&ndash;    <td>&ndash;&gt;-->
-<!--    &lt;!&ndash;      <a class="navbar-button"&ndash;&gt;-->
-<!--    &lt;!&ndash;         routerLink="/map"&ndash;&gt;-->
-<!--    &lt;!&ndash;         routerLinkActive="activebutton">&ndash;&gt;-->
-<!--    &lt;!&ndash;        MAP&ndash;&gt;-->
-<!--    &lt;!&ndash;      </a>&ndash;&gt;-->
-<!--    &lt;!&ndash;    </td>&ndash;&gt;-->
-<!--    <td>-->
-<!--      <a class="navbar-button"-->
-<!--         routerLink="/reservation"-->
-<!--         routerLinkActive="activebutton">-->
-<!--        RESERVATION-->
-<!--      </a>-->
-<!--    </td>-->
-<!--    <td>-->
-<!--      <a class="navbar-button"-->
-<!--         routerLink="/login"-->
-<!--         routerLinkActive="activebutton"-->
-<!--         *ngIf="!isLogged">-->
-<!--        LOGIN-->
-<!--      </a>-->
-<!--    </td>-->
-<!--    <td>-->
-<!--      <a class="navbar-button"-->
-<!--         (click)="logout()"-->
-<!--         routerLinkActive="activebutton"-->
-<!--         *ngIf="isLogged">-->
-<!--        LOGOUT-->
-<!--      </a>-->
-<!--    </td>-->
-<!--  </tr>-->
-<!--</table>-->

+ 1 - 1
boat-reservation-view/src/app/not-found/not-found.component.html

@@ -1 +1 @@
-<p>404 Not found!</p>
+<h1 translate="not_found.title"></h1>

+ 0 - 0
boat-reservation-view/src/app/page-not-found/page-not-found.component.css


+ 0 - 2
boat-reservation-view/src/app/page-not-found/page-not-found.component.html

@@ -1,2 +0,0 @@
-<h1>You are lost my friend</h1>
-<h3>Error 404: Not found</h3>

+ 0 - 10
boat-reservation-view/src/app/page-not-found/page-not-found.component.ts

@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'app-page-not-found',
-  templateUrl: './page-not-found.component.html',
-  styleUrls: ['./page-not-found.component.css']
-})
-export class PageNotFoundComponent {
-
-}

+ 20 - 18
boat-reservation-view/src/app/profile-page/profile-page.component.html

@@ -3,10 +3,10 @@
   <table>
     <tr>
       <th>ID</th>
-      <th>Username</th>
+      <th translate="admin.username"></th>
       <th>Email</th>
-      <th>Roles</th>
-      <th>Actions</th>
+      <th translate="admin.actions"></th>
+      <th translate="admin.actions"></th>
     </tr>
     <tr>
       <td>{{user_info.id}}</td>
@@ -16,30 +16,32 @@
       <td>
         <button class="button change-role"
                 (click)="sendEmail()"
-                style="margin-left:2px; margin-right: 2px">Send Billing on Email
+                style="margin-left:2px; margin-right: 2px"
+                translate="profile.billing">
         </button>
         <button class="button change-role"
                 (click)="deleteAccount()"
-                style="margin-left:2px; margin-right: 2px">Delete User
+                style="margin-left:2px; margin-right: 2px"
+                translate="profile.delete">
         </button>
       </td>
     </tr>
   </table>
 
   <br>
-  <div><b>Reservations</b></div>
+  <div><b translate="manager.title"></b></div>
   <br>
 
   <table>
     <tr>
       <th>ID</th>
-      <th>Boat</th>
-      <th>Date</th>
-      <th>From</th>
-      <th>To</th>
-      <th>Cost</th>
-      <th>Status</th>
-      <th>Actions</th>
+      <th translate="manager.boat"></th>
+      <th translate="manager.date"></th>
+      <th translate="manager.from"></th>
+      <th translate="manager.to"></th>
+      <th translate="manager.cost"></th>
+      <th translate="manager.status"></th>
+      <th translate="manager.actions"></th>
     </tr>
 
     <tr *ngFor="let res of reservations_info">
@@ -50,21 +52,21 @@
       <td>{{res.startHour}}:00</td>
       <td>{{res.endHour}}:00</td>
       <td>{{res.bill.totalCost}}</td>
-      <td>{{res.bill.status}}</td>
+      <td translate="status.{{res.bill.status}}"></td>
       <td>
         <button
           *ngIf="res.bill.status === 'Pending'"
           class="button change-role"
           (click)="confirmReservation(res.bill.id)"
-          style="margin-left:2px; margin-right: 2px">
-          Confirm
+          style="margin-left:2px; margin-right: 2px"
+          translate="manager.confirm">
         </button>
         <button
           *ngIf="res.bill.status !== 'Cancelled' && res.bill.status !== 'Completed' && res.bill.status !== 'Active'"
           class="button change-role"
           (click)="cancelReservation(res.bill.id)"
-          style="margin-left:2px; margin-right: 2px">
-          Cancel
+          style="margin-left:2px; margin-right: 2px"
+          translate="manager.cancel">
         </button>
       </td>
     </tr>

+ 22 - 16
boat-reservation-view/src/app/register-view/register-view.component.html

@@ -1,10 +1,10 @@
 <div style="text-align: center;">
-  <h2>REGISTER NEW USER</h2>
+  <h2 translate="register.title"></h2>
 </div>
 <form (submit)="registerUser()" [formGroup]="formData"
       style="text-align: center; font-family: Courier New, monospace;">
   <div class="form-group">
-    <label for="username">Username:</label>
+    <label for="username" translate="register.username"></label>
     <input type="text"
            id="username"
            name="name" formControlName="username"
@@ -12,17 +12,19 @@
            maxlength="20">
   </div>
   <div style="color: #b70b0b" *ngIf="formData.controls['username'].hasError('required') &&
-  (formData.controls['username'].dirty || formData.controls['username'].touched)">
-    Username is required!<br><br>
+  (formData.controls['username'].dirty || formData.controls['username'].touched)"
+       translate="register.username_required">
+    <br><br>
   </div>
   <div style="color: #b70b0b" *ngIf="(formData.controls['username'].hasError('minlength') ||
   formData.controls['username'].hasError('maxlength')) &&
-  (formData.controls['username'].dirty || formData.controls['username'].touched)">
-    Password length has to be between 5 and 20!<br><br>
+  (formData.controls['username'].dirty || formData.controls['username'].touched)"
+       translate="register.username_length">
+    <br><br>
   </div>
 
   <div class="form-group">
-    <label for="password">Password:</label>
+    <label for="password" translate="register.password"></label>
     <input type="text"
            id="password"
            name="name" formControlName="password"
@@ -30,13 +32,15 @@
            maxlength="20">
   </div>
   <div style="color: #b70b0b" *ngIf="formData.controls['password'].hasError('required') &&
-  (formData.controls['password'].dirty || formData.controls['password'].touched)">
-    Password is required!<br><br>
+  (formData.controls['password'].dirty || formData.controls['password'].touched)"
+       translate="register.password_required">
+    <br><br>
   </div>
   <div style="color: #b70b0b" *ngIf="(formData.controls['password'].hasError('minlength') ||
   formData.controls['password'].hasError('maxlength')) &&
-  (formData.controls['password'].dirty || formData.controls['password'].touched)">
-    Password length has to be between 5 and 20!<br><br>
+  (formData.controls['password'].dirty || formData.controls['password'].touched)"
+       translate="register.password_length">
+    <br><br>
   </div>
 
   <div class="form-group">
@@ -46,12 +50,14 @@
            name="name" formControlName="email">
   </div>
   <div style="color: #b70b0b" *ngIf="formData.controls['email'].hasError('required') &&
-  (formData.controls['email'].dirty || formData.controls['email'].touched)">
-    Email is required!<br><br>
+  (formData.controls['email'].dirty || formData.controls['email'].touched)"
+       translate="register.email_required">
+    <br><br>
   </div>
   <div style="color: #b70b0b" *ngIf="formData.controls['email'].hasError('email') &&
-  (formData.controls['email'].dirty || formData.controls['email'].touched)">
-    Invalid email format<br><br>
+  (formData.controls['email'].dirty || formData.controls['email'].touched)"
+       translate="register.email_format">
+    <br><br>
   </div>
 
   <br>
@@ -66,7 +72,7 @@
       ((formData.controls['username'].dirty || formData.controls['username'].touched)||
       (formData.controls['password'].dirty || formData.controls['password'].touched)||
       (formData.controls['email'].dirty || formData.controls['email'].touched))">
-    <b>Fix the errors above to proceed.</b><br>
+    <b translate="register.errors"></b><br>
   </div>
   <br>
   <div style="color: #b70b0b">

+ 18 - 17
boat-reservation-view/src/app/reservation-view/reservation-view.component.html

@@ -1,10 +1,11 @@
 <div style="text-align: center;">
-  <h2>MAKE RESERVATION</h2>
+  <h2 translate="reservations.title"></h2>
 </div>
 <form (submit)="makeReservation()" #myForm="ngForm" style="font-family: Courier New, monospace;">
-  <h3 style="text-align: center;margin-bottom: 0;">BOAT</h3>
-  <label id="boat_label" for="boat"
-         style="font-family: Courier New, monospace;text-align: center;margin-bottom: 5px;"></label>
+  <h3 style="text-align: center;margin-bottom: 0;" translate="reservations.boat"></h3>
+  <label translate="reservations.info" style="color: darkslategray; font-size: 80%;"></label>
+  <!--  <label id="boat_label" for="boat"-->
+  <!--         style="font-family: Courier New, monospace;text-align: center;margin-bottom: 5px;"></label>-->
   <select name="_boats"
           id="boat"
           style="text-align: center; width: 280px"
@@ -12,8 +13,8 @@
     <option *ngFor="let boat of boats">{{ boat.label.toString() }}</option>
   </select>
 
-  <h3 style="text-align: center">DATE</h3>
-  <p *ngIf="isDateFromPast()" style="color: #b70b0b">You can't select date from the past.</p>
+  <h3 style="text-align: center" translate="reservations.date"></h3>
+  <p *ngIf="isDateFromPast()" style="color: #b70b0b" translate="reservations.errors.past_date"></p>
   <div style="text-align: center">
     <input type="date"
            id="date"
@@ -33,24 +34,24 @@
         </tr>
         <tr style="font-size: 12px;text-align: left">
           <td id="free" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
-          <td colspan="3">Available</td>
+          <td colspan="3" translate="reservations.available"></td>
           <td id="taken" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
-          <td colspan="3">Reserved</td>
+          <td colspan="3" translate="reservations.reserved"></td>
           <td id="selected" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
-          <td colspan="3">Selected</td>
+          <td colspan="3" translate="reservations.selected"></td>
           <td id="conflict" style="border:1px #333333 solid;width: 20px; height: 20px"></td>
-          <td colspan="3">Conflict</td>
+          <td colspan="3" translate="reservations.conflict"></td>
         </tr>
       </table>
     </div>
   </div>
 
-  <h3 style="text-align: center">TIME</h3>
+  <h3 style="text-align: center" translate="reservations.time"></h3>
   <div style="display: flex; justify-content: center; align-items: center;">
     <table style="width: 90%">
       <tr>
         <td style="width: 25%; text-align: right; padding-right: 10px;">
-          <label for="from_hour" style="font-family: Courier New, monospace;">START AT</label>
+          <label for="from_hour" style="font-family: Courier New, monospace;" translate="reservations.start_at"></label>
         </td>
         <td style="width: 25%; text-align: left">
           <select id="from_hour" style="width: 70px; box-sizing: border-box;" (change)="onStartHourSelected()">
@@ -73,7 +74,7 @@
           </select>
         </td>
         <td style="width: 25%; text-align: right; padding-right: 10px;">
-          <label for="to_hour" style="font-family: Courier New, monospace;">END AT</label>
+          <label for="to_hour" style="font-family: Courier New, monospace;" translate="reservations.ends_at"></label>
         </td>
         <td style="width: 25%; text-align: left">
           <select id="to_hour" style="width: 70px; box-sizing: border-box;" (change)="onEndHourSelected()">
@@ -98,13 +99,13 @@
       </tr>
     </table>
   </div>
-  <p *ngIf="areHoursConflicting()" style="color: #b70b0b">Selected hours are conflicting with existing reservations.</p>
-  <p *ngIf="areHoursOrderWrong()" style="color: #b70b0b">The start hour should be before the end hour.</p>
-  <p *ngIf="isItTooLateToStart()" style="color: #b70b0b">It is too late to start this reservation.</p>
+  <p *ngIf="areHoursConflicting()" style="color: #b70b0b" translate="reservations.errors.conflict"></p>
+  <p *ngIf="areHoursOrderWrong()" style="color: #b70b0b" translate="reservations.errors.time_order"></p>
+  <p *ngIf="isItTooLateToStart()" style="color: #b70b0b" translate="reservations.errors.too_late"></p>
 
   <br *ngIf="!(areHoursOrderWrong() || areHoursConflicting() || isItTooLateToStart())">
   <div style="text-align: center;">
-    <p *ngIf="isFormNotValid()" style="color: #b70b0b"><b>Fix the errors above to make reservation.</b></p>
+    <p *ngIf="isFormNotValid()" style="color: #b70b0b"><b translate="reservations.errors.general"></b></p>
     <input type="submit" value="Submit" [disabled]="isFormNotValid()"
            [ngClass]="{'disabled': isFormNotValid(), 'enabled': !isFormNotValid()}">
   </div>

+ 6 - 2
boat-reservation-view/src/app/services/reservation.service.ts

@@ -12,9 +12,13 @@ export class ReservationService {
   }
 
   getReservations(): Observable<Reservation[]> {
-    const url = 'http://localhost:2137/getAllBoats';
+    const url = 'http://localhost:2137/getAllReservations';
 
-    return this.http.get<Reservation[]>(url);
+    const headers = new HttpHeaders()
+      .set('Content-Type', 'application/json')
+      .set('Authorization', sessionStorage.getItem("jwtToken")!);
+
+    return this.http.get<Reservation[]>(url, {headers});
   }
 
   findReservations(boat_id: number, date: string): Observable<Reservation[]> {

+ 101 - 0
boat-reservation-view/src/assets/i18n/en.json

@@ -0,0 +1,101 @@
+{
+  "navbar": {
+    "admin": "ADMIN PANEL",
+    "manager": "MANAGER PANEL",
+    "boats": "BOATS",
+    "reservations": "RESERVATIONS",
+    "profile": "PROFILE",
+    "logout": "LOGOUT",
+    "login": "LOGIN"
+  },
+  "footer": {
+    "log_info": "Logged as: ",
+    "langs": "Languages: "
+  },
+  "homePage1": "Welcome to Oslo Boating Service website, your premier choice for boat rentals in Oslo, Norway. With our extensive fleet of well-maintained boats, we offer the perfect opportunity to explore the stunning Norwegian fjords and picturesque landscapes at your own pace.",
+  "homePage2": "At Oslo Boat Rentals, we pride ourselves on delivering exceptional service and unforgettable experiences. Whether you're planning a relaxing day on the water, a fishing excursion, or a scenic cruise with your loved ones, our diverse selection of boats ensures there's something for everyone.",
+  "homePage3": "Our knowledgeable and friendly staff are ready to assist you in selecting the ideal boat for your adventure, providing safety instructions, and offering insider tips on the best routes and hidden gems to explore along the Oslo coast.",
+  "homePage4": "Experience the freedom and tranquility of sailing on Oslo's pristine waters, surrounded by breathtaking scenery and wildlife. Create cherished memories as you navigate the fjords, soak up the sun, and enjoy the serenity of the Norwegian outdoors.",
+  "homePage5": "Choose Oslo Boat Rentals for an unforgettable boating experience in Oslo. We're committed to ensuring your satisfaction and making your time on the water truly remarkable. Book your boat rental today and embark on a remarkable journey through Norway's captivating beauty.",
+  "admin": {
+    "title": "Users",
+    "username": "Username",
+    "roles": "Roles",
+    "actions": "Actions",
+    "switch_a": "Switch A",
+    "switch_m": "Switch M",
+    "delete": "Delete"
+  },
+  "manager": {
+    "title": "Reservations",
+    "client": "Client",
+    "boat": "Boat",
+    "date": "Date",
+    "from": "From",
+    "to": "To",
+    "cost": "Cost",
+    "status": "Status",
+    "actions": "Actions",
+    "confirm": "Confirm",
+    "cancel": "Cancel"
+  },
+  "status": {
+    "Pending": "Pending",
+    "Confirmed": "Confirmed",
+    "Active": "Active",
+    "Completed": "Completed",
+    "Cancelled": "Cancelled"
+  },
+  "boats": {
+    "title": "Our boats",
+    "capacity": "Max people capacity: ",
+    "cost": "Cost per hour:",
+    "add": "Add new boat"
+  },
+  "reservations": {
+    "title": "Create a reservation",
+    "boat": "Boat: ",
+    "info": "(name | capacity | cost/hour)",
+    "date": "Date:",
+    "available": "Available",
+    "reserved": "Reserved",
+    "selected": "Selected",
+    "conflict": "Conflict",
+    "time": "Time",
+    "start_at": "Starts at",
+    "ends_at": "Ends at",
+    "submit": "Submit",
+    "errors": {
+      "past_date": "You can't select date from the past.",
+      "conflict": "Selected hours are conflicting with existing reservations.",
+      "time_order": "The start hour should be before the end hour.",
+      "too_late": "It is too late to start this reservation.",
+      "general": "Fix the errors above to make reservation."
+    }
+  },
+  "profile": {
+    "billing": "Send Billing on Email",
+    "delete": "Delete user"
+  },
+  "login": {
+    "title": "LOGIN:",
+    "username": "Username:",
+    "password": "Password:",
+    "question": "Don't have an account?"
+  },
+  "register": {
+    "title": "REGISTER NEW USER:",
+    "username": "Username:",
+    "username_required": "Username is required!",
+    "username_length": "Nazwa użytkownika musi zawierać od 5 do 20 znaków!",
+    "password": "Hasło:",
+    "password_required": "Hasło jest wymagane!",
+    "password_length": "Hasło musi zawierać od 5 do 20 znaków!",
+    "email_required": "Email jest wymagany!",
+    "email_format": "Niewłaściwy format email'u!",
+    "errors": "Napraw powyższe błędy aby kontynuować"
+  },
+  "not_found": {
+    "title": "404 Given page was not found :("
+  }
+}

+ 101 - 0
boat-reservation-view/src/assets/i18n/pl.json

@@ -0,0 +1,101 @@
+{
+  "navbar": {
+    "admin": "PANEL ADMINA",
+    "manager": "PANEL MANAGERA",
+    "boats": "ŁODZIE",
+    "reservations": "REZERWACJE",
+    "profile": "PROFIL",
+    "logout": "WYLOGUJ",
+    "login": "ZALOGUJ"
+  },
+  "footer": {
+    "log_info": "Zalogowano jako: ",
+    "langs": "Języki: "
+  },
+  "homePage1": "Witamy w Oslo Boating Service, najlepszym miejscu do wynajęcia łodzi w Oslo w Norwegii. Dzięki naszej rozległej flocie dobrze utrzymanych łodzi oferujemy doskonałą okazję do odkrywania wspaniałych norweskich fiordów i malowniczych krajobrazów we własnym tempie.",
+  "homePage2": "W Oslo Boat Rentals jesteśmy dumni z zapewniania wyjątkowej obsługi i niezapomnianych wrażeń. Niezależnie od tego, czy planujesz relaksujący dzień na wodzie, wyprawę wędkarską, czy rejs z ukochaną osobą, nasza różnorodna oferta łodzi gwarantuje, że każdy znajdzie coś dla siebie.",
+  "homePage3": "Nasz kompetentny i przyjazny personel jest gotowy, aby pomóc Ci wybrać idealną łódź do Twojej przygody, udzielając instrukcji bezpieczeństwa i oferując poufne wskazówki dotyczące najlepszych tras i ukrytych klejnotów do odkrycia wzdłuż wybrzeża Oslo.",
+  "homePage4": "Doświadcz wolności i spokoju żeglugi po dziewiczych wodach Oslo, w otoczeniu zapierających dech w piersiach krajobrazów i dzikiej przyrody. Twórz cenne wspomnienia, żeglując po fiordach, rozkoszując się słońcem i ciesząc się spokojem norweskiego krajobrazu.",
+  "homePage5": "Wybierz Oslo Boat Rentals, aby przeżyć niezapomniane wrażenia żeglarskie w Oslo. Dokładamy wszelkich starań, aby zapewnić Ci satysfakcję i sprawić, że Twój czas na wodzie będzie naprawdę wyjątkowy. Zarezerwuj wynajem łodzi już dziś i wyrusz w niezwykłą podróż po urzekającej urodzie Norwegii.",
+  "admin": {
+    "title": "Użytkownicy",
+    "username": "Nazwa użytkownika",
+    "roles": "Role",
+    "actions": "Operacje",
+    "switch_a": "Zmień A",
+    "switch_m": "Zmień M",
+    "delete": "Usuń"
+  },
+  "manager": {
+    "title": "Rezerwacje",
+    "client": "Klient",
+    "boat": "Łódź",
+    "date": "Data",
+    "from": "Od",
+    "to": "Do",
+    "cost": "Koszt",
+    "status": "Status",
+    "actions": "Operacje",
+    "confirm": "Potwierdź",
+    "cancel": "Odwołaj"
+  },
+  "status": {
+    "Pending": "Oczekująca",
+    "Confirmed": "Potwierdzona",
+    "Active": "Aktywna",
+    "Completed": "Ukończona",
+    "Cancelled": "Odwołana"
+  },
+  "boats": {
+    "title": "Nasze łodzie",
+    "capacity": "Maksymalna pojemność osób: ",
+    "cost": "Koszt za godzinę:",
+    "add": "Dodaj nową łódź"
+  },
+  "reservations": {
+    "title": "Stwórz rezerwację",
+    "boat": "Łódź: ",
+    "info": "(nazwa | ładowność | koszt/godzina)",
+    "date": "Data:",
+    "available": "Dostępne",
+    "reserved": "Zajęte",
+    "selected": "Wybrane",
+    "conflict": "Konflikt",
+    "time": "Godzina:",
+    "start_at": "Start o",
+    "ends_at": "Koniec o",
+    "submit": "Zatwierdź",
+    "errors": {
+      "past_date": "Nie możesz wybrać daty z przeszłości.",
+      "conflict": "Wybrane godziny powodują konflikt z istniejącymi rezerwacjami.",
+      "time_order": "Godzina startu powinna być przed godziną zakończenia.",
+      "too_late": "Jest za późno, aby zacząć tę rezerwację.",
+      "general": "Napraw powyższe błędy, aby stworzyć rezerwację."
+    }
+  },
+  "profile": {
+    "billing": "Wyślij Billing na Email",
+    "delete": "Usuń użytkownika"
+  },
+  "login": {
+    "title": "LOGOWANIE:",
+    "username": "Nazwa użytkownika:",
+    "password": "Hasło:",
+    "question": "Nie masz konta?"
+  },
+  "register": {
+    "title": "REJESTRACJA NOWEGO UŻYTKOWNIKA:",
+    "username": "Nazwa użytkownika:",
+    "username_required": "Nazwa użytkownika jest wymagana!",
+    "username_length": "Nazwa użytkownika musi zawierać od 5 do 20 znaków!",
+    "password": "Hasło:",
+    "password_required": "Hasło jest wymagane!",
+    "password_length": "Hasło musi zawierać od 5 do 20 znaków!",
+    "email_required": "Email jest wymagany!",
+    "email_format": "Niewłaściwy format email'u!",
+    "errors": "Napraw powyższe błędy aby kontynuować"
+  },
+  "not_found": {
+    "title": "404 Nie znaleziono danej strony :("
+  }
+}