Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// === Drag and Drop Styles ===
.cdk-drag-preview {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);

td {
display: flex;
align-items: center;
}
}

.cdk-drag-placeholder {
opacity: 0;
}

.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

.cdk-drop-list-dragging .mat-row:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
// === End Drag and Drop Styles ===
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ export class EditActionButtonsModalComponent
* @param data.dashboard Current dashboard
* @param data.form Current form
* @param dialog dialog module for button edition / creation / deletion
* @param translateService used to translate modal text
* @param translate used to translate modal text
* @param applicationService shared application service
*/
constructor(
public dialogRef: DialogRef<ActionButton[]>,
@Inject(DIALOG_DATA)
private data: { dashboard?: Dashboard; form?: Page | Step },
public dialog: Dialog,
public translateService: TranslateService,
public translate: TranslateService,
public applicationService: ApplicationService
) {
super();
Expand Down Expand Up @@ -173,20 +173,14 @@ export class EditActionButtonsModalComponent
const { ConfirmModalComponent } = await import('@oort-front/shared');
const dialogRef = this.dialog.open(ConfirmModalComponent, {
data: {
title: this.translateService.instant('common.deleteObject', {
name: this.translateService.instant(
'models.dashboard.actionButtons.one'
),
title: this.translate.instant('common.deleteObject', {
name: this.translate.instant('models.dashboard.actionButtons.one'),
}),
content: this.translateService.instant(
content: this.translate.instant(
'models.dashboard.actionButtons.confirmDelete'
),
confirmText: this.translateService.instant(
'components.confirmModal.delete'
),
cancelText: this.translateService.instant(
'components.confirmModal.cancel'
),
confirmText: this.translate.instant('components.confirmModal.delete'),
cancelText: this.translate.instant('components.confirmModal.cancel'),
confirmVariant: 'danger',
},
});
Expand All @@ -210,9 +204,9 @@ export class EditActionButtonsModalComponent
*/
public async onDuplicateActionButton(actionButton: ActionButton) {
const newActionButton = structuredClone(actionButton);
newActionButton.text = `${
newActionButton.text
} (${this.translateService.instant('common.copy')})`;
newActionButton.text = `${newActionButton.text} (${this.translate.instant(
'common.copy'
)})`;
this.actionButtons.push(newActionButton);
this.searchTerm = '';
this.updateTable();
Expand Down
7 changes: 7 additions & 0 deletions libs/shared/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1876,6 +1876,13 @@
},
"openEditor": "Open html",
"openSettings": "Open settings",
"rowAction": {
"fields": {
"columnLabel": {
"text": "Column label"
}
}
},
"tooltip": {
"displayEditor": "Display html in modal",
"displayMap": "Display map",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ButtonModule, TooltipModule } from '@oort-front/ui';
import { TranslateModule } from '@ngx-translate/core';
import { Dialog } from '@angular/cdk/dialog';
import { DataTemplateService } from '../../services/data-template/data-template.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Router } from '@angular/router';
import { EmailService } from '../email/email.service';
import { Apollo } from 'apollo-angular';
import { EmailService as SharedEmailService } from '../../services/email/email.service';
Expand All @@ -26,7 +26,6 @@ import { lastValueFrom, map, of, Subject, takeUntil, tap } from 'rxjs';
import { Resource, ResourceQueryResponse } from '../../models/resource.model';
import { GET_RECORD_BY_ID, GET_RESOURCE_BY_ID } from './graphql/queries';
import { EDIT_RECORD } from './graphql/mutations';
import { Dashboard } from '../../models/dashboard.model';
import {
EditRecordMutationResponse,
RecordQueryResponse,
Expand All @@ -52,25 +51,25 @@ export class ActionButtonComponent
{
/** Action button definition */
@Input() actionButton!: ActionButton;
/** Dashboard */
@Input() dashboard?: Dashboard;
/** Should refresh button, some of them ( subscribe / unsubscribe ) can depend on other buttons */
@Input() refresh!: Subject<void>;
/** Reload dashboard event emitter */
@Output() reloadDashboard = new EventEmitter<void>();
/** Context id of the current dashboard */
public contextId!: string;
/** Record id */
@Input() recordId?: string;
/** Resource id */
@Input() resourceId?: string;
/** Reload parent event emitter */
@Output() reloadParent = new EventEmitter<void>();
/** Email notification, for subscribe & unsubscribe actions */
private emailNotification?: EmailNotification;
/** Current environment */
private environment: any;

/** @returns Should hide button */
get showButton(): boolean {
if (this.actionButton.editRecord && !this.contextId) {
if (this.actionButton.editRecord && !this.recordId) {
return false;
}
if (this.actionButton.cloneRecord && !this.contextId) {
if (this.actionButton.cloneRecord && !this.recordId) {
return false;
}
if (this.actionButton.subscribeToNotification) {
Expand Down Expand Up @@ -98,7 +97,6 @@ export class ActionButtonComponent
* @param dataTemplateService DataTemplate service
* @param router Angular router
* @param emailService Email service
* @param activatedRoute Activated route
* @param apollo Apollo
* @param location Angular location
* @param sharedEmailService Shared email service
Expand All @@ -114,7 +112,6 @@ export class ActionButtonComponent
private dataTemplateService: DataTemplateService,
private router: Router,
private emailService: EmailService,
private activatedRoute: ActivatedRoute,
private apollo: Apollo,
private location: Location,
private sharedEmailService: SharedEmailService,
Expand All @@ -126,11 +123,6 @@ export class ActionButtonComponent
) {
super();
this.environment = environment;
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe({
next: ({ id }) => {
this.contextId = id;
},
});
}

ngOnInit(): void {
Expand Down Expand Up @@ -218,7 +210,7 @@ export class ActionButtonComponent
this.actionButton.sendNotification.distributionList
) {
try {
const selectedIds = !isNil(this.contextId) ? [this.contextId] : [];
const selectedIds = !isNil(this.recordId) ? [this.recordId] : [];
const templates = await this.getSelectedNotificationTemplates(
this.actionButton.sendNotification.templates || []
);
Expand Down Expand Up @@ -246,10 +238,8 @@ export class ActionButtonComponent
);
const snackBarSpinner = snackBarRef.instance.nestedComponent;
let resource!: Resource;
if (this.dashboard?.page?.context?.resource) {
resource = (await this.getResourceById(
this.dashboard?.page?.context?.resource
)) as Resource;
if (this.resourceId) {
resource = (await this.getResourceById(this.resourceId)) as Resource;
}
const distributionList = await this.getSelectedDistributionListData(
this.actionButton.sendNotification.distributionList
Expand Down Expand Up @@ -315,11 +305,11 @@ export class ActionButtonComponent

// Prefill data for addRecord & cloneRecord
const loadPrefillData$ = () => {
if (this.actionButton.cloneRecord && this.contextId) {
if (this.actionButton.cloneRecord && this.recordId) {
return this.apollo
.query<RecordQueryResponse>({
query: GET_RECORD_BY_ID,
variables: { id: this.contextId, includeResource: false },
variables: { id: this.recordId, includeResource: false },
})
.pipe(
takeUntil(this.destroy$),
Expand Down Expand Up @@ -350,13 +340,13 @@ export class ActionButtonComponent
// Callback to be executed at the end of action
const callback = () => {
if (shouldReload) {
this.reloadDashboard.emit();
this.reloadParent.emit();
}
};
const dialogRef = this.dialog.open(FormModalComponent, {
disableClose: true,
data: {
...(this.actionButton.editRecord && { recordId: this.contextId }), // Modal will open current record
...(this.actionButton.editRecord && { recordId: this.recordId }), // Modal will open current record
...(template && { template }),
actionButtonCtx: true,
prefillData,
Expand All @@ -374,15 +364,15 @@ export class ActionButtonComponent
this.actionButton.addRecord.fieldsForUpdate || [];
// Execute callback if possible
if (
this.contextId &&
this.recordId &&
Array.isArray(fieldsForUpdate) &&
fieldsForUpdate.length > 0
) {
this.apollo
.query<RecordQueryResponse>({
query: GET_RECORD_BY_ID,
variables: {
id: this.contextId,
id: this.recordId,
includeResource: true,
},
})
Expand Down Expand Up @@ -420,7 +410,7 @@ export class ActionButtonComponent
.mutate<EditRecordMutationResponse>({
mutation: EDIT_RECORD,
variables: {
id: this.contextId,
id: this.recordId,
data: update,
},
})
Expand Down Expand Up @@ -519,24 +509,6 @@ export class ActionButtonComponent
return distributionListResponse.emailDistributionLists.edges[0].node;
}

/**
* Get default resource meta data
*
* @param fields Selected resource fields for the given action button
* @returns default resource meta data
*/
private async getResourceMetaData(fields: string[]) {
const { data: resourceMetaDataResponse } = await lastValueFrom(
// Fetch resource metadata for email sending
this.queryBuilder.getQueryMetaData(
this.dashboard?.page?.context?.resource as string
)
);
return resourceMetaDataResponse.resource.metadata?.filter((md) =>
fields.includes(md.name)
);
}

/**
* Fetch resource data needed for field display
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<shared-action-button
*ngFor="let button of actionButtons; let i = index"
[actionButton]="button"
[dashboard]="dashboard"
[recordId]="contextId"
[resourceId]="dashboard?.page?.context?.resource"
[refresh]="refresh"
(reloadDashboard)="reloadDashboard.emit()"
(reloadParent)="reloadDashboard.emit()"
></shared-action-button>
</div>
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
Component,
EventEmitter,
inject,
Input,
OnInit,
Output,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActionButtonComponent } from '../action-button/action-button.component';
import { ActionButton } from '../action-button/action-button.type';
import { Dashboard } from '../../models/dashboard.model';
import { Subject } from 'rxjs';
import { Subject, takeUntil } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { UnsubscribeComponent } from '../utils/unsubscribe/unsubscribe.component';

/**
* Dashboard action buttons component.
Expand All @@ -15,7 +24,10 @@ import { Subject } from 'rxjs';
templateUrl: './action-buttons.component.html',
styleUrls: ['./action-buttons.component.scss'],
})
export class ActionButtonsComponent {
export class ActionButtonsComponent
extends UnsubscribeComponent
implements OnInit
{
/** List of action buttons */
@Input() actionButtons: ActionButton[] = [];
/** Dashboard */
Expand All @@ -24,4 +36,18 @@ export class ActionButtonsComponent {
@Output() reloadDashboard = new EventEmitter<void>();
/** Should refresh buttons, some of them ( subscribe / unsubscribe ) can depend on other buttons */
public refresh = new Subject<void>();
/** Context id of the current dashboard, if available */
public contextId?: string;
/** Activated route */
private activatedRoute = inject(ActivatedRoute);

ngOnInit(): void {
if (this.dashboard) {
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe({
next: ({ id }) => {
this.contextId = id;
},
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@
[canAdd]="canCreateRecords"
[canDownload]="canDownloadRecords"
[searchable]="searchable"
(reload)="reloadData()"
>
</shared-grid>
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,27 @@
</ng-container>
</kendo-grid-column-group>
</ng-container>
<!-- CUSTOM ROW ACTIONS -->
<kendo-grid-column
*ngFor="let group of customRowActionGroups"
[columnMenu]="false"
[width]="108"
[resizable]="true"
[title]="group.label"
>
<ng-template kendoGridCellTemplate let-dataItem="dataItem">
<div class="flex flex-row gap-2">
<ng-container *ngFor="let customRowAction of group.actions">
<shared-action-button
[actionButton]="$any(customRowAction)"
[recordId]="dataItem.id"
[resourceId]="widget?.settings.resource"
(reloadParent)="reload.emit()"
></shared-action-button>
</ng-container>
</div>
</ng-template>
</kendo-grid-column>
<!-- ROW DETAILS -->
<kendo-grid-column
*ngIf="hasDetails && actions.showDetails"
Expand Down
Loading