chore: copia de libs/shared con prefijo Pi y APIs de terceros actualizadas

This commit is contained in:
2026-07-27 21:14:48 +02:00
parent 0758b00d54
commit 771d83c0aa
672 changed files with 47352 additions and 4692 deletions

View File

@@ -0,0 +1,40 @@
import { Directive, ElementRef, HostBinding, Input, OnInit, Renderer2 } from '@angular/core';
import { unit } from '../models';
@Directive()
export abstract class PiBaseAffixDirective implements OnInit {
@HostBinding('class') get classes(): string {
return ['pi-affix', `pi-affix__appearance--${this.appearance}`].join(' ');
}
@Input() unit: unit | string | undefined;
@Input() appearance: 'ghost' | 'fill' = 'ghost';
constructor(private renderer: Renderer2, private el: ElementRef) {}
ngOnInit() {
if (this.unit) {
const span = this.renderer.createElement('span');
const text = this.renderer.createText(this.unit);
this.renderer.appendChild(span, text);
this.renderer.appendChild(this.el.nativeElement, span);
}
}
}
@Directive({
standalone: false,
selector: 'pi-prefix',
})
export class PiPrefixDirective extends PiBaseAffixDirective {
@HostBinding('class') className = 'pi-affix__prefix';
}
@Directive({
standalone: false,
selector: 'pi-suffix',
})
export class PiSuffixDirective extends PiBaseAffixDirective {
@HostBinding('class') className = 'pi-affix__suffix';
}

View File

@@ -0,0 +1,3 @@
export * from './affix.directive';
export * from './util-affix.module';

View File

@@ -0,0 +1,10 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { PiPrefixDirective, PiSuffixDirective } from './affix.directive';
@NgModule({
imports: [CommonModule],
declarations: [PiPrefixDirective, PiSuffixDirective],
exports: [PiPrefixDirective, PiSuffixDirective],
})
export class PiSharedUtilAffixModule {}

View File

@@ -0,0 +1,44 @@
import { isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AmplitudeService {
private amp: any;
private ampEnabled = false;
constructor(@Inject(PLATFORM_ID) public platformId: object) {
if (isPlatformBrowser(this.platformId)) {
this.amp = (window as any).amplitude;
if (this.amp) this.ampEnabled = true;
}
}
trackEvent(eventName: string, extraParam?: any): void {
if (!this.ampEnabled) {
this.enableCheck();
return;
}
this.amp.logEvent(eventName, extraParam);
console.log('Event tracked: ', eventName, extraParam);
}
identifyUser(tag: string, data: unknown): void {
const identify1: unknown = new this.amp.Identify().set(tag, data);
this.amp.identify(identify1);
}
identifyUserMultiple(data: unknown): void {
const identify: unknown = new this.amp.setUserProperties(data);
this.amp.identify(identify);
}
setUserId(userId: string): void {
this.amp.setUserId(userId);
}
private enableCheck(): void {
if (!this.ampEnabled) console.log('Amplitude is not working');
}
}

View File

@@ -0,0 +1,70 @@
export const AMP_EVENTS: Record <string, string> = {
// PUBLIC EVENTS
APP_OPENED: 'App Opened',
GOOGLE_SIGNIN_SUCCESS: 'Sign in with Google Success',
LOGIN_SUCCESS: 'Login Success',
USER_REGISTERED: 'User Registered',
WEB_OPENED: 'Web Opened',
// AUTHENTICATION
EMAIL_PASSWORD_PAGE_VIEWED: 'Email and Password Page Viewed',
PERSONAL_INFORMATION_PAGE_VIEWED: 'Personal Information and User Type Page Viewed',
PERSONAL_INFO_UPDATED: 'Personal Information Updated',
// PRIVATE EVENTS
// COMPANY FORM
COMPANY_DETAILS_VIEWED: 'Company Details Viewed',
COMPANY_LIST_VIEWED: 'Company List Viewed',
COMPANY_MODIFIED: 'Company Modified',
COMPANY_REGISTERED: 'Company Registered',
// DEALS
CONNECTED_COMPANY: 'Connected Company',
DEAL_ACCEPTED: 'Deal Accepted',
DEAL_VIEWED: 'Deal Viewed',
DEALBOX_VIEWED: 'Dealbox Viewed',
// QA
QA_SESSION_CREATED: 'QA Session Created',
QUESTION_ADDED: 'Question Added',
QUESTION_ANSWERED: 'Question Answered',
QUESTIONS_SENT: 'Questions Sent',
ANSWERS_SENT: 'Answers Sent',
// INVESTOR FORM
SUBSCRIBE: 'Subscribe',
SUBSCRIBE_PRICING_PAGE_VIEWED: 'Subscribe Pricing Page Viewed',
USER_SUBSCRIBED: 'User Subscribed',
THESIS_SAVED: 'Thesis Saved',
THESIS_FORM_VIEWED: 'Thesis Form Viewed',
SUBSCRIBE_PRICING_VIEW: 'Subscribe Pricing Page Viewed',
// COMPANY PUBLIC
BASIC_PANEL_SAVED: 'Basic Panel Saved',
PROFIT_LOSS_PANEL_SAVED: ' Profit And Loss Panel Saved',
BALANCE_PANEL_SAVED: 'Balance Panel Saved',
BUSINESS_PLAN_PANEL_SAVED: 'Business Plan Panel Saved',
TEAM_PANEL_SAVED: 'Team Panel Saved',
INVESTMENT_TRANSACTION_SAVED: 'Investment Transaction Panel Saved',
PRODUCTS_CLIENTS_SAVED: 'Products And Clients Panel Saved',
MY_COMPANY_OPENED: 'My Company Opened',
TEASER_TYPEFORM_OPENED: 'Teaser Typeform Opened',
TEASER_TYPEFORM_MODAL_OPENED: 'Teaser Typeform Modal Opened',
TEASER_TYPEFORM_MODAL_CLOSED: 'Teaser Typeform Modal Closed',
// INVESTOR LIST
INVESTOR_LIST_VIEWED: 'Investor List Viewed',
TEASER_DOWNLOADED: 'Teaser Downloaded',
// ADVISOR
ADVISOR_COMPANY_CREATION_FORM_PAGE_VIEWED: 'Advisor Company Creation Form Page Viewed',
ADVISOR_COMPANY_REGISTERED: 'Advisor Company Registered',
ADVISOR_COMPANY_MODIFIED: 'Advisor Company Modified',
// WEB
VALORAR_MI_EMPRESA_TYPEFORM_MODAL_OPENED: 'Valorar Mi Empresa Typeform Modal Opened',
VENDER_MI_EMPRESA_TYPEFORM_MODAL_OPENED: 'Vender Mi Empresa Typeform Modal Opened'
};

View File

@@ -0,0 +1,5 @@
// Amplitude services
export * from './amplitude.service';
// Events
export * from './events';

View File

@@ -0,0 +1,116 @@
import { Directive, HostBinding, Input, OnInit } from '@angular/core';
import { density, mode, opacity, styleType } from '../../../styles';
@Directive()
export abstract class PiBaseColor implements OnInit {
@HostBinding('class') get commonClasses(): string {
return [this.densityClass, this.modeClass].join(' ');
}
@Input() density: density = '';
@Input() opacity: opacity = '100';
@Input() mode: mode = 'dark';
ngOnInit(): void {
this.densityClass;
}
protected get densityClass(): string {
return !!this.density ? `pi-color__density--${this.density}` : '';
}
protected get modeClass(): string {
return this.mode && `pi-color__mode--${this.mode}`;
}
}
@Directive({
standalone: false,
selector: '[pi-dynamic-color]',
})
export class PiDynamicColorDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return this.colorClass;
}
@Input() color!: styleType;
protected get colorClass(): string {
return !!this.color && `pi-color__${this.color}`;
}
}
@Directive({
standalone: false,
selector: '[pi-primary]',
})
export class PiColorPrimaryDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__primary';
}
}
@Directive({
standalone: false,
selector: '[pi-accent]',
})
export class PiColorAccentDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__accent';
}
}
@Directive({
standalone: false,
selector: '[pi-warn]',
})
export class PiColorWarnDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__warn';
}
}
@Directive({
standalone: false,
selector: '[pi-success]',
})
export class PiColorSuccessDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__success';
}
}
@Directive({
standalone: false,
selector: '[pi-highlighted]',
})
export class PiColorHighlightedDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__highlighted';
}
}
@Directive({
standalone: false,
selector: '[pi-white]',
})
export class PiColorWhiteDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__white';
}
}
@Directive({
standalone: false,
selector: '[pi-grey]',
})
export class PiColorGreyDirective extends PiBaseColor {
@HostBinding('class') get classes(): string {
return 'pi-color__grey';
}
}
@Directive({
standalone: false,
selector: '[pi-black]',
})
export class PiColorBlackDirective {
@HostBinding('class') get classes(): string {
return 'pi-color__black';
}
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,3 @@
export * from './util-color.module';
export * from './directives/colors.directive';

View File

@@ -0,0 +1,42 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import {
PiColorAccentDirective,
PiColorBlackDirective,
PiColorWarnDirective,
PiColorGreyDirective,
PiColorHighlightedDirective,
PiColorPrimaryDirective,
PiColorSuccessDirective,
PiColorWhiteDirective,
PiDynamicColorDirective,
} from './directives/colors.directive';
@NgModule({
imports: [CommonModule, RouterModule],
declarations: [
PiDynamicColorDirective,
PiColorPrimaryDirective,
PiColorAccentDirective,
PiColorSuccessDirective,
PiColorWarnDirective,
PiColorHighlightedDirective,
PiColorWhiteDirective,
PiColorGreyDirective,
PiColorBlackDirective,
],
exports: [
PiDynamicColorDirective,
PiColorPrimaryDirective,
PiColorAccentDirective,
PiColorSuccessDirective,
PiColorWarnDirective,
PiColorHighlightedDirective,
PiColorWhiteDirective,
PiColorGreyDirective,
PiColorBlackDirective,
],
})
export class PiSharedUtilColorModule {}

View File

@@ -0,0 +1,159 @@
// Angular
import { HttpParams } from '@angular/common/http';
import { Inject, PLATFORM_ID } from '@angular/core';
// Shared
import { Params } from '../models';
import { timer } from 'rxjs';
export class Commons {
constructor(@Inject(PLATFORM_ID) public platformId: object) {}
static followUrl($event: MouseEvent, item: { url: string }): void {
Commons.followExternalUrl($event, item);
}
static followExternalUrl(
$event: MouseEvent,
item: { url: string; externalLink?: boolean }
): void {
if (
item &&
item.url &&
(item.externalLink || $event.button === 1 || ($event.button === 0 && $event.ctrlKey))
) {
Commons.openExternalUrl(item.url);
}
}
static openExternalSelfUrl(url: string): void {
Commons.openExternalUrl(url, '_self');
}
static openExternalUrlWithoutParams(url: string, target?: string): void {
window.open(url, target || '_blank');
}
static openExternalUrl(url: string, target?: string): void {
window.open(url + window.location.search, target || '_blank');
}
static downloadPDFBase64(pdf: string, name: string): void {
const linkSource = `data:application/pdf;base64,${pdf}`;
const downloadLink: HTMLAnchorElement = document.createElement('a');
const fileName: string = name;
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
}
static downloadPDF(url: string, name: string, target?: string): void {
const targetLink = target || '_self';
let link = document.createElement('a');
link.href = url;
link.download = name;
link.target = targetLink;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
link = null;
}
static getDefaultLanguage(): string {
return navigator.language.split('-')[0] as string;
}
static getFullName(user: { name: string; lastName: string }): string {
if (!user || !user.name) {
return ' ';
}
if (!user.lastName) {
return user.name;
}
return `${user.name} ${user.lastName}`;
}
static setParamsToQueryUrl(obj: Params): string {
let params: string = '';
for (const key in obj) {
if (obj[key]) {
const element = obj[key];
params = params + '&' + key + '=' + element;
}
}
return params;
}
static isEmpty(obj: unknown): boolean {
if (!obj) {
return true;
}
return JSON.stringify(obj) === JSON.stringify({});
}
static isNotEmpty(obj: unknown): boolean {
return !this.isEmpty(obj);
}
static isStringEmpty = (str: string) => !str?.length;
static buildRangeString(
minValue: number,
maxValue: number,
unit: string,
defaultLocale?: string
): string {
if (!!minValue && !!maxValue) {
return `${minValue}${unit} - ${maxValue}${unit}`;
}
if (!!minValue) {
return `>${minValue}${unit}`;
}
if (!!maxValue) {
return `<${maxValue}${unit}`;
}
return defaultLocale;
}
static removeEmptyValuesFromObject(obj: unknown): unknown {
return Object.fromEntries(
Object.entries(obj).filter(
([_, v]) => v != null && v !== '' && (v as Array<string>).length !== 0
)
);
}
static removeNullValuesFromQueryParams(params: HttpParams) {
const paramsKeysAux = params.keys();
paramsKeysAux.forEach((key) => {
const value = params.get(key);
if (value === null || value === undefined || value === '' || value === 'null') {
params['map'].delete(key);
}
});
return params;
}
static removeDiacriticsFromString(str: string): string {
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
static getScrollParent(node: HTMLElement): HTMLElement {
if (node == null) {
return null;
}
if (node.scrollHeight > node.clientHeight) {
return node;
} else {
return this.getScrollParent(node.parentNode as HTMLElement);
}
}
static createIntervalUsingTimer(delayValue1: number, delayValue2: number) {
return timer(delayValue1, delayValue2);
}
static isNotNullish(value: unknown): boolean {
return value !== undefined && value !== null;
}
}

View File

@@ -0,0 +1,4 @@
// Common
export * from './common';
export * from './sector';
export * from './select-options';

View File

@@ -0,0 +1,26 @@
// Angular
import { Inject, PLATFORM_ID } from '@angular/core';
import { Sector } from '../models';
import { SectorType, SectorUidMatchLocale } from '../models';
export class SectorCommons {
constructor(@Inject(PLATFORM_ID) public platformId: object) {}
static getSectorLocaleFromUid(uid: string): string {
const sectorLocale: string = SectorUidMatchLocale[uid as SectorType];
return `common.sectors.${sectorLocale}`;
}
static getLocaleListFromUidList(sectorUids: Array<string>): Array<string> {
return sectorUids?.map((uid) => SectorCommons.getSectorLocaleFromUid(uid));
}
static convertUidListToSectorList(sectorUids: Array<string>, sectors: Array<Sector>): Array<Sector> {
return sectors?.filter((sector) => sectorUids?.includes(sector.sectorUid));
}
static convertSectorListToUidList(sectors: Array<Sector>): Array<string> {
return sectors?.map((sector) => sector?.sectorUid);
}
}

View File

@@ -0,0 +1,21 @@
import { Inject, PLATFORM_ID } from '@angular/core';
import { SelectOption } from '../../ui/select';
export class SelectOptionsCommons {
constructor(@Inject(PLATFORM_ID) public platformId: object) {}
static convertStringstoSelectOptions(
values: Array<any>,
options: Array<SelectOption>
): Array<SelectOption> {
return options?.filter((item) => values?.includes(item.value));
}
static convertSelectOptionstoStrings(options: Array<SelectOption>): Array<any> {
return options?.map((item) => item?.value);
}
static getSelectOptionValue(value: string, options: Array<any>): any {
return options?.find((item) => item?.value == value);
}
}

View File

@@ -0,0 +1 @@
export const datoCms = "https://graphql.datocms.com/"; // our GraphQL API

View File

@@ -0,0 +1 @@
export * from './util-dato-cms.module';

View File

@@ -0,0 +1,36 @@
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
// Apollo
import { InMemoryCache } from '@apollo/client/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
// Shared
import { environment } from '../environments';
// datoCMS
import { datoCms } from './graphql.module';
/**
* Sustituye a `PiSharedUtilDatoCmsModule`: apollo-angular ya no expone
* `ApolloModule`, la configuración se hace con providers.
*/
export function providePiDatoCms(): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
cache: new InMemoryCache(),
link: httpLink.create({
uri: datoCms,
headers: {
Authorization: environment.DATO_KEY,
} as any,
}),
};
},
deps: [HttpLink],
},
]);
}

