diff --git a/compose.dev.yml b/compose.dev.yml
index cb9433736b..ceb93c8369 100644
--- a/compose.dev.yml
+++ b/compose.dev.yml
@@ -48,6 +48,10 @@ services:
- /app/node_modules
- ./services/collaboration:/app
+ collaboration-db:
+ ports:
+ - 27020:27017
+
history:
command: npm run dev
ports:
diff --git a/frontend/src/_services/history.service.ts b/frontend/src/_services/history.service.ts
index 3ee0240ca3..41b5a71930 100644
--- a/frontend/src/_services/history.service.ts
+++ b/frontend/src/_services/history.service.ts
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
-import { historyResponse, MatchingHistory } from '../app/account/history/history.model';
+import { historyResponse, MatchingHistory } from '../app/history/history.model';
import { ApiService } from './api.service';
@Injectable({
@@ -22,7 +22,8 @@ export class HistoryService extends ApiService {
id: item._id,
roomId: item.roomId,
collaborator: item.collaborator.username,
- question: item.question,
+ title: item.question.title,
+ description: item.question.description,
topics: item.question.topics,
difficulty: item.question.difficulty,
status: item.status,
diff --git a/frontend/src/app/account/account.component.ts b/frontend/src/app/account/account.component.ts
index aa1bc14b19..a2f6e4c0f1 100644
--- a/frontend/src/app/account/account.component.ts
+++ b/frontend/src/app/account/account.component.ts
@@ -5,7 +5,6 @@ import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { LayoutComponent } from './layout.component';
import { ProfileComponent } from './profile/profile.component';
-import { HistoryComponent } from './history/history.component';
const routes: Routes = [
{
@@ -16,7 +15,6 @@ const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'profile', component: ProfileComponent },
- { path: 'history', component: HistoryComponent },
],
},
];
diff --git a/frontend/src/app/account/account.module.ts b/frontend/src/app/account/account.module.ts
index 10b87cfb34..33e7cb9efa 100644
--- a/frontend/src/app/account/account.module.ts
+++ b/frontend/src/app/account/account.module.ts
@@ -7,7 +7,6 @@ import { RegisterComponent } from './register/register.component';
import { LayoutComponent } from './layout.component';
import { AccountRoutingModule } from './account.component';
import { ProfileComponent } from './profile/profile.component';
-import { HistoryComponent } from './history/history.component';
@NgModule({
imports: [
@@ -18,7 +17,6 @@ import { HistoryComponent } from './history/history.component';
LoginComponent,
RegisterComponent,
ProfileComponent,
- HistoryComponent,
],
})
export class AccountModule {}
diff --git a/frontend/src/app/account/history/history.component.html b/frontend/src/app/account/history/history.component.html
deleted file mode 100644
index ed1d597ccf..0000000000
--- a/frontend/src/app/account/history/history.component.html
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
Matching History
-
-
-
-
-
-
-
-
- Question
- |
-
- Difficulty
- |
- Topics |
-
- Collaborator
- |
- Status |
- Time |
-
-
-
-
- {{ history.question.title }} |
- {{ history.difficulty }} |
- {{ history.topics.join(', ') }} |
- {{ history.collaborator }} |
-
- @if (history.status === 'COMPLETED') {
-
- } @else if (history.status === 'FORFEITED') {
-
- } @else if (history.status === 'IN_PROGRESS') {
-
- }
- |
- {{ history.time }} |
-
-
-
-
-
-
-
-
{{ panelHistory?.question?.description }}
-
-
-
-
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 59b77f39aa..2ce0db61b0 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -5,6 +5,7 @@ import { MatchingComponent } from './matching/matching.component';
import { HomeComponent } from './home/home.component';
import { AuthGuardService } from '../_services/auth.guard.service';
import { CollabGuardService } from '../_services/collab.guard.service';
+import { HistoryComponent } from './history/history.component';
const accountModule = () => import('./account/account.module').then(x => x.AccountModule);
@@ -33,6 +34,11 @@ export const routes: Routes = [
component: HomeComponent,
canActivate: [AuthGuardService],
},
+ {
+ path: 'history',
+ component: HistoryComponent,
+ canActivate: [AuthGuardService],
+ },
{
path: '**',
redirectTo: '/home',
diff --git a/frontend/src/app/account/history/history.component.css b/frontend/src/app/history/history.component.css
similarity index 82%
rename from frontend/src/app/account/history/history.component.css
rename to frontend/src/app/history/history.component.css
index 2053d5c2d0..155f32abb6 100644
--- a/frontend/src/app/account/history/history.component.css
+++ b/frontend/src/app/history/history.component.css
@@ -1,3 +1,12 @@
+.container {
+ min-height: calc(100dvh - 160px);
+ width: 100%;
+ justify-content: center;
+ align-items: center;
+ padding: 1rem;
+ margin-top: auto;
+}
+
.sliding-panel {
position: fixed;
top: 0;
@@ -9,6 +18,7 @@
box-shadow: -2px 0 5px rgba(0,0,0,0.5);
transition: right 0.3s ease;
z-index: 1000;
+ max-width: 90%;
}
.sliding-panel.open {
diff --git a/frontend/src/app/history/history.component.html b/frontend/src/app/history/history.component.html
new file mode 100644
index 0000000000..050c8801c4
--- /dev/null
+++ b/frontend/src/app/history/history.component.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
Matching History
+
+
+
+
+
+
+
+
+
+
+ Question |
+
+ Difficulty
+ |
+ Topics |
+
+ Collaborator
+ |
+ Status |
+ Time |
+
+
+
+
+ {{ history.title }} |
+ {{ history.difficulty }} |
+ {{ history.topics.join(', ') }} |
+ {{ history.collaborator }} |
+
+ @if (history.status === 'COMPLETED') {
+
+ } @else if (history.status === 'FORFEITED') {
+
+ } @else if (history.status === 'IN_PROGRESS') {
+
+ }
+ |
+ {{ history.time | date: 'dd/MM/yyyy hh:mm a' }} |
+
+
+
+
+
+
+ {{ panelHistory?.title }}
+
+
+
{{ panelHistory?.description }}
+
Submitted Solution
+
+
+
+
+
diff --git a/frontend/src/app/account/history/history.component.ts b/frontend/src/app/history/history.component.ts
similarity index 53%
rename from frontend/src/app/account/history/history.component.ts
rename to frontend/src/app/history/history.component.ts
index 728a0b8782..f6d10f75aa 100644
--- a/frontend/src/app/account/history/history.component.ts
+++ b/frontend/src/app/history/history.component.ts
@@ -1,50 +1,61 @@
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
-import { TableModule } from 'primeng/table';
+import { Table, TableModule } from 'primeng/table';
import { CommonModule, DatePipe } from '@angular/common';
import { HistoryStatus, MatchingHistory } from './history.model';
-import { HistoryService } from '../../../_services/history.service';
-import { MessageService } from 'primeng/api';
+import { HistoryService } from '../../_services/history.service';
+import { MessageService, SortEvent } from 'primeng/api';
import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
import { oneDark } from '@codemirror/theme-one-dark';
-import { EditorState } from '@codemirror/state';
+import { EditorState, Extension } from '@codemirror/state';
import { EditorView, basicSetup } from 'codemirror';
-import { languageMap } from '../../collaboration/editor/languages';
+import { languageMap } from '../collaboration/editor/languages';
import { ToastModule } from 'primeng/toast';
+import { SidebarModule } from 'primeng/sidebar';
import { Router } from '@angular/router';
@Component({
standalone: true,
- imports: [TableModule, CommonModule, InputTextModule, ButtonModule, IconFieldModule, InputIconModule, ToastModule],
+ imports: [
+ SidebarModule,
+ TableModule,
+ CommonModule,
+ InputTextModule,
+ ButtonModule,
+ IconFieldModule,
+ InputIconModule,
+ ToastModule,
+ ],
providers: [MessageService, DatePipe],
templateUrl: './history.component.html',
styleUrl: './history.component.css',
})
export class HistoryComponent implements OnInit {
@ViewChild('editor') editor!: ElementRef;
+ @ViewChild('dt') dt!: Table;
histories: MatchingHistory[] = [];
+ initialValue: MatchingHistory[] = [];
loading = true;
isPanelVisible = false;
panelHistory: MatchingHistory | null = null;
editorView: EditorView | null = null;
+ customTheme!: Extension;
+ isSorted: null | undefined | boolean;
constructor(
private historyService: HistoryService,
private messageService: MessageService,
- private datePipe: DatePipe,
private router: Router,
) {}
ngOnInit() {
this.historyService.getHistories().subscribe({
next: data => {
- this.histories = data.map(history => ({
- ...history,
- time: this.datePipe.transform(history.time, 'short'), // Pipe to format date for searching
- }));
+ this.histories = data;
+ this.initialValue = [...data];
this.loading = false;
},
error: () => {
@@ -60,6 +71,33 @@ export class HistoryComponent implements OnInit {
});
}
+ customSort(event: Required) {
+ event.data?.sort((data1, data2) => {
+ const value1 = data1[event.field];
+ const value2 = data2[event.field];
+ let result = 0;
+
+ // Null checks
+ if (value1 === null && value2 !== null) {
+ result = -1;
+ } else if (value1 !== null && value2 === null) {
+ result = 1;
+ } else if (value1 === null && value2 === null) {
+ result = 0;
+ } else if (event.field == 'time') {
+ result = new Date(value1) >= new Date(value2) ? 1 : -1;
+ } else if (typeof value1 === 'string' && typeof value2 === 'string') {
+ // String comparison
+ result = value1.localeCompare(value2);
+ } else {
+ // Generic comparison for numbers and other types
+ result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;
+ }
+
+ return event.order * result;
+ });
+ }
+
onRowSelect(history: MatchingHistory) {
this.panelHistory = history;
if (history.status != HistoryStatus.IN_PROGRESS) {
@@ -80,10 +118,29 @@ export class HistoryComponent implements OnInit {
this.editorView.destroy();
}
+ const customTheme = EditorView.theme(
+ {
+ '&': {
+ backgroundColor: 'var(--surface-section)',
+ },
+ '.cm-gutters': {
+ backgroundColor: 'var(--surface-section)',
+ },
+ },
+ { dark: true },
+ );
+
const languageExtension = languageMap[language] || languageMap['java'];
const state = EditorState.create({
doc: code,
- extensions: [basicSetup, languageExtension, oneDark, EditorView.editable.of(false)],
+ extensions: [
+ basicSetup,
+ languageExtension,
+ customTheme,
+ oneDark,
+ EditorView.lineWrapping,
+ EditorView.editable.of(false),
+ ],
});
this.editorView = new EditorView({
diff --git a/frontend/src/app/account/history/history.model.ts b/frontend/src/app/history/history.model.ts
similarity index 91%
rename from frontend/src/app/account/history/history.model.ts
rename to frontend/src/app/history/history.model.ts
index e0972b1411..a10893323e 100644
--- a/frontend/src/app/account/history/history.model.ts
+++ b/frontend/src/app/history/history.model.ts
@@ -1,4 +1,4 @@
-import { DifficultyLevels } from '../../questions/difficulty-levels.enum';
+import { DifficultyLevels } from '../questions/difficulty-levels.enum';
export enum HistoryStatus {
COMPLETED = 'COMPLETED',
@@ -10,7 +10,8 @@ export interface MatchingHistory {
id: string;
roomId: string;
collaborator: string; // collaborator username
- question: Question; // question
+ title: string;
+ description: string;
difficulty: DifficultyLevels; // question difficulty
topics: string[]; // question topics
status: HistoryStatus; // status of the session
diff --git a/frontend/src/app/navigation-bar/navigation-bar.component.ts b/frontend/src/app/navigation-bar/navigation-bar.component.ts
index 1bf5fb0bce..774cfd5aab 100644
--- a/frontend/src/app/navigation-bar/navigation-bar.component.ts
+++ b/frontend/src/app/navigation-bar/navigation-bar.component.ts
@@ -71,7 +71,7 @@ export class NavigationBarComponent implements OnInit {
{
label: 'Match History',
icon: 'pi pi-trophy',
- routerLink: '/account/history',
+ routerLink: '/history',
class: 'p-submenu-list',
},
{