Integrate admin panel to backend

This commit is contained in:
yunusyerli1 2023-02-23 14:26:33 +03:00 committed by Clemente Raposo
parent c2f3288f95
commit 6ee178318e
16 changed files with 256 additions and 307 deletions

View file

@ -0,0 +1,50 @@
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2023 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
import {ObjectMap} from 'common';
export interface AdminMetadata {
adminPanel?: AdminMetadataModel[];
[key: string]: any;
}
export interface AdminMetadataModel {
titleLabelKey: string,
descriptionLabelKey: string,
linkGroup: AdminLinkGroupModel[],
icon: string
}
export interface AdminLinkGroupModel {
[key: string]: any;
category?: string,
descriptionKey?: string,
titleKey?: string,
icon?: string
link?: string,
params?: ObjectMap
}

View file

@ -0,0 +1,118 @@
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable, of} from 'rxjs';
import {distinctUntilChanged, map, shareReplay} from 'rxjs/operators';
import {AppStateStore} from '../app-state/app-state.store';
import {StateStore} from '../state';
import {deepClone, ObjectMap} from 'common';
import {AdminMetadata} from './admin-metadata.model';
const initialState: AdminMetadata = {
adminPanel: []
};
let internalState: AdminMetadata = deepClone(initialState);
let cache$: Observable<any> = null;
@Injectable({
providedIn: 'root',
})
export class AdminMetadataStore implements StateStore {
/**
* Public long-lived observable streams
*/
adminPanel$: Observable<ObjectMap>;
protected store = new BehaviorSubject<AdminMetadata>(internalState);
protected state$ = this.store.asObservable();
constructor(
protected appStateStore: AppStateStore,
) {
this.adminPanel$ = this.state$.pipe(map(state => state.adminPanel), distinctUntilChanged());
}
/**
* Public Api
*/
/**
* Clear state
*/
public clear(): void {
cache$ = null;
this.updateState(deepClone(initialState));
}
public clearAuthBased(): void {
this.clear();
}
/**
* Returns the currently active admin panel
*
* @returns {object} the admin panel
*/
public getAdminPanel(): AdminMetadata {
return internalState.adminPanel;
}
/**
* Check if loaded
*/
public isCached(): boolean {
return cache$ !== null;
}
/**
* Internal API
*/
/**
* Update the state
*
* @param {object} state to set
*/
protected updateState(state: AdminMetadata): void {
this.store.next(internalState = state);
}
/**
* Set pre-loaded adminMetadata and cache
*/
public set(adminMetadata: AdminMetadata): void {
cache$ = of(adminMetadata).pipe(shareReplay(1));
this.updateState(adminMetadata);
}
}

View file

