diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 7e5ebaf..3dabea3 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -18,7 +18,7 @@ jobs: - name: Clone the repository uses: actions/checkout@v4 - - name: Set up Node.js + - name: Install Node.js uses: actions/setup-node@v4 with: node-version: '22' diff --git a/angular.json b/angular.json index a219883..501bd88 100644 --- a/angular.json +++ b/angular.json @@ -79,6 +79,8 @@ ], "tsConfig": "tsconfig.spec.json", "assets": [ + "src/favicon.ico", + "src/assets", { "glob": "**/*", "input": "public" diff --git a/src/app/app.component.css b/src/app/app.component.css index e69de29..b907744 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -0,0 +1,40 @@ +main { + width: 90%; + max-width: 50rem; + margin: 2.5rem auto; + display: grid; + grid-auto-flow: row; + gap: 2rem; +} + +#users { + list-style: none; + margin: 0; + padding: 0; + display: flex; + gap: 0.5rem; + overflow: auto; +} + +#fallback { + font-weight: bold; + font-size: 1.15rem; + margin: 0; + text-align: center; +} + +@media (min-width: 768px) { + main { + margin: 4rem auto; + grid-template-columns: 1fr 3fr; + } + + #users { + flex-direction: column; + } + + #fallback { + font-size: 1.5rem; + text-align: left; + } +} diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..0f935c5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,336 +1,23 @@ - - - - - - - - + - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
- - - - - - - - - - - diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts deleted file mode 100644 index a321242..0000000 --- a/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'todo-app-angular' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('todo-app-angular'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, todo-app-angular'); - }); -}); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 7b4acf9..6298bdf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,12 +1,26 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import {Component, signal} from '@angular/core'; +import { HeaderComponent } from './header/header.component'; +import { UserComponent } from './user/user.component'; +import { DUMMY_USERS } from '../dummy-users'; +import { User } from './user/user.model'; +import {TasksComponent} from './tasks/tasks.component'; @Component({ selector: 'app-root', - imports: [RouterOutlet], + standalone: true, + imports: [HeaderComponent, UserComponent, TasksComponent], templateUrl: './app.component.html', styleUrl: './app.component.css' }) export class AppComponent { - title = 'todo-app-angular'; + users = DUMMY_USERS; + selectedUserId?: string; + + get selectedUser() { + return this.users.find((user) => user.id === this.selectedUserId); + } + + onSelectUser(id: string) { + this.selectedUserId = id; + } } diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css new file mode 100644 index 0000000..abfb984 --- /dev/null +++ b/src/app/header/header.component.css @@ -0,0 +1,52 @@ +header { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + width: 90%; + max-width: 50rem; + margin: 0 auto 2rem auto; + text-align: center; + background: linear-gradient( + to bottom, + #2c0a4c, + #450d80 + ); + padding: 1rem; + border-bottom-right-radius: 12px; + border-bottom-left-radius: 12px; + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.6); +} + +img { + width: 3.5rem; + object-fit: contain; +} + +h1 { + font-size: 1.25rem; + margin: 0; + padding: 0; +} + +p { + margin: 0; + font-size: 0.8rem; + text-wrap: balance; +} + +@media (min-width: 768px) { + header { + padding: 2rem; + } + + img { + width: 4.5rem; + } + + h1 { + font-size: 1.5rem; + margin: 0; + padding: 0; + } +} diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html new file mode 100644 index 0000000..d06cbfe --- /dev/null +++ b/src/app/header/header.component.html @@ -0,0 +1,8 @@ +
+ Task Management +
+

EasyTask

+

Enterprise-level task management without friction

+
+
diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts new file mode 100644 index 0000000..d10a1eb --- /dev/null +++ b/src/app/header/header.component.ts @@ -0,0 +1,9 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: 'app-header', + standalone: true, + templateUrl: './header.component.html', + styleUrl: './header.component.css' +}) +export class HeaderComponent {} diff --git a/src/app/task/task.component.css b/src/app/task/task.component.css new file mode 100644 index 0000000..54ba924 --- /dev/null +++ b/src/app/task/task.component.css @@ -0,0 +1,38 @@ +article { + padding: 1rem; + color: #25113d; + background-color: #bf9ee5; + border-radius: 8px; +} + +h2 { + margin: 0; +} + +time { + color: #3c2c50; +} + +.actions { + text-align: right; + margin: 0; +} + +.actions button { + font: inherit; + font-size: 0.9rem; + cursor: pointer; + background-color: #380774; + color: #decdf2; + border-radius: 4px; + padding: 0.5rem 1.5rem; + border: none; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease; +} + +.actions button:hover, +.actions button:active { + background-color: #4a0774; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.3); +} diff --git a/src/app/task/task.component.html b/src/app/task/task.component.html new file mode 100644 index 0000000..869184b --- /dev/null +++ b/src/app/task/task.component.html @@ -0,0 +1,8 @@ +
+

{{ task.title }}

+ +

{{ task.summary }}

+

+ +

+
diff --git a/src/app/task/task.component.ts b/src/app/task/task.component.ts new file mode 100644 index 0000000..adfc690 --- /dev/null +++ b/src/app/task/task.component.ts @@ -0,0 +1,17 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { Task } from './task.model'; + +@Component({ + selector: 'app-task', + imports: [], + templateUrl: './task.component.html', + styleUrl: './task.component.css' +}) +export class TaskComponent { + @Input({ required: true }) task!: Task; + @Output() complete = new EventEmitter(); + + onCompleteTask() { + this.complete.emit(this.task.id); + } +} diff --git a/src/app/task/task.model.ts b/src/app/task/task.model.ts new file mode 100644 index 0000000..aabb365 --- /dev/null +++ b/src/app/task/task.model.ts @@ -0,0 +1,7 @@ +export interface Task { + id: string; + userId: string; + title: string; + summary: string; + dueDate: string; +} diff --git a/src/app/tasks/tasks.component.css b/src/app/tasks/tasks.component.css new file mode 100644 index 0000000..f59efa1 --- /dev/null +++ b/src/app/tasks/tasks.component.css @@ -0,0 +1,63 @@ +#tasks { + padding: 1rem; + border-radius: 8px; + max-height: 60vh; + overflow: auto; + background-color: #3a2c54; +} + +header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 2rem; + gap: 1rem; +} + +h2 { + margin: 0; + font-size: 0.9rem; + width: 60%; + text-wrap: balance; +} + +menu { + margin: 0; + padding: 0; +} + +menu button { + font: inherit; + cursor: pointer; + background-color: #9965dd; + border-radius: 4px; + border: none; + padding: 0.35rem 0.8rem; + font-size: 0.9rem; +} + +menu button:hover, +menu button:active { + background-color: #a565dd +} + +ul { + list-style: none; + margin: 1rem 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 1rem; + max-height: 50vh; + overflow: auto; +} + +@media (min-width: 768px) { + h2 { + font-size: 1.25rem; + } + + menu { + width: auto; + } +} diff --git a/src/app/tasks/tasks.component.html b/src/app/tasks/tasks.component.html new file mode 100644 index 0000000..14fe5ec --- /dev/null +++ b/src/app/tasks/tasks.component.html @@ -0,0 +1,23 @@ +
+
+

{{ user.name }}'s Tasks

+ + + +
+ + @if (selectedUserTasks) { +
    + @for (task of selectedUserTasks; track task.id) { +
  • + +
  • + } +
+ } @else { +

Create a new task for his user.

+ } +
diff --git a/src/app/tasks/tasks.component.ts b/src/app/tasks/tasks.component.ts new file mode 100644 index 0000000..9b2f553 --- /dev/null +++ b/src/app/tasks/tasks.component.ts @@ -0,0 +1,48 @@ +import {Component, Input} from '@angular/core'; +import {User} from '../user/user.model'; +import {Task} from '../task/task.model'; +import {TaskComponent} from '../task/task.component'; + +@Component({ + selector: 'app-tasks', + imports: [ TaskComponent ], + templateUrl: './tasks.component.html', + styleUrl: './tasks.component.css' +}) +export class TasksComponent { + @Input({ required: true }) user!: User; + + tasks: Task[] = [ + { + id: 't1', + userId: 'u1', + title: 'Master Angular', + summary: + 'Learn all the basic and advanced features of Angular & how to apply them.', + dueDate: '2025-12-31', + }, + { + id: 't2', + userId: 'u3', + title: 'Build first prototype', + summary: 'Build a first prototype of the online shop website', + dueDate: '2024-05-31', + }, + { + id: 't3', + userId: 'u3', + title: 'Prepare issue template', + summary: + 'Prepare and describe an issue template which will help with project management', + dueDate: '2024-06-15', + }, + ]; + + get selectedUserTasks() { + return this.tasks.filter((task) => task.userId === this.user.id); + } + + onDeleteTask(taskId: string) { + this.tasks = this.tasks.filter((task) => task.id !== taskId); + } +} diff --git a/src/app/user/user.component.css b/src/app/user/user.component.css new file mode 100644 index 0000000..546a2f6 --- /dev/null +++ b/src/app/user/user.component.css @@ -0,0 +1,41 @@ +div { + border-radius: 6px; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.35rem 0.5rem; + background-color: #433352; + color: #c3b3d1; + border: none; + font: inherit; + cursor: pointer; + width: 100%; + min-width: 10rem; + text-align: left; +} + +button:hover, +button:active, +.active { + background-color: #9965dd; + color: #150722; +} + +img { + width: 2rem; + object-fit: contain; + border-radius: 50%; + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.3); +} + +span { + margin: 0; + padding: 0; + font-size: 0.8rem; + font-weight: normal; +} diff --git a/src/app/user/user.component.html b/src/app/user/user.component.html new file mode 100644 index 0000000..81496ba --- /dev/null +++ b/src/app/user/user.component.html @@ -0,0 +1,12 @@ +
+ +
diff --git a/src/app/user/user.component.ts b/src/app/user/user.component.ts new file mode 100644 index 0000000..007c1e2 --- /dev/null +++ b/src/app/user/user.component.ts @@ -0,0 +1,23 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import { User } from './user.model'; + +@Component({ + selector: 'app-user', + standalone: true, + templateUrl: './user.component.html', + styleUrl: './user.component.css' +}) +export class UserComponent { + @Input({required: true}) user!: User; + @Input({ required: true }) selected!: boolean; + + @Output() select = new EventEmitter(); + + get imagePath() { + return 'assets/users/' + this.user.avatar; + } + + onSelectUser() { + this.select.emit(this.user.id); + } +} diff --git a/src/app/user/user.model.ts b/src/app/user/user.model.ts new file mode 100644 index 0000000..c109cac --- /dev/null +++ b/src/app/user/user.model.ts @@ -0,0 +1,5 @@ +export interface User { + id: string; + name: string; + avatar: string; +} diff --git a/public/favicon.ico b/src/assets/favicon.ico similarity index 100% rename from public/favicon.ico rename to src/assets/favicon.ico diff --git a/src/assets/task-management-logo.png b/src/assets/task-management-logo.png new file mode 100644 index 0000000..c8dadc9 Binary files /dev/null and b/src/assets/task-management-logo.png differ diff --git a/src/assets/users/user-1.jpg b/src/assets/users/user-1.jpg new file mode 100644 index 0000000..beb5c49 Binary files /dev/null and b/src/assets/users/user-1.jpg differ diff --git a/src/assets/users/user-2.jpg b/src/assets/users/user-2.jpg new file mode 100644 index 0000000..20075ec Binary files /dev/null and b/src/assets/users/user-2.jpg differ diff --git a/src/assets/users/user-3.jpg b/src/assets/users/user-3.jpg new file mode 100644 index 0000000..ba89298 Binary files /dev/null and b/src/assets/users/user-3.jpg differ diff --git a/src/assets/users/user-4.jpg b/src/assets/users/user-4.jpg new file mode 100644 index 0000000..df2ef1d Binary files /dev/null and b/src/assets/users/user-4.jpg differ diff --git a/src/assets/users/user-5.jpg b/src/assets/users/user-5.jpg new file mode 100644 index 0000000..ddb1bd6 Binary files /dev/null and b/src/assets/users/user-5.jpg differ diff --git a/src/assets/users/user-6.jpg b/src/assets/users/user-6.jpg new file mode 100644 index 0000000..3db31b9 Binary files /dev/null and b/src/assets/users/user-6.jpg differ diff --git a/src/dummy-users.ts b/src/dummy-users.ts new file mode 100644 index 0000000..d46ce80 --- /dev/null +++ b/src/dummy-users.ts @@ -0,0 +1,34 @@ +import { User } from './app/user/user.model'; + +export const DUMMY_USERS: User[] = [ + { + id: 'u1', + name: 'Jasmine Washington', + avatar: 'user-1.jpg', + }, + { + id: 'u2', + name: 'Emily Thompson', + avatar: 'user-2.jpg', + }, + { + id: 'u3', + name: 'Marcus Johnson', + avatar: 'user-3.jpg', + }, + { + id: 'u4', + name: 'David Miller', + avatar: 'user-4.jpg', + }, + { + id: 'u5', + name: 'Priya Patel', + avatar: 'user-5.jpg', + }, + { + id: 'u6', + name: 'Arjun Singh', + avatar: 'user-6.jpg', + }, +]; diff --git a/src/index.html b/src/index.html index 75eb6a5..18c8162 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,19 @@ - + - - TodoAppAngular - - - + + EasyTrack + + + + + + - + diff --git a/src/styles.css b/src/styles.css index 90d4ee0..710f0e2 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,17 @@ /* You can add global styles to this file, and also import other style files */ +* { + box-sizing: border-box; +} + +html { + height: 100%; +} + +body { + font-family: "Poppins", sans-serif; + background: radial-gradient(circle at top left, #181023, #0b0519); + color: #c3b3d8; + margin: 0; + padding: 0; + height: 100%; +}