View File

@@ -0,0 +1,35 @@
// Angular
import { Injectable } from '@angular/core';
// Ngrx
import { Store } from '@ngrx/store';
// Rxjs
import { Observable } from 'rxjs';
// State
import { Browser, Device, Size } from '../entities/device-configs.model';
import * as fromActions from '../state/device-configs.actions';
import * as fromState from '../state/device-configs.reducer';
import * as fromSelectors from '../state/device-configs.selectors';
@Injectable({ providedIn: 'root' })
export class DeviceConfigsFacade {
// Device
readonly device$: Observable<Device> = this.store.select(fromSelectors.getDevice);
readonly size$: Observable<Size> = this.store.select(fromSelectors.getSize);
readonly isSmallDevice$: Observable<boolean> = this.store.select(fromSelectors.getIsSmallDevice);
// Browser
readonly browser$: Observable<Browser> = this.store.select(fromSelectors.getBrowser);
constructor(private store: Store<fromState.DeviceConfigsState>) {}
getBrowser(): void {
this.store.dispatch(fromActions.BrowserActions.getBrowser());
}
setDevice(device: Device): void {
this.store.dispatch(fromActions.DeviceActions.saveDeviceType({ device }));
}
}

View File

@@ -0,0 +1,19 @@
// BreakPoint Observer
// Main Breakpoints
export const MaxMobileBreakpoint = '(max-width: 640px)';
export const MinTabletBreakpoint = '(min-width: 640px)';
export const MaxTabletBreakpoint = '(max-width: 1240px)';
export const MinDesktopBreakpoint = '(min-width: 1240px)';
// Inner Breakpoints
export const MinTabletSmallBreakpoint = '(min-width: 640px)';
export const MaxTabletSmallBreakpoint = '(max-width: 992px)';
export const MinTabletLargeBreakpoint = '(min-width: 992px)';
export const MaxTabletLargeBreakpoint = '(max-width: 1240px)';
export const MinDesktopSmallBreakpoint = '(min-width: 1240px)';
export const MaxDesktopSmallBreakpoint = '(max-width: 1440px)';
export const MinDesktopMediumBreakpoint = '(min-width: 1440px)';
export const MaxDesktopMediumBreakpoint = '(max-width: 1920px)';
export const MinDesktopLargeBreakpoint = '(min-width: 1920px)';

View File

@@ -0,0 +1,5 @@
export type Device = 'MOBILE' | 'TABLET' | 'DESKTOP';
export type Size = 'SMALL' | 'MEDIUM' | 'LARGE';
export type Browser = 'EXPLORER' | 'FIREFOX' | 'EDGE' | 'CHROME' | 'SAFARI' | 'UNKNOWN';

View File

@@ -0,0 +1,18 @@
// Application
export * from './application/device-configs.facade';
// Entities
export * from './entities/breakpoints';
export * from './entities/device-configs.model';
// Services
export * from './services/device-configs.service';
// State
export * from './state/device-configs.actions';
export * from './state/device-configs.effects';
export * from './state/device-configs.selectors';
export * from './state/device-configs.reducer';
import * as DeviceTypeActions from './state/device-configs.actions';
import * as DeviceTypeSelectors from './state/device-configs.selectors';
export { DeviceTypeActions, DeviceTypeSelectors };

View File

