Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
992c909
Add InitialChoiceComponent with basic structure and tests
iptoux Apr 18, 2025
c91f147
Add initial choice selection functionality
iptoux Apr 19, 2025
b377db9
Add tasks page and update app routing structure
iptoux Apr 19, 2025
45a4617
Add CreateAccountComponent with basic structure and logic
iptoux Apr 19, 2025
1b189e8
Remove unused InitialChoiceComponent import
iptoux Apr 19, 2025
d896c2d
Revamp create-account UI with form and dark mode support
iptoux Apr 19, 2025
ba3327f
Remove unused 'inject' import from create-account component.
iptoux Apr 19, 2025
91cb16f
Add avatar and enhanced instructions to account creation
iptoux Apr 19, 2025
fae2c7d
Add AccountService with basic methods and unit test
iptoux Apr 19, 2025
3178f64
Refactor account creation with reactive forms and validations
iptoux Apr 19, 2025
f50767d
Update form validation to use 'dirty' instead of 'touched'
iptoux Apr 19, 2025
368fe77
Add image resizing to account creation process
iptoux Apr 19, 2025
19e90fa
Add user account handling and pass to footer component
iptoux Apr 19, 2025
ffe7e82
Update footer to display user avatar and logout tooltip
iptoux Apr 19, 2025
ed6f6ba
Remove unused NgOptimizedImage import from footer component.
iptoux Apr 19, 2025
4d49464
Remove logout icon and switch default avatar to SVG
iptoux Apr 19, 2025
9010bd0
Merge pull request #37 from iptoux/feature/online-offline
iptoux Apr 19, 2025
105bc0f
Add EventEmitter to notify account changes in AccountService
iptoux Apr 19, 2025
a2ce1fd
Add getSecret method and update account creation navigation
iptoux Apr 20, 2025
d75fd12
Add task migration component with encryption and progress tracking
iptoux Apr 20, 2025
218a9a7
Add task migration route and extend Task interface
iptoux Apr 20, 2025
7008220
Decrypt task descriptions using AES-GCM.
iptoux Apr 20, 2025
75b2113
Remove unused decryptTaskDesk method from task-migration component.
iptoux Apr 20, 2025
4a69668
Merge pull request #38 from iptoux/feature/online-offline
iptoux Apr 20, 2025
bad43d8
Refactor task display with encrypted description handling
iptoux Apr 20, 2025
bcaea55
Merge branch 'develop' into feature/online-offline
iptoux Apr 20, 2025
d5f705e
Merge pull request #39 from iptoux/feature/online-offline
iptoux Apr 20, 2025
d3159d2
Update package version to 1.7.0
iptoux Apr 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mytaskapplication",
"version": "1.6.0",
"version": "1.7.0",
"main": "app.js",
"author": {
"name": "Maik Roland Damm",
Expand Down
28 changes: 11 additions & 17 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
<app-header></app-header>
<div class="container">
<app-announcement-box></app-announcement-box>
@if(showNotifications) {
<app-notification-box></app-notification-box>
}
<app-filter-controls>
</app-filter-controls>
<app-task-add>
</app-task-add>
<div class="progress-box" [style.opacity]="showProgressBar ? '1' : '0'">
<app-todo-progressbar></app-todo-progressbar>
<div class="page-container d-flex flex-column min-vh-100">
<app-header></app-header>

<div class="container flex-grow-1 mb-5"> <!-- Added flex-grow-1 and margin-bottom -->
<app-announcement-box></app-announcement-box>
@if(showNotifications) {
<app-notification-box></app-notification-box>
}
<router-outlet></router-outlet>
</div>
<div class="progress-placeholder" [style.opacity]="showProgressBar ? '0' : '1'"></div>
<app-task-list>
</app-task-list>
</div>
<app-footer></app-footer>

<app-footer [useraccount]="useraccount"></app-footer>
</div>
37 changes: 23 additions & 14 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,52 @@
import {Component, inject} from '@angular/core';
import {Component, inject, OnInit} from '@angular/core';
import {HeaderComponent} from './components/header/header.component';
import {FilterControlsComponent} from './components/filter-controls/filter-controls.component';
import {TaskAddComponent} from './components/task-add/task-add.component';
import {TaskListComponent} from './components/task-list/task-list.component';
import {TodoProgressbarComponent} from './components/todo-progressbar/todo-progressbar.component';
import {FooterComponent} from './components/footer/footer.component';
import {AnnouncementBoxComponent} from './components/announcement-box/announcement-box.component';
import {DarkModeService} from './services/dark-mode.service';
import {SettingsService} from './services/settings.service';
import {NotificationBoxComponent} from './components/notification-box/notification-box.component';
import {Router, RouterOutlet} from '@angular/router';
import {Account} from './interfaces/account';
import {AccountService} from './services/account.service';

@Component({
selector: 'app-root',
imports: [HeaderComponent, FilterControlsComponent, TaskAddComponent, TaskListComponent, TodoProgressbarComponent, FooterComponent, AnnouncementBoxComponent, NotificationBoxComponent],
imports: [HeaderComponent,FooterComponent, AnnouncementBoxComponent, NotificationBoxComponent, RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
export class AppComponent implements OnInit {
title = 'untitled1';
useraccount: Account | undefined

private settingsService = inject(SettingsService)
protected settings = this.settingsService.getSettings();

private initialChoice = this.settings()[0]?.initialChoice;

get isDarkMode(): boolean {
return this.darkModeService.isDarkMode();
}

get showProgressBar():boolean {
return this.settings()[0]?.showProgressBar || false
}

get showNotifications():boolean {
return this.settings()[0]?.showNotifications || false
}

constructor(private darkModeService: DarkModeService) {}
constructor(
private darkModeService: DarkModeService,
private router: Router,
private accountService: AccountService) {
this.accountService.accountChanged.subscribe(account => {
this.useraccount = account;
});

toggleDarkMode(): void {
this.darkModeService.toggleDarkMode();
}

ngOnInit() {
if(this.initialChoice === undefined) {
void this.router.navigate(['/initial-choice']);
} else
if(this.settings()[0]?.initialChoice === 'offline')
this.useraccount = this.accountService.loadLocalAccount()
}
}
11 changes: 10 additions & 1 deletion src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Routes } from '@angular/router';
import {TasksComponent} from './components/page/tasks/tasks.component';
import {InitialChoiceComponent} from './components/initial-choice/initial-choice.component';
import {CreateAccountComponent} from './components/account/create-account/create-account.component';
import {TaskMigrationComponent} from './components/task-migration/task-migration.component';

export const routes: Routes = [
{ path: '', redirectTo: 'index.html', pathMatch: 'full' },
{ path: '', redirectTo: 'tasks', pathMatch: 'full' },
{ path: 'initial-choice', component: InitialChoiceComponent},
{ path: 'account/create/:action', component: CreateAccountComponent },
{ path: 'tasks', component: TasksComponent },
{ path: 'tasks/migration', component: TaskMigrationComponent },
{ path: '**', redirectTo: 'index.html' }
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.card {
border-radius: 0.15rem;
}

form > *, form > * > * > * {
border-radius: 0;
}

form > button {
background-color: #4c8bf5;
border: 0;
color: #fff;
}

form > button:hover {
background-color: #9463f7;
color: rgba(255, 255, 255, 0.85);
}


/* Dark theme styles for task-add component */
.dark-theme {
background-color: var(--surface-color);
border-color: var(--border-color);
color: var(--text-color);
}

/* Style for input fields in dark mode */
.dark-theme .form-control {
background-color: #1a1a2e; /* Dark background for input */
color: var(--text-color); /* White text */
border-color: var(--border-color);
}

/* Style for placeholder text in dark mode */
.dark-theme .form-control::placeholder {
color: rgba(255, 255, 255, 0.6); /* Slightly dimmed for placeholder */
}

/* Style for focused input in dark mode */
.dark-theme .form-control:focus {
background-color: #2a2a3e; /* Slightly lighter when focused */
box-shadow: 0 0 0 0.25rem rgba(187, 134, 252, 0.25); /* Glow that matches primary color */
}

/* Dark theme specific styles for file input */
.dark-theme input[type="file"].form-control {
background-color: #1a1a2e;
color: var(--text-color);
}

.dark-theme input[type="file"].form-control::file-selector-button {
background-color: #333345;
color: white;
}

.dark-theme input[type="file"].form-control::file-selector-button:hover {
background-color: #4c4c6d;
}



Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<div class="col col-xl-10 m-auto mt-5 d-flex flex-column" style="min-height: 0;">
<div class="col-12 text-center">
<h1>Local Account Setup</h1>
<p class="lead mt-5 mb-5">
To create your local account, simply enter a username, choose a password (which will encrypt all your data), and
upload an avatar of your choice. Be sure to remember this password—if you ever export your data and import it on
another device, you’ll need the exact same secret to restore access.
</p>
</div>

<div class="card d-flex flex-column flex-grow-1" [ngClass]="isDarkMode ? 'dark-theme' : ''">
<div class="card-header d-flex justify-content-between align-items-center">
Setup your local account
</div>
<div class="card-body p-4">
<form class="d-flex flex-column gap-3" [formGroup]="accountForm">
<div class="form-outline flex-fill">
<div class="mb-3">
<label for="username" class="mb-1">Username:</label>
<input
type="text"
id="username"
formControlName="username"
class="form-control"
placeholder="Enter username...."
autocomplete="username" />
@if (accountForm.get('username')?.invalid && (accountForm.get('username')?.dirty || accountForm.get('username')?.touched)) {
<div class="text-danger">
Username is required
</div>
}

</div>
<div class="mb-3">
<label for="password" class="mb-1">Password:</label>
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Any secret here..."
autocomplete="current-password" />
@if (accountForm.get('password')?.invalid && (accountForm.get('password')?.dirty || accountForm.get('password')?.touched)) {
<div class="text-danger">
Password is required
</div>
}

</div>
<div class="mb-3">
<label for="avatar" class="mb-1">Avatar:</label>
<input
type="file"
id="avatar"
(change)="onFileSelected($event)"
class="form-control"
placeholder="Choose your avatar..."
autocomplete="photo" />
@if (!selectedFile && accountForm.dirty) {
<div class="text-danger">
Avatar is required
</div>
}

</div>
</div>
<!-- (click)="createAccount(accName.value, accPass.value)" -->
<button type="button" class="btn btn-info ms-2" (click)="submitForm()">Create</button>

</form>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { CreateAccountComponent } from './create-account.component';

describe('CreateAccountComponent', () => {
let component: CreateAccountComponent;
let fixture: ComponentFixture<CreateAccountComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CreateAccountComponent]
})
.compileComponents();

fixture = TestBed.createComponent(CreateAccountComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading