mirror of https://github.com/ghostfolio/ghostfolio
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							521 lines
						
					
					
						
							16 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							521 lines
						
					
					
						
							16 KiB
						
					
					
				| @if (hasPermissionToCreateActivity) { | |
|   <div class="d-flex justify-content-end"> | |
|     <button | |
|       class="align-items-center d-flex" | |
|       mat-stroked-button | |
|       (click)="onImport()" | |
|     > | |
|       <ion-icon class="mr-2" name="cloud-upload-outline" /> | |
|       <ng-container i18n>Import Activities</ng-container>... | |
|     </button> | |
|     @if (hasPermissionToExportActivities) { | |
|       <button | |
|         class="mx-1 no-min-width px-2" | |
|         mat-stroked-button | |
|         [matMenuTriggerFor]="activitiesMenu" | |
|         (click)="$event.stopPropagation()" | |
|       > | |
|         <ion-icon name="ellipsis-vertical" /> | |
|       </button> | |
|     } | |
|     <mat-menu #activitiesMenu="matMenu" class="no-max-width" xPosition="before"> | |
|       <button | |
|         mat-menu-item | |
|         [disabled]="dataSource?.data.length === 0" | |
|         (click)="onImportDividends()" | |
|       > | |
|         <span class="align-items-center d-flex"> | |
|           <ion-icon class="mr-2" name="color-wand-outline" /> | |
|           <ng-container i18n>Import Dividends</ng-container>... | |
|         </span> | |
|       </button> | |
|       @if (hasPermissionToExportActivities) { | |
|         <button | |
|           class="align-items-center d-flex" | |
|           mat-menu-item | |
|           [disabled]="dataSource?.data.length === 0" | |
|           (click)="onExport()" | |
|         > | |
|           <span class="align-items-center d-flex"> | |
|             <ion-icon class="mr-2" name="cloud-download-outline" /> | |
|             <span i18n>Export Activities</span> | |
|           </span> | |
|         </button> | |
|       } | |
|       @if (hasPermissionToExportActivities) { | |
|         <button | |
|           class="align-items-center d-flex" | |
|           mat-menu-item | |
|           [disabled]="!hasDrafts" | |
|           (click)="onExportDrafts()" | |
|         > | |
|           <span class="align-items-center d-flex"> | |
|             <ion-icon class="mr-2" name="calendar-clear-outline" /> | |
|             <span i18n>Export Drafts as ICS</span> | |
|           </span> | |
|         </button> | |
|       } | |
|       <hr class="m-0" /> | |
|       <button | |
|         class="align-items-center d-flex" | |
|         mat-menu-item | |
|         [disabled]="!hasPermissionToDeleteActivity" | |
|         (click)="onDeleteActivities()" | |
|       > | |
|         <span class="align-items-center d-flex"> | |
|           <ion-icon class="mr-2" name="trash-outline" /> | |
|           <span i18n>Delete Activities</span> | |
|         </span> | |
|       </button> | |
|     </mat-menu> | |
|   </div> | |
| } | |
| 
 | |
