mirror of
https://github.com/mempool/mempool.git
synced 2025-09-27 20:47:01 +02:00
cubo widget tweaks
This commit is contained in:
@@ -334,6 +334,7 @@
|
|||||||
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
<fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
<app-simpleproof-cubo-widget [label]="widget.props.label" [key]="widget.props.key" [widget]="true"></app-simpleproof-cubo-widget>
|
<app-simpleproof-cubo-widget [label]="widget.props.label" [key]="widget.props.key" [widget]="true"></app-simpleproof-cubo-widget>
|
||||||
|
<div style="margin-top: -0.6rem;"><a [routerLink]="['/sp/cubo' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -29,10 +29,22 @@
|
|||||||
i18n-placeholder="simpleproof.search_placeholder"
|
i18n-placeholder="simpleproof.search_placeholder"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-borderless" [class.table-fixed]="widget">
|
<table class="table table-borderless table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<th class="filename text-left" [ngClass]="{'widget': widget}" i18n="simpleproof.student_name">Student Name</th>
|
<th class="student text-left sortable" [ngClass]="{'widget': widget}" (click)="$event.stopPropagation(); sortBy('student_name')" style="cursor: pointer;">
|
||||||
<th class="id text-left" [ngClass]="{'widget': widget}" i18n="simpleproof.id_code">ID</th>
|
<span i18n="simpleproof.student_name">Student Name</span>
|
||||||
|
<div class="sort-icons ml-1">
|
||||||
|
<fa-icon [icon]="['fas', 'caret-up']" [fixedWidth]="true" [class.active]="sortField === 'student_name' && sortDirection === 'asc'" (click)="$event.stopPropagation(); sortBy('student_name', 'asc')"></fa-icon>
|
||||||
|
<fa-icon [icon]="['fas', 'caret-down']" [fixedWidth]="true" [class.active]="sortField === 'student_name' && sortDirection === 'desc'" (click)="$event.stopPropagation(); sortBy('student_name', 'desc')"></fa-icon>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="id text-left sortable" [ngClass]="{'widget': widget}" (click)="$event.stopPropagation(); sortBy('id_code')" style="cursor: pointer;">
|
||||||
|
<span i18n="simpleproof.id_code">ID</span>
|
||||||
|
<div class="sort-icons ml-1">
|
||||||
|
<fa-icon [icon]="['fas', 'caret-up']" [fixedWidth]="true" [class.active]="sortField === 'id_code' && sortDirection === 'asc'" (click)="$event.stopPropagation(); sortBy('id_code', 'asc')"></fa-icon>
|
||||||
|
<fa-icon [icon]="['fas', 'caret-down']" [fixedWidth]="true" [class.active]="sortField === 'id_code' && sortDirection === 'desc'" (click)="$event.stopPropagation(); sortBy('id_code', 'desc')"></fa-icon>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
<th class="proof text-right" [ngClass]="{'widget': widget}" i18n="simpleproof.proof">Proof</th>
|
<th class="proof text-right" [ngClass]="{'widget': widget}" i18n="simpleproof.proof">Proof</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="verifiedPage; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
<tbody *ngIf="verifiedPage; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
|
||||||
@@ -50,7 +62,21 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let item of verifiedPage">
|
<tr *ngFor="let item of verifiedPage">
|
||||||
<td class="filename text-left" [class]="widget ? 'widget' : ''">{{ item.student_name }}</td>
|
@if (widget) {
|
||||||
|
<td class="student text-left" [class]="widget ? 'widget' : ''">
|
||||||
|
@if (item.sanitized_download_url) {
|
||||||
|
<a [href]="item.sanitized_download_url" target="_blank">
|
||||||
|
<span>{{ item.student_name }}</span>
|
||||||
|
<span class="icon ml-2">
|
||||||
|
<fa-icon [icon]="['fas', 'file-pdf']" [fixedWidth]="true"></fa-icon>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
} @else {
|
||||||
|
{{ item.student_name }}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
} @else {
|
||||||
|
<td class="student text-left" [class]="widget ? 'widget' : ''">{{ item.student_name }}</td>
|
||||||
<td class="id text-left" [class]="widget ? 'widget' : ''">
|
<td class="id text-left" [class]="widget ? 'widget' : ''">
|
||||||
@if (item.sanitized_download_url) {
|
@if (item.sanitized_download_url) {
|
||||||
<a [href]="item.sanitized_download_url" target="_blank">
|
<a [href]="item.sanitized_download_url" target="_blank">
|
||||||
@@ -63,6 +89,7 @@
|
|||||||
{{ item.id_code }}
|
{{ item.id_code }}
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
|
}
|
||||||
<td class="proof text-right" [class]="widget ? 'widget' : ''">
|
<td class="proof text-right" [class]="widget ? 'widget' : ''">
|
||||||
<a [href]="item.sanitized_simpleproof_url" target="_blank" class="badge badge-primary badge-verify" style="font-size: 1.035em;">
|
<a [href]="item.sanitized_simpleproof_url" target="_blank" class="badge badge-primary badge-verify" style="font-size: 1.035em;">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
|
@@ -13,6 +13,9 @@ export interface SimpleProofCubo {
|
|||||||
parsed?: { type: string; year: number; studentNumber: number };
|
parsed?: { type: string; year: number; studentNumber: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SortField = 'student_name' | 'id_code' | null;
|
||||||
|
export type SortDirection = 'asc' | 'desc';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-simpleproof-cubo-widget',
|
selector: 'app-simpleproof-cubo-widget',
|
||||||
templateUrl: './simpleproof-cubo-widget.component.html',
|
templateUrl: './simpleproof-cubo-widget.component.html',
|
||||||
@@ -35,6 +38,8 @@ export class SimpleProofCuboWidgetComponent implements OnChanges {
|
|||||||
lastPage = 1;
|
lastPage = 1;
|
||||||
itemsPerPage = 15;
|
itemsPerPage = 15;
|
||||||
paginationMaxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
paginationMaxSize = window.innerWidth <= 767.98 ? 3 : 5;
|
||||||
|
sortField: SortField = null;
|
||||||
|
sortDirection: SortDirection = 'asc';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private servicesApiService: ServicesApiServices,
|
private servicesApiService: ServicesApiServices,
|
||||||
@@ -122,6 +127,11 @@ export class SimpleProofCuboWidgetComponent implements OnChanges {
|
|||||||
} else {
|
} else {
|
||||||
this.filteredVerified = this.verified;
|
this.filteredVerified = this.verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.sortField) {
|
||||||
|
this.applySort();
|
||||||
|
}
|
||||||
|
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.updatePage();
|
this.updatePage();
|
||||||
}
|
}
|
||||||
@@ -134,4 +144,56 @@ export class SimpleProofCuboWidgetComponent implements OnChanges {
|
|||||||
this.page = page;
|
this.page = page;
|
||||||
this.updatePage();
|
this.updatePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortBy(field: SortField, direction?: SortDirection): boolean {
|
||||||
|
if (field && direction) {
|
||||||
|
this.sortField = field;
|
||||||
|
this.sortDirection = direction;
|
||||||
|
} else {
|
||||||
|
if (this.sortField === field) {
|
||||||
|
if (this.sortDirection === 'asc') {
|
||||||
|
this.sortDirection = 'desc';
|
||||||
|
} else {
|
||||||
|
this.sortField = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.sortField = field;
|
||||||
|
this.sortDirection = 'asc';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applySort();
|
||||||
|
this.page = 1;
|
||||||
|
this.updatePage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
applySort(): void {
|
||||||
|
// default to ascending sort by id
|
||||||
|
const sortByField = this.sortField || 'id_code';
|
||||||
|
const sortDirection = this.sortField ? this.sortDirection : 'asc';
|
||||||
|
|
||||||
|
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
||||||
|
|
||||||
|
this.filteredVerified.sort((a, b) => {
|
||||||
|
let comparison = 0;
|
||||||
|
if (sortByField === 'student_name') {
|
||||||
|
comparison = collator.compare(a.student_name, b.student_name);
|
||||||
|
} else if (sortByField === 'id_code') {
|
||||||
|
// For ID sorting, try to use the parsed Cubo key logic first
|
||||||
|
if (a.parsed && b.parsed) {
|
||||||
|
if (a.parsed.year !== b.parsed.year) {
|
||||||
|
comparison = a.parsed.year - b.parsed.year;
|
||||||
|
} else if (a.parsed.type !== b.parsed.type) {
|
||||||
|
comparison = a.parsed.type.localeCompare(b.parsed.type);
|
||||||
|
} else {
|
||||||
|
comparison = a.parsed.studentNumber - b.parsed.studentNumber;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
comparison = collator.compare(a.id_code, b.id_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sortDirection === 'asc' ? comparison : -comparison;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,14 @@ tr, td, th {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.student {
|
||||||
|
width: 50%;
|
||||||
|
max-width: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.hash {
|
.hash {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
@@ -70,9 +78,7 @@ tr, td, th {
|
|||||||
td.hash {
|
td.hash {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
.widget .hash {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.hash {
|
.hash {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -93,10 +99,52 @@ td.verified {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.proof {
|
.proof {
|
||||||
width: 25%;
|
width: 120px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.widget {
|
||||||
|
&.id, &.hash {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.student {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&.proof {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable {
|
||||||
|
user-select: none;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icons {
|
||||||
|
display: inline-flex;
|
||||||
|
line-height: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 1em;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
fa-icon {
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
margin: -0.25em 0;
|
||||||
|
pointer-events: all;
|
||||||
|
color: var(--secondary);
|
||||||
|
&.active {
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.badge-verify {
|
.badge-verify {
|
||||||
font-size: 1.05em;
|
font-size: 1.05em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
Reference in New Issue
Block a user