@@ -0,0 +1,176 @@
import { BreakpointObserver } from '@angular/cdk/layout';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { Store } from '@ngrx/store';
// Entities
import {
MaxDesktopMediumBreakpoint,
MaxDesktopSmallBreakpoint,
MaxMobileBreakpoint,
MaxTabletBreakpoint,
MaxTabletLargeBreakpoint,
MaxTabletSmallBreakpoint,
MinDesktopBreakpoint,
MinDesktopLargeBreakpoint,
MinDesktopMediumBreakpoint,
MinDesktopSmallBreakpoint,
MinTabletBreakpoint,
MinTabletLargeBreakpoint,
MinTabletSmallBreakpoint,
} from '../entities/breakpoints';
import { Browser } from '../entities/device-configs.model';
// State
import * as fromActions from '../state/device-configs.actions';
import { DeviceConfigsState } from '../state/device-configs.reducer';
@Injectable({
providedIn: 'root',
})
export class DeviceConfigsService {
private htmlTag!: HTMLElement;
constructor(
private store: Store<DeviceConfigsState>,
private breakpointObserver: BreakpointObserver,
@Inject(DOCUMENT) private document: Document,
@Inject(PLATFORM_ID) public platformId: Object
) {
this.deviceConfigsHandler();
}
getUserBrowser(): Browser {
const userAgent = window.navigator.userAgent;
if (/MSIE|Trident/.test(userAgent)) {
return 'EXPLORER';
} else if (/Firefox/.test(userAgent)) {
return'FIREFOX';
} else if (/Edg|Edge/.test(userAgent)) {
return 'EDGE';
} else if (/Chrome/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
return 'CHROME';
} else if (/Safari/.test(userAgent) && !/Edg|Edge/.test(userAgent)) {
return 'SAFARI';
} else {
return 'UNKNOWN';
}
}
private deviceConfigsHandler(): void {
if (isPlatformBrowser(this.platformId)) {
this.htmlTag = this.document?.documentElement;
this.breakpointObserver
.observe([
MaxMobileBreakpoint,
MinTabletBreakpoint,
MaxTabletBreakpoint,
MinDesktopBreakpoint,
// Inner
MinTabletSmallBreakpoint,
MaxTabletSmallBreakpoint,
MinTabletLargeBreakpoint,
MaxTabletLargeBreakpoint,
MinDesktopSmallBreakpoint,
MaxDesktopSmallBreakpoint,
MinDesktopMediumBreakpoint,
MaxDesktopMediumBreakpoint,
MinDesktopLargeBreakpoint,
])
.subscribe((result) => {
if (result.matches) {
// Mobile
if (result.breakpoints[MaxMobileBreakpoint]) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceType({ device: 'MOBILE' }));
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }));
this.setClassIsMobile();
}
// Tablet
if (
result.breakpoints[MinTabletBreakpoint] &&
result.breakpoints[MaxTabletBreakpoint]
) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceType({ device: 'TABLET' }));
this.setClassIsTablet();
// Tablet S
if (
result.breakpoints[MinTabletSmallBreakpoint] &&
result.breakpoints[MaxTabletSmallBreakpoint]
) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }));
this.setClassIsTabletSmall();
}
// Tablet L
if (
result.breakpoints[MinTabletLargeBreakpoint] &&
result.breakpoints[MaxTabletLargeBreakpoint]
) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'LARGE' }));
this.setClassIsTabletLarge();
}
}
// Desktop
if (result.breakpoints[MinDesktopBreakpoint]) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceType({ device: 'DESKTOP' }));
this.setClassIsDesktop();
// Desktop S
if (
result.breakpoints[MinDesktopSmallBreakpoint] &&
result.breakpoints[MaxDesktopSmallBreakpoint]
) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'SMALL' }));
this.setClassIsDesktopSmall();
}
// Desktop M
if (
result.breakpoints[MinDesktopMediumBreakpoint] &&
result.breakpoints[MaxDesktopMediumBreakpoint]
) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'MEDIUM' }));
this.setClassIsDesktopMedium();
}
// Desktop L
if (result.breakpoints[MinDesktopLargeBreakpoint]) {
this.store.dispatch(fromActions.DeviceActions.saveDeviceSize({ size: 'LARGE' }));
this.setClassIsDesktopLarge();
}
}
}
});
}
}
private setClassIsMobile(): void {
this.htmlTag.setAttribute('class', 'is-mobile');
}
private setClassIsTablet(): void {
this.htmlTag.setAttribute('class', 'is-tablet');
}
private setClassIsTabletSmall(): void {
this.htmlTag.setAttribute('class', 'is-tablet is-tablet__small');
}
private setClassIsTabletLarge(): void {
this.htmlTag.setAttribute('class', 'is-tablet is-tablet__large');
}
private setClassIsDesktop(): void {
this.htmlTag.setAttribute('class', 'is-desktop');
}
private setClassIsDesktopSmall(): void {
this.htmlTag.setAttribute('class', 'is-desktop is-desktop__small');
}
private setClassIsDesktopMedium(): void {
this.htmlTag.setAttribute('class', 'is-desktop is-desktop__medium');
}
private setClassIsDesktopLarge(): void {
this.htmlTag.setAttribute('class', 'is-desktop is-desktop__large');
}
}

View File

@@ -0,0 +1,22 @@
import { createActionGroup, emptyProps, props } from '@ngrx/store';
// Entities
import { Device, Size, Browser } from '../entities/device-configs.model';
export const DeviceActions = createActionGroup({
source: '[Main] Get Device Breakpoints',
events: {
'Save Device Type': props<{ device: Device }>(),
'Save Device Size': props<{ size: Size }>()
},
});
export const BrowserActions = createActionGroup({
source: '[Main] Get Browser',
events: {
'Get Browser': emptyProps(),
'Get Browser Success': props<{ response: Browser }>(),
'Get Browser Error': emptyProps(),
},
});

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
// Rxjs
import { of } from 'rxjs';
import { catchError, exhaustMap, map } from 'rxjs/operators';
// Ngrx
import { Actions, createEffect, ofType } from '@ngrx/effects';
// Actions
import * as fromActions from './device-configs.actions';
// Service
import { DeviceConfigsService } from '../services/device-configs.service';
@Injectable()
export class DeviceConfigsEffects {
constructor(private deviceConfigsService: DeviceConfigsService, private actions: Actions) {}
getBrowser$ = createEffect(() => {
return this.actions.pipe(
ofType(fromActions.BrowserActions.getBrowser),
exhaustMap(() =>
of(this.deviceConfigsService.getUserBrowser()).pipe(
map((response) => {
return fromActions.BrowserActions.getBrowserSuccess({ response });
}),
catchError(() => {
return of(fromActions.BrowserActions.getBrowserError());
})
)
)
);
});
}

View File

@@ -0,0 +1,32 @@
import { createReducer, on } from '@ngrx/store';
import * as fromActions from './device-configs.actions';
import { Device, Size, Browser } from '../entities/device-configs.model';
export const featureKey = 'CORE_deviceConfigs';
export interface DeviceConfigsState {
device: Device;
size: Size;
browser: Browser
}
export const initialState: DeviceConfigsState = {
device: 'DESKTOP',
size: null,
browser: 'UNKNOWN'
};
export const deviceConfigsReducer = createReducer(
initialState,
on(fromActions.DeviceActions.saveDeviceType, (state, action) => {
return { ...state, device: action.device };
}),
on(fromActions.DeviceActions.saveDeviceSize, (state, action) => {
return { ...state, size: action.size };
}),
on(fromActions.BrowserActions.getBrowserSuccess, (state, action) => {
return { ...state, browser: action.response };
})
);

View File

@@ -0,0 +1,19 @@
import { createSelector, createFeatureSelector } from '@ngrx/store';
import * as fromReducer from './device-configs.reducer';
export const getFeatureState = createFeatureSelector<fromReducer.DeviceConfigsState>(
fromReducer.featureKey
);
// Device Breakpoints
export const getDevice = createSelector(getFeatureState, (state) => state.device);
export const getSize = createSelector(getFeatureState, (state) => state.size);
// Mobile & Tablet S
export const getIsSmallDevice = createSelector(getFeatureState, (state) => {
return state.device !== 'DESKTOP' && state.size !== 'LARGE';
});
// Browser
export const getBrowser = createSelector(getFeatureState, (state) => state.browser);

View File

@@ -0,0 +1,26 @@
export const environment: any = {
production: false,
mandatorySignIn: false,
authenticationFlowType: 'USER_SRP_AUTH',
identityPoolId: 'eu-west-1:25f8388c-3826-4d38-acaa-0bfcb3f6251f',
region: 'eu-west-1',
identityPoolRegion: 'eu-west-1',
userPoolId: 'eu-west-1_vYyt3eApk',
userPoolWebClientId: '4bss36r712padssbhco9vg2uej',
API: 'https://d1u3b69g4c.execute-api.eu-west-1.amazonaws.com/staging/',
CREATE_LEAD_INVESTOR:
'https://d1u3b69g4c.execute-api.eu-west-1.amazonaws.com/staging/lead/investor',
COGNITO_ID: '4bss36r712padssbhco9vg2uej',
INTERCOM_IDENTITY_VERIFICATION_SECRET: 'jpIdk_gk6pGDaJkTOMpfEWzhaoTKCi6zpR-GbkRi',
DATO_KEY: 'Bearer ca9c292a00a5ca85e7b08e5a04185a',
DATO_DEV_ENVIRONMENT: 'development',
DATO_PROD_ENVIRONMENT: 'master',
oauth: {
domain: 'staging-oauth.deale.es',
scope: ['phone', 'email', 'openid', 'profile', 'aws.cognito.signin.user.admin'],
redirectSignIn: 'https://staging-app.deale.es/auth',
redirectSignOut: 'https://staging-app.deale.es/auth',
responseType: 'code',
},
federationTarget: 'COGNITO_USER_POOLS',
};

View File

@@ -0,0 +1,25 @@
export const environment: any = {
production: true,
identityPoolId: 'eu-west-1:571db516-5c84-40bd-bd16-aeb121805dea',
region: 'eu-west-1',
identityPoolRegion: 'eu-west-1',
userPoolId: 'eu-west-1_nZwcGc84a',
userPoolWebClientId: '31k1diku58oftoddd35l5ablri',
mandatorySignIn: false,
authenticationFlowType: 'USER_SRP_AUTH',
API: 'https://9e02sawl11.execute-api.eu-west-1.amazonaws.com/prod/',
CREATE_LEAD_INVESTOR: 'https://9e02sawl11.execute-api.eu-west-1.amazonaws.com/prod/lead/investor',
COGNITO_ID: '31k1diku58oftoddd35l5ablri',
INTERCOM_IDENTITY_VERIFICATION_SECRET: "jpIdk_gk6pGDaJkTOMpfEWzhaoTKCi6zpR-GbkRi",
DATO_KEY: 'Bearer ca9c292a00a5ca85e7b08e5a04185a',
DATO_DEV_ENVIRONMENT: 'development',
DATO_PROD_ENVIRONMENT: 'master',
oauth: {
domain: 'oauth.deale.es',
scope: ['phone', 'email', 'openid', 'profile', 'aws.cognito.signin.user.admin'],
redirectSignIn: 'https://app.deale.es/auth',
redirectSignOut: 'https://app.deale.es/auth',
responseType: 'code',
},
federationTarget: 'COGNITO_USER_POOLS',
};

