Переглянути джерело

Write HTML & CSS for Admin Account Management Page

Eldar Mukhtarov 9 місяців тому
батько
коміт
df27c9e81f

+ 235 - 0
project/frontend-angular/src/app/admin/admin.css

@@ -0,0 +1,235 @@
+.accounts-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 40px 20px;
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 40px;
+  animation: fadeIn 0.6s ease-in;
+}
+
+@keyframes fadeIn {
+  from { 
+    opacity: 0; 
+    transform: translateY(20px); 
+  }
+  to { 
+    opacity: 1; 
+    transform: translateY(0); 
+  }
+}
+
+.account-section {
+  background: rgba(255, 255, 255, 0.02);
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  border-radius: 16px;
+  padding: 30px;
+  transition: all 0.3s ease;
+}
+
+.account-section:hover {
+  background: rgba(255, 255, 255, 0.05);
+  border-color: rgba(255, 255, 255, 0.15);
+  transform: translateY(-2px);
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+}
+
+.section-title {
+  font-size: 24px;
+  font-weight: 600;
+  color: #ffffff;
+  margin-bottom: 24px;
+  padding-bottom: 16px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.search-controls {
+  margin-bottom: 24px;
+}
+
+.search-label {
+  display: block;
+  font-size: 14px;
+  font-weight: 500;
+  color: #e0e0e0;
+  margin-bottom: 8px;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.search-input-group {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+}
+
+.search-input {
+  flex: 1;
+  padding: 12px 16px;
+  background: rgba(255, 255, 255, 0.05);
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  border-radius: 8px;
+  color: #ffffff;
+  font-size: 14px;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  transition: all 0.3s ease;
+}
+
+.search-input:focus {
+  outline: none;
+  border-color: rgba(255, 255, 255, 0.3);
+  background: rgba(255, 255, 255, 0.08);
+  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
+}
+
+.search-input::placeholder {
+  color: #888;
+}
+
+.sort-button {
+  background: rgba(255, 255, 255, 0.1);
+  border: 1px solid rgba(255, 255, 255, 0.2);
+  border-radius: 8px;
+  color: #e0e0e0;
+  padding: 12px 16px;
+  font-size: 16px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  min-width: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.sort-button:hover {
+  background: rgba(255, 255, 255, 0.15);
+  transform: translateY(-1px);
+  border-color: rgba(255, 255, 255, 0.3);
+}
+
+.sort-button:active {
+  transform: translateY(0);
+}
+
+.account-list {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.account-item {
+  background: rgba(255, 255, 255, 0.03);
+  border: 1px solid rgba(255, 255, 255, 0.08);
+  border-radius: 12px;
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  transition: all 0.3s ease;
+}
+
+.account-item:hover {
+  background: rgba(255, 255, 255, 0.06);
+  border-color: rgba(255, 255, 255, 0.15);
+  transform: translateY(-1px);
+}
+
+.account-info {
+  flex: 1;
+}
+
+.account-id {
+  font-family: 'Courier New', monospace;
+  font-size: 12px;
+  color: #888;
+  font-weight: 400;
+  margin-bottom: 4px;
+  display: block;
+}
+
+.account-name {
+  font-size: 16px;
+  font-weight: 500;
+  color: #ffffff;
+  margin: 0;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.delete-button {
+  background: linear-gradient(135deg, #8C231E 0%, #a02b26 100%);
+  border: none;
+  border-radius: 8px;
+  color: #ffffff;
+  padding: 10px 16px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.delete-button:hover {
+  transform: translateY(-1px);
+  box-shadow: 0 4px 12px rgba(140, 35, 30, 0.4);
+  background: linear-gradient(135deg, #a02b26 0%, #b83530 100%);
+}
+
+.delete-button:active {
+  transform: translateY(0);
+}
+
+@media (max-width: 768px) {
+  .accounts-container {
+    grid-template-columns: 1fr;
+    gap: 24px;
+    padding: 20px 15px;
+  }
+
+  .account-section {
+    padding: 20px;
+  }
+
+  .section-title {
+    font-size: 20px;
+  }
+
+  .search-input-group {
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .search-input {
+    width: 100%;
+  }
+
+  .sort-button {
+    width: 100%;
+    justify-content: center;
+  }
+
+  .account-item {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 16px;
+  }
+
+  .delete-button {
+    align-self: flex-end;
+    width: auto;
+  }
+}
+
+.account-list:empty::after {
+  content: "No accounts found";
+  display: block;
+  text-align: center;
+  padding: 40px 20px;
+  color: #888;
+  font-style: italic;
+  font-size: 16px;
+}

+ 51 - 18
project/frontend-angular/src/app/admin/admin.html

@@ -1,26 +1,59 @@
-<div>
-  <div>
-    <h4>Student accounts</h4>
-    <ul>
-      <li *ngFor="let student of studentList">
-        <div>
-          <span>ID: {{student.id}}</span>
-          <p>{{student.firstname}} {{student.lastname}}</p>
+<div class="accounts-container">
+  <div class="account-section">
+    <h4 class="section-title">Student accounts</h4>
+    <div class="search-controls">
+      <label class="search-label" for="student-search">Search by Name:</label>
+      <div class="search-input-group">
+        <input 
+          type="text" 
+          id="student-search" 
+          class="search-input"
+          [(ngModel)]="studentSearchTerm" 
+          (input)="filterStudents()" 
+          placeholder="Enter student name"
+        >
+        <button class="sort-button" (click)="sortStudents()">
+          {{ studentSortDirection === 'asc' ? '↑' : '↓' }}
+        </button>
+      </div>
+    </div>
+    <ul class="account-list">
+      <li class="account-item" *ngFor="let student of filteredStudentList">
+        <div class="account-info">
+          <span class="account-id">ID: {{student.id}}</span>
+          <p class="account-name">{{student.firstname}} {{student.lastname}}</p>
         </div>
-        <button (click)="deleteStudent(student)">Delete</button>
+        <button class="delete-button" (click)="deleteStudent(student)">Delete</button>
       </li>
     </ul>
   </div>
-  <div>
-    <h4>Teacher accounts</h4>
-    <ul>
-      <li *ngFor="let teacher of teacherList">
-        <div>
-          <span>ID: {{teacher.id}}</span>
-          <p>{{teacher.firstname}} {{teacher.lastname}}</p>
+  
+  <div class="account-section">
+    <h4 class="section-title">Teacher accounts</h4>
+    <div class="search-controls">
+      <label class="search-label" for="teacher-search">Search by Name:</label>
+      <div class="search-input-group">
+        <input 
+          type="text" 
+          id="teacher-search" 
+          class="search-input"
+          [(ngModel)]="teacherSearchTerm" 
+          (input)="filterTeachers()" 
+          placeholder="Enter teacher name"
+        >
+        <button class="sort-button" (click)="sortTeachers()">
+          {{ teacherSortDirection === 'asc' ? '↑' : '↓' }}
+        </button>
+      </div>
+    </div>
+    <ul class="account-list">
+      <li class="account-item" *ngFor="let teacher of filteredTeacherList">
+        <div class="account-info">
+          <span class="account-id">ID: {{teacher.id}}</span>
+          <p class="account-name">{{teacher.firstname}} {{teacher.lastname}}</p>
         </div>
-        <button (click)="deleteTeacher(teacher)">Delete</button>
+        <button class="delete-button" (click)="deleteTeacher(teacher)">Delete</button>
       </li>
     </ul>
   </div>
-</div>
+</div>

+ 57 - 3
project/frontend-angular/src/app/admin/admin.ts

@@ -4,12 +4,14 @@ import {Student} from "../models/student";
 import {TeacherService} from "../services/teacher";
 import {StudentService} from "../services/student";
 import {NgForOf} from '@angular/common';
+import {FormsModule} from '@angular/forms';
 
 @Component({
   selector: 'app-admin',
   templateUrl: './admin.html',
   imports: [
-    NgForOf
+    NgForOf,
+    FormsModule
   ],
   styleUrls: ['./admin.css']
 })
@@ -17,6 +19,14 @@ export class Admin implements OnInit{
   teacherList?: Teacher[];
   studentList?: Student[];
 
+  studentSearchTerm: string = '';
+  filteredStudentList: Student[] = [];
+  studentSortDirection: 'asc' | 'desc' = 'asc';
+
+  teacherSearchTerm: string = '';
+  filteredTeacherList: Teacher[] = [];
+  teacherSortDirection: 'asc' | 'desc' = 'asc';
+
   constructor(private teacherService: TeacherService, private studentService: StudentService) { }
 
   ngOnInit() {
@@ -26,12 +36,56 @@ export class Admin implements OnInit{
 
   getTeachers(): void{
     this.teacherService.getTeachers()
-      .subscribe(teacherList => this.teacherList = teacherList);
+      .subscribe(teacherList => {
+        this.teacherList = teacherList;
+        this.filterTeachers();
+      });
   }
 
   getStudents(): void {
     this.studentService.getStudents()
-      .subscribe(studentList => this.studentList = studentList);
+      .subscribe(studentList => {
+        this.studentList = studentList;
+        this.filterStudents();
+      });
+  }
+
+  filterStudents(): void {
+    if (!this.studentList) {
+      this.filteredStudentList = [];
+      return;
+    }
+    let filtered = this.studentList.filter(student =>
+      (student.firstname + ' ' + student.lastname).toLowerCase().includes(this.studentSearchTerm.toLowerCase())
+    );
+    filtered = this.studentSortDirection === 'asc'
+      ? filtered.sort((a, b) => (a.firstname + a.lastname).localeCompare(b.firstname + b.lastname))
+      : filtered.sort((a, b) => (b.firstname + b.lastname).localeCompare(a.firstname + a.lastname));
+    this.filteredStudentList = filtered;
+  }
+
+  sortStudents(): void {
+    this.studentSortDirection = this.studentSortDirection === 'asc' ? 'desc' : 'asc';
+    this.filterStudents();
+  }
+
+  filterTeachers(): void {
+    if (!this.teacherList) {
+      this.filteredTeacherList = [];
+      return;
+    }
+    let filtered = this.teacherList.filter(teacher =>
+      (teacher.firstname + ' ' + teacher.lastname).toLowerCase().includes(this.teacherSearchTerm.toLowerCase())
+    );
+    filtered = this.teacherSortDirection === 'asc'
+      ? filtered.sort((a, b) => (a.firstname + a.lastname).localeCompare(b.firstname + b.lastname))
+      : filtered.sort((a, b) => (b.firstname + b.lastname).localeCompare(a.firstname + a.lastname));
+    this.filteredTeacherList = filtered;
+  }
+
+  sortTeachers(): void {
+    this.teacherSortDirection = this.teacherSortDirection === 'asc' ? 'desc' : 'asc';
+    this.filterTeachers();
   }
 
   deleteTeacher(teacher: Teacher): void {

+ 0 - 24
project/frontend-angular/src/app/grade-student/grade-student.css

@@ -1,21 +1,3 @@
-/* Better WIKAMP - Subjects List Interface CSS */
-
-/* CSS Reset & Base Styles */
-* {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
-}
-
-body {
-    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
-    background-color: #121212;
-    color: #ffffff;
-    line-height: 1.6;
-    min-height: 100vh;
-}
-
-/* Animation Keyframes */
 @keyframes fadeIn {
     from {
         opacity: 0;
@@ -27,7 +9,6 @@ body {
     }
 }
 
-/* Main Container */
 .subjects-container {
     max-width: 1200px;
     margin: 0 auto;
@@ -35,7 +16,6 @@ body {
     animation: fadeIn 0.6s ease-in;
 }
 
-/* Search Section */
 .search-section {
     background: rgba(255, 255, 255, 0.02);
     border: 1px solid rgba(255, 255, 255, 0.1);
@@ -80,7 +60,6 @@ body {
     color: #888;
 }
 
-/* Table Section */
 .table-section {
     background: rgba(255, 255, 255, 0.02);
     border: 1px solid rgba(255, 255, 255, 0.1);
@@ -149,7 +128,6 @@ body {
     color: #ffffff;
 }
 
-/* Grades Container */
 .grades-container {
     display: flex;
     flex-wrap: wrap;
@@ -176,7 +154,6 @@ body {
     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
 }
 
-/* Empty State */
 .empty-state {
     text-align: center;
     padding: 40px 20px;
@@ -185,7 +162,6 @@ body {
     font-style: italic;
 }
 
-/* Responsive Design */
 @media (max-width: 768px) {
     .subjects-container {
         padding: 20px 15px;