diff --git a/angular.json b/angular.json index e523c20..0c0b581 100644 --- a/angular.json +++ b/angular.json @@ -29,10 +29,13 @@ "glob": "**/*", "input": "src/assets", "output": "assets" - } + }, + "src/manifest.webmanifest" ], "styles": ["src/global.scss", "src/theme/variables.scss"], - "scripts": [] + "scripts": [], + "serviceWorker": true, + "ngswConfigPath": "ngsw-config.json" }, "configurations": { "production": { diff --git a/ngsw-config.json b/ngsw-config.json new file mode 100644 index 0000000..6c8ccf4 --- /dev/null +++ b/ngsw-config.json @@ -0,0 +1,29 @@ +{ + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "resources": { + "files": [ + "/favicon.ico", + "/index.html", + "/manifest.webmanifest", + "/*.css", + "/*.js" + ] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": [ + "/**/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" + ] + } + } + ] +} diff --git a/package-lock.json b/package-lock.json index 78ee481..daee4bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@angular/platform-browser": "^18.0.0", "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", + "@angular/service-worker": "^18.0.0", "@capacitor/app": "6.0.1", "@capacitor/core": "6.1.2", "@capacitor/haptics": "6.0.1", @@ -648,6 +649,24 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/service-worker": { + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.2.tgz", + "integrity": "sha512-az0v0gNkAjOQ4DThDWfNJv2DkH63B4Vj/WnXd8pbY/C7Be6w3S1mN2y9vJClWAzUH/GSLQHnOrZJfnZtTc8M0w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "bin": { + "ngsw-config": "ngsw-config.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.2", + "@angular/core": "18.2.2" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", diff --git a/package.json b/package.json index 8aaedf1..1f0c798 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@angular/platform-browser": "^18.0.0", "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", + "@angular/service-worker": "^18.0.0", "@capacitor/app": "6.0.1", "@capacitor/core": "6.1.2", "@capacitor/haptics": "6.0.1", @@ -61,4 +62,4 @@ "typescript": "~5.4.0" }, "description": "An Ionic project" -} +} \ No newline at end of file diff --git a/src/app/time/time.page.html b/src/app/time/time.page.html index aa69f48..0d6a615 100644 --- a/src/app/time/time.page.html +++ b/src/app/time/time.page.html @@ -6,7 +6,7 @@ - + Zeiterfassung @@ -27,7 +27,7 @@
- {{entry.type === 'login' ? "Eingestempelt" : "Ausgestempelt"}} + {{getTypeText(entry.type)}} {{entry.registeredAt.toLocaleTimeString()}} {{generateSeparatorText(data[index - 1], entry)}} @@ -37,7 +37,7 @@
{{currentAction === 'login' ? "Einstempeln" : "Ausstempeln"}} - +
@@ -70,6 +70,8 @@ Einstempeln Ausstempeln + Dienstreise starten + Dienstreise beenden
diff --git a/src/app/time/time.page.scss b/src/app/time/time.page.scss index 30736af..b5e867f 100644 --- a/src/app/time/time.page.scss +++ b/src/app/time/time.page.scss @@ -1,6 +1,6 @@ .button-container { - position: absolute; - bottom: 75px; + position: fixed; + bottom: 25px; left: 0; right: 0; display: flex; @@ -13,6 +13,7 @@ flex-direction: column; gap: 65px; margin: 20px; + padding-bottom: 75px; .entry { --inner-border-width: 0 0 0 0; diff --git a/src/app/time/time.page.ts b/src/app/time/time.page.ts index f9e5e0c..2f3859b 100644 --- a/src/app/time/time.page.ts +++ b/src/app/time/time.page.ts @@ -57,23 +57,45 @@ export class TimePage { return this.data.filter(entry => entry.registeredAt.getDay() === today); } + public getTypeText(type: TimeType): string { + switch (type) { + case "login": + return "Eingestempelt"; + + case "logout": + return "Ausgestempelt"; + + case "start-drive": + return "Dienstreise gestartet"; + + case "end-drive": + return "Dienstreise beendet"; + } + } + public generateSeparatorText(entry1: TimeEntry, entry2: TimeEntry): string { const difference = +entry2.registeredAt.getTime() - +entry1.registeredAt.getTime() - 3600000; const date = new Date(difference); - const text = entry1.type === 'login' ? "Arbeit " : "Pause "; + + let text = entry1.type === 'login' ? "Arbeit " : "Pause "; + if (entry1.type === 'start-drive' && entry2.type === 'end-drive') { + text = "Dienstreise"; + } + return text + `(${date.toLocaleTimeString()})`; } public addEntry(): void { + const animateIndex = this.shouldAnimate.length; this.shouldAnimate.push(true) - setTimeout(() => this.shouldAnimate[this.shouldAnimate.length - 1] = false, 5000); + setTimeout(() => this.shouldAnimate[animateIndex] = false, 2000); this.data.push({ registeredAt: new Date(Date.now()), type: this.currentAction }); this.saveData(); - this.currentAction = this.currentAction === 'login' ? 'logout' : 'login'; + this.updateCurrentAction(); } public removeEntry(index: number): void { diff --git a/src/assets/icon/favicon.png b/src/assets/icon/favicon.png index 51888a7..2b1c739 100644 Binary files a/src/assets/icon/favicon.png and b/src/assets/icon/favicon.png differ diff --git a/src/global.scss b/src/global.scss index 999ff53..6b2884b 100644 --- a/src/global.scss +++ b/src/global.scss @@ -35,3 +35,7 @@ /* @import "@ionic/angular/css/palettes/dark.always.css"; */ /* @import "@ionic/angular/css/palettes/dark.class.css"; */ @import '@ionic/angular/css/palettes/dark.system.css'; + +.ios .icon-button { + width: 50px; +} diff --git a/src/index.html b/src/index.html index c74d135..e57ee40 100644 --- a/src/index.html +++ b/src/index.html @@ -2,25 +2,28 @@ - + Zeiterfassung - + - - - - + + + + - + - - + + + + + diff --git a/src/main.ts b/src/main.ts index db355ec..debaf4d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,11 +4,16 @@ import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalo import { routes } from './app/app.routes'; import { AppComponent } from './app/app.component'; +import { isDevMode } from '@angular/core'; +import { provideServiceWorker } from '@angular/service-worker'; bootstrapApplication(AppComponent, { providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, provideIonicAngular(), - provideRouter(routes, withPreloading(PreloadAllModules)), + provideRouter(routes, withPreloading(PreloadAllModules)), provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: 'registerWhenStable:30000' + }), ], }); diff --git a/src/manifest.webmanifest b/src/manifest.webmanifest new file mode 100644 index 0000000..24c653d --- /dev/null +++ b/src/manifest.webmanifest @@ -0,0 +1,17 @@ +{ + "name": "Zeiterfassung", + "short_name": "Zeiterfassung", + "theme_color": "#121212", + "background_color": "#121212", + "display": "standalone", + "scope": "./", + "start_url": "./", + "icons": [ + { + "src": "assets/icon/favicon.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any" + } + ] +} diff --git a/src/models/timeEntry.ts b/src/models/timeEntry.ts index ba137b2..5287cf4 100644 --- a/src/models/timeEntry.ts +++ b/src/models/timeEntry.ts @@ -3,4 +3,4 @@ export interface TimeEntry { type: TimeType; } -export type TimeType = 'login' | 'logout'; +export type TimeType = 'login' | 'logout' | 'start-drive' | 'end-drive';