@ -28,7 +28,7 @@ import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable, of} from 'rxjs';
import {catchError, map, shareReplay, tap} from 'rxjs/operators';
import {EntityGQL} from '../../services/api/graphql-api/api.entity.get';
import {deepClone, emptyObject} from 'common';
import {deepClone, emptyObject, ObjectMap} from 'common';
import {StateStore} from '../state';
import {AppStateStore} from '../app-state/app-state.store';
import {LanguageStore, LanguageStrings} from '../language/language.store';
@ -38,6 +38,9 @@ import {UserPreferenceMap, UserPreferenceStore} from '../user-preference/user-pr
import {NavigationStore} from '../navigation/navigation.store';
import {Metadata, MetadataMap, MetadataStore} from '../metadata/metadata.store.service';
import {ApolloQueryResult} from '@apollo/client/core';
import {AdminMetadataStore} from '../admin-metadata/admin-metadata.store';
import {AuthService} from '../../services/auth/auth.service';
import {AdminMetadata} from '../admin-metadata/admin-metadata.model';
export interface AppMetadata {
loaded?: boolean;
@ -49,6 +52,7 @@ export interface AppMetadata {
navigation?: any;
moduleMetadata?: MetadataMap;
minimalModuleMetadata?: MetadataMap;
adminMetadata?: AdminMetadata;
}
export interface AppMetadataFlags {
@ -61,6 +65,7 @@ export interface AppMetadataFlags {
navigation?: boolean;
moduleMetadata?: boolean;
minimalModuleMetadata?: boolean;
adminMetadata?: boolean;
}
@ -72,7 +77,8 @@ const initialState: AppMetadataFlags = {
modStrings: false,
themeImages: false,
navigation: false,
moduleMetadata: false
moduleMetadata: false,
adminMetadata: false
};
let internalState: AppMetadataFlags = deepClone(initialState);
@ -105,6 +111,7 @@ export class AppMetadataStore implements StateStore {
'themeImages',
'navigation',
'moduleMetadata',
'adminMetadata'
];
constructor(
@ -115,6 +122,8 @@ export class AppMetadataStore implements StateStore {
protected config: SystemConfigStore,
protected preferences: UserPreferenceStore,
protected navigation: NavigationStore,
protected adminMetadataStore:AdminMetadataStore,
protected auth: AuthService
) {
this.metadata$ = this.state$;
}
@ -217,6 +226,10 @@ export class AppMetadataStore implements StateStore {
types.push('themeImages')
}
if (!this.isAdminMetadataLoaded()) {
types.push('adminMetadata');
}
return types;
}
@ -240,6 +253,10 @@ export class AppMetadataStore implements StateStore {
return this.navigation.isCached();
}
protected isAdminMetadataLoaded(): boolean {
return !!(internalState.adminMetadata ?? false);
}
/**
* Internal API
*/
@ -259,7 +276,6 @@ export class AppMetadataStore implements StateStore {
* @returns {object} Observable<{}>
*/
protected fetch(module: string, types: string[] = []): Observable<AppMetadataFlags> {
const fieldsToRetrieve = {
fields: [
...this.fieldsMetadata.fields,
@ -286,7 +302,7 @@ export class AppMetadataStore implements StateStore {
this.setNavigation(appMetadata, result);
this.setLanguages(appMetadata, result);
this.setModuleMetadata(appMetadata, result);
this.setAdminMetadata(appMetadata,result);
return appMetadata;
})
);
@ -373,6 +389,14 @@ export class AppMetadataStore implements StateStore {
}
}
protected setAdminMetadata(currentMetadata: AppMetadataFlags, appMetadata: AppMetadata) {
const adminMetadata = appMetadata?.adminMetadata ?? {};
if (!emptyObject(adminMetadata)) {
currentMetadata.adminMetadata = true;
this.adminMetadataStore.set(adminMetadata);
}
}
protected mapPreferences(preferences: any): UserPreferenceMap {
const userPreferences: UserPreferenceMap = {};
Object.keys(preferences).forEach((prefKey) => {

View file

@ -26,13 +26,22 @@
*/
-->
<div class="card text-center border-0" >
<scrm-image image="{{content.icon}}" [wrapperClass]="'sicon-3x'"></scrm-image>
<div class="card text-center border-0" *ngIf="content">
<scrm-image [image]="content.icon" [wrapperClass]="'sicon-3x'"></scrm-image>
<div class="card-body">
<h6 class="card-title admin-card-title" title="{{content.titleDesc}}">{{content.title}}</h6>
<div *ngFor="let link of content.links">
<a class="card-link admin-card-link" routerLink="{{link.path}}" title="{{link.titleDesc}}">{{link.title}}</a>
</div>
<h6 class="card-title admin-card-title" [title]="language.getFieldLabel(content.descriptionLabelKey)">
<scrm-label [labelKey]="content.titleLabelKey"></scrm-label>
</h6>
<ng-container *ngIf="content.linkGroup">
<div *ngFor="let item of content.linkGroup">
<a class="card-link admin-card-link"
[routerLink]="item.link"
[queryParams]="item?.params"
queryParamsHandling="merge"
[title]="language.getFieldLabel(item.descriptionKey)">
<scrm-label [labelKey]="item.titleKey"></scrm-label>
</a>
</div>
</ng-container>
</div>
</div>

View file

@ -25,7 +25,8 @@
*/
import {Component, Input} from '@angular/core';
import {MenuItem} from 'common';
import {LanguageStore} from '../../../../store/language/language.store';
import {AdminLinkGroupModel} from '../../../../store/admin-metadata/admin-metadata.model';
@Component({
@ -34,8 +35,8 @@ import {MenuItem} from 'common';
styleUrls: [],
})
export class AdminCardComponent {
@Input() content: any;
@Input() content: AdminLinkGroupModel;
constructor() {}
constructor(public language: LanguageStore) {}
}

View file

@ -28,7 +28,7 @@
<div class="admin-view d-flex align-items-center">
<div class="row">
<div class="col-12 col-sm-6 col-md-4 col-lg-3" *ngFor="let data of mockData">
<div class="col-12 col-sm-6 col-md-4 col-lg-3" *ngFor="let data of adminPanelData">
<scrm-admin-card [content]="data"></scrm-admin-card>

View file

@ -24,8 +24,13 @@
* the words "Supercharged by SuiteCRM".
*/
import {Component} from '@angular/core';
import {SystemConfigStore} from '../../../../store/system-config/system-config.store';
import {Component, OnInit} from '@angular/core';
import {AdminMetadataStore} from '../../../../store/admin-metadata/admin-metadata.store';
import {
AdminLinkGroupModel,
AdminMetadata,
AdminMetadataModel
} from '../../../../store/admin-metadata/admin-metadata.model';
@Component({
@ -33,296 +38,39 @@ import {SystemConfigStore} from '../../../../store/system-config/system-config.s
templateUrl: './admin-panel.component.html',
styleUrls: [],
})
export class AdminPanelComponent {
export class AdminPanelComponent implements OnInit {
mockData:Array<any> = [
{
icon: 'icon_users_and_authentication',
title: 'Users & Authentication',
titleDesc: 'Create, edit, activate and deactivate users in SuiteCRM.',
links: [
{
title:'User Management',
titleDesc:'Manage user accounts and passwords',
path:'/'
},
{
title:'Role Management',
titleDesc:'Manage role membership and properties',
path:'/'
},
{
title:'Password Management',
titleDesc:'Manage password requirements and expiration',
path:'/'
},
{
title:'OAuth2 Clients and Tokens',
titleDesc:'Manage which clients have access to the OAuth2 Server and view session log and revoke active sessions',
path:'/'
},
{
title:'OAuth Keys',
titleDesc:'OAuth key management',
path:'/'
},
{
title:'Security Suite Group Management',
titleDesc:'Security Suite Group Editor',
path:'/'
},
{
title:'Security Suite Settings',
titleDesc:'Configure Security Suite settings such as group inheritance, additive security, etc',
path:'/'
}
]
},
{
icon: 'icon_system',
title: 'System',
titleDesc: 'Configure the system-wide settings according to the specifications of your organization. Users can override some of the default locale settings within their user settings page.',
links: [
{
title: 'System Settings',
titleDesc: 'Configure system-wide settings',
path:'/'
},
{
title: 'Currencies',
titleDesc: 'Set up currencies and conversion rates',
path:'/'
},
{
title: 'Languages',
titleDesc: 'Manage which languages are available for users',
path:'/'
},
{
title: 'Locale',
titleDesc: 'Set default localization settings for your system',
path:'/'
},
{
title: 'PDF Settings',
titleDesc: 'Change PDF Settings',
path:'/'
},
{
title: 'Search Settings',
titleDesc: 'Configure the global search preferences for the system',
path:'/'
},
{
title: 'Elasticsearch',
titleDesc: 'Configure Elasticsearch preferences',
path:'/'
},
{
title: 'Scheduler',
titleDesc: 'Set up scheduled events',
path:'/'
},
{
title: 'Themes',
titleDesc: 'Choose themes for users to be able to select',
path:'/'
},
]
},
{
icon: 'icon_module_settings',
title: 'Module Settings',
titleDesc: 'Configure Module specifics and settings',
links: [
{
title: 'Activity Stream Settings',
titleDesc: 'Enable the user feed and module feeds for the My Activity Stream dashlet',
path:'/'
},
{
title: 'Business hours',
titleDesc: 'Restrict Workflow & Case automations to certain days and times',
path:'/'
},
{
title: 'Case Module Settings',
titleDesc: 'Change settings for Cases and the Cases Portal',
path:'/'
},
{
title: 'Configure Module Menu Filters',
titleDesc: 'Create and edit module menu filters',
path:'/'
},
{
title: 'Connectors',
titleDesc: 'Manage connector settings',
path:'/'
},
{
title: 'Display Modules and Subpanels',
titleDesc: 'Choose which modules are displayed in the navigation bar and which subpanels are displayed system-wide',
path:'/'
},
{
title: 'History Subpanel',
titleDesc: 'Enable/Disable contacts\' emails in history',
path:'/'
},
{
title: 'Sales Module Settings',
titleDesc: 'Change settings for Quotes, Contracts and Invoices',
path:'/'
},
{
title: 'Releases',
titleDesc: 'Manage releases and versions',
path:'/'
},
]
},
{
icon: 'icon_email',
title: 'Email',
titleDesc: 'Manage outbound and inbound emails. The email settings must be configured in order to enable users to send out email and newsletter campaigns.',
links: [
{
title: 'Email Settings',
titleDesc: 'Configure email settings',
path:'/'
},
{
title: 'Inbound Email',
titleDesc: 'Set up group mail accounts for monitoring inbound email and manage personal inbound mail account information for users',
path:'/'
},
{
title: 'Outbound Email',
titleDesc: 'Configure outbound email settings',
path:'/'
},
{
title: 'External OAuth Connections',
titleDesc: 'Setup external OAuth connections',
path:'/'
},
{
title: 'Campaign Email Settings',
titleDesc: 'Configure email settings for campaigns',
path:'/'
},
{
title: 'Email Queue',
titleDesc: 'Manage the outbound email queue',
path:'/'
},
]
},
{
icon: 'icon_admin_tools',
title: 'Admin Tools',
titleDesc: 'Repair, backup and run diagnosis on your SuiteCRM instance',
links: [
{
title: 'Repair',
titleDesc: 'Check and repair SuiteCRM',
path:'/'
},
{
title: 'Backups',
titleDesc: 'Backup SuiteCRM files',
path:'/'
},
{
title: 'Diagnostic Tool',
titleDesc: 'Capture system configuration for diagnostics and analysis',
path:'/'
},
{
title: 'Import Wizard',
titleDesc: 'Use the import wizard to easily import records into the system',
path:'/'
},
{
title: 'Module Loader',
titleDesc: 'Add or remove SuiteCRM modules, themes, language packs and other extensions',
path:'/'
},
]
},
{
icon: 'icon_developer_tools',
title: 'Developer Tools',
titleDesc: 'Create and edit modules and module layouts, manage standard and custom fields and configure tabs.',
links: [
{
title: 'Studio',
titleDesc: 'Customize module fields, layouts and relationships',
path:'/'
},
{
title: 'Rename Modules',
titleDesc: 'Change the names of the modules appearing within the application',
path:'/'
},
{
title: 'Module Builder',
titleDesc: 'Build new modules to expand the functionality of SuiteCRM',
path:'/'
},
{
title: 'Dropdown Editor',
titleDesc: 'Add, delete, or change the dropdown lists',
path:'/'
},
{
title: 'Workflow Manager',
titleDesc: 'Manage, Add, delete or change Workflow processes',
path:'/'
}
]
},
{
icon: 'icon_google_suite',
title: 'Google Suite',
titleDesc: 'Manage your Google Suite Integration.',
links: [
{
title: 'Google Calendar Settings',
titleDesc: 'Configuration settings to adjust your Google Calendar',
path:'/'
},
{
title: 'Google Maps Settings',
titleDesc: 'Configuration settings to adjust your Google Maps',
path:'/'
},
{
title: 'Geocoded Counts',
titleDesc: 'Shows the number of module objects geocoded, grouped by geocoding response',
path:'/'
},
{
title: 'Geocoding Test',
titleDesc: 'Run a single geocoding test with detailed display results.',
path:'/'
},
{
title: 'Geocode Addresses',
titleDesc: 'Geocode your object addreses. This process may take a few minutes!',
path:'/'
},
{
title: 'Address Cache',
titleDesc: 'Provides access to Address Cache information. This is only cache.',
path:'/'
},
]
}
];
adminPanelData: AdminMetadataModel[] = [];
constructor(protected configs: SystemConfigStore) {
constructor(protected adminMetaData: AdminMetadataStore) {
}
ngOnInit(): void {
const adminData: AdminMetadata = this.adminMetaData.getAdminPanel();
this.setData(adminData);
}
protected setData(adminData: AdminMetadata): void {
adminData.forEach(({icon, titleLabelKey, descriptionLabelKey, linkGroup}) => {
this.adminPanelData.push({
icon,
titleLabelKey,
descriptionLabelKey,
linkGroup: this.setLinkGroups(linkGroup)
});
});
}
protected setLinkGroups(groupData: AdminLinkGroupModel): AdminLinkGroupModel[] {
let linkGroups: AdminLinkGroupModel[] = [];
let linkGroupKeys: string[] = Object.keys(groupData);
for (let j = 0; j < linkGroupKeys.length; j++) {
let linkGroup = groupData[linkGroupKeys[j]];
let links = Object.values(linkGroup);
for (let i = 0; i < links.length; i++) {
linkGroups.push(links[i]);
}
}
return linkGroups;
}
}

View file

@ -39,7 +39,6 @@ import {AdminCardModule} from '../admin-card/admin-card.module';
imports: [
CommonModule,
AdminCardModule
]
})
export class AdminPanelModule {

View file

@ -144,7 +144,7 @@ export class AppInit {
});
routes.push({
path: 'administration/new',
path: 'administration/index',
component: AdminPanelComponent,
canActivate: [AuthGuard],
runGuardsAndResolvers: 'always',

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After