View File

@@ -0,0 +1,39 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment: any = {
production: false,
mandatorySignIn: false,
authenticationFlowType: 'USER_SRP_AUTH',
identityPoolId: 'eu-west-1:25f8388c-3826-4d38-acaa-0bfcb3f6251f',
region: 'eu-west-1',
identityPoolRegion: 'eu-west-1',
userPoolId: 'eu-west-1_vYyt3eApk',
userPoolWebClientId: '4bss36r712padssbhco9vg2uej',
API: 'https://d1u3b69g4c.execute-api.eu-west-1.amazonaws.com/staging/',
CREATE_LEAD_INVESTOR:
'https://d1u3b69g4c.execute-api.eu-west-1.amazonaws.com/staging/lead/investor',
COGNITO_ID: '4bss36r712padssbhco9vg2uej',
INTERCOM_IDENTITY_VERIFICATION_SECRET: 'jpIdk_gk6pGDaJkTOMpfEWzhaoTKCi6zpR-GbkRi',
DATO_KEY: 'Bearer ca9c292a00a5ca85e7b08e5a04185a',
DATO_DEV_ENVIRONMENT: 'development',
DATO_PROD_ENVIRONMENT: 'master',
oauth: {
domain: 'staging-oauth.deale.es',
scope: ['phone', 'email', 'openid', 'profile', 'aws.cognito.signin.user.admin'],
redirectSignIn: 'https://staging-app.deale.es/auth',
redirectSignOut: 'https://staging-app.deale.es/auth',
responseType: 'code',
},
federationTarget: 'COGNITO_USER_POOLS',
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@@ -0,0 +1 @@
export * from './environment';

View File

@@ -0,0 +1,52 @@
import { Injectable, Injector } from '@angular/core';
import { ControlContainer, ControlValueAccessor } from '@angular/forms';
@Injectable()
export class PiFormControl implements ControlValueAccessor {
value: number | string | Array<string> | boolean;
onChange = (value: number | string | Array<string> | boolean) => {};
onTouched = () => {};
disabled = false;
focused = false;
touched = false;
constructor(protected injector: Injector) {}
get controlContainer(): any {
return this.injector.get(ControlContainer);
}
writeValue(value: number | string | Array<string> | boolean) {
this.value = value;
}
registerOnChange(onChange: any) {
this.onChange = onChange;
}
registerOnTouched(onTouched: any) {
this.onTouched = onTouched;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
setDisabledState(disabled: boolean) {
this.disabled = disabled;
}
onFocus(): void {
this.focused = true;
}
onBlur(): void {
this.focused = false;
}
}

View File

@@ -0,0 +1 @@
export * from './form-control';

View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { customIcons } from './icons.data';
@Injectable({
providedIn: 'root',
})
export class IconRegistryService {
constructor(
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer
) {}
init(): void {
this.registerIcons(customIcons, 'assets/icons/');
}
registerIcons(icons: Array<string>, path: string) {
icons.forEach((icon) => {
this.matIconRegistry.addSvgIcon(
icon,
this.domSanitizer.bypassSecurityTrustResourceUrl(path + icon + '.svg')
);
});
}
}

View File

@@ -0,0 +1,167 @@
export const customIcons = [
'add-stroked',
'add',
'agriculture',
'arrow-back',
'arrow-down',
'arrow-forward',
'arrow-secondary-back',
'arrow-secondary-down',
'arrow-secondary-forward',
'arrow-secondary-up',
'arrow-up',
'arrow-up-rounded',
'attachment',
'bankruptcy',
'biotech',
'briefcase',
'box',
'buildings',
'calendar',
'calendar-plus',
'calendly',
'caret',
'chat',
'check-rounded',
'check',
'chemistry-and-materials',
'chevron-back',
'chevron-down',
'chevron-forward',
'chevron-up',
'choose',
'circle',
'click',
'clip',
'close-rounded',
'close',
'coin',
'coins',
'connect',
'construction',
'consumer',
'dashboard',
'dealbox',
'details',
'diagram',
'dialog',
'distribution',
'document',
'document-add',
'document-search',
'document-euro',
'document-substract',
'dollar',
'double-chevron-back',
'download-teaser',
'download',
'due-diligence',
'e-commerce',
'ebitda',
'edit',
'education',
'email',
'email-plus',
'enter',
'entertainment',
'estimation-time',
'euro',
'exclamation',
'exclusivity',
'expand',
'eye-off',
'eye',
'file-coins',
'filter',
'financial-services',
'flag-grey',
'focus',
'globe',
'google',
'graphic',
'grid',
'hamburguer',
'headquarters',
'healthcare',
'hospitality',
'industrial',
'info',
'it-internet',
'legal',
'light-bulb',
'link',
'linkedin',
'list',
'location',
'lock',
'login',
'logistics',
'manufacturing',
'marketing-media',
'meeting',
'messages',
'messages-left',
'messy-coin-stack',
'metric-down-arrow',
'metric-up-arrow',
'mining',
'mobility',
'money-transfer',
'more',
'my-company',
'nif-company',
'notification-off',
'notification-on',
'operations',
'opportunities',
'optimum',
'other-services',
'percentage',
'person-lines',
'phone',
'phone-arrow',
'physical',
'play-arrow',
'premium',
'pricing',
'professional-services',
'public-administration',
'purchase-mandates',
'question',
'raw-materials',
'real-estate',
'search',
'select',
'sell-mandates',
'send-information',
'shareholders',
'signature',
'sort',
'star-primary',
'star-fill',
'star-outline',
'substract-stroked',
'substract',
'support',
'telecomunications',
'telephone',
'telephone-fill',
'time-detail',
'time-zone',
'transmission',
'transport-logistics',
'trash',
'twitter',
'upload',
'user-profile',
'user-profile-advisor',
'user-profile-company',
'user',
'utilities',
'verification-list',
'verified-money',
'website',
'whatsapp',
'youtube',
'zoom',
];

View File

@@ -0,0 +1,3 @@
export * from './util-icon-registry.module';
export * from './icon-registry.service';

View File

@@ -0,0 +1,16 @@
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { IconRegistryService } from './icon-registry.service';
@NgModule({
providers: [
{
provide: APP_INITIALIZER,
useFactory: (iconRegistryService: IconRegistryService) => {
return () => iconRegistryService.init();
},
deps: [IconRegistryService],
multi: true,
},
],
})
export class PiSharedUtilIconRegistryModule {}

View File

@@ -0,0 +1 @@
export * from './infinite-scroll.data';

View File

@@ -0,0 +1,6 @@
export enum INFINITE_SCROLL {
page = 1,
limit = 12,
throttle = 50,
scrollDistance = 2,
}

View File

@@ -0,0 +1,28 @@
// Angular
import { Injectable } from '@angular/core';
// Ngrx
import { Store } from '@ngrx/store';
// Rxjs
import { Observable } from 'rxjs';
// State
import * as fromSelectors from '../state/list-configs.selectors';
import * as fromActions from '../state/list-configs.actions';
import * as fromState from '../state/list-configs.reducer';
@Injectable({ providedIn: 'root' })
export class ListConfigsFacade {
readonly mode$: Observable<fromState.ListMode> = this.store.select(fromSelectors.getListMode);
constructor(private store: Store<fromState.ListConfigsState>) {}
setListMode(payload: fromState.ListMode): void {
this.store.dispatch(fromActions.setListMode({ payload }));
}
resetListMode(): void {
this.store.dispatch(fromActions.resetListMode());
}
}

View File

@@ -0,0 +1,7 @@
// Facade
export * from './application/list-configs.facade';
// State
export * from './state/list-configs.actions';
export * from './state/list-configs.selectors';
export * from './state/list-configs.reducer';

View File

@@ -0,0 +1,15 @@
import { createAction, props } from '@ngrx/store';
import { ListMode } from './list-configs.reducer';
export enum ListModeActionsType {
SET_LIST_MODE = '[Main] Set List Mode',
RESET_LIST_MODE = '[Main] Reset List Mode',
}
export const setListMode = createAction(
ListModeActionsType.SET_LIST_MODE,
props<{ payload: ListMode }>()
);
export const resetListMode = createAction(ListModeActionsType.RESET_LIST_MODE);

View File

@@ -0,0 +1,25 @@
import { createReducer, on } from '@ngrx/store';
import { resetListMode, setListMode } from './list-configs.actions';
export const featureKey = 'CORE_listConfigs';
export interface ListConfigsState {
mode: ListMode;
}
export type ListMode = 'grid' | 'list';
export const initialState: ListConfigsState = {
mode: 'list',
};
export const listConfigsReducer = createReducer(
initialState,
on(setListMode, (state, action) => {
return { ...state, mode: action.payload };
}),
on(resetListMode, (state) => {
return { ...initialState };
})
);

View File

@@ -0,0 +1,9 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromReducer from './list-configs.reducer';
export const getFeatureState = createFeatureSelector<fromReducer.ListConfigsState>(
fromReducer.featureKey
);
export const getListMode = createSelector(getFeatureState, (state) => state.mode);

View File

@@ -0,0 +1,28 @@
// Angular
import { Injectable } from '@angular/core';
// RxJS
import { Observable, Subscribable } from 'rxjs';
// NgRx
import { Store, select } from '@ngrx/store';
// Store
import * as LoaderSelectors from '../state/loader.selectors';
import * as LoaderActions from '../state/loader.actions';
@Injectable()
export class LoaderFacade {
loader$: Observable<boolean> = this.store.pipe(select(LoaderSelectors.getLoader));
showLoader$: Observable<unknown> | Subscribable<unknown> | Promise<unknown>;
constructor(private store: Store) {}
showLoader() {
this.store.dispatch(LoaderActions.showLoader());
}
hideLoader() {
this.store.dispatch(LoaderActions.hideLoader());
}
}

View File

@@ -0,0 +1,11 @@
// Module
export * from './util-loader.module';
// Facade
export * from './aplication/loader.facade';
// State
export * from './state/loader.actions';
export * from './state/loader.reducer';
export * from './state/loader.selectors';

View File

@@ -0,0 +1,6 @@
// NgRx
import { createAction } from '@ngrx/store';
export const showLoader = createAction('[Loader] Show Loader');
export const hideLoader = createAction('[Loader] Hide Loader');

View File

@@ -0,0 +1,37 @@
// NgRx
import { createReducer, on, Action } from '@ngrx/store';
// Store
import * as LoaderActions from './loader.actions';
export const loadersKey = 'CORE_loaderState';
export interface LoaderState {
loader: boolean;
}
export const initialLoaderState: LoaderState = {
loader: false,
};
export const loaderReducer = createReducer(
initialLoaderState,
on(LoaderActions.showLoader, (state) => {
return {
...state,
loader: true,
};
}),
on(LoaderActions.hideLoader, (state) => {
return {
...state,
loader: false,
};
})
);
export function loaderReducerFunction(state: LoaderState | undefined, action: Action) {
return loaderReducer(state, action);
}

View File

@@ -0,0 +1,11 @@
// NgRx
import { createFeatureSelector, createSelector } from '@ngrx/store';
// Store
import { loadersKey, LoaderState } from './loader.reducer';
export const selectLoaderState = createFeatureSelector<LoaderState>(loadersKey);
export const getLoader = createSelector(selectLoaderState, (state) => {
return state.loader;
});

View File

@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { StoreModule } from '@ngrx/store';
import * as fromReducer from './state/loader.reducer';
@NgModule({
imports: [
CommonModule,
StoreModule.forFeature(fromReducer.loadersKey, fromReducer.loaderReducerFunction),
],
})
export class PiSharedUtilLoadersModule {}

View File

@@ -0,0 +1 @@
export * from './local-storage.service';

View File

@@ -0,0 +1,26 @@
// Angular
import { isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class LocalStorageService {
constructor(@Inject(PLATFORM_ID) public platformId: Object) {}
getItem(tag: string) {
if (isPlatformBrowser(this.platformId)) {
const value = (window as any).localStorage.getItem(tag);
if (value) return JSON.parse(value);
}
return undefined;
}
setItem(tag: string, value: any) {
if (isPlatformBrowser(this.platformId))
(window as Window).localStorage.setItem(tag, JSON.stringify(value));
}
deleteItem(tag: string) {
if (isPlatformBrowser(this.platformId))
(window as Window).localStorage.removeItem(tag);
}
}

View File

@@ -0,0 +1,54 @@
// Angular
import { ContentChild, Directive, HostBinding, Input, TemplateRef } from '@angular/core';
// Style definitions
import { MenuEntry, MenuItemText } from './entities/menu-entry.model';
@Directive()
export abstract class PiBaseMenuComponent {
@HostBinding('class') baseClassName = 'pi-menu pi-menu__parent';
isMouseOver = false;
@Input()
label: string;
_menuItems: Array<MenuEntry> = [];
@Input()
set menuItems(menuItems: MenuEntry[]) {
this._menuItems = menuItems;
}
get menuItems(): MenuEntry[] {
return this._menuItems;
}
/**
* Parent Menu
*/
@ContentChild('parentMenuTemplate', { static: false })
parentMenuTemplateRef: TemplateRef<unknown> | undefined;
/**
* Children Menu (Level 1)
*/
@ContentChild('childMenuTemplate', { static: false })
childMenuTemplateRef: TemplateRef<unknown> | undefined;
/**
* Additional Children Menu (Level 1)
*/
@ContentChild('childAsideMenuTemplate', { static: false })
childAsideMenuTemplateRef: TemplateRef<unknown> | undefined;
/**
* Additional Children Menu (Level 1)
*/
@ContentChild('asideMenuTemplate', { static: false })
asideMenuTemplateRef: TemplateRef<unknown> | undefined;
trackByFn(index: number, item: MenuEntry): MenuItemText | string | undefined {
return item.label;
}
}

View File

@@ -0,0 +1,57 @@
import {
Directive,
ElementRef,
HostBinding,
HostListener,
Renderer2
} from '@angular/core';
@Directive()
export abstract class PiBaseMenuEventItem {
@HostBinding('class') className = 'pi-menu-event-item';
constructor(
protected el: ElementRef,
protected renderer: Renderer2
) {}
@HostListener('click')
onClick() {
if (!this.isChildrenVisible()) {
this.setIsVisible();
} else {
this.setIsHidden();
}
}
@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent) {
if (!this.el.nativeElement.contains(event.target)) {
this.setIsHidden();
}
}
protected setIsVisible(): void {
const parent = this.el.nativeElement as HTMLElement;
const children = (parent.children as HTMLCollection).item(1) ?? null;
parent && this.renderer.addClass(parent, 'is-selected');
children && this.renderer.addClass(children, 'is-visible');
}
protected setIsHidden(): void {
const parent = this.el.nativeElement;
const children = (parent.children as HTMLCollection).item(1) ?? null;
parent && this.renderer.removeClass(parent, 'is-selected');
children && this.renderer.removeClass(children, 'is-visible');
}
protected isChildrenVisible(): boolean {
const parent = this.el.nativeElement as HTMLElement;
const children = (parent.children as HTMLCollection).item(1) ?? null;
return children?.classList.contains('is-visible');
}
}

View File

@@ -0,0 +1,15 @@
import { Directive, ElementRef, HostBinding, HostListener, Renderer2 } from '@angular/core';
@Directive({
standalone: false,
selector: '[pi-menu-event-children]',
})
export class PiMenuEventChildrenDirective {
@HostBinding('class') className = 'pi-menu pi-menu__children';
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('click') onClickMenuItem() {
const menuChildren = this.el.nativeElement;
this.renderer.removeClass(menuChildren, 'is-visible');
}
}

View File

@@ -0,0 +1,15 @@
import { Directive, HostBinding, Input } from '@angular/core';
import { MenuEntryContext } from '../entities/menu-entry.model';
@Directive({
standalone: false,
selector: '[pi-menu-entry]',
})
export class PiMenuEntryDirective {
@HostBinding('class') get classes(): string {
const contextClass = this.context && `pi-menu-entry__context--${this.context}`;
return ['pi-menu-entry', contextClass].join(' ');
}
@Input() context: MenuEntryContext = 'parent-menu';
}

View File

@@ -0,0 +1,46 @@
import { styleType } from '../../../styles';
export type MenuEntry = Partial<{
id: number;
icon?: MenuItemIcon | string;
label?: MenuItemText | string;
route?: string | null;
link?: string;
userProfile?: string;
rights?: Array<Rights>;
// Parent & Child Level 1
entries?: Array<MenuEntry>;
// Child Additional Level 1
asideEntries?: Array<MenuEntry>;
state?: boolean;
data?: any;
isDisabled?: boolean;
isActive?: boolean;
anchor?: string;
color?: styleType;
}>;
export type Rights = {
id: number;
name: string;
};
export interface MenuItemIcon {
name: string;
color?: styleType;
}
export interface MenuItemText {
name: string;
description?: string;
}
export interface FooterLink {
id: number;
icon?: string;
label?: string;
link?: string;
}
// Defines where the menu entry belongs
export type MenuEntryContext = 'parent-menu' | 'children-menu' | 'children-submenu' | 'aside-menu';

View File

@@ -0,0 +1,9 @@
export * from './ui-menu.module';
export * from './entities/menu-entry.model';
export * from './base.menu.component';
export * from './directives/base-menu-item.directive';
export * from './directives/menu-children.directive';
export * from './directives/menu-entry.directive';

View File

@@ -0,0 +1,20 @@
// Angular
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslatePipe, TranslateDirective } from '@ngx-translate/core';
// Directives
import { PiMenuEventChildrenDirective } from './directives/menu-children.directive';
import { PiMenuEntryDirective } from './directives/menu-entry.directive';
@NgModule({
imports: [CommonModule, TranslatePipe, TranslateDirective],
declarations: [
PiMenuEventChildrenDirective,
PiMenuEntryDirective,
],
exports: [PiMenuEventChildrenDirective, PiMenuEntryDirective],
})
export class PiSharedUtilMenuModule {}

View File

@@ -0,0 +1,23 @@
export interface Dataset {
label: string;
data: Array<number>;
borderColor: DatasetColor;
cubicInterpolationMode: 'monotone' | 'default';
unit?: string;
unitPosition?: 'before' | 'after';
backgroundColor?: DatasetColor;
}
export enum DatasetColor {
primary = '#799ef0',
primary50 = '#F0F4FF',
highlighted = '#0D2A6B',
highlighted50 = '#EEF1fB',
accent = '#EDA82A',
accent50 = '#FFFAED',
grey = '#AEAEB6',
grey50 = '#F3F4F4',
grey75 = '#EEEFF1',
grey300 = '#89888F',
white= '#FFFFFF',
}

View File

@@ -0,0 +1,5 @@
export interface Date {
day: string;
month: string;
weekDay?: string;
}

View File

@@ -0,0 +1,4 @@
export interface FAQ {
question: string;
answer: string;
}

View File

@@ -0,0 +1,7 @@
export * from './chart-data';
export * from './faqs';
export * from './params';
export * from './pricing';
export * from './sector';
export * from './unit';
export * from './date';

View File

@@ -0,0 +1,3 @@
export interface Params {
[key: string]: string;
}

View File

@@ -0,0 +1,59 @@
import { mode, theming } from '../../styles';
export enum UserType {
INVESTOR = 'INVESTOR',
COMPANY = 'COMPANY',
ADVISOR = 'ADVISOR',
ADMIN = 'ADMIN',
}
export enum PricingPlanType {
FREE = 'FREE',
BASIC = 'BASIC',
PREMIUM = 'PREMIUM',
}
export enum BillingCycleType {
MONTHLY = 'MONTHLY',
YEARLY = 'YEARLY',
BIANNUAL = 'BIANNUAL',
QUARTERLY = 'QUARTERLY',
}
export interface PricingCardLocales {
cta?: string;
icon: string;
priceSuffix: string;
subtitle: string;
title: string;
}
export interface PricingPlan {
billingCycle: BillingCycleType;
price: number;
planType: PricingPlanType;
mode: mode;
color: theming;
featureOverview: Array<string>;
priceHelpText?: Array<string>;
}
export interface PricingPlanCompleteInfo extends PricingPlan, PricingCardLocales {}
export interface BillingCycleOption {
value: BillingCycleType;
label: string;
discount?: number;
}
export interface Feature {
isEnabledFor: Array<PricingPlanType>;
localeKey: string;
categoryLocaleKey?: string;
}
export interface Phase {
icon: string;
features: Array<Feature>;
localeKey: string;
}

View File

@@ -0,0 +1,62 @@
export enum SectorUidMatchLocale {
AGRICULTURE_FORESTRY_FISHING = 'agricultureAndOthers',
CONSTRUCTION = 'construction',
CONSUMER = 'consumer',
DISTRIBUTION_WHOLESALE_TRADE = 'distributionWholesaleTrade',
E_COMMERCE = 'ecommerce',
EDUCATION = 'education',
ENTERTAINMENT = 'entertainment',
FINANCIAL_SERVICES_INSURANCE = 'financialServicesInsurance',
HEALTHCARE = 'healthcare',
HOSPITALITY = 'hospitality',
IT_INTERNET = 'itInternet',
LEGAL_ACCOUNTING_MANAGEMENT_SERVICES = 'legalAccountingManagementServices',
MANUFACTURING = 'manufacturing',
MARKETING_MEDIA = 'marketingMedia',
MINING_OTHER_CHEMICAL_MATERIALS_EXTRACTION = 'miningAndChemicals',
MOBILITY = 'mobility',
OTHER_SERVICES = 'otherServices',
PHYSICAL_AND_LIFE_SCIENCES = 'scientificServices',
PUBLIC_ADMINISTRATION = 'publicAdministration',
REAL_ESTATE = 'realEstate',
TELECOMUNICATIONS = 'telecommunications',
TRANSPORT_LOGISTICS = 'transportLogistics',
UTILITIES = 'utilities',
}
export enum SectorType {
AGRICULTURE_FORESTRY_FISHING = 'AGRICULTURE_FORESTRY_FISHING',
CONSTRUCTION = 'CONSTRUCTION',
CONSUMER = 'CONSUMER',
DISTRIBUTION_WHOLESALE_TRADE = 'DISTRIBUTION_WHOLESALE_TRADE',
E_COMMERCE = 'E_COMMERCE',
EDUCATION = 'EDUCATION',
ENTERTAINMENT = 'ENTERTAINMENT',
FINANCIAL_SERVICES_INSURANCE = 'FINANCIAL_SERVICES_INSURANCE',
HEALTHCARE = 'HEALTHCARE',
HOSPITALITY = 'HOSPITALITY',
IT_INTERNET = 'IT_INTERNET',
LEGAL_ACCOUNTING_MANAGEMENT_SERVICES = 'LEGAL_ACCOUNTING_MANAGEMENT_SERVICES',
MANUFACTURING = 'MANUFACTURING',
MARKETING_MEDIA = 'MARKETING_MEDIA',
MINING_OTHER_CHEMICAL_MATERIALS_EXTRACTION = 'MINING_OTHER_CHEMICAL_MATERIALS_EXTRACTION',
MOBILITY = 'MOBILITY',
OTHER_SERVICES = 'OTHER_SERVICES',
PHYSICAL_AND_LIFE_SCIENCES = 'PHYSICAL_AND_LIFE_SCIENCES',
PUBLIC_ADMINISTRATION = 'PUBLIC_ADMINISTRATION',
REAL_ESTATE = 'REAL_ESTATE',
TELECOMUNICATIONS = 'TELECOMUNICATIONS',
TRANSPORT_LOGISTICS = 'TRANSPORT_LOGISTICS',
UTILITIES = 'UTILITIES',
}
/**
* Movido aquí desde `deale-saas/util/models` (lib de aplicación, fuera del
* alcance de esta librería) porque `SectorCommons` lo necesita.
*/
export interface Sector {
description: string;
name: string;
sectorUid: string;
icon?: string;
}

View File

@@ -0,0 +1 @@
export type unit = '€' | '%' | '$' | 'M' | 'x' | '';

View File

@@ -0,0 +1,4 @@
// pipes
export * from './util-pipes.module';
export * from './pipes';

View File

@@ -0,0 +1,80 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
standalone: false,
name: 'numberFormat',
})
export class NumberFormatPipe implements PipeTransform {
transform(value: unknown, ...args: Array<unknown>): unknown {
return new Intl.NumberFormat('de-DE').format(+value);
}
}
@Pipe({
standalone: false, name: 'replace' })
export class ReplacePipe implements PipeTransform {
transform(value: string, strToReplace: string, replacementStr: string): string {
if (!value || !strToReplace || !replacementStr) {
return value;
}
return value.replace(new RegExp(strToReplace, 'g'), replacementStr);
}
}
@Pipe({
standalone: false,
name: 'sanitizerAltImage',
})
export class SanitizerAltImagePipe implements PipeTransform {
transform(name: string): string {
return this.sanitizeAltImage(name);
}
private sanitizeAltImage(name: string): string {
return name.split('.').slice(0, -1).join('.').replace(/-/g, ' ');
}
}
@Pipe({
standalone: false,
name: 'firstWord',
})
export class FirstWordPipe implements PipeTransform {
transform(value: string): string {
if (!value) {
return '';
}
return value.split(' ')[0];
}
}
@Pipe({
standalone: false, name: 'truncate' })
export class TruncatePipe implements PipeTransform {
transform(text: string, length: number = 20, suffix: string = '...'): string {
if (text.length > length) {
const truncated: string = text.substring(0, length).trim() + suffix;
return truncated;
}
return text;
}
}
@Pipe({
standalone: false, name: 'maxTwoDecimals' })
export class MaxTwoDecimalsPipe implements PipeTransform {
transform(value: unknown, ...args: Array<unknown>): unknown {
return Math.round(+value * 100) / 100;
}
}
@Pipe({
standalone: false, name: 'anonymize' })
export class AnonymizePipe implements PipeTransform {
transform(text?: string, length = 1, char = '*') {
return text?.split(' ').map(word =>
word.split('').map((letter, index) => (index < length ? letter : char)).join('')
).join(' ')
}
}