| <div class="overflow-x-auto"> | |
|   <table | |
|     class="gf-table w-100" | |
|     mat-table | |
|     matSort | |
|     [dataSource]="dataSource" | |
|     [matSortActive]="sortColumn" | |
|     [matSortDirection]="sortDirection" | |
|     [matSortDisabled]="sortDisabled" | |
|   > | |
|     <ng-container matColumnDef="select" sticky> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell> | |
|         <mat-checkbox | |
|           color="primary" | |
|           [checked]=" | |
|             areAllRowsSelected() && !hasErrors && selectedRows.hasValue() | |
|           " | |
|           [disabled]="hasErrors" | |
|           [indeterminate]="selectedRows.hasValue() && !areAllRowsSelected()" | |
|           (change)="$event ? toggleAllRows() : null" | |
|         /> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         <mat-checkbox | |
|           color="primary" | |
|           [checked]="element.error ? false : selectedRows.isSelected(element)" | |
|           [disabled]="element.error" | |
|           (change)="$event ? selectedRows.toggle(element) : null" | |
|           (click)="$event.stopPropagation()" | |
|         /> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="importStatus"> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell></th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         @if (element.error) { | |
|           <div | |
|             class="d-flex" | |
|             matTooltipPosition="above" | |
|             [matTooltip]="element.error.message" | |
|           > | |
|             <ion-icon class="text-danger" name="alert-circle-outline" /> | |
|           </div> | |
|         } | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="icon" sticky> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell></th> | |
|       <td *matCellDef="let element" class="px-1 text-center" mat-cell> | |
|         <gf-entity-logo | |
|           [dataSource]="element.SymbolProfile?.dataSource" | |
|           [symbol]="element.SymbolProfile?.symbol" | |
|           [tooltip]="element.SymbolProfile?.name" | |
|         /> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="nameWithSymbol"> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell> | |
|         <ng-container i18n>Name</ng-container> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         <div class="align-items-center d-flex line-height-1"> | |
|           <div> | |
|             <span class="text-truncate">{{ element.SymbolProfile?.name }}</span> | |
|             @if (element.isDraft) { | |
|               <span class="badge badge-secondary ml-1" i18n>Draft</span> | |
|             } | |
|           </div> | |
|         </div> | |
|         @if (!isUUID(element.SymbolProfile?.symbol)) { | |
|           <div> | |
|             <small class="text-muted">{{ | |
|               element.SymbolProfile?.symbol | gfSymbol | |
|             }}</small> | |
|           </div> | |
|         } | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="type"> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header> | |
|         <ng-container i18n>Type</ng-container> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         <gf-activity-type [activityType]="element.type" /> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="date"> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header> | |
|         <ng-container i18n>Date</ng-container> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         <div class="d-flex"> | |
|           <gf-value | |
|             [deviceType]="deviceType" | |
|             [isDate]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.date" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="quantity"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-none d-lg-table-cell justify-content-end px-1" | |
|         mat-header-cell | |
|         mat-sort-header | |
|       > | |
|         <ng-container i18n>Quantity</ng-container> | |
|       </th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         <div class="d-flex justify-content-end"> | |
|           <gf-value | |
|             [isCurrency]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.quantity" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="unitPrice"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-none d-lg-table-cell justify-content-end px-1" | |
|         mat-header-cell | |
|         mat-sort-header | |
|       > | |
|         <ng-container i18n>Unit Price</ng-container> | |
|       </th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         <div class="d-flex justify-content-end"> | |
|           <gf-value | |
|             [isCurrency]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.unitPrice" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="fee"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-none d-lg-table-cell justify-content-end px-1" | |
|         mat-header-cell | |
|         mat-sort-header | |
|       > | |
|         <ng-container i18n>Fee</ng-container> | |
|       </th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         <div class="d-flex justify-content-end"> | |
|           <gf-value | |
|             [isCurrency]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.fee" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="value"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-none d-lg-table-cell px-1 text-right" | |
|         mat-header-cell | |
|       > | |
|         <ng-container i18n>Value</ng-container> | |
|       </th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         <div class="d-flex justify-content-end"> | |
|           <gf-value | |
|             [isCurrency]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.value" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="currency"> | |
|       <th *matHeaderCellDef class="d-none d-lg-table-cell px-1" mat-header-cell> | |
|         <ng-container i18n>Currency</ng-container> | |
|       </th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         {{ element.currency ?? element.SymbolProfile?.currency }} | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="valueInBaseCurrency"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-lg-none d-xl-none px-1 text-right" | |
|         mat-header-cell | |
|       > | |
|         <ng-container i18n>Value</ng-container> | |
|       </th> | |
|       <td *matCellDef="let element" class="d-lg-none d-xl-none px-1" mat-cell> | |
|         <div class="d-flex justify-content-end"> | |
|           <gf-value | |
|             [isCurrency]="true" | |
|             [locale]="locale" | |
|             [value]="isLoading ? undefined : element.valueInBaseCurrency" | |
|           /> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="account"> | |
|       <th *matHeaderCellDef class="px-1" mat-header-cell> | |
|         <span class="d-none d-lg-block" i18n>Account</span> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1" mat-cell> | |
|         <div class="d-flex"> | |
|           @if (element.account?.platform?.url) { | |
|             <gf-entity-logo | |
|               class="mr-1" | |
|               [tooltip]="element.account?.platform?.name" | |
|               [url]="element.account?.platform?.url" | |
|             /> | |
|           } | |
|           <span class="d-none d-lg-block">{{ element.account?.name }}</span> | |
|         </div> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="comment"> | |
|       <th | |
|         *matHeaderCellDef | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-header-cell | |
|       ></th> | |
|       <td | |
|         *matCellDef="let element" | |
|         class="d-none d-lg-table-cell px-1" | |
|         mat-cell | |
|       > | |
|         @if (element.comment) { | |
|           <button | |
|             class="mx-1 no-min-width px-2" | |
|             mat-button | |
|             title="Note" | |
|             (click)="onOpenComment(element.comment); $event.stopPropagation()" | |
|           > | |
|             <ion-icon name="document-text-outline" /> | |
|           </button> | |
|         } | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <ng-container matColumnDef="actions" stickyEnd> | |
|       <th *matHeaderCellDef class="px-1 text-center" mat-header-cell> | |
|         @if ( | |
|           !hasPermissionToCreateActivity && hasPermissionToExportActivities | |
|         ) { | |
|           <button | |
|             class="mx-1 no-min-width px-2" | |
|             mat-button | |
|             [matMenuTriggerFor]="activitiesMenu" | |
|             (click)="$event.stopPropagation()" | |
|           > | |
|             <ion-icon name="ellipsis-vertical" /> | |
|           </button> | |
|         } | |
|         <mat-menu | |
|           #activitiesMenu="matMenu" | |
|           class="no-max-width" | |
|           xPosition="before" | |
|         > | |
|           @if (hasPermissionToCreateActivity) { | |
|             <button | |
|               class="align-items-center d-flex" | |
|               mat-menu-item | |
|               (click)="onImport()" | |
|             > | |
|               <span class="align-items-center d-flex"> | |
|                 <ion-icon class="mr-2" name="cloud-upload-outline" /> | |
|                 <ng-container i18n>Import Activities</ng-container>... | |
|               </span> | |
|             </button> | |
|           } | |
|           @if (hasPermissionToCreateActivity) { | |
|             <button | |
|               mat-menu-item | |
|               [disabled]="dataSource?.data.length === 0" | |
|               (click)="onImportDividends()" | |
|             > | |
|               <span class="align-items-center d-flex"> | |
|                 <ion-icon class="mr-2" name="color-wand-outline" /> | |
|                 <ng-container i18n>Import Dividends</ng-container>... | |
|               </span> | |
|             </button> | |
|           } | |
|           @if (hasPermissionToExportActivities) { | |
|             <button | |
|               class="align-items-center d-flex" | |
|               mat-menu-item | |
|               [disabled]="dataSource?.data.length === 0" | |
|               (click)="onExport()" | |
|             > | |
|               <span class="align-items-center d-flex"> | |
|                 <ion-icon class="mr-2" name="cloud-download-outline" /> | |
|                 <span i18n>Export Activities</span> | |
|               </span> | |
|             </button> | |
|           } | |
|           @if (hasPermissionToExportActivities) { | |
|             <button | |
|               class="align-items-center d-flex" | |
|               mat-menu-item | |
|               [disabled]="!hasDrafts" | |
|               (click)="onExportDrafts()" | |
|             > | |
|               <span class="align-items-center d-flex"> | |
|                 <ion-icon class="mr-2" name="calendar-clear-outline" /> | |
|                 <span i18n>Export Drafts as ICS</span> | |
|               </span> | |
|             </button> | |
|           } | |
|         </mat-menu> | |
|       </th> | |
|       <td *matCellDef="let element" class="px-1 text-center" mat-cell> | |
|         @if (showActions) { | |
|           <button | |
|             class="mx-1 no-min-width px-2" | |
|             mat-button | |
|             [matMenuTriggerFor]="activityMenu" | |
|             (click)="$event.stopPropagation()" | |
|           > | |
|             <ion-icon name="ellipsis-horizontal" /> | |
|           </button> | |
|         } | |
|         <mat-menu | |
|           #activityMenu="matMenu" | |
|           class="no-max-width" | |
|           xPosition="before" | |
|         > | |
|           <button mat-menu-item (click)="onUpdateActivity(element)"> | |
|             <span class="align-items-center d-flex"> | |
|               <ion-icon class="mr-2" name="create-outline" /> | |
|               <span i18n>Edit</span> | |
|             </span> | |
|           </button> | |
|           <button mat-menu-item (click)="onCloneActivity(element)"> | |
|             <span class="align-items-center d-flex"> | |
|               <ion-icon class="mr-2" name="copy-outline" /> | |
|               <span i18n>Clone</span> | |
|             </span> | |
|           </button> | |
|           <button | |
|             mat-menu-item | |
|             [disabled]="!element.isDraft" | |
|             (click)="onExportDraft(element.id)" | |
|           > | |
|             <span class="align-items-center d-flex"> | |
|               <ion-icon class="mr-2" name="calendar-clear-outline" /> | |
|               <span i18n>Export Draft as ICS</span> | |
|             </span> | |
|           </button> | |
|           <hr class="m-0" /> | |
|           <button | |
|             mat-menu-item | |
|             [disabled]="!hasPermissionToDeleteActivity" | |
|             (click)="onDeleteActivity(element.id)" | |
|           > | |
|             <span class="align-items-center d-flex"> | |
|               <ion-icon class="mr-2" name="trash-outline" /> | |
|               <span i18n>Delete</span> | |
|             </span> | |
|           </button> | |
|         </mat-menu> | |
|       </td> | |
|     </ng-container> | |
| 
 | |
