@@ -142,12 +108,6 @@ import { TaskFormComponent } from '@org/web/tasks';
padding: 0 1rem;
}
- @media (min-width: 576px) {
- :host {
- padding: 0 1.5rem;
- }
- }
-
.loading-container,
.error-container {
display: flex;
@@ -157,13 +117,6 @@ import { TaskFormComponent } from '@org/web/tasks';
margin: 2rem 0;
}
- @media (min-width: 576px) {
- .loading-container,
- .error-container {
- margin: 3rem 0;
- }
- }
-
.error-container {
text-align: center;
}
@@ -176,14 +129,7 @@ import { TaskFormComponent } from '@org/web/tasks';
mat-toolbar {
margin-bottom: 1.5rem;
- border-radius: 4px;
- }
-
- @media (min-width: 576px) {
- mat-toolbar {
- margin-bottom: 2rem;
- border-radius: 8px;
- }
+ border-radius: 8px;
}
.user-container {
@@ -194,59 +140,15 @@ import { TaskFormComponent } from '@org/web/tasks';
@media (min-width: 768px) {
.user-container {
- gap: 2rem;
grid-template-columns: 1fr 2fr;
}
}
mat-card {
border-radius: 8px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 1rem;
height: fit-content;
- width: 100%;
- box-sizing: border-box;
- overflow: hidden;
- }
-
- mat-card-header {
- margin-bottom: 1.25rem;
- flex-direction: column;
- align-items: flex-start;
- }
-
- @media (min-width: 576px) {
- mat-card-header {
- margin-bottom: 1.5rem;
- flex-direction: row;
- align-items: center;
- }
- }
-
- mat-card-title {
- font-size: 1.25rem;
- font-weight: 500;
- margin-bottom: 0.25rem;
- word-break: break-word;
- }
-
- @media (min-width: 576px) {
- mat-card-title {
- font-size: 1.5rem;
- margin-bottom: 0.5rem;
- }
- }
-
- mat-card-subtitle {
- font-size: 0.875rem;
- color: rgba(0, 0, 0, 0.6);
- word-break: break-word;
- }
-
- @media (min-width: 576px) {
- mat-card-subtitle {
- font-size: 1rem;
- }
}
.tasks-section {
@@ -255,17 +157,10 @@ import { TaskFormComponent } from '@org/web/tasks';
padding: 1rem;
}
- @media (min-width: 576px) {
- .tasks-section {
- padding: 1.5rem;
- }
- }
-
.tasks-section h2 {
font-size: 1.25rem;
margin-top: 0;
margin-bottom: 1rem;
- color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 0.5rem;
display: flex;
@@ -273,60 +168,19 @@ import { TaskFormComponent } from '@org/web/tasks';
gap: 0.5rem;
}
- @media (min-width: 576px) {
- .tasks-section h2 {
- font-size: 1.5rem;
- margin-bottom: 1.5rem;
- padding-bottom: 0.75rem;
- }
- }
-
- mat-list {
- border-radius: 4px;
- overflow: hidden;
- background-color: white;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
- }
-
mat-list-item {
margin-bottom: 0.5rem;
- border-left: 4px solid #3f51b5;
- transition: transform 0.2s, box-shadow 0.2s;
- height: auto !important;
- min-height: 48px;
- padding: 8px 0;
- }
-
- mat-list-item:hover {
- transform: translateX(2px);
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
- }
- [matListItemTitle] {
- white-space: normal;
- word-break: break-word;
- }
-
- [matListItemLine] {
- white-space: normal;
- word-break: break-word;
- overflow: visible;
- text-overflow: unset;
- display: block;
- max-height: none;
- line-height: 1.5;
- margin-top: 4px;
+ border-left: 3px solid #3f51b5;
}
.task-title {
margin: 0;
font-weight: 500;
- font-size: 1rem;
}
.task-description {
white-space: pre-line;
color: rgba(0, 0, 0, 0.6);
- padding-top: 4px;
}
.no-tasks {
@@ -337,37 +191,17 @@ import { TaskFormComponent } from '@org/web/tasks';
border-radius: 8px;
}
- @media (min-width: 576px) {
- .no-tasks {
- padding: 2rem;
- }
- }
-
lib-task-form {
display: block;
margin-top: 1.5rem;
padding: 1rem;
background-color: white;
border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
- }
-
- @media (min-width: 576px) {
- lib-task-form {
- margin-top: 2rem;
- padding: 1.5rem;
- }
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.back-button {
margin-top: 1rem;
- width: 100%;
- }
-
- @media (min-width: 576px) {
- .back-button {
- width: auto;
- }
}
`,
],
diff --git a/client/libs/web/users/src/lib/user-list.component.ts b/client/libs/web/users/src/lib/user-list.component.ts
index 133e57c..3844ffd 100644
--- a/client/libs/web/users/src/lib/user-list.component.ts
+++ b/client/libs/web/users/src/lib/user-list.component.ts
@@ -15,16 +15,11 @@ import {
Validators,
} from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
-import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog';
-import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
-import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
-import { MatToolbarModule } from '@angular/material/toolbar';
-import { RouterModule } from '@angular/router';
import { CommentResponse, TaskResponse, User } from '@org/shared/api';
import {
CommentStoreService,
@@ -33,6 +28,7 @@ import {
} from '@org/shared/stores';
import { ToastService } from '@org/shared/toast';
import { AddUserDialogComponent } from './add-user-dialog.component';
+import { UserCardComponent } from './user-card.component';
type UserWithCounts = User & { taskCount: number; commentCount: number };
@@ -42,15 +38,11 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
CommonModule,
MatFormFieldModule,
MatInputModule,
- MatListModule,
- RouterModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
MatIconModule,
- MatToolbarModule,
MatButtonModule,
- MatCardModule,
- MatDividerModule,
+ UserCardComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@@ -82,36 +74,12 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
} @else {
@for (user of filteredUsers(); track user.id) {
-
-
-
-
- {{
- user.name
- }}
-
- {{ user.email }}
-
-
-
-
- assignment
- {{ user.taskCount }} Tasks
-
-
- comment
- {{ user.commentCount }} Comments
-
-
-
-
+
}
}
@@ -139,64 +107,51 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
.empty-state {
text-align: center;
padding: 2rem;
- background-color: var(--mat-grey-100, #f5f5f5);
+ background-color: #f5f5f5;
border-radius: 4px;
}
.user-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
- margin-top: 2rem;
+ margin-top: 1.5rem;
}
.user-card {
position: relative;
- background: var(--mat-card-background, #fff);
- border-radius: 18px;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
- transition: box-shadow 0.2s, transform 0.2s;
- will-change: box-shadow, transform;
- animation: fadeInUp 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ background: #fff;
+ border-radius: 12px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
+ transition: all 0.2s ease;
padding-bottom: 0.5rem;
- overflow: visible;
+ overflow: hidden;
}
.user-card:hover {
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.14);
- transform: translateY(-4px) scale(1.02);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ transform: translateY(-3px);
}
.user-link {
text-decoration: none;
color: #1976d2;
font-weight: 600;
- font-size: 1.15rem;
- transition: color 0.2s;
+ font-size: 1.1rem;
}
.user-link:hover {
- color: #125ea2;
text-decoration: underline;
}
.delete-btn {
position: absolute;
- top: 0.7rem;
- right: 0.7rem;
+ top: 0.5rem;
+ right: 0.5rem;
z-index: 2;
- background: #fff;
- border-radius: 50%;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
- transition: background 0.2s;
- }
- .delete-btn:hover {
- background: #ffeaea;
}
.user-meta {
display: flex;
- gap: 1.2rem;
- margin-top: 0.7rem;
- font-size: 0.98rem;
+ gap: 1rem;
+ margin-top: 0.5rem;
+ font-size: 0.9rem;
color: #555;
- align-items: center;
}
.meta-icon {
- font-size: 1.1em;
vertical-align: middle;
margin-right: 0.2em;
color: #1976d2;
@@ -213,57 +168,6 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
flex-direction: column;
gap: 0.5rem;
}
- .dialog-card {
- min-width: 90vw;
- }
- }
- .dialog-backdrop {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.2);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- animation: fadeIn 0.25s;
- }
- .dialog-card {
- min-width: 320px;
- max-width: 90vw;
- border-radius: 18px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
- animation: scaleIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);
- }
- @keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(30px);
- }
- to {
- opacity: 1;
- transform: none;
- }
- }
- @keyframes fadeIn {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
- }
- @keyframes scaleIn {
- from {
- opacity: 0;
- transform: scale(0.95);
- }
- to {
- opacity: 1;
- transform: scale(1);
- }
}
`,
],
diff --git a/client/libs/web/users/src/lib/user-task-list.component.ts b/client/libs/web/users/src/lib/user-task-list.component.ts
new file mode 100644
index 0000000..293a906
--- /dev/null
+++ b/client/libs/web/users/src/lib/user-task-list.component.ts
@@ -0,0 +1,117 @@
+import { CommonModule } from '@angular/common';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ input,
+ output,
+} from '@angular/core';
+import { MatIconModule } from '@angular/material/icon';
+import { MatListModule } from '@angular/material/list';
+import { TaskResponse } from '@org/shared/api';
+import { TaskFormComponent } from '@org/web/tasks';
+
+@Component({
+ selector: 'lib-user-task-list',
+ imports: [CommonModule, MatIconModule, MatListModule, TaskFormComponent],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+
+
+ assignment
+ User Tasks
+
+
+ @if (tasks().length) {
+
+ @for (task of tasks(); track task.tasks.id) {
+
+ task_alt
+
+
{{ task.tasks.title }}
+
+
+ {{ task.tasks.description || 'No description' }}
+
+
+ }
+
+ } @else {
+
+
assignment_late
+
No tasks found for this user.
+
+ }
+
+
+
+ `,
+ styles: [
+ `
+ .tasks-section {
+ background-color: #f9f9f9;
+ border-radius: 8px;
+ padding: 1rem;
+ }
+
+ .tasks-section h2 {
+ font-size: 1.25rem;
+ margin-top: 0;
+ margin-bottom: 1rem;
+ border-bottom: 1px solid #eee;
+ padding-bottom: 0.5rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ mat-list {
+ border-radius: 4px;
+ overflow: hidden;
+ background-color: white;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+ }
+
+ mat-list-item {
+ margin-bottom: 0.5rem;
+ border-left: 3px solid #3f51b5;
+ }
+
+ .task-title {
+ margin: 0;
+ font-weight: 500;
+ }
+
+ .task-description {
+ white-space: pre-line;
+ color: rgba(0, 0, 0, 0.6);
+ }
+
+ .no-tasks {
+ text-align: center;
+ padding: 1.5rem;
+ color: rgba(0, 0, 0, 0.5);
+ background-color: white;
+ border-radius: 8px;
+ }
+
+ lib-task-form {
+ display: block;
+ margin-top: 1.5rem;
+ padding: 1rem;
+ background-color: white;
+ border-radius: 8px;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+ }
+ `,
+ ],
+})
+export class UserTaskListComponent {
+ readonly tasks = input.required
();
+ readonly userId = input.required();
+ readonly taskAdded = output();
+}
diff --git a/server/src/db/local.db b/server/src/db/local.db
index 001f315..313276e 100644
Binary files a/server/src/db/local.db and b/server/src/db/local.db differ
+forum
+ Comments
+
+ +