View File

@@ -0,0 +1,13 @@
// Angular
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
// Pipes
import { AnonymizePipe, FirstWordPipe, MaxTwoDecimalsPipe, NumberFormatPipe, SanitizerAltImagePipe, TruncatePipe } from './pipes';
@NgModule({
imports: [CommonModule],
declarations: [AnonymizePipe, FirstWordPipe, NumberFormatPipe, SanitizerAltImagePipe, TruncatePipe, MaxTwoDecimalsPipe ],
exports: [AnonymizePipe, FirstWordPipe, NumberFormatPipe, SanitizerAltImagePipe, TruncatePipe, MaxTwoDecimalsPipe],
})
export class PiSharedUtilPipesModule {}

View File

@@ -0,0 +1,31 @@
// Angular
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
// Router
import { RouterStateUrl } from './state/router.reducer';
// Ngrx
import * as fromRouter from '@ngrx/router-store';
export class CustomSerializer implements fromRouter.RouterStateSerializer<RouterStateUrl> {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
const { url } = routerState;
const { queryParams } = routerState.root;
let route: ActivatedRouteSnapshot = routerState.root;
while (route.firstChild) {
route = route.firstChild;
}
const { params, routeConfig } = route;
// Only return an object including the URL, params and query params
// instead of the entire snapshot
return {
url,
params,
queryParams,
title: typeof routeConfig?.title === 'string' ? routeConfig.title : '',
};
}
}