|     <tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> | |
|     <tr | |
|       *matRowDef="let row; columns: displayedColumns" | |
|       mat-row | |
|       [ngClass]="{ | |
|         'cursor-pointer': | |
|           hasPermissionToOpenDetails && | |
|           isExcludedFromAnalysis(row) === false && | |
|           row.isDraft === false && | |
|           ['BUY', 'DIVIDEND', 'SELL'].includes(row.type) | |
|       }" | |
|       (click)="onClickActivity(row)" | |
|     ></tr> | |
|   </table> | |
| </div> | |
| 
 | |
| @if (isLoading) { | |
|   <ngx-skeleton-loader | |
|     animation="pulse" | |
|     class="px-4 py-3" | |
|     [theme]="{ | |
|       height: '1.5rem', | |
|       width: '100%' | |
|     }" | |
|   /> | |
| } | |
| 
 | |
| <mat-paginator | |
|   [length]="totalItems" | |
|   [ngClass]="{ | |
|     'd-none': (isLoading && !totalItems) || totalItems <= pageSize | |
|   }" | |
|   [pageIndex]="pageIndex" | |
|   [pageSize]="pageSize" | |
|   [showFirstLastButtons]="true" | |
|   (page)="onChangePage($event)" | |
| /> | |
| 
 | |
| @if ( | |
|   !hasActivities && | |
|   dataSource?.data.length === 0 && | |
|   hasPermissionToCreateActivity && | |
|   !isLoading | |
| ) { | |
|   <div class="p-3 text-center"> | |
|     <gf-no-transactions-info-indicator [hasBorder]="false" /> | |
|   </div> | |
| }
 | |
| 
 |