Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
609005c
feat: add dot-UVE portlet library with shell component and project co…
rjvelazco Sep 28, 2025
7e778e3
feat: integrate dot-uve portlet into routing and update configuration
rjvelazco Sep 28, 2025
3541544
fix: route
rjvelazco Sep 28, 2025
d20a504
feat: enhance dot-uve shell with navigation bar and styling
rjvelazco Sep 28, 2025
6ba6d4c
feat: add dot-uve-content component and toolbar to UVE portlet
rjvelazco Sep 28, 2025
848e37c
feat: implement UVE utility functions and state management store
rjvelazco Sep 28, 2025
8185649
feat: add language selector component to dot-uve for improved localiz…
rjvelazco Sep 28, 2025
8c2b0ae
feat: integrate workflow actions component into dot-uve toolbar for e…
rjvelazco Sep 29, 2025
c80028b
feat: enhance dot-uve workflow actions component with improved error …
rjvelazco Sep 29, 2025
444184c
feat: add persona selector component to dot-uve for enhanced user exp…
rjvelazco Sep 29, 2025
2773fba
feat: improve persona selector component readability
rjvelazco Sep 29, 2025
b589f99
feat: add utility functions and enhance dot-uve toolbar with copy URL…
rjvelazco Sep 29, 2025
452d2d0
feat: implement dot-uve bookmark component and integrate it into the …
rjvelazco Sep 29, 2025
e9fb0d3
feat: update dot-uve toolbar with new bookmark functionality for impr…
rjvelazco Sep 30, 2025
d8b82d4
feat: refactor dot-uve mode selector component with new template and …
rjvelazco Oct 2, 2025
344d3d0
feat: add dot-uve device selector component with template and styles,…
rjvelazco Oct 2, 2025
d35f7cb
feat: refactor dot-uve device selector component to streamline menu i…
rjvelazco Oct 2, 2025
bce8d2c
feat: enhance dot-uve toolbar with calendar component for date select…
rjvelazco Oct 2, 2025
b558b41
Merge branch 'main' into improve-uve-stage-management
rjvelazco Oct 3, 2025
cae63a5
Merge branch 'main' into improve-uve-stage-management
rjvelazco Oct 8, 2025
a3a75d5
Merge branch 'main' into improve-uve-stage-management
rjvelazco Oct 14, 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
1 change: 1 addition & 0 deletions core-web/apps/dotcms-ui/proxy-dev.conf.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default [
'^/assets/manifest.json': '/dotAdmin/assets/manifest.json',
'^/assets/monaco-editor/min': '/dotAdmin/assets/monaco-editor/min',
'^/assets/edit-ema': '/dotAdmin/assets/edit-ema',
'^/assets/dot-uve': '/dotAdmin/assets/dot-uve',
'^/assets/seo': '/dotAdmin/assets/seo',
'^/assets': '/dotAdmin',
'^/tinymce': '/dotAdmin/tinymce'
Expand Down
29 changes: 4 additions & 25 deletions core-web/apps/dotcms-ui/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
/* eslint-disable @nx/enforce-module-boundaries */

import { inject, NgModule } from '@angular/core';
import {
ActivatedRouteSnapshot,
Route,
RouteReuseStrategy,
RouterModule,
Routes
} from '@angular/router';
import { NgModule } from '@angular/core';
import { Route, RouteReuseStrategy, RouterModule, Routes } from '@angular/router';

import { DotExperimentsService, EmaAppConfigurationService } from '@dotcms/data-access';
import { DotExperimentsService } from '@dotcms/data-access';
import { DotEnterpriseLicenseResolver } from '@dotcms/ui';

import { AuthGuardService } from './api/services/guards/auth-guard.service';
import { ContentletGuardService } from './api/services/guards/contentlet-guard.service';
import { DefaultGuardService } from './api/services/guards/default-guard.service';
import { editContentGuard } from './api/services/guards/edit-content.guard';
import { editPageGuard } from './api/services/guards/ema-app/edit-page.guard';
import { MenuGuardService } from './api/services/guards/menu-guard.service';
import { PagesGuardService } from './api/services/guards/pages-guard.service';
import { PublicAuthGuardService } from './api/services/guards/public-auth-guard.service';
Expand Down Expand Up @@ -126,21 +119,7 @@ const PORTLETS_ANGULAR: Route[] = [
},
{
path: 'edit-page',
canMatch: [editPageGuard],
loadChildren: () =>
import('@portlets/dot-edit-page/dot-edit-page.module').then((m) => m.DotEditPageModule)
},
{
path: 'edit-page',
data: {
reuseRoute: false
},
resolve: {
uveConfig: (route: ActivatedRouteSnapshot) => {
return inject(EmaAppConfigurationService).get(route.queryParams.url);
}
},
loadChildren: () => import('@dotcms/portlets/dot-ema').then((m) => m.DotEmaRoutes)
loadChildren: () => import('@dotcms/portlets/dot-uve').then((m) => m.dotUVERoutes)
},
{
canActivate: [editContentGuard],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Observable, of as observableOf } from 'rxjs';

import { DOCUMENT } from '@angular/common';
import { CommonModule, DOCUMENT } from '@angular/common';
import { Component, Input, OnChanges, ViewChild, inject } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { ActivatedRoute, Params, RouterModule } from '@angular/router';

import { TooltipModule } from 'primeng/tooltip';

import { map } from 'rxjs/operators';

Expand All @@ -16,6 +18,7 @@ import {
FeaturedFlags
} from '@dotcms/dotcms-models';
import { DotPageToolsSeoComponent } from '@dotcms/portlets/dot-ema/ui';
import { DotIconModule, DotMessagePipe } from '@dotcms/ui';

import { DotContentletEditorService } from '../../../../view/components/dot-contentlet-editor/services/dot-contentlet-editor.service';

Expand All @@ -40,7 +43,14 @@ interface DotEditPageNavItem {
selector: 'dot-edit-page-nav',
templateUrl: './dot-edit-page-nav.component.html',
styleUrls: ['./dot-edit-page-nav.component.scss'],
standalone: false
imports: [
CommonModule,
RouterModule,
TooltipModule,
DotIconModule,
DotMessagePipe,
DotPageToolsSeoComponent
]
})
export class DotEditPageNavComponent implements OnChanges {
private dotLicenseService = inject(DotLicenseService);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { TooltipModule } from 'primeng/tooltip';

import { DotPageToolsSeoComponent } from '@dotcms/portlets/dot-ema/ui';
import { DotIconModule, DotMessagePipe, DotSafeHtmlPipe } from '@dotcms/ui';

import { DotEditPageNavComponent } from './dot-edit-page-nav.component';

@NgModule({
imports: [
CommonModule,
RouterModule,
TooltipModule,
DotIconModule,
DotSafeHtmlPipe,
DotMessagePipe,
DotPageToolsSeoComponent
],
declarations: [DotEditPageNavComponent],
imports: [DotEditPageNavComponent],
exports: [DotEditPageNavComponent]
})
export class DotEditPageNavModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ export class DotFavoritePageService {
sortOrder?: ESOrderDirection | string;
}): Observable<ESContent> {
const { limit, userId, identifier, url, offset, sortField, sortOrder } = params;
const urlRaw = url === '/' ? '/index' : url;

let extraQueryParams = '';
if (identifier) {
extraQueryParams = `+identifier:${identifier}`;
} else if (url) {
extraQueryParams = `+DotFavoritePage.url_dotraw:${url}`;
extraQueryParams = `+DotFavoritePage.url_dotraw:${urlRaw}*`;
}

return this.dotESContentService.get({
Expand Down
18 changes: 18 additions & 0 deletions core-web/libs/portlets/dot-uve/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../../.eslintrc.base.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
7 changes: 7 additions & 0 deletions core-web/libs/portlets/dot-uve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# portlet

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test portlet` to execute the unit tests.
21 changes: 21 additions & 0 deletions core-web/libs/portlets/dot-uve/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
displayName: 'dot-uve-portlet',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/portlets/dot-uve',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$'
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment'
]
};
20 changes: 20 additions & 0 deletions core-web/libs/portlets/dot-uve/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "dot-uve-portlet",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/portlets/dot-uve/src",
"prefix": "dot",
"projectType": "library",
"tags": ["type:feature", "scope:dotcms-ui", "portlet:uve"],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/portlets/dot-uve/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
3 changes: 3 additions & 0 deletions core-web/libs/portlets/dot-uve/src/assets/experiments.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions core-web/libs/portlets/dot-uve/src/assets/orientation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<p-button
[pTooltip]="'editpage.toolbar.bookmark' | dm"
tooltipPosition="bottom"
styleClass="p-button-text p-button-sm p-button-rounded"
(click)="toggleBookmark()"
[icon]="$bookmarked() ? 'pi pi-star-fill' : 'pi pi-star'"
[loading]="$loading()"
data-testId="bookmark-button" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
inject,
signal,
untracked
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

import { ButtonModule } from 'primeng/button';
import { TooltipModule } from 'primeng/tooltip';

import { map } from 'rxjs/operators';

import { DotESContentService, DotFavoritePageService } from '@dotcms/data-access';
import { DotCMSContentlet } from '@dotcms/dotcms-models';
import { DotMessagePipe } from '@dotcms/ui';

import { UVEStore } from '../../../store/dot-uve.store';

@Component({
selector: 'dot-uve-bookmark',
imports: [ButtonModule, DotMessagePipe, TooltipModule],
templateUrl: './dot-uve-bookmark.component.html',
styleUrl: './dot-uve-bookmark.component.scss',
providers: [DotFavoritePageService, DotESContentService],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DotUVEBookmarkComponent {
private readonly dotFavoritePageService = inject(DotFavoritePageService);
private readonly destroyRef = inject(DestroyRef);
protected readonly store = inject(UVEStore);

protected $favoritePage = signal<DotCMSContentlet | null>(null);
protected $bookmarked = signal(false);
protected $loading = signal(false);

constructor() {
effect(() => {
const pageAssetData = this.store.pageAssetData();

if (pageAssetData) {
untracked(() => this.fetchFavoritePage(this.store.url()));
}
});
}

toggleBookmark(): void {
// this.dialogService.open(DotFavoritePageComponent, {
// header: this.dotMessageService.get('favoritePage.dialog.header'),
// width: '80rem',
// data: {
// page: {
// favoritePageUrl: this.url,
// favoritePage: this.favoritePage
// },
// onSave: (favoritePageUrl: string) => {
// this.fetchFavoritePage(favoritePageUrl);
// },
// onDelete: (favoritePageUrl: string) => {
// this.fetchFavoritePage(favoritePageUrl);
// }
// }
// });
}

/**
* Fetch favorite page
*
* @private
* @param {string} url
* @memberof DotEmaBookmarksComponent
*/
private fetchFavoritePage(url: string): void {
this.$loading.set(true);
const userId = this.store.currentUser()?.userId ?? '';
this.dotFavoritePageService
.get({ url, userId, limit: 10 })
.pipe(
takeUntilDestroyed(this.destroyRef),
map((res) => res.jsonObjectView.contentlets[0])
)
.subscribe((favoritePage) => {
this.$loading.set(false);
this.$bookmarked.set(!!favoritePage);
this.$favoritePage.set(favoritePage);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
@for (device of systemDevices; track $index) {
@let isActive = device.inode === $currentDeviceInode();
<p-button
tooltipPosition="bottom"
[icon]="device.icon"
[pTooltip]="device.name | dm"
[styleClass]="
'p-button-text p-button-sm p-button-rounded ' + (isActive ? 'active-device' : '')
"
(click)="onDeviceSelect(device)" />
}

<p-button
styleClass="p-button-text p-button-sm p-button-rounded orientation p-button-icon-only"
[ngClass]="'vertical'"
[disabled]="$allowRotation()"
(click)="onOrientationChange()"
data-testId="orientation">
<svg fill="currentColor" viewBox="0 0 24 24" width="24px" height="24px">
<use href="./assets/edit-ema/orientation.svg#orientation" width="24px" height="24px"></use>
</svg>
</p-button>

@if ($menuItems().length) {
<p-button
icon="pi pi-angle-down"
iconPos="right"
[label]="'more' | dm"
[class.active]="false"
styleClass="p-button-text p-2 p-button-sm"
(click)="menu.toggle($event)"
data-testId="more-button" />

<p-menu
#menu
[model]="$menuItems()"
[popup]="true"
styleClass="more-menu"
data-testId="more-menu">
<ng-template pTemplate="submenuheader" let-item>
<span class="text-primary font-bold">{{ item.label | dm }}</span>
</ng-template>
<ng-template pTemplate="item" let-item>
<a
pRipple
class="flex align-items-center p-menuitem-link menu-item"
[ngClass]="{ 'menu-item--active': item.id === 'social-media' }">
<span>{{ item.label }}</span>
</a>
</ng-template>
</p-menu>
}
Loading
Loading