View File

@@ -0,0 +1,11 @@
import { ErrorHandler, Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(private injector: Injector) { }
handleError(error: any) {
const router = this.injector.get(Router);
console.error(error.stack.toString());
}
}

View File

@@ -0,0 +1,10 @@
export * from './util-router-store.module';
// State
export * from './state/router.actions';
export * from './state/router.effects';
export * from './state/router.reducer';
export * from './state/router.selectors';
// Error handler
export * from './error-handler';

View File

@@ -0,0 +1,29 @@
import { NavigationExtras } from '@angular/router';
import { createAction, props } from '@ngrx/store';
export const navigateTo = createAction(
'[Core Router] Navigate To',
props<{
payload: {
path: any[];
query?: object;
extras?: NavigationExtras;
};
}>()
);
export const navigateRefreshTo = createAction(
'[Core Router] Navigate Refresh To',
props<{
payload: {
path: any[];
query?: object;
extras?: NavigationExtras;
};
}>()
);
export const navigateBack = createAction('[Core Router] Navigate Back');
export const navigateForward = createAction('[Core Router] Navigate Forward');

View File

@@ -0,0 +1,74 @@
import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as RouterActions from './router.actions';
import { map, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Location } from '@angular/common';
import { Router } from '@angular/router';
@Injectable()
export class RouterEffects {
navigateTo$ = createEffect(
() => {
return this.action$.pipe(
ofType(RouterActions.navigateTo),
map((action) => {
return action.payload;
}),
tap(({ path, query: queryParams, extras }) => {
this._router.navigate(path, { queryParams, ...extras });
})
);
},
{ dispatch: false }
);
navigateRefreshTo$ = createEffect(
() => {
return this.action$.pipe(
ofType(RouterActions.navigateRefreshTo),
map((action) => {
return action.payload;
}),
tap(({ path, query: queryParams, extras }) => {
this._router.routeReuseStrategy.shouldReuseRoute = () => false;
this._router.onSameUrlNavigation = 'reload';
this._router.navigate(path, { queryParams, ...extras });
})
);
},
{ dispatch: false }
);
navigateBack$ = createEffect(
() => {
return this.action$.pipe(
ofType(RouterActions.navigateBack),
tap(() => {
this._location.back();
})
);
},
{ dispatch: false }
);
navigateForward$ = createEffect(
() => {
return this.action$.pipe(
ofType(RouterActions.navigateForward),
tap(() => {
this._location.forward();
})
);
},
{ dispatch: false }
);
constructor(
private _location: Location,
private action$: Actions,
private _router: Router
) {}
}

