Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f57333f
Main screen reworked
May 11, 2026
efd249b
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
May 12, 2026
64c20cb
Built routing to data preview
May 12, 2026
38966a5
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
May 22, 2026
a1643e7
Data sent between views
May 22, 2026
15e9ffa
Run cleanup
May 22, 2026
391d9a6
Sort filter bar config
May 26, 2026
b472386
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
May 26, 2026
2047cf8
Create participant-import-service and add filter button
May 27, 2026
36004ec
Add participant-import-service, headbar to table, colors to table icons
May 29, 2026
c6eea21
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 9, 2026
4377152
Create viewParticipantImport wrapper class
Jun 9, 2026
9e7da3b
Filters and Search bar and work, and fully integrate wrapper class
Jun 10, 2026
5dcb2c0
Added CSV-related buttons to sort-filter-bar. Updated filters.
Jun 12, 2026
9903b03
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 12, 2026
9c50fd7
Built encoding option layout. Footer prototype
Jun 12, 2026
91dae4a
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 15, 2026
eca9b09
Footer button display. Added new filters. CSV-Reload button
Jun 15, 2026
25789f2
Advances on encoding options, filters and footer
Jun 16, 2026
f343c93
Added horizontal scroll to table
Jun 17, 2026
0d101c9
Better HScroll support in Chrome. Minor UI Fixes
Jun 17, 2026
2361b05
CSV-Options build. Import dialog prototype
Jun 18, 2026
2c5b73d
Added import option. Added Loading dialog. Summary dialog prototype
Jun 19, 2026
116e398
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 25, 2026
9b3efcf
Summary correctly displayed
Jun 25, 2026
aa0764d
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 30, 2026
1756142
Referenced users displayed on table view
Jun 30, 2026
f82fb3d
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jun 30, 2026
ebdfcd7
Referenced participants displayed in table, footer and added to filters
Jun 30, 2026
ec4402d
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 1, 2026
b0d8da1
Correctly displayed buttons on import error. Updated summary titles
Jul 1, 2026
c2f4836
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 2, 2026
ecb5a5f
Headers correctly displayed and buttons updated on import
Jul 2, 2026
0fb4ddb
Disabled import button when file contains errors
Jul 3, 2026
b3ebeae
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 3, 2026
74e1b21
Footer in darkmode, redirect after import
Jul 3, 2026
36b60b6
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 6, 2026
8d28b63
Scrollbars fixed. Sticky headers
Jul 6, 2026
1979cd1
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 7, 2026
90ba0aa
CSV-reload feature added
Jul 7, 2026
e06dfde
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 8, 2026
c963e57
Fix drag&drop. Header names ordered. Updated icon won't show without …
Jul 8, 2026
f682a20
Icon hide on unchanged state
Jul 9, 2026
78812f9
merge origin
Jul 9, 2026
40625a3
solve conflicts
Jul 9, 2026
b30df3f
Added icons to updated rows. Changed titles. Fixed minor UI issues
Jul 14, 2026
9b388a7
Updated to Angular 22
Jul 15, 2026
b6186e3
Solved conflicts
Jul 15, 2026
d1b4db9
Fixed minor dependency errors
Jul 15, 2026
b012418
Fixed transparent header on hover. Increased 'Email' column width
Jul 17, 2026
2dc19c7
Merge remote-tracking branch 'origin' into 5124-participant-import-re…
Jul 17, 2026
46cbeba
Fixed footer stepping on filter mat-card
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,7 @@ export abstract class BaseBackendImportService implements BackendImportService {
.catch(e => e);

if (typeof results !== `boolean`) {
this._currentImportPhaseSubject.next(BackendImportPhase.LOADING_PREVIEW);
this.clearAll();
this._currentImportPhaseSubject.next(BackendImportPhase.FINISHED);
this.matSnackbar.open(
this.translate.instant(results.error?.message ?? results?.message ?? results),
this.translate.instant(`Ok`)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<os-head-bar [goBack]="false" [nav]="false">
<div class="title-slot">
<h1 class="mock-h2" translate>Import participants</h1>
</div>
<ng-container class="menu-slot">
<button mat-button [disabled]="uploadButton" (click)="importData(ImportDialog, ImportDialogSummary)">
<span class="upper">Import</span>
</button>
</ng-container>
</os-head-bar>
<div class="static-sort-filter-bar">
<os-view-list
[csvConfiguration]="csvConfiguration"
[csvReload]="importer"
[csvReloadButton]="csvReloadButton"
[filterProps]="filterProps"
[filterService]="filterService"
[horizontalScroll]="horizontalScroll"
[listObservable]="dataSource"
[searchFieldInput]="searchFieldInput"
[searchService]="searchService"
[shortenedSummary]="shortenSummary(summary)"
[showFilterBar]="showFilterBar"
[showHeader]="showHeader"
[totalCount]="_totalCountObservable | async"
[vScrollFixed]="50"
(searchFilterUpdated)="searchFilterUpdated.emit($event)"
(selectNewFile)="getCsvReload($event)"
(selectedColSepOutput)="onColSepChanged($event)"
(selectedEncodingOutput)="onEncodingChanged($event)"
(selectedTextSeparatorOutput)="onTextSeparatorChanged($event)"
>
@if (dataSource | async) {
@for (column of previewColumns; track column) {
<div
*osScrollingTableCell="
column.property;
row as row;
definition as def;
isDefault: true;
config: getColumnConfig(column.property)
"
[class.error-icon]="containsError(row.data, def)"
>
@let entry = row.data;
@let type = column.type;
<div *osScrollingTableCellLabel>{{ getColumnLabel(column.property) }}</div>
@if (entry && (entry[def] || type === 'boolean')) {
<div class="flex-vertical-center">
@if (getHeader(def).is_list && entry[def]) {
@for (item of entry[def]; track item) {
@if (getHeader(def).is_object) {
<div>
@if (getHeader(def).type === 'string') {
{{ item.value }}
}
@if (getHeader(def).type === 'number') {
{{ item.value }}
}
@if (getHeader(def).type === 'date') {
{{ item.value }}
}
@if (getHeader(def).type === 'boolean') {
<mat-checkbox disabled [checked]="item.value" />
}
&nbsp;

@if (getActionIconRow(item)) {
<mat-icon class="lower-margin-top" color="accent" inline>
{{ getEntryIcon(item) }}
</mat-icon>
}
</div>
} @else {
@if (getHeader(def).type === 'string') {
{{ item.value }}
}
@if (getHeader(def).type === 'number') {
{{ item.value }}
}
@if (getHeader(def).type === 'decimal') {
{{ this.getShortenedDecimal(entry[def].value) }}
}
@if (getHeader(def).type === 'date') {
{{ item.value }}
}
@if (getHeader(def).type === 'boolean') {
<mat-checkbox disabled [checked]="item.value" />
}
&nbsp;
}
}
}
@if (!getHeader(def).is_list && entry[def] !== undefined) {
@if (getHeader(def).is_object) {
@let icon = getActionIconEntry(entry[def]);
<div class="cell-wrapper">
<div [class.email-cell]="getHeader(def).property === 'email'">
@if (getHeader(def).type === 'string') {
{{ entry[def].value }}
}
@if (getHeader(def).type === 'number') {
{{ entry[def].value }}
}
@if (getHeader(def).type === 'decimal') {
{{ this.getShortenedDecimal(entry[def].value) }}
}
@if (getHeader(def).type === 'date') {
{{ entry[def].value }}
}
@if (getHeader(def).type === 'boolean') {
<mat-checkbox disabled [checked]="entry[def].value" />
}
</div>
<div>
@if (getEntryIcon(entry[def]) || checkChanges(row, column.property)) {
<mat-icon
class="lower-margin-top"
inline
[class.error-icon]="icon === 'error_outline'"
[color]="icon !== 'error_outline' ? 'accent' : null"
>
{{ icon }}
</mat-icon>
}
</div>
</div>
}
}
</div>
}
</div>
}
<div
*osScrollingTableCell="'errors'; row as row; config: { width: 55, position: START_POSITION }"
class="flex-vertical-center icon-container"
>
<div *osScrollingTableCellLabel style="text-align: center; width: 100%">Action</div>
@if (row.state === 'error') {
<mat-icon class="red-warning-text" matTooltip="{{ getRowTooltip(row) }}" matTooltipPosition="right">
{{ getActionIconRow(row) }}
</mat-icon>
}
@if (row.state !== 'error' && row.messages.length) {
<mat-icon class="warn" matTooltip="{{ getWarningRowTooltip(row) }}" matTooltipPosition="right">
warning
</mat-icon>
}
@let icon = getActionIconRow(row);
@if (row.state !== 'error' && icon !== '') {
<mat-icon
matTooltip="{{ getRowTooltip(row) }}"
matTooltipPosition="right"
[ngClass]="getColorIcon(row)"
>
{{ icon }}
</mat-icon>
}
</div>
}
</os-view-list>
<!-- <footer class="footer">
@let cols = shortenSummary(summary);
@for (col of cols; track col; let i = $index) {
@let colInfo = getSummaryInformation(col.name);
<mat-icon inline style="margin-top: 5px; font-size: x-large" [ngClass]="colInfo[1]">
{{ colInfo[0] }}
</mat-icon>
<div style="margin-bottom: 8px">&nbsp;{{ col.value }}&nbsp;{{ col.name }}&nbsp; &nbsp;</div>
}
</footer> -->
</div>

<ng-template #ImportDialog>
<div class="import-dialog">
<h2 class="import-dialog-title" mat-dialog-title style="margin-bottom: 1em" translate>
Importing participants
</h2>

<mat-dialog-content class="import-dialog-content">
<mat-spinner color="accent" diameter="100" strokeWidth="8"></mat-spinner>
<div class="import-status">
<div class="import-status-title" translate>Import in progress!</div>
<div class="import-status-subtitle" translate>
Do not close the tab if you want to see the import status!
</div>
</div>
</mat-dialog-content>
</div>
</ng-template>

<ng-template #ImportDialogSummary>
<div class="summary-dialog">
<div cdk-focus-start class="summary-dialog-header">
<h2 class="summary-dialog-title" mat-dialog-title translate>Import successful</h2>
<button class="close-button" mat-dialog-close mat-icon-button translate>
<mat-icon>close</mat-icon>
</button>
</div>
<h4 class="summary-dialog-subtitle">{{ summary[0]?.value }}&nbsp;participants have been processed</h4>
<div style="justify-content: center; margin-bottom: 22px">
<mat-dialog-content class="summary-dialog-content">
<div>
@let shortenedSummary = shortenSummary(summary);
@for (row of shortenedSummary; track row) {
@let colInfo = getSummaryInformation(row.name);
<div class="summary-row">
<mat-icon inline [ngClass]="colInfo[1]">
{{ colInfo[0] }}
</mat-icon>
<div class="summary-text">{{ row.value }}&nbsp;{{ row.name }}</div>
</div>
}
</div>
</mat-dialog-content>
@let extraInformation = summaryRest(summary);
@for (row of extraInformation; track row) {
<div class="summary-dialog-extraInfo">{{ row.value }}&nbsp;{{ row.name }}</div>
}
</div>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
$os-green: rgb(15, 228, 15);
$os-yellow: rgb(255, 193, 7);

.os-green {
color: $os-green;
}

.os-yellow {
color: $os-yellow;
}

.icon-container {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
}

.error-icon {
color: red;
font-weight: 500;
}

.lower-margin-top {
margin-top: -4px;
padding-left: 2px;
}

.list-field-entry {
display: inline-flex;
margin-right: 10px;
}

.cell-wrapper {
display: flex;
align-items: center;
width: 100%;
max-width: 300px;
overflow: hidden;

.email-cell {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

.static-sort-filter-bar {
margin: 0;
height: 100%;
overflow: hidden;
}

.import-dialog {
padding: 8px;
color: var(--theme-accent-400);

.import-dialog-title {
color: var(--theme-accent-400);
font-size: 20px;
font-weight: 500;
margin-bottom: 0;
}

.import-dialog-content {
min-height: fit-content;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: var(--theme-accent-400);
text-align: center;
}

.import-status {
margin-top: 32px;
color: var(--theme-accent-400);
}

.import-status-title {
font-size: 23px;
font-weight: 400;
margin-bottom: 12px;
}

.import-status-subtitle {
font-size: 18px;
}
}

.summary-dialog {
.summary-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
}

.close-button {
margin-right: 0.5em;
margin-top: 0.5em;
}

.summary-dialog-title {
font-size: 20px;
font-weight: 500;
margin: 0;
}

.summary-dialog-content {
display: flex;
font-size: 17px;
color: var(--theme-contrast-400);
text-align: center;
justify-content: center;
}

.summary-row {
display: flex;
justify-content: start;
gap: 10px;
margin: 6px 0;
}

.summary-row mat-icon {
font-size: x-large;
}

.summary-dialog-subtitle {
font-size: 18px;
margin-left: 1.4em;
margin-top: 0;
}

.summary-dialog-extraInfo {
font-size: 17px;
margin-left: 1.4em;
}

.summary-dialog-extraInfo:last-child {
margin-bottom: 16px;
}
}
Loading
Loading