Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f6b1896
Changed method of rendering table with versions to toggle dropdown
jr-rk Sep 18, 2025
640a076
Refactor by Copilot's suggestions
jr-rk Sep 22, 2025
fa5214e
Added version history as item-field
jr-rk Sep 23, 2025
6796924
Divided Admin view and Anonymous/User view of version history and cha…
jr-rk Sep 24, 2025
28b2caf
Added clarin-item-versions-field to publication template, added spaci…
jr-rk Sep 24, 2025
f887127
Refactoring
jr-rk Sep 24, 2025
8522e4e
Refactoring vol.2
jr-rk Sep 24, 2025
13b0367
Added keyboard event handling (enter)
jr-rk Sep 25, 2025
d90f75d
Edit of manual Observable creation
jr-rk Sep 25, 2025
523ca91
clarin-item-versions-field extends item-versions to avoid duplicate code
jr-rk Sep 25, 2025
b93139f
reverted new admin view of item-versions history to usual look
jr-rk Sep 25, 2025
ba781a5
Usage of more common way for checking if is Admin
jr-rk Sep 25, 2025
a4acf79
removed duplicate interface
jr-rk Sep 25, 2025
e7d02ac
change to duplicate interfaces, but avoiding any type
jr-rk Sep 25, 2025
6a594e7
implementation of more effective method concating strings
jr-rk Sep 25, 2025
a0d031b
Added documentation for the need of protected access modifiers
jr-rk Sep 25, 2025
16370c8
Fixed documentation to valid JSDoc tag, prevention for not initializi…
jr-rk Sep 26, 2025
3451178
Changed isAdmin warpper to section
jr-rk Sep 26, 2025
b38331f
added calc() to scss
jr-rk Sep 26, 2025
59e8ed8
Fix of uninitialized isAdmin handling
jr-rk Sep 26, 2025
9797d0a
Optimization of hasDraftVersion logic - exposing it as an observable
jr-rk Sep 26, 2025
b293024
Fixed logic checking value of this.hasDraftVersion$
jr-rk Sep 26, 2025
cf44845
Reverted css, removed unclear typing from new methods
jr-rk Sep 26, 2025
3a47814
Fixed versionItem type handling
jr-rk Sep 26, 2025
a23f37b
Changed expand/collapse text to translation keys
jr-rk Nov 6, 2025
c33458f
fix: avoid array mutation by creating copy before reversing version list
jr-rk Nov 6, 2025
cfe6241
perf: optimize version history by pre-computing workspace/workflow ID…
jr-rk Nov 6, 2025
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
2 changes: 2 additions & 0 deletions src/app/item-page/item-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { NgChartsModule } from 'ng2-charts';
import { ClarinGenericItemFieldComponent } from './simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component';
import { ClarinCollectionsItemFieldComponent } from './simple/field-components/clarin-collections-item-field/clarin-collections-item-field.component';
import { ClarinFilesItemFieldComponent } from './simple/field-components/clarin-files-item-field/clarin-files-item-field.component';
import { ClarinItemVersionsFieldComponent } from './simple/field-components/clarin-item-versions-field/clarin-item-versions-field.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import {PreviewSectionComponent} from './simple/field-components/preview-section/preview-section.component';
import {
Expand Down Expand Up @@ -147,6 +148,7 @@ const DECLARATIONS = [
ClarinGenericItemFieldComponent,
ClarinCollectionsItemFieldComponent,
ClarinFilesItemFieldComponent,
ClarinItemVersionsFieldComponent,
ClarinSponsorItemFieldComponent,
PreviewSectionComponent,
FileDescriptionComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<div class="row clarin-item-page-field justify-content-start" *ngIf="showMetadataValue | async">
<div class="col-lg-3 col-2-5 d-flex align-self-start">
<div><i [class]="'fas ' + iconName + ' fa-xs'"></i></div>
<!-- Toggle Header -->
<div
class="pl-1 d-flex align-items-center"
(click)="toggleVersionHistory()"
(keydown.enter)="toggleVersionHistory()"
role="button"
tabindex="0"
[attr.aria-expanded]="showVersionHistory"
aria-controls="version-history-content"
[attr.aria-label]="getToggleAriaLabel()">
<b class="mr-1">{{ "item.version.history.head" | translate }}</b>
<i class="fas" [ngClass]="showVersionHistory ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
</div>
</div>

<!-- Collapsible Content -->
<div
id="version-history-content"
class="col-lg-9 collapse"
[ngClass]="{'show': showVersionHistory}"
[attr.aria-hidden]="!showVersionHistory">
<div *ngIf="enhancedVersions$ | async as enhancedVersions">
<ul class="list-unstyled dropdown-versions">
<li *ngFor="let enhancedVersion of enhancedVersions; trackBy: trackByVersionId"
[id]="'version-row-' + enhancedVersion.version.id">
<ng-container *ngIf="(enhancedVersion.versionItem$ | async)?.payload as versionItem">
<ng-container *ngIf="(enhancedVersion.workspaceId$ | async) || (enhancedVersion.workflowId$ | async); else itemNameWithLink">
{{ getVersionItemDisplayName(versionItem) }}
</ng-container>
<ng-template #itemNameWithLink>
<a [routerLink]="getVersionRoute(enhancedVersion.version.id)">
{{ getVersionItemDisplayName(versionItem) }}
</a>
</ng-template>
<span *ngIf="enhancedVersion.isCurrentVersion">*</span>
</ng-container>
</li>
</ul>
<div>*&nbsp;{{ "item.version.history.selected" | translate }}</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@import '../../item-page.component.scss';

@media (min-width: 992px) {
.col-2-5 {
flex: 0 0 20%;
max-width: 20%;
}
}

.dropdown-versions {
background-color: #f8f9fa;
border-radius: 4px;
padding: 2%;

max-height: calc(var(--ds-dso-selector-list-max-height) / 2);
overflow-y: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { Component, Input, OnInit } from '@angular/core';
import { Observable, of, combineLatest } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { ItemVersionsComponent } from '../../../versions/item-versions.component';
import { Item } from '../../../../core/shared/item.model';
import { Version } from '../../../../core/shared/version.model';
import { RemoteData } from '../../../../core/data/remote-data';

/**
* Local type definition matching the parent component's VersionsDTO structure
*/
interface VersionsDTO {
totalElements: number;
versionDTOs: VersionDTO[];
}

interface VersionDTO {
version: Version;
canEditVersion: Observable<boolean>;
canDeleteVersion: Observable<boolean>;
}

/**
* Enhanced VersionDTO with pre-computed workspace/workflow IDs for template optimization
*/
interface EnhancedVersionDTO extends VersionDTO {
versionItem$: Observable<RemoteData<Item>>;
workspaceId$: Observable<string | undefined>;
workflowId$: Observable<string | undefined>;
isCurrentVersion: boolean;
}

/**
* Clarin-specific field component for User/Anonymous view of item version history that extends ItemVersionsComponent
*/
@Component({
selector: 'ds-clarin-item-versions-field',
templateUrl: './clarin-item-versions-field.component.html',
styleUrls: ['./clarin-item-versions-field.component.scss']
})
export class ClarinItemVersionsFieldComponent extends ItemVersionsComponent implements OnInit {

/**
* Icon name for the clarin field
*/
@Input() iconName?: string;

/**
* Toggle state for version history display
*/
showVersionHistory = false;

/**
* Observable to check if metadata field should be shown - clarin-specific implementation
* Returns true if there are multiple versions to display
*/
showMetadataValue: Observable<boolean>;

/**
* Enhanced versions with pre-computed workspace/workflow IDs
*/
enhancedVersions$: Observable<EnhancedVersionDTO[]>;

ngOnInit(): void {
// Call parent's ngOnInit first to set up all the observables
super.ngOnInit();

// Set up clarin-specific showMetadataValue logic
if (this.versionsDTO$) {
this.showMetadataValue = this.versionsDTO$.pipe(
map((versionsDTO: VersionsDTO) => versionsDTO && versionsDTO.totalElements > 1)
);

// Pre-compute workspace/workflow IDs to optimize template performance
this.enhancedVersions$ = combineLatest([
this.versionsDTO$,
this.versionRD$
]).pipe(
map(([versionsDTO, versionRD]) => {
const currentVersionId = versionRD?.payload?.id;
return versionsDTO.versionDTOs.map(versionDTO => {
const versionItem$ = versionDTO.version.item;
const workspaceId$ = (this.hasDraftVersion$ ?? of(false)).pipe(
switchMap(hasDraftVersion =>
hasDraftVersion ? this.getWorkspaceId(versionItem$) : of(undefined)
)
);
const workflowId$ = workspaceId$.pipe(
switchMap((workspaceId) =>
workspaceId ? of(undefined) : this.getWorkflowId(versionItem$)
)
);
return {
...versionDTO,
versionItem$,
workspaceId$,
workflowId$,
isCurrentVersion: versionDTO.version.id === currentVersionId
} as EnhancedVersionDTO;
});
})
);
} else {
// Fallback: check if isAdmin$ is available, otherwise hide the component
this.showMetadataValue = this.isAdmin$ ? this.isAdmin$ : of(false);
}
}

/**
* Toggle the visibility of version history
*/
toggleVersionHistory(): void {
this.showVersionHistory = !this.showVersionHistory;
}

/**
* Get the display name for a version item
* @param versionItem the item to get the name for
*/
getVersionItemDisplayName(versionItem: Item): string {
return versionItem?.firstMetadataValue('dc.title') || versionItem?.name || 'Untitled';
}

/**
* Get the appropriate aria-label for the toggle button
* @returns The aria-label text for accessibility
*/
getToggleAriaLabel(): string {
const action = this.showVersionHistory
? this.translateService.instant('item.version.history.collapse')
: this.translateService.instant('item.version.history.expand');
const history = this.translateService.instant('item.version.history.label');
return `${action} ${history}`;
}

/**
* Get workspace ID for a version item if there's a draft version, otherwise return undefined
* This method optimizes the template logic by pre-computing the conditional check
* @param versionItem the version item's observable
*/
getVersionWorkspaceId(versionItem: Observable<Item>): Observable<string | undefined> {
return (this.hasDraftVersion$ ?? of(false)).pipe(
switchMap(hasDraftVersion =>
hasDraftVersion ? this.getWorkspaceId(versionItem) : of(undefined)
)
);
}

/**
* Get workflow ID for a version item if workspace ID is not available
* This method optimizes the template logic by handling the conditional workflow ID logic
* @param versionItem the version item's observable
* @param workspaceId$ the workspace ID observable
*/
getVersionWorkflowId(versionItem: Observable<Item>, workspaceId$: Observable<string | undefined>): Observable<string | undefined> {
return workspaceId$.pipe(
switchMap((workspaceId) =>
workspaceId ? of(undefined) : this.getWorkflowId(versionItem)
)
);
}

/**
* TrackBy function for version list to optimize *ngFor performance
* @param index the index of the item
* @param versionDTO the version DTO to track
*/
trackByVersionId(index: number, versionDTO: EnhancedVersionDTO): string {
return versionDTO.version.id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
[iconName]="'fa-sitemap'"
[separator]="'<br />'">
</ds-clarin-collections-item-field>
<ds-clarin-item-versions-field
[item]="object"
[iconName]="'fa-history'">
</ds-clarin-item-versions-field>
<div class="clarin-item-page-field">
<a [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@
[iconName]="'fa-sitemap'"
[separator]="'<br />'">
</ds-clarin-collections-item-field>
<ds-clarin-item-versions-field
[item]="object"
[iconName]="'fa-history'">
</ds-clarin-item-versions-field>
<div class="clarin-item-page-field">
<a [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}}
Expand Down
Loading
Loading