View File

@@ -0,0 +1,16 @@
import * as fromRouter from '@ngrx/router-store';
import { Params } from '@angular/router';
export const coreRouterFeatureKey = 'CORE_router';
export interface RouterStateUrl {
url: string;
queryParams: Params;
params: Params;
title: string;
}
export interface RouterState {
routerReducer: fromRouter.RouterReducerState<RouterStateUrl>;
}

View File

@@ -0,0 +1,15 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromRouter from '@ngrx/router-store';
import * as fromReducer from './router.reducer';
export const getRouterState = createFeatureSelector<
fromRouter.RouterReducerState<fromReducer.RouterStateUrl>
>(fromReducer.coreRouterFeatureKey);
export const getTitle = createSelector(getRouterState, (state) => {
return state?.state?.title
});
export const getRoute = createSelector(getRouterState, (state) => state?.state?.url);

View File

@@ -0,0 +1,16 @@
import { ErrorHandler, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterState, StoreRouterConnectingModule } from '@ngrx/router-store';
import { CustomSerializer } from './custom-route-serializer';
@NgModule({
imports: [
CommonModule,
StoreRouterConnectingModule.forRoot({
routerState: RouterState.Minimal,
serializer: CustomSerializer,
}),
],
})
export class PiSharedUtilRouterStoreModule {}

View File

@@ -0,0 +1 @@
export * from './schema.service';

View File

@@ -0,0 +1,20 @@
// Angular
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, Renderer2 } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class SchemaService {
script: HTMLScriptElement;
constructor(@Inject(DOCUMENT) private doc: Document) {}
loadJsScript(renderer: Renderer2, data: any): HTMLScriptElement {
const script = renderer.createElement('script');
script.type = 'application/ld+json';
script.text = `${JSON.stringify(data)}`;
renderer.appendChild(this.doc.body, script);
return script;
}
}

View File

@@ -0,0 +1,5 @@
export * from './scroll-to.module';
export * from './scroll-to.directive';
export * from './scroll-to.service';

View File

@@ -0,0 +1,15 @@
import {
Directive,
HostBinding
} from '@angular/core';
@Directive({
standalone: false,
selector: '[pi-scroll-to-element]',
})
export class PiScrollToElementDirective {
@HostBinding('class') get classes(): string {
return 'pi-scroll-to pi-scroll-to__element';
}
}

View File

@@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PiScrollToElementDirective } from './scroll-to.directive';
import { PiSharedUtilScrollService } from './scroll-to.service';
@NgModule({
imports: [CommonModule],
declarations: [PiScrollToElementDirective],
exports: [PiScrollToElementDirective],
providers: [PiSharedUtilScrollService],
})
export class PiSharedUtilScrollToModule {}

View File

@@ -0,0 +1,43 @@
import { ElementRef, Injectable, QueryList } from '@angular/core';
@Injectable()
export class PiSharedUtilScrollService {
scrollToNextElement(elements: QueryList<ElementRef>): void {
for (let i = 0; i <= elements.length; i++) {
const element: ElementRef = elements.toArray()[i];
if (this.isScrolledToBottom()) {
this.scrollToTop();
break;
}
if (i === elements.length) {
this.scrollToBottom();
break;
}
if (this.isElementInView(element.nativeElement)) {
element.nativeElement.scrollIntoView({ behavior: 'smooth' });
break;
}
this.scrollToTop();
}
}
isElementInView(element: Element): boolean {
const rect = element.getBoundingClientRect();
return rect.top > 40;
}
scrollToTop(): void {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
scrollToBottom(): void {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
}
isScrolledToBottom(): boolean {
const documentHeight = document.body.scrollHeight;
const currentScroll = window.scrollY + window.innerHeight;
return documentHeight <= currentScroll;
}
}

View File

@@ -0,0 +1,58 @@
import { Directive, OnChanges, HostBinding, Input, SimpleChanges } from '@angular/core';
@Directive({
standalone: false,
selector: '[pi-disabled]',
})
export class PiDisabledDirective implements OnChanges {
@HostBinding('style.opacity') get opacity(): number {
return this.isDisabled ? 0.33 : 1;
}
@HostBinding('class') get classes(): string | boolean {
return [this.isDisabled && 'is-disabled'].join(' ');
}
@Input() isDisabled: boolean;
ngOnChanges(changes: SimpleChanges): void {
if (changes) {
this.isDisabled = changes['isDisabled'].currentValue;
}
}
}
@Directive({
standalone: false,
selector: '[is-active]',
})
export class PiIsActiveDirective {
@HostBinding('class') get classes(): string | boolean {
return [this.isActive ? 'is-active' : '', 'pi-status'].join(' ');
}
@Input() isActive!: boolean;
ngOnChanges(changes: SimpleChanges): void {
if (changes) {
this.isActive = changes['isActive'].currentValue;
}
}
}
@Directive({
standalone: false,
selector: '[is-visited]',
})
export class PiIsVisitedDirective {
@HostBinding('class') get classes(): string | boolean {
return [this.isVisited ? 'is-visited' : '', 'pi-status'].join(' ');
}
@Input() isVisited!: boolean;
ngOnChanges(changes: SimpleChanges): void {
if (changes) {
this.isVisited = changes['isVisited'].currentValue;
}
}
}

View File

@@ -0,0 +1 @@
export type status = 'success' | 'error' | 'pending' | 'info' | 'disabled';

View File

@@ -0,0 +1,5 @@
export * from './shared-util-status.module';
export * from './directives/status.directive';
export * from './entities/status.model';

View File

@@ -0,0 +1,22 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
PiDisabledDirective,
PiIsActiveDirective,
PiIsVisitedDirective,
} from './directives/status.directive';
@NgModule({
imports: [CommonModule],
declarations: [
PiDisabledDirective,
PiIsActiveDirective,
PiIsVisitedDirective
],
exports: [
PiDisabledDirective,
PiIsActiveDirective,
PiIsVisitedDirective,
],
})
export class PiSharedUtilStatusModule {}

View File

@@ -0,0 +1,7 @@
import { TranslateService } from '@ngx-translate/core';
export function appInitializerFactory(translateService: TranslateService) {
return () => {
translateService.use('es');
};
}

View File

@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class I18nService {
constructor(private translateService: TranslateService) {}
setLanguage(lang: string): Observable<any> {
return new Observable((observer) => {
this.translateService.use(lang).subscribe({
next: () => {
observer.next();
observer.complete();
},
error: (error: any) => observer.error(error),
});
});
}
getTranslation(key: string): Observable<string> {
return this.translateService.get(key);
}
}

View File

@@ -0,0 +1,18 @@
export * from './translate.module';
// Initializer
export * from './custom-initializer';
// Data
export * from './translate.data';
// Model
export * from './translate.model';
// Translate server
export * from './loaders/translate-browser.loader';
export * from './loaders/translate-server.loader';
// Service
export * from './i18n.service';

View File

@@ -0,0 +1,35 @@
import { Observable } from 'rxjs';
import { TranslateLoader } from '@ngx-translate/core';
import { makeStateKey, StateKey, TransferState } from '@angular/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';
export class TranslateBrowserLoader implements TranslateLoader {
constructor(private http: HttpClient, private transferState: TransferState) {}
public getTranslation(lang: string): Observable<any> {
const key: StateKey<number> = makeStateKey<number>(
'transfer-translate-' + lang
);
const data = this.transferState.get(key, null);
// First we are looking for the translations in transfer-state,
// if none found, http load as fallback
if (data) {
return new Observable((observer) => {
observer.next(data);
observer.complete();
});
} else {
return new TranslateHttpLoader(this.http).getTranslation(lang);
}
}
}
export function translateBrowserLoaderFactory(
httpClient: HttpClient,
transferState: TransferState
) {
return new TranslateBrowserLoader(httpClient, transferState);
}

View File

@@ -0,0 +1,39 @@
import { Observable } from 'rxjs';
import { TranslateLoader } from '@ngx-translate/core';
import { makeStateKey, StateKey, TransferState } from '@angular/core';
import { join } from 'path';
import { readFileSync } from 'fs';
export class TranslateServerLoader implements TranslateLoader {
constructor(
private transferState: TransferState,
private prefix: string = 'i18n',
private suffix: string = '.json'
) {}
public getTranslation(lang: string): Observable<any> {
return new Observable((observer) => {
const assets_folder = join(
process.cwd(),
'dist',
'deale-website', // Your project name here
'browser',
'assets',
this.prefix
);
const jsonData = JSON.parse(readFileSync(`${assets_folder}/${lang}${this.suffix}`, 'utf8'));
// Here we save the translations in the transfer-state
const key: StateKey<number> = makeStateKey<number>('transfer-translate-' + lang);
this.transferState.set(key, jsonData);
observer.next(jsonData);
observer.complete();
});
}
}
export function translateServerLoaderFactory(transferState: TransferState) {
return new TranslateServerLoader(transferState);
}

View File

@@ -0,0 +1,10 @@
import { LanguageProps } from './translate.model';
export const LANGUAGES: Map<string, LanguageProps> = new Map([
['es', { languageCombo: 'common.languages.spanish', rtl: false, currency: 'EUR' }],
['en', { languageCombo: 'common.languages.english', rtl: false, currency: 'USD' }],
['ca', { languageCombo: 'common.languages.catalan', rtl: false, currency: 'EUR' }],
['de', { languageCombo: 'common.languages.german', rtl: false, currency: 'EUR' }],
['it', { languageCombo: 'common.languages.italian', rtl: false, currency: 'EUR' }],
['fr', { languageCombo: 'common.languages.french', rtl: false, currency: 'EUR' }],
]);

View File

@@ -0,0 +1,10 @@
export interface LanguageProps {
languageCombo: string;
rtl: boolean;
currency: string;
}
export interface Language {
locale: string;
value: string;
}

View File

@@ -0,0 +1,28 @@
import { HttpClient } from '@angular/common/http';
import { EnvironmentProviders, makeEnvironmentProviders, provideAppInitializer, inject } from '@angular/core';
import { TranslateLoader, TranslateService, provideTranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { appInitializerFactory } from './custom-initializer';
export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
/**
* Sustituye a `PiSharedUtilTranslateModule`: ngx-translate 18 ya no expone
* `TranslateModule.forRoot()`, la configuración se hace con providers.
*/
export function providePiTranslate(): EnvironmentProviders {
return makeEnvironmentProviders([
provideTranslateService({
fallbackLang: 'es',
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient],
},
}),
provideAppInitializer(() => appInitializerFactory(inject(TranslateService))()),
]);
}

View File

@@ -0,0 +1,18 @@
export const TextFieldMaxLength = 300;
export const dniPattern = /^[\d]{8}[A-Za-z]{1}$/;
export const niePattern = /^[A-Za-z]{1}[\d]{7}[A-Za-z]{1}$/;
export const nifPattern = /^([A-Za-z][0-9]{8}|[0-9]{8}[A-Za-z])$/;
export const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
export const emailPattern =
/^[A-Za-z0-9.+\-_]*@[A-Za-z0-9.\-_]*\.[A-Za-z]{1,20}$/;
export const phonePattern = /^(\+34|0034|34)?[ -]*(6|7|8|9)[ -]*([0-9][ -]*){8}$/;
export const passwordPattern = /^.{8,}$/;
export const fieldNamePattern = /^[A-Za-z0-9_]*$/;
export const onlyPositiveNumberPattern = /^[1-9]+[0-9]*$/;
export const positiveNumbersWithDecimals = /^\d+([.,]\d{0,2})?$/;
export const positiveNumbersIncludingZero = /^(0|\d+|\d+.\d+)$/;
export const onlyNumbersPattern = /^[0-9]*$/;
export const onlyNumbersDecimalsPattern = /^[0-9]*(\\,[0-9]{0,2})?$/;
export const repeatedPattern = /^(?!.*(\w)\1{3,}).+$/;
export const cognitoPhonePattern = /^\+[1-9]\d{10,14}$/;
export const monthYearPattern = /^(0[1-9]|1[0-2])\/([0-9]{2})$/;

View File

@@ -0,0 +1,2 @@
export * from './definitions';
export * from './utils';

View File

@@ -0,0 +1,263 @@
import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
import { environment } from '../environments';
import {
dniPattern,
emailPattern,
urlPattern,
niePattern,
nifPattern,
onlyNumbersDecimalsPattern,
onlyNumbersPattern,
onlyPositiveNumberPattern,
passwordPattern,
phonePattern,
positiveNumbersWithDecimals,
positiveNumbersIncludingZero,
repeatedPattern,
cognitoPhonePattern,
monthYearPattern,
} from './definitions';
export function validateRequired(control: AbstractControl): ValidationErrors | null {
const value: string = control.value as string;
if (value === null || value === undefined || value === '' || value.length === 0) {
return { message: ['common.validator.requiredField'] };
}
return null;
}
export function validateRequiredIfDirty(control: AbstractControl): ValidationErrors | null {
const value: string = control.value as string;
if (value === null || value === undefined || value === '' || value.length === 0 || value === 'undefined') {
if (control.dirty) {
return { message: ['common.validator.requiredField'] };
}
return null;
}
return null;
}
export function validateNotZero(control: AbstractControl): ValidationErrors | null {
const value: number = control.value as number;
if (value === 0) {
return { message: ['common.validator.requiredField'] };
}
return null;
}
export function validateRequiredWithoutMessage(control: AbstractControl): ValidationErrors | null {
const value: boolean = control.value as boolean;
if (!value) {
return { message: [''] };
}
return null;
}
export function validatePassword(control: AbstractControl): ValidationErrors | null {
const password: string = control.value as string;
if (password && !passwordPattern.test(password)) {
return { message: ['common.validator.minimumPasswordCharacters'] };
}
return null;
}
export function validateEmail(control: AbstractControl): ValidationErrors | null {
const email: string = control.value as string;
if (email && !emailPattern.test(email)) {
return { message: ['common.validator.enterValidEmail'] };
}
return null;
}
export function validateUrl(control: AbstractControl): ValidationErrors | null {
const url: string = control.value as string;
if (url && !urlPattern.test(url)) {
return { message: ['common.validator.enterValidUrl'] };
}
return null;
}
export function validatePhone(control: AbstractControl): ValidationErrors | null {
const phone: string = control.value as string;
if (phone && !phonePattern.test(phone)) {
return { message: ['common.validator.enterValidPhoneNumber'] };
}
return null;
}
export function validateCognitoPhone(control: AbstractControl): ValidationErrors | null {
const phone: string = control.value as string;
if (phone && !cognitoPhonePattern.test(phone)) {
return { message: ['common.validator.enterValidPhoneNumber'] };
}
return null;
}
export function validateMaxLength(maxLength: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const maxChars: string = control.value as string;
if (maxChars && maxChars.length > maxLength) {
return {
message: [
'common.validator.noMoreThanCharacters.1',
`${maxLength}`,
'common.validator.noMoreThanCharacters.2',
],
};
}
return null;
};
}
export function validateMinLength(minLength: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const minChars: string = control.value as string;
if (minChars && minChars.length <= minLength) {
return {
message: [
'common.validator.noLessThanCharacters.1',
`${minLength}`,
'common.validator.noLessThanCharacters.2',
],
};
}
return null;
};
}
export function validateMaximumNumber(maximum: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const num: number = control.value as number;
if (maximum && num > Number(maximum)) {
return {
message: ['common.validator.noMoreThanMaxNumber.1', `${maximum}`],
};
}
return null;
};
}
export function validateMinimumNumber(minimum: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const num: number = control.value as number;
if (minimum && num < Number(minimum)) {
return { message: ['common.validator.noLessThanMinNumbers', minimum] };
}
return null;
};
}
export function validatePositiveNumber(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !onlyPositiveNumberPattern.test(num)) {
return { message: ['common.validator.onlyPositiveNumbers'] };
}
return null;
}
export function validateNif(control: AbstractControl): ValidationErrors | null {
const nif: string = control.value as string;
if (nif && !nifPattern.test(nif)) {
return { message: ['common.validator.letterAndNumberNIF'] };
}
return null;
}
export function validateNifInvestor(control: AbstractControl): ValidationErrors | null {
const nif: string = control.value as string;
if (nif && !nifPattern.test(nif) && !dniPattern.test(nif) && !niePattern.test(nif)) {
return { message: ['common.validator.incorrectNIF'] };
}
return null;
}
export function validateNifProd(control: AbstractControl): ValidationErrors | null {
const nif: string = control.value as string;
if (environment.production) {
if (nif && (nif === 'A00000000' || nif === 'B00000000')) {
return { message: ['common.validator.invalidNIF'] };
}
}
return null;
}
export function validateOnlyNumbers(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !onlyNumbersPattern.test(num)) {
return { message: ['common.validator.onlyNumbers'] };
}
return null;
}
export function validateOnlyNumbersWithDecimals(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !onlyNumbersDecimalsPattern.test(num)) {
return { message: ['common.validator.numbersWithDecimals'] };
}
return null;
}
export function validatePositiveNumbersWithDecimals(
control: AbstractControl
): ValidationErrors | null {
const num: string = control.value as string;
if (num && !positiveNumbersWithDecimals.test(num)) {
return { message: ['common.validator.numbersWithDecimals'] };
}
return null;
}
export function validatePositiveNumbersIncludingZero(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !positiveNumbersIncludingZero.test(num)) {
return { message: ['common.validator.positiveNumbersIncludingZero']}
}
return null;
}
export function validateNotOnlyNumbers(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && onlyNumbersPattern.test(num)) {
return { message: ['common.validator.notOnlyNumbers'] };
}
return null;
}
export function notRepeatedChar(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !repeatedPattern.test(num)) {
return { message: ['common.validator.incorrectName'] };
}
return null;
}
export function validateMultipleChoiceRequired(control: AbstractControl): ValidationErrors | null {
const choices: { [key: string]: boolean } = control.value as {
[key: string]: boolean;
};
let selected: boolean = false;
selected = !!choices ? Object.entries(choices)?.some(([key, value]) => value) : null;
if (choices && !selected) {
return { message: ['common.validator.atLeastOneOption'] };
}
return null;
}
export function rangeValidator(control: AbstractControl): ValidationErrors | null {
const from = Number(control.get('from').value);
const to = Number(control.get('to').value);
if (!from || !to) return null;
return from <= to
? null
: { message: ['common.validator.minNotSuperiorToMax'] };
}
export function validateMonthYear(control: AbstractControl): ValidationErrors | null {
const num: string = control.value as string;
if (num && !monthYearPattern.test(num)) {
return { message: ['common.validator.monthYear'] };
}
return null;
}

View File

@@ -0,0 +1,21 @@
import { Directive, HostBinding, Input } from '@angular/core';
@Directive({
standalone: false,
selector: '[pi-value]',
})
export class PiValueDirective {
@HostBinding('class') get classes(): string | boolean {
return ['pi-value', this.value > 0 ? 'is-positive' : 'is-negative'].join(' ');
}
@Input() value!: number;
checkValue(): string {
if (this.value > 0) {
return 'is-positive';
} else {
return 'is-negative';
}
}
}

View File

@@ -0,0 +1,3 @@
export * from './util-value.module';
export * from './directives/value.directive';

Some files were not shown because too many files have changed in this diff Show More