Second pass on the formatting.

This commit is contained in:
Sercan Yemen 2024-05-27 14:06:26 +03:00
parent 8545291203
commit 7782dbd3db
658 changed files with 48434 additions and 36597 deletions

View File

@ -1,8 +1,13 @@
import { HttpErrorResponse, HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http'; import {
HttpErrorResponse,
HttpEvent,
HttpHandlerFn,
HttpRequest,
} from '@angular/common/http';
import { inject } from '@angular/core'; import { inject } from '@angular/core';
import { AuthService } from 'app/core/auth/auth.service'; import { AuthService } from 'app/core/auth/auth.service';
import { AuthUtils } from 'app/core/auth/auth.utils'; import { AuthUtils } from 'app/core/auth/auth.utils';
import { catchError, Observable, throwError } from 'rxjs'; import { Observable, catchError, throwError } from 'rxjs';
/** /**
* Intercept * Intercept
@ -10,8 +15,10 @@ import { catchError, Observable, throwError } from 'rxjs';
* @param req * @param req
* @param next * @param next
*/ */
export const authInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => export const authInterceptor = (
{ req: HttpRequest<unknown>,
next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
const authService = inject(AuthService); const authService = inject(AuthService);
// Clone the request object // Clone the request object
@ -25,20 +32,23 @@ export const authInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn):
// for the protected API routes which our response interceptor will // for the protected API routes which our response interceptor will
// catch and delete the access token from the local storage while logging // catch and delete the access token from the local storage while logging
// the user out from the app. // the user out from the app.
if ( authService.accessToken && !AuthUtils.isTokenExpired(authService.accessToken) ) if (
{ authService.accessToken &&
!AuthUtils.isTokenExpired(authService.accessToken)
) {
newReq = req.clone({ newReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + authService.accessToken), headers: req.headers.set(
'Authorization',
'Bearer ' + authService.accessToken
),
}); });
} }
// Response // Response
return next(newReq).pipe( return next(newReq).pipe(
catchError((error) => catchError((error) => {
{
// Catch "401 Unauthorized" responses // Catch "401 Unauthorized" responses
if ( error instanceof HttpErrorResponse && error.status === 401 ) if (error instanceof HttpErrorResponse && error.status === 401) {
{
// Sign out // Sign out
authService.signOut(); authService.signOut();
@ -47,6 +57,6 @@ export const authInterceptor = (req: HttpRequest<unknown>, next: HttpHandlerFn):
} }
return throwError(error); return throwError(error);
}), })
); );
}; };

View File

@ -1,10 +1,14 @@
import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ENVIRONMENT_INITIALIZER, EnvironmentProviders, inject, Provider } from '@angular/core'; import {
ENVIRONMENT_INITIALIZER,
EnvironmentProviders,
Provider,
inject,
} from '@angular/core';
import { authInterceptor } from 'app/core/auth/auth.interceptor'; import { authInterceptor } from 'app/core/auth/auth.interceptor';
import { AuthService } from 'app/core/auth/auth.service'; import { AuthService } from 'app/core/auth/auth.service';
export const provideAuth = (): Array<Provider | EnvironmentProviders> => export const provideAuth = (): Array<Provider | EnvironmentProviders> => {
{
return [ return [
provideHttpClient(withInterceptors([authInterceptor])), provideHttpClient(withInterceptors([authInterceptor])),
{ {

View File

@ -5,8 +5,7 @@ import { UserService } from 'app/core/user/user.service';
import { catchError, Observable, of, switchMap, throwError } from 'rxjs'; import { catchError, Observable, of, switchMap, throwError } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthService export class AuthService {
{
private _authenticated: boolean = false; private _authenticated: boolean = false;
private _httpClient = inject(HttpClient); private _httpClient = inject(HttpClient);
private _userService = inject(UserService); private _userService = inject(UserService);
@ -18,13 +17,11 @@ export class AuthService
/** /**
* Setter & getter for access token * Setter & getter for access token
*/ */
set accessToken(token: string) set accessToken(token: string) {
{
localStorage.setItem('accessToken', token); localStorage.setItem('accessToken', token);
} }
get accessToken(): string get accessToken(): string {
{
return localStorage.getItem('accessToken') ?? ''; return localStorage.getItem('accessToken') ?? '';
} }
@ -37,8 +34,7 @@ export class AuthService
* *
* @param email * @param email
*/ */
forgotPassword(email: string): Observable<any> forgotPassword(email: string): Observable<any> {
{
return this._httpClient.post('api/auth/forgot-password', email); return this._httpClient.post('api/auth/forgot-password', email);
} }
@ -47,8 +43,7 @@ export class AuthService
* *
* @param password * @param password
*/ */
resetPassword(password: string): Observable<any> resetPassword(password: string): Observable<any> {
{
return this._httpClient.post('api/auth/reset-password', password); return this._httpClient.post('api/auth/reset-password', password);
} }
@ -57,17 +52,14 @@ export class AuthService
* *
* @param credentials * @param credentials
*/ */
signIn(credentials: { email: string; password: string }): Observable<any> signIn(credentials: { email: string; password: string }): Observable<any> {
{
// Throw error, if the user is already logged in // Throw error, if the user is already logged in
if ( this._authenticated ) if (this._authenticated) {
{
return throwError('User is already logged in.'); return throwError('User is already logged in.');
} }
return this._httpClient.post('api/auth/sign-in', credentials).pipe( return this._httpClient.post('api/auth/sign-in', credentials).pipe(
switchMap((response: any) => switchMap((response: any) => {
{
// Store the access token in the local storage // Store the access token in the local storage
this.accessToken = response.accessToken; this.accessToken = response.accessToken;
@ -79,26 +71,25 @@ export class AuthService
// Return a new observable with the response // Return a new observable with the response
return of(response); return of(response);
}), })
); );
} }
/** /**
* Sign in using the access token * Sign in using the access token
*/ */
signInUsingToken(): Observable<any> signInUsingToken(): Observable<any> {
{
// Sign in using the token // Sign in using the token
return this._httpClient.post('api/auth/sign-in-with-token', { return this._httpClient
.post('api/auth/sign-in-with-token', {
accessToken: this.accessToken, accessToken: this.accessToken,
}).pipe( })
.pipe(
catchError(() => catchError(() =>
// Return false // Return false
of(false), of(false)
), ),
switchMap((response: any) => switchMap((response: any) => {
{
// Replace the access token with the new one if it's available on // Replace the access token with the new one if it's available on
// the response object. // the response object.
// //
@ -106,8 +97,7 @@ export class AuthService
// in using the token, you should generate a new one on the server // in using the token, you should generate a new one on the server
// side and attach it to the response object. Then the following // side and attach it to the response object. Then the following
// piece of code can replace the token with the refreshed one. // piece of code can replace the token with the refreshed one.
if ( response.accessToken ) if (response.accessToken) {
{
this.accessToken = response.accessToken; this.accessToken = response.accessToken;
} }
@ -119,15 +109,14 @@ export class AuthService
// Return true // Return true
return of(true); return of(true);
}), })
); );
} }
/** /**
* Sign out * Sign out
*/ */
signOut(): Observable<any> signOut(): Observable<any> {
{
// Remove the access token from the local storage // Remove the access token from the local storage
localStorage.removeItem('accessToken'); localStorage.removeItem('accessToken');
@ -143,8 +132,12 @@ export class AuthService
* *
* @param user * @param user
*/ */
signUp(user: { name: string; email: string; password: string; company: string }): Observable<any> signUp(user: {
{ name: string;
email: string;
password: string;
company: string;
}): Observable<any> {
return this._httpClient.post('api/auth/sign-up', user); return this._httpClient.post('api/auth/sign-up', user);
} }
@ -153,31 +146,29 @@ export class AuthService
* *
* @param credentials * @param credentials
*/ */
unlockSession(credentials: { email: string; password: string }): Observable<any> unlockSession(credentials: {
{ email: string;
password: string;
}): Observable<any> {
return this._httpClient.post('api/auth/unlock-session', credentials); return this._httpClient.post('api/auth/unlock-session', credentials);
} }
/** /**
* Check the authentication status * Check the authentication status
*/ */
check(): Observable<boolean> check(): Observable<boolean> {
{
// Check if the user is logged in // Check if the user is logged in
if ( this._authenticated ) if (this._authenticated) {
{
return of(true); return of(true);
} }
// Check the access token availability // Check the access token availability
if ( !this.accessToken ) if (!this.accessToken) {
{
return of(false); return of(false);
} }
// Check the access token expire date // Check the access token expire date
if ( AuthUtils.isTokenExpired(this.accessToken) ) if (AuthUtils.isTokenExpired(this.accessToken)) {
{
return of(false); return of(false);
} }

View File

@ -5,8 +5,7 @@
// https://github.com/auth0/angular2-jwt // https://github.com/auth0/angular2-jwt
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
export class AuthUtils export class AuthUtils {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -17,11 +16,9 @@ export class AuthUtils
* @param token * @param token
* @param offsetSeconds * @param offsetSeconds
*/ */
static isTokenExpired(token: string, offsetSeconds?: number): boolean static isTokenExpired(token: string, offsetSeconds?: number): boolean {
{
// Return if there is no token // Return if there is no token
if ( !token || token === '' ) if (!token || token === '') {
{
return true; return true;
} }
@ -30,8 +27,7 @@ export class AuthUtils
offsetSeconds = offsetSeconds || 0; offsetSeconds = offsetSeconds || 0;
if ( date === null ) if (date === null) {
{
return true; return true;
} }
@ -50,17 +46,16 @@ export class AuthUtils
* @param str * @param str
* @private * @private
*/ */
private static _b64decode(str: string): string private static _b64decode(str: string): string {
{ const chars =
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let output = ''; let output = '';
str = String(str).replace(/=+$/, ''); str = String(str).replace(/=+$/, '');
if ( str.length % 4 === 1 ) if (str.length % 4 === 1) {
{
throw new Error( throw new Error(
'\'atob\' failed: The string to be decoded is not correctly encoded.', "'atob' failed: The string to be decoded is not correctly encoded."
); );
} }
@ -72,16 +67,13 @@ export class AuthUtils
(buffer = str.charAt(idx++)); (buffer = str.charAt(idx++));
// character found in table? initialize bit storage and add its ascii value; // character found in table? initialize bit storage and add its ascii value;
~buffer && ~buffer &&
( ((bs = bc % 4 ? bs * 64 + buffer : buffer),
(bs = bc % 4 ? bs * 64 + buffer : buffer),
// and if not first of each 4 characters, // and if not first of each 4 characters,
// convert the first 8 bits to one ascii character // convert the first 8 bits to one ascii character
bc++ % 4 bc++ % 4)
)
? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6)))) ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
: 0 : 0
) ) {
{
// try to find character in table (0-63, not found => -1) // try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer); buffer = chars.indexOf(buffer);
} }
@ -96,12 +88,15 @@ export class AuthUtils
* @param str * @param str
* @private * @private
*/ */
private static _b64DecodeUnicode(str: any): string private static _b64DecodeUnicode(str: any): string {
{
return decodeURIComponent( return decodeURIComponent(
Array.prototype.map Array.prototype.map
.call(this._b64decode(str), (c: any) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) .call(
.join(''), this._b64decode(str),
(c: any) =>
'%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
)
.join('')
); );
} }
@ -111,27 +106,21 @@ export class AuthUtils
* @param str * @param str
* @private * @private
*/ */
private static _urlBase64Decode(str: string): string private static _urlBase64Decode(str: string): string {
{
let output = str.replace(/-/g, '+').replace(/_/g, '/'); let output = str.replace(/-/g, '+').replace(/_/g, '/');
switch ( output.length % 4 ) switch (output.length % 4) {
{ case 0: {
case 0:
{
break; break;
} }
case 2: case 2: {
{
output += '=='; output += '==';
break; break;
} }
case 3: case 3: {
{
output += '='; output += '=';
break; break;
} }
default: default: {
{
throw Error('Illegal base64url string!'); throw Error('Illegal base64url string!');
} }
} }
@ -144,27 +133,25 @@ export class AuthUtils
* @param token * @param token
* @private * @private
*/ */
private static _decodeToken(token: string): any private static _decodeToken(token: string): any {
{
// Return if there is no token // Return if there is no token
if ( !token ) if (!token) {
{
return null; return null;
} }
// Split the token // Split the token
const parts = token.split('.'); const parts = token.split('.');
if ( parts.length !== 3 ) if (parts.length !== 3) {
{ throw new Error(
throw new Error('The inspected token doesn\'t appear to be a JWT. Check to make sure it has three parts and see https://jwt.io for more.'); "The inspected token doesn't appear to be a JWT. Check to make sure it has three parts and see https://jwt.io for more."
);
} }
// Decode the token using the Base64 decoder // Decode the token using the Base64 decoder
const decoded = this._urlBase64Decode(parts[1]); const decoded = this._urlBase64Decode(parts[1]);
if ( !decoded ) if (!decoded) {
{
throw new Error('Cannot decode the token.'); throw new Error('Cannot decode the token.');
} }
@ -177,14 +164,12 @@ export class AuthUtils
* @param token * @param token
* @private * @private
*/ */
private static _getTokenExpirationDate(token: string): Date | null private static _getTokenExpirationDate(token: string): Date | null {
{
// Get the decoded token // Get the decoded token
const decodedToken = this._decodeToken(token); const decodedToken = this._decodeToken(token);
// Return if the decodedToken doesn't have an 'exp' field // Return if the decodedToken doesn't have an 'exp' field
if ( !decodedToken.hasOwnProperty('exp') ) if (!decodedToken.hasOwnProperty('exp')) {
{
return null; return null;
} }

View File

@ -3,19 +3,21 @@ import { CanActivateChildFn, CanActivateFn, Router } from '@angular/router';
import { AuthService } from 'app/core/auth/auth.service'; import { AuthService } from 'app/core/auth/auth.service';
import { of, switchMap } from 'rxjs'; import { of, switchMap } from 'rxjs';
export const AuthGuard: CanActivateFn | CanActivateChildFn = (route, state) => export const AuthGuard: CanActivateFn | CanActivateChildFn = (route, state) => {
{
const router: Router = inject(Router); const router: Router = inject(Router);
// Check the authentication status // Check the authentication status
return inject(AuthService).check().pipe( return inject(AuthService)
switchMap((authenticated) => .check()
{ .pipe(
switchMap((authenticated) => {
// If the user is not authenticated... // If the user is not authenticated...
if ( !authenticated ) if (!authenticated) {
{
// Redirect to the sign-in page with a redirectUrl param // Redirect to the sign-in page with a redirectUrl param
const redirectURL = state.url === '/sign-out' ? '' : `redirectURL=${state.url}`; const redirectURL =
state.url === '/sign-out'
? ''
: `redirectURL=${state.url}`;
const urlTree = router.parseUrl(`sign-in?${redirectURL}`); const urlTree = router.parseUrl(`sign-in?${redirectURL}`);
return of(urlTree); return of(urlTree);
@ -23,6 +25,6 @@ export const AuthGuard: CanActivateFn | CanActivateChildFn = (route, state) =>
// Allow the access // Allow the access
return of(true); return of(true);
}), })
); );
}; };

View File

@ -3,22 +3,24 @@ import { CanActivateChildFn, CanActivateFn, Router } from '@angular/router';
import { AuthService } from 'app/core/auth/auth.service'; import { AuthService } from 'app/core/auth/auth.service';
import { of, switchMap } from 'rxjs'; import { of, switchMap } from 'rxjs';
export const NoAuthGuard: CanActivateFn | CanActivateChildFn = (route, state) => export const NoAuthGuard: CanActivateFn | CanActivateChildFn = (
{ route,
state
) => {
const router: Router = inject(Router); const router: Router = inject(Router);
// Check the authentication status // Check the authentication status
return inject(AuthService).check().pipe( return inject(AuthService)
switchMap((authenticated) => .check()
{ .pipe(
switchMap((authenticated) => {
// If the user is authenticated... // If the user is authenticated...
if ( authenticated ) if (authenticated) {
{
return of(router.parseUrl('')); return of(router.parseUrl(''));
} }
// Allow the access // Allow the access
return of(true); return of(true);
}), })
); );
}; };

View File

@ -1,8 +1,12 @@
import { ENVIRONMENT_INITIALIZER, EnvironmentProviders, inject, Provider } from '@angular/core'; import {
ENVIRONMENT_INITIALIZER,
EnvironmentProviders,
inject,
Provider,
} from '@angular/core';
import { IconsService } from 'app/core/icons/icons.service'; import { IconsService } from 'app/core/icons/icons.service';
export const provideIcons = (): Array<Provider | EnvironmentProviders> => export const provideIcons = (): Array<Provider | EnvironmentProviders> => {
{
return [ return [
{ {
provide: ENVIRONMENT_INITIALIZER, provide: ENVIRONMENT_INITIALIZER,

View File

@ -3,23 +3,53 @@ import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class IconsService export class IconsService {
{
/** /**
* Constructor * Constructor
*/ */
constructor() constructor() {
{
const domSanitizer = inject(DomSanitizer); const domSanitizer = inject(DomSanitizer);
const matIconRegistry = inject(MatIconRegistry); const matIconRegistry = inject(MatIconRegistry);
// Register icon sets // Register icon sets
matIconRegistry.addSvgIconSet(domSanitizer.bypassSecurityTrustResourceUrl('icons/material-twotone.svg')); matIconRegistry.addSvgIconSet(
matIconRegistry.addSvgIconSetInNamespace('mat_outline', domSanitizer.bypassSecurityTrustResourceUrl('icons/material-outline.svg')); domSanitizer.bypassSecurityTrustResourceUrl(
matIconRegistry.addSvgIconSetInNamespace('mat_solid', domSanitizer.bypassSecurityTrustResourceUrl('icons/material-solid.svg')); 'icons/material-twotone.svg'
matIconRegistry.addSvgIconSetInNamespace('feather', domSanitizer.bypassSecurityTrustResourceUrl('icons/feather.svg')); )
matIconRegistry.addSvgIconSetInNamespace('heroicons_outline', domSanitizer.bypassSecurityTrustResourceUrl('icons/heroicons-outline.svg')); );
matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', domSanitizer.bypassSecurityTrustResourceUrl('icons/heroicons-solid.svg')); matIconRegistry.addSvgIconSetInNamespace(
matIconRegistry.addSvgIconSetInNamespace('heroicons_mini', domSanitizer.bypassSecurityTrustResourceUrl('icons/heroicons-mini.svg')); 'mat_outline',
domSanitizer.bypassSecurityTrustResourceUrl(
'icons/material-outline.svg'
)
);
matIconRegistry.addSvgIconSetInNamespace(
'mat_solid',
domSanitizer.bypassSecurityTrustResourceUrl(
'icons/material-solid.svg'
)
);
matIconRegistry.addSvgIconSetInNamespace(
'feather',
domSanitizer.bypassSecurityTrustResourceUrl('icons/feather.svg')
);
matIconRegistry.addSvgIconSetInNamespace(
'heroicons_outline',
domSanitizer.bypassSecurityTrustResourceUrl(
'icons/heroicons-outline.svg'
)
);
matIconRegistry.addSvgIconSetInNamespace(
'heroicons_solid',
domSanitizer.bypassSecurityTrustResourceUrl(
'icons/heroicons-solid.svg'
)
);
matIconRegistry.addSvgIconSetInNamespace(
'heroicons_mini',
domSanitizer.bypassSecurityTrustResourceUrl(
'icons/heroicons-mini.svg'
)
);
} }
} }

View File

@ -4,10 +4,10 @@ import { Navigation } from 'app/core/navigation/navigation.types';
import { Observable, ReplaySubject, tap } from 'rxjs'; import { Observable, ReplaySubject, tap } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NavigationService export class NavigationService {
{
private _httpClient = inject(HttpClient); private _httpClient = inject(HttpClient);
private _navigation: ReplaySubject<Navigation> = new ReplaySubject<Navigation>(1); private _navigation: ReplaySubject<Navigation> =
new ReplaySubject<Navigation>(1);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -16,8 +16,7 @@ export class NavigationService
/** /**
* Getter for navigation * Getter for navigation
*/ */
get navigation$(): Observable<Navigation> get navigation$(): Observable<Navigation> {
{
return this._navigation.asObservable(); return this._navigation.asObservable();
} }
@ -28,13 +27,11 @@ export class NavigationService
/** /**
* Get all navigation data * Get all navigation data
*/ */
get(): Observable<Navigation> get(): Observable<Navigation> {
{
return this._httpClient.get<Navigation>('api/common/navigation').pipe( return this._httpClient.get<Navigation>('api/common/navigation').pipe(
tap((navigation) => tap((navigation) => {
{
this._navigation.next(navigation); this._navigation.next(navigation);
}), })
); );
} }
} }

View File

@ -1,7 +1,6 @@
import { FuseNavigationItem } from '@fuse/components/navigation'; import { FuseNavigationItem } from '@fuse/components/navigation';
export interface Navigation export interface Navigation {
{
compact: FuseNavigationItem[]; compact: FuseNavigationItem[];
default: FuseNavigationItem[]; default: FuseNavigationItem[];
futuristic: FuseNavigationItem[]; futuristic: FuseNavigationItem[];

View File

@ -4,8 +4,7 @@ import { Translation, TranslocoLoader } from '@ngneat/transloco';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TranslocoHttpLoader implements TranslocoLoader export class TranslocoHttpLoader implements TranslocoLoader {
{
private _httpClient = inject(HttpClient); private _httpClient = inject(HttpClient);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -17,8 +16,7 @@ export class TranslocoHttpLoader implements TranslocoLoader
* *
* @param lang * @param lang
*/ */
getTranslation(lang: string): Observable<Translation> getTranslation(lang: string): Observable<Translation> {
{
return this._httpClient.get<Translation>(`./i18n/${lang}.json`); return this._httpClient.get<Translation>(`./i18n/${lang}.json`);
} }
} }

View File

@ -4,8 +4,7 @@ import { User } from 'app/core/user/user.types';
import { map, Observable, ReplaySubject, tap } from 'rxjs'; import { map, Observable, ReplaySubject, tap } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class UserService export class UserService {
{
private _httpClient = inject(HttpClient); private _httpClient = inject(HttpClient);
private _user: ReplaySubject<User> = new ReplaySubject<User>(1); private _user: ReplaySubject<User> = new ReplaySubject<User>(1);
@ -18,14 +17,12 @@ export class UserService
* *
* @param value * @param value
*/ */
set user(value: User) set user(value: User) {
{
// Store the value // Store the value
this._user.next(value); this._user.next(value);
} }
get user$(): Observable<User> get user$(): Observable<User> {
{
return this._user.asObservable(); return this._user.asObservable();
} }
@ -36,13 +33,11 @@ export class UserService
/** /**
* Get the current signed-in user data * Get the current signed-in user data
*/ */
get(): Observable<User> get(): Observable<User> {
{
return this._httpClient.get<User>('api/common/user').pipe( return this._httpClient.get<User>('api/common/user').pipe(
tap((user) => tap((user) => {
{
this._user.next(user); this._user.next(user);
}), })
); );
} }
@ -51,13 +46,11 @@ export class UserService
* *
* @param user * @param user
*/ */
update(user: User): Observable<any> update(user: User): Observable<any> {
{
return this._httpClient.patch<User>('api/common/user', { user }).pipe( return this._httpClient.patch<User>('api/common/user', { user }).pipe(
map((response) => map((response) => {
{
this._user.next(response); this._user.next(response);
}), })
); );
} }
} }

View File

@ -1,5 +1,4 @@
export interface User export interface User {
{
id: string; id: string;
name: string; name: string;
email: string; email: string;

View File

@ -1,20 +1,21 @@
<!-- Button --> <!-- Button -->
<button <button mat-icon-button [matMenuTriggerFor]="languages">
mat-icon-button <ng-container
[matMenuTriggerFor]="languages"> *ngTemplateOutlet="flagImage; context: { $implicit: activeLang }"
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: activeLang}"></ng-container> ></ng-container>
</button> </button>
<!-- Language menu --> <!-- Language menu -->
<mat-menu <mat-menu [xPosition]="'before'" #languages="matMenu">
[xPosition]="'before'"
#languages="matMenu">
<ng-container *ngFor="let lang of availableLangs; trackBy: trackByFn"> <ng-container *ngFor="let lang of availableLangs; trackBy: trackByFn">
<button <button mat-menu-item (click)="setActiveLang(lang.id)">
mat-menu-item
(click)="setActiveLang(lang.id)">
<span class="flex items-center"> <span class="flex items-center">
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: lang.id}"></ng-container> <ng-container
*ngTemplateOutlet="
flagImage;
context: { $implicit: lang.id }
"
></ng-container>
<span class="ml-3">{{ lang.label }}</span> <span class="ml-3">{{ lang.label }}</span>
</span> </span>
</button> </button>
@ -22,14 +23,15 @@
</mat-menu> </mat-menu>
<!-- Flag image template --> <!-- Flag image template -->
<ng-template <ng-template let-lang #flagImage>
let-lang <span class="relative w-6 overflow-hidden rounded-sm shadow">
#flagImage> <span
<span class="relative w-6 shadow rounded-sm overflow-hidden"> class="absolute inset-0 ring-1 ring-inset ring-black ring-opacity-10"
<span class="absolute inset-0 ring-1 ring-inset ring-black ring-opacity-10"></span> ></span>
<img <img
class="w-full" class="w-full"
[src]="'images/flags/' + flagCodes[lang].toUpperCase() + '.svg'" [src]="'images/flags/' + flagCodes[lang].toUpperCase() + '.svg'"
[alt]="'Flag image for ' + lang"> [alt]="'Flag image for ' + lang"
/>
</span> </span>
</ng-template> </ng-template>

View File

@ -1,8 +1,18 @@
import { NgFor, NgTemplateOutlet } from '@angular/common'; import { NgFor, NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
ViewEncapsulation,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { AvailableLangs, TranslocoService } from '@ngneat/transloco'; import { AvailableLangs, TranslocoService } from '@ngneat/transloco';
import { take } from 'rxjs'; import { take } from 'rxjs';
@ -15,8 +25,7 @@ import { take } from 'rxjs';
standalone: true, standalone: true,
imports: [MatButtonModule, MatMenuModule, NgTemplateOutlet, NgFor], imports: [MatButtonModule, MatMenuModule, NgTemplateOutlet, NgFor],
}) })
export class LanguagesComponent implements OnInit, OnDestroy export class LanguagesComponent implements OnInit, OnDestroy {
{
availableLangs: AvailableLangs; availableLangs: AvailableLangs;
activeLang: string; activeLang: string;
flagCodes: any; flagCodes: any;
@ -27,10 +36,8 @@ export class LanguagesComponent implements OnInit, OnDestroy
constructor( constructor(
private _changeDetectorRef: ChangeDetectorRef, private _changeDetectorRef: ChangeDetectorRef,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService,
private _translocoService: TranslocoService, private _translocoService: TranslocoService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -39,14 +46,12 @@ export class LanguagesComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Get the available languages from transloco // Get the available languages from transloco
this.availableLangs = this._translocoService.getAvailableLangs(); this.availableLangs = this._translocoService.getAvailableLangs();
// Subscribe to language changes // Subscribe to language changes
this._translocoService.langChanges$.subscribe((activeLang) => this._translocoService.langChanges$.subscribe((activeLang) => {
{
// Get the active lang // Get the active lang
this.activeLang = activeLang; this.activeLang = activeLang;
@ -56,17 +61,15 @@ export class LanguagesComponent implements OnInit, OnDestroy
// Set the country iso codes for languages for flags // Set the country iso codes for languages for flags
this.flagCodes = { this.flagCodes = {
'en': 'us', en: 'us',
'tr': 'tr', tr: 'tr',
}; };
} }
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
@ -77,8 +80,7 @@ export class LanguagesComponent implements OnInit, OnDestroy
* *
* @param lang * @param lang
*/ */
setActiveLang(lang: string): void setActiveLang(lang: string): void {
{
// Set the active lang // Set the active lang
this._translocoService.setActiveLang(lang); this._translocoService.setActiveLang(lang);
} }
@ -89,8 +91,7 @@ export class LanguagesComponent implements OnInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
@ -104,8 +105,7 @@ export class LanguagesComponent implements OnInit, OnDestroy
* @param lang * @param lang
* @private * @private
*/ */
private _updateNavigation(lang: string): void private _updateNavigation(lang: string): void {
{
// For the demonstration purposes, we will only update the Dashboard names // For the demonstration purposes, we will only update the Dashboard names
// from the navigation but you can do a full swap and change the entire // from the navigation but you can do a full swap and change the entire
// navigation data. // navigation data.
@ -114,11 +114,13 @@ export class LanguagesComponent implements OnInit, OnDestroy
// it's up to you. // it's up to you.
// Get the component -> navigation data -> item // Get the component -> navigation data -> item
const navComponent = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>('mainNavigation'); const navComponent =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
'mainNavigation'
);
// Return if the navigation component does not exist // Return if the navigation component does not exist
if ( !navComponent ) if (!navComponent) {
{
return null; return null;
} }
@ -126,12 +128,15 @@ export class LanguagesComponent implements OnInit, OnDestroy
const navigation = navComponent.navigation; const navigation = navComponent.navigation;
// Get the Project dashboard item and update its title // Get the Project dashboard item and update its title
const projectDashboardItem = this._fuseNavigationService.getItem('dashboards.project', navigation); const projectDashboardItem = this._fuseNavigationService.getItem(
if ( projectDashboardItem ) 'dashboards.project',
{ navigation
this._translocoService.selectTranslate('Project').pipe(take(1)) );
.subscribe((translation) => if (projectDashboardItem) {
{ this._translocoService
.selectTranslate('Project')
.pipe(take(1))
.subscribe((translation) => {
// Set the title // Set the title
projectDashboardItem.title = translation; projectDashboardItem.title = translation;
@ -141,12 +146,15 @@ export class LanguagesComponent implements OnInit, OnDestroy
} }
// Get the Analytics dashboard item and update its title // Get the Analytics dashboard item and update its title
const analyticsDashboardItem = this._fuseNavigationService.getItem('dashboards.analytics', navigation); const analyticsDashboardItem = this._fuseNavigationService.getItem(
if ( analyticsDashboardItem ) 'dashboards.analytics',
{ navigation
this._translocoService.selectTranslate('Analytics').pipe(take(1)) );
.subscribe((translation) => if (analyticsDashboardItem) {
{ this._translocoService
.selectTranslate('Analytics')
.pipe(take(1))
.subscribe((translation) => {
// Set the title // Set the title
analyticsDashboardItem.title = translation; analyticsDashboardItem.title = translation;

View File

@ -1,11 +1,12 @@
<!-- Messages toggle --> <!-- Messages toggle -->
<button <button mat-icon-button (click)="openPanel()" #messagesOrigin>
mat-icon-button
(click)="openPanel()"
#messagesOrigin>
<ng-container *ngIf="unreadCount > 0"> <ng-container *ngIf="unreadCount > 0">
<span class="absolute top-0 right-0 left-0 flex items-center justify-center h-3"> <span
<span class="flex items-center justify-center shrink-0 min-w-4 h-4 px-1 ml-4 mt-2.5 rounded-full bg-indigo-600 text-indigo-50 text-xs font-medium"> class="absolute left-0 right-0 top-0 flex h-3 items-center justify-center"
>
<span
class="ml-4 mt-2.5 flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full bg-indigo-600 px-1 text-xs font-medium text-indigo-50"
>
{{ unreadCount }} {{ unreadCount }}
</span> </span>
</span> </span>
@ -15,18 +16,19 @@
<!-- Messages panel --> <!-- Messages panel -->
<ng-template #messagesPanel> <ng-template #messagesPanel>
<div
<div class="fixed inset-0 sm:static sm:inset-auto flex flex-col sm:min-w-90 sm:w-90 sm:rounded-2xl overflow-hidden shadow-lg"> class="fixed inset-0 flex flex-col overflow-hidden shadow-lg sm:static sm:inset-auto sm:w-90 sm:min-w-90 sm:rounded-2xl"
>
<!-- Header --> <!-- Header -->
<div class="flex shrink-0 items-center py-4 pr-4 pl-6 bg-primary text-on-primary"> <div
<div class="sm:hidden -ml-1 mr-3"> class="flex shrink-0 items-center bg-primary py-4 pl-6 pr-4 text-on-primary"
<button >
mat-icon-button <div class="-ml-1 mr-3 sm:hidden">
(click)="closePanel()"> <button mat-icon-button (click)="closePanel()">
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:x-mark'"></mat-icon> [svgIcon]="'heroicons_solid:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
<div class="text-lg font-medium leading-10">Messages</div> <div class="text-lg font-medium leading-10">Messages</div>
@ -36,38 +38,48 @@
mat-icon-button mat-icon-button
[disabled]="unreadCount === 0" [disabled]="unreadCount === 0"
[matTooltip]="'Mark all as read'" [matTooltip]="'Mark all as read'"
(click)="markAllAsRead()"> (click)="markAllAsRead()"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:envelope-open'"></mat-icon> [svgIcon]="'heroicons_solid:envelope-open'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card"> <div
class="bg-card relative flex flex-auto flex-col divide-y overflow-y-auto sm:max-h-120"
>
<!-- Messages --> <!-- Messages -->
<ng-container *ngFor="let message of messages; trackBy: trackByFn"> <ng-container *ngFor="let message of messages; trackBy: trackByFn">
<div <div
class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5" class="group flex hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
[ngClass]="{'unread': !message.read}"> [ngClass]="{ unread: !message.read }"
>
<!-- Message with a link --> <!-- Message with a link -->
<ng-container *ngIf="message.link"> <ng-container *ngIf="message.link">
<!-- Normal links --> <!-- Normal links -->
<ng-container *ngIf="!message.useRouter"> <ng-container *ngIf="!message.useRouter">
<a <a
class="flex flex-auto py-5 pl-6 cursor-pointer" class="flex flex-auto cursor-pointer py-5 pl-6"
[href]="message.link"> [href]="message.link"
<ng-container *ngTemplateOutlet="messageContent"></ng-container> >
<ng-container
*ngTemplateOutlet="messageContent"
></ng-container>
</a> </a>
</ng-container> </ng-container>
<!-- Router links --> <!-- Router links -->
<ng-container *ngIf="message.useRouter"> <ng-container *ngIf="message.useRouter">
<a <a
class="flex flex-auto py-5 pl-6 cursor-pointer" class="flex flex-auto cursor-pointer py-5 pl-6"
[routerLink]="message.link"> [routerLink]="message.link"
<ng-container *ngTemplateOutlet="messageContent"></ng-container> >
<ng-container
*ngTemplateOutlet="messageContent"
></ng-container>
</a> </a>
</ng-container> </ng-container>
</ng-container> </ng-container>
@ -75,32 +87,43 @@
<!-- Message without a link --> <!-- Message without a link -->
<ng-container *ngIf="!message.link"> <ng-container *ngIf="!message.link">
<div class="flex flex-auto py-5 pl-6"> <div class="flex flex-auto py-5 pl-6">
<ng-container *ngTemplateOutlet="messageContent"></ng-container> <ng-container
*ngTemplateOutlet="messageContent"
></ng-container>
</div> </div>
</ng-container> </ng-container>
<!-- Actions --> <!-- Actions -->
<div class="relative flex flex-col my-5 mr-6 ml-2"> <div class="relative my-5 ml-2 mr-6 flex flex-col">
<!-- Indicator --> <!-- Indicator -->
<button <button
class="w-6 h-6 min-h-6" class="h-6 min-h-6 w-6"
mat-icon-button mat-icon-button
(click)="toggleRead(message)" (click)="toggleRead(message)"
[matTooltip]="message.read ? 'Mark as unread' : 'Mark as read'"> [matTooltip]="
message.read ? 'Mark as unread' : 'Mark as read'
"
>
<span <span
class="w-2 h-2 rounded-full" class="h-2 w-2 rounded-full"
[ngClass]="{'bg-gray-400 dark:bg-gray-500 sm:opacity-0 sm:group-hover:opacity-100': message.read, [ngClass]="{
'bg-primary': !message.read}"></span> 'bg-gray-400 dark:bg-gray-500 sm:opacity-0 sm:group-hover:opacity-100':
message.read,
'bg-primary': !message.read
}"
></span>
</button> </button>
<!-- Remove --> <!-- Remove -->
<button <button
class="w-6 h-6 min-h-6 sm:opacity-0 sm:group-hover:opacity-100" class="h-6 min-h-6 w-6 sm:opacity-0 sm:group-hover:opacity-100"
mat-icon-button mat-icon-button
(click)="delete(message)" (click)="delete(message)"
[matTooltip]="'Remove'"> [matTooltip]="'Remove'"
>
<mat-icon <mat-icon
class="icon-size-4" class="icon-size-4"
[svgIcon]="'heroicons_solid:x-mark'"></mat-icon> [svgIcon]="'heroicons_solid:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
@ -109,33 +132,39 @@
<ng-template #messageContent> <ng-template #messageContent>
<!-- Icon --> <!-- Icon -->
<ng-container *ngIf="message.icon && !message.image"> <ng-container *ngIf="message.icon && !message.image">
<div class="flex shrink-0 items-center justify-center w-8 h-8 mr-4 rounded-full bg-gray-100 dark:bg-gray-700"> <div
class="mr-4 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-700"
>
<mat-icon <mat-icon
class="icon-size-5" class="icon-size-5"
[svgIcon]="message.icon"> [svgIcon]="message.icon"
>
</mat-icon> </mat-icon>
</div> </div>
</ng-container> </ng-container>
<!-- Image --> <!-- Image -->
<ng-container *ngIf="message.image"> <ng-container *ngIf="message.image">
<img <img
class="shrink-0 w-8 h-8 mr-4 rounded-full overflow-hidden object-cover object-center" class="mr-4 h-8 w-8 shrink-0 overflow-hidden rounded-full object-cover object-center"
[src]="message.image" [src]="message.image"
[alt]="'Message image'"> [alt]="'Message image'"
/>
</ng-container> </ng-container>
<!-- Title, description & time --> <!-- Title, description & time -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<ng-container *ngIf="message.title"> <ng-container *ngIf="message.title">
<div <div
class="font-semibold line-clamp-1" class="line-clamp-1 font-semibold"
[innerHTML]="message.title"></div> [innerHTML]="message.title"
></div>
</ng-container> </ng-container>
<ng-container *ngIf="message.description"> <ng-container *ngIf="message.description">
<div <div
class="line-clamp-2" class="line-clamp-2"
[innerHTML]="message.description"></div> [innerHTML]="message.description"
></div>
</ng-container> </ng-container>
<div class="mt-2 text-sm leading-none text-secondary"> <div class="text-secondary mt-2 text-sm leading-none">
{{ message.time | date: 'MMM dd, h:mm a' }} {{ message.time | date: 'MMM dd, h:mm a' }}
</div> </div>
</div> </div>
@ -144,14 +173,25 @@
<!-- No messages --> <!-- No messages -->
<ng-container *ngIf="!messages || !messages.length"> <ng-container *ngIf="!messages || !messages.length">
<div class="flex flex-col flex-auto items-center justify-center sm:justify-start py-12 px-8"> <div
<div class="flex flex-0 items-center justify-center w-14 h-14 rounded-full bg-primary-100 dark:bg-primary-600"> class="flex flex-auto flex-col items-center justify-center px-8 py-12 sm:justify-start"
>
<div
class="flex h-14 w-14 flex-0 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-600"
>
<mat-icon <mat-icon
class="text-primary-700 dark:text-primary-50" class="text-primary-700 dark:text-primary-50"
[svgIcon]="'heroicons_outline:inbox'"></mat-icon> [svgIcon]="'heroicons_outline:inbox'"
></mat-icon>
</div>
<div class="mt-5 text-2xl font-semibold tracking-tight">
No messages
</div>
<div
class="text-secondary mt-1 w-full max-w-60 text-center text-md"
>
When you have messages, they will appear here.
</div> </div>
<div class="mt-5 text-2xl font-semibold tracking-tight">No messages</div>
<div class="w-full max-w-60 mt-1 text-md text-center text-secondary">When you have messages, they will appear here.</div>
</div> </div>
</ng-container> </ng-container>
</div> </div>

View File

@ -1,7 +1,23 @@
import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { DatePipe, NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import {
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; DatePipe,
NgClass,
NgFor,
NgIf,
NgTemplateOutlet,
} from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
import { MatButton, MatButtonModule } from '@angular/material/button'; import { MatButton, MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
@ -17,10 +33,19 @@ import { Subject, takeUntil } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'messages', exportAs: 'messages',
standalone: true, standalone: true,
imports : [MatButtonModule, NgIf, MatIconModule, MatTooltipModule, NgFor, NgClass, NgTemplateOutlet, RouterLink, DatePipe], imports: [
MatButtonModule,
NgIf,
MatIconModule,
MatTooltipModule,
NgFor,
NgClass,
NgTemplateOutlet,
RouterLink,
DatePipe,
],
}) })
export class MessagesComponent implements OnInit, OnDestroy export class MessagesComponent implements OnInit, OnDestroy {
{
@ViewChild('messagesOrigin') private _messagesOrigin: MatButton; @ViewChild('messagesOrigin') private _messagesOrigin: MatButton;
@ViewChild('messagesPanel') private _messagesPanel: TemplateRef<any>; @ViewChild('messagesPanel') private _messagesPanel: TemplateRef<any>;
@ -36,10 +61,8 @@ export class MessagesComponent implements OnInit, OnDestroy
private _changeDetectorRef: ChangeDetectorRef, private _changeDetectorRef: ChangeDetectorRef,
private _messagesService: MessagesService, private _messagesService: MessagesService,
private _overlay: Overlay, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef, private _viewContainerRef: ViewContainerRef
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -48,13 +71,11 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to message changes // Subscribe to message changes
this._messagesService.messages$ this._messagesService.messages$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((messages: Message[]) => .subscribe((messages: Message[]) => {
{
// Load the messages // Load the messages
this.messages = messages; this.messages = messages;
@ -69,15 +90,13 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
// Dispose the overlay // Dispose the overlay
if ( this._overlayRef ) if (this._overlayRef) {
{
this._overlayRef.dispose(); this._overlayRef.dispose();
} }
} }
@ -89,37 +108,34 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* Open the messages panel * Open the messages panel
*/ */
openPanel(): void openPanel(): void {
{
// Return if the messages panel or its origin is not defined // Return if the messages panel or its origin is not defined
if ( !this._messagesPanel || !this._messagesOrigin ) if (!this._messagesPanel || !this._messagesOrigin) {
{
return; return;
} }
// Create the overlay if it doesn't exist // Create the overlay if it doesn't exist
if ( !this._overlayRef ) if (!this._overlayRef) {
{
this._createOverlay(); this._createOverlay();
} }
// Attach the portal to the overlay // Attach the portal to the overlay
this._overlayRef.attach(new TemplatePortal(this._messagesPanel, this._viewContainerRef)); this._overlayRef.attach(
new TemplatePortal(this._messagesPanel, this._viewContainerRef)
);
} }
/** /**
* Close the messages panel * Close the messages panel
*/ */
closePanel(): void closePanel(): void {
{
this._overlayRef.detach(); this._overlayRef.detach();
} }
/** /**
* Mark all messages as read * Mark all messages as read
*/ */
markAllAsRead(): void markAllAsRead(): void {
{
// Mark all as read // Mark all as read
this._messagesService.markAllAsRead().subscribe(); this._messagesService.markAllAsRead().subscribe();
} }
@ -127,8 +143,7 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* Toggle read status of the given message * Toggle read status of the given message
*/ */
toggleRead(message: Message): void toggleRead(message: Message): void {
{
// Toggle the read status // Toggle the read status
message.read = !message.read; message.read = !message.read;
@ -139,8 +154,7 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* Delete the given message * Delete the given message
*/ */
delete(message: Message): void delete(message: Message): void {
{
// Delete the message // Delete the message
this._messagesService.delete(message.id).subscribe(); this._messagesService.delete(message.id).subscribe();
} }
@ -151,8 +165,7 @@ export class MessagesComponent implements OnInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
@ -163,15 +176,17 @@ export class MessagesComponent implements OnInit, OnDestroy
/** /**
* Create the overlay * Create the overlay
*/ */
private _createOverlay(): void private _createOverlay(): void {
{
// Create the overlay // Create the overlay
this._overlayRef = this._overlay.create({ this._overlayRef = this._overlay.create({
hasBackdrop: true, hasBackdrop: true,
backdropClass: 'fuse-backdrop-on-mobile', backdropClass: 'fuse-backdrop-on-mobile',
scrollStrategy: this._overlay.scrollStrategies.block(), scrollStrategy: this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position() positionStrategy: this._overlay
.flexibleConnectedTo(this._messagesOrigin._elementRef.nativeElement) .position()
.flexibleConnectedTo(
this._messagesOrigin._elementRef.nativeElement
)
.withLockedPosition(true) .withLockedPosition(true)
.withPush(true) .withPush(true)
.withPositions([ .withPositions([
@ -203,8 +218,7 @@ export class MessagesComponent implements OnInit, OnDestroy
}); });
// Detach the overlay from the portal on backdrop click // Detach the overlay from the portal on backdrop click
this._overlayRef.backdropClick().subscribe(() => this._overlayRef.backdropClick().subscribe(() => {
{
this._overlayRef.detach(); this._overlayRef.detach();
}); });
} }
@ -214,13 +228,11 @@ export class MessagesComponent implements OnInit, OnDestroy
* *
* @private * @private
*/ */
private _calculateUnreadCount(): void private _calculateUnreadCount(): void {
{
let count = 0; let count = 0;
if ( this.messages && this.messages.length ) if (this.messages && this.messages.length) {
{ count = this.messages.filter((message) => !message.read).length;
count = this.messages.filter(message => !message.read).length;
} }
this.unreadCount = count; this.unreadCount = count;

View File

@ -4,16 +4,15 @@ import { Message } from 'app/layout/common/messages/messages.types';
import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs'; import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class MessagesService export class MessagesService {
{ private _messages: ReplaySubject<Message[]> = new ReplaySubject<Message[]>(
private _messages: ReplaySubject<Message[]> = new ReplaySubject<Message[]>(1); 1
);
/** /**
* Constructor * Constructor
*/ */
constructor(private _httpClient: HttpClient) constructor(private _httpClient: HttpClient) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -22,8 +21,7 @@ export class MessagesService
/** /**
* Getter for messages * Getter for messages
*/ */
get messages$(): Observable<Message[]> get messages$(): Observable<Message[]> {
{
return this._messages.asObservable(); return this._messages.asObservable();
} }
@ -34,13 +32,11 @@ export class MessagesService
/** /**
* Get all messages * Get all messages
*/ */
getAll(): Observable<Message[]> getAll(): Observable<Message[]> {
{
return this._httpClient.get<Message[]>('api/common/messages').pipe( return this._httpClient.get<Message[]>('api/common/messages').pipe(
tap((messages) => tap((messages) => {
{
this._messages.next(messages); this._messages.next(messages);
}), })
); );
} }
@ -49,20 +45,22 @@ export class MessagesService
* *
* @param message * @param message
*/ */
create(message: Message): Observable<Message> create(message: Message): Observable<Message> {
{
return this.messages$.pipe( return this.messages$.pipe(
take(1), take(1),
switchMap(messages => this._httpClient.post<Message>('api/common/messages', {message}).pipe( switchMap((messages) =>
map((newMessage) => this._httpClient
{ .post<Message>('api/common/messages', { message })
.pipe(
map((newMessage) => {
// Update the messages with the new message // Update the messages with the new message
this._messages.next([...messages, newMessage]); this._messages.next([...messages, newMessage]);
// Return the new message from observable // Return the new message from observable
return newMessage; return newMessage;
}), })
)), )
)
); );
} }
@ -72,18 +70,21 @@ export class MessagesService
* @param id * @param id
* @param message * @param message
*/ */
update(id: string, message: Message): Observable<Message> update(id: string, message: Message): Observable<Message> {
{
return this.messages$.pipe( return this.messages$.pipe(
take(1), take(1),
switchMap(messages => this._httpClient.patch<Message>('api/common/messages', { switchMap((messages) =>
this._httpClient
.patch<Message>('api/common/messages', {
id, id,
message, message,
}).pipe( })
map((updatedMessage: Message) => .pipe(
{ map((updatedMessage: Message) => {
// Find the index of the updated message // Find the index of the updated message
const index = messages.findIndex(item => item.id === id); const index = messages.findIndex(
(item) => item.id === id
);
// Update the message // Update the message
messages[index] = updatedMessage; messages[index] = updatedMessage;
@ -93,8 +94,9 @@ export class MessagesService
// Return the updated message // Return the updated message
return updatedMessage; return updatedMessage;
}), })
)), )
)
); );
} }
@ -103,15 +105,18 @@ export class MessagesService
* *
* @param id * @param id
*/ */
delete(id: string): Observable<boolean> delete(id: string): Observable<boolean> {
{
return this.messages$.pipe( return this.messages$.pipe(
take(1), take(1),
switchMap(messages => this._httpClient.delete<boolean>('api/common/messages', {params: {id}}).pipe( switchMap((messages) =>
map((isDeleted: boolean) => this._httpClient
{ .delete<boolean>('api/common/messages', { params: { id } })
.pipe(
map((isDeleted: boolean) => {
// Find the index of the deleted message // Find the index of the deleted message
const index = messages.findIndex(item => item.id === id); const index = messages.findIndex(
(item) => item.id === id
);
// Delete the message // Delete the message
messages.splice(index, 1); messages.splice(index, 1);
@ -121,24 +126,25 @@ export class MessagesService
// Return the deleted status // Return the deleted status
return isDeleted; return isDeleted;
}), })
)), )
)
); );
} }
/** /**
* Mark all messages as read * Mark all messages as read
*/ */
markAllAsRead(): Observable<boolean> markAllAsRead(): Observable<boolean> {
{
return this.messages$.pipe( return this.messages$.pipe(
take(1), take(1),
switchMap(messages => this._httpClient.get<boolean>('api/common/messages/mark-all-as-read').pipe( switchMap((messages) =>
map((isUpdated: boolean) => this._httpClient
{ .get<boolean>('api/common/messages/mark-all-as-read')
.pipe(
map((isUpdated: boolean) => {
// Go through all messages and set them as read // Go through all messages and set them as read
messages.forEach((message, index) => messages.forEach((message, index) => {
{
messages[index].read = true; messages[index].read = true;
}); });
@ -147,8 +153,9 @@ export class MessagesService
// Return the updated status // Return the updated status
return isUpdated; return isUpdated;
}), })
)), )
)
); );
} }
} }

View File

@ -1,5 +1,4 @@
export interface Message export interface Message {
{
id: string; id: string;
icon?: string; icon?: string;
image?: string; image?: string;

View File

@ -1,11 +1,12 @@
<!-- Notifications toggle --> <!-- Notifications toggle -->
<button <button mat-icon-button (click)="openPanel()" #notificationsOrigin>
mat-icon-button
(click)="openPanel()"
#notificationsOrigin>
<ng-container *ngIf="unreadCount > 0"> <ng-container *ngIf="unreadCount > 0">
<span class="absolute top-0 right-0 left-0 flex items-center justify-center h-3"> <span
<span class="flex items-center justify-center shrink-0 min-w-4 h-4 px-1 ml-4 mt-2.5 rounded-full bg-teal-600 text-indigo-50 text-xs font-medium"> class="absolute left-0 right-0 top-0 flex h-3 items-center justify-center"
>
<span
class="ml-4 mt-2.5 flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full bg-teal-600 px-1 text-xs font-medium text-indigo-50"
>
{{ unreadCount }} {{ unreadCount }}
</span> </span>
</span> </span>
@ -15,18 +16,19 @@
<!-- Notifications panel --> <!-- Notifications panel -->
<ng-template #notificationsPanel> <ng-template #notificationsPanel>
<div
<div class="fixed inset-0 sm:static sm:inset-auto flex flex-col sm:min-w-90 sm:w-90 sm:rounded-2xl overflow-hidden shadow-lg"> class="fixed inset-0 flex flex-col overflow-hidden shadow-lg sm:static sm:inset-auto sm:w-90 sm:min-w-90 sm:rounded-2xl"
>
<!-- Header --> <!-- Header -->
<div class="flex shrink-0 items-center py-4 pr-4 pl-6 bg-primary text-on-primary"> <div
<div class="sm:hidden -ml-1 mr-3"> class="flex shrink-0 items-center bg-primary py-4 pl-6 pr-4 text-on-primary"
<button >
mat-icon-button <div class="-ml-1 mr-3 sm:hidden">
(click)="closePanel()"> <button mat-icon-button (click)="closePanel()">
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:x-mark'"></mat-icon> [svgIcon]="'heroicons_solid:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
<div class="text-lg font-medium leading-10">Notifications</div> <div class="text-lg font-medium leading-10">Notifications</div>
@ -36,38 +38,50 @@
mat-icon-button mat-icon-button
[matTooltip]="'Mark all as read'" [matTooltip]="'Mark all as read'"
[disabled]="unreadCount === 0" [disabled]="unreadCount === 0"
(click)="markAllAsRead()"> (click)="markAllAsRead()"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:envelope-open'"></mat-icon> [svgIcon]="'heroicons_solid:envelope-open'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card">
<!-- Notifications -->
<ng-container *ngFor="let notification of notifications; trackBy: trackByFn">
<div <div
class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5" class="bg-card relative flex flex-auto flex-col divide-y overflow-y-auto sm:max-h-120"
[ngClass]="{'unread': !notification.read}"> >
<!-- Notifications -->
<ng-container
*ngFor="let notification of notifications; trackBy: trackByFn"
>
<div
class="group flex hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
[ngClass]="{ unread: !notification.read }"
>
<!-- Notification with a link --> <!-- Notification with a link -->
<ng-container *ngIf="notification.link"> <ng-container *ngIf="notification.link">
<!-- Normal links --> <!-- Normal links -->
<ng-container *ngIf="!notification.useRouter"> <ng-container *ngIf="!notification.useRouter">
<a <a
class="flex flex-auto py-5 pl-6 cursor-pointer" class="flex flex-auto cursor-pointer py-5 pl-6"
[href]="notification.link"> [href]="notification.link"
<ng-container *ngTemplateOutlet="notificationContent"></ng-container> >
<ng-container
*ngTemplateOutlet="notificationContent"
></ng-container>
</a> </a>
</ng-container> </ng-container>
<!-- Router links --> <!-- Router links -->
<ng-container *ngIf="notification.useRouter"> <ng-container *ngIf="notification.useRouter">
<a <a
class="flex flex-auto py-5 pl-6 cursor-pointer" class="flex flex-auto cursor-pointer py-5 pl-6"
[routerLink]="notification.link"> [routerLink]="notification.link"
<ng-container *ngTemplateOutlet="notificationContent"></ng-container> >
<ng-container
*ngTemplateOutlet="notificationContent"
></ng-container>
</a> </a>
</ng-container> </ng-container>
</ng-container> </ng-container>
@ -75,68 +89,88 @@
<!-- Notification without a link --> <!-- Notification without a link -->
<ng-container *ngIf="!notification.link"> <ng-container *ngIf="!notification.link">
<div class="flex flex-auto py-5 pl-6"> <div class="flex flex-auto py-5 pl-6">
<ng-container *ngTemplateOutlet="notificationContent"></ng-container> <ng-container
*ngTemplateOutlet="notificationContent"
></ng-container>
</div> </div>
</ng-container> </ng-container>
<!-- Actions --> <!-- Actions -->
<div class="relative flex flex-col my-5 mr-6 ml-2"> <div class="relative my-5 ml-2 mr-6 flex flex-col">
<!-- Indicator --> <!-- Indicator -->
<button <button
class="w-6 h-6 min-h-6" class="h-6 min-h-6 w-6"
mat-icon-button mat-icon-button
(click)="toggleRead(notification)" (click)="toggleRead(notification)"
[matTooltip]="notification.read ? 'Mark as unread' : 'Mark as read'"> [matTooltip]="
notification.read
? 'Mark as unread'
: 'Mark as read'
"
>
<span <span
class="w-2 h-2 rounded-full" class="h-2 w-2 rounded-full"
[ngClass]="{'bg-gray-400 dark:bg-gray-500 sm:opacity-0 sm:group-hover:opacity-100': notification.read, [ngClass]="{
'bg-primary': !notification.read}"></span> 'bg-gray-400 dark:bg-gray-500 sm:opacity-0 sm:group-hover:opacity-100':
notification.read,
'bg-primary': !notification.read
}"
></span>
</button> </button>
<!-- Remove --> <!-- Remove -->
<button <button
class="w-6 h-6 min-h-6 sm:opacity-0 sm:group-hover:opacity-100" class="h-6 min-h-6 w-6 sm:opacity-0 sm:group-hover:opacity-100"
mat-icon-button mat-icon-button
(click)="delete(notification)" (click)="delete(notification)"
[matTooltip]="'Remove'"> [matTooltip]="'Remove'"
>
<mat-icon <mat-icon
class="icon-size-4" class="icon-size-4"
[svgIcon]="'heroicons_solid:x-mark'"></mat-icon> [svgIcon]="'heroicons_solid:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
<!-- Notification content template --> <!-- Notification content template -->
<ng-template #notificationContent> <ng-template #notificationContent>
<!-- Icon --> <!-- Icon -->
<ng-container *ngIf="notification.icon && !notification.image"> <ng-container
<div class="flex shrink-0 items-center justify-center w-8 h-8 mr-4 rounded-full bg-gray-100 dark:bg-gray-700"> *ngIf="notification.icon && !notification.image"
>
<div
class="mr-4 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-700"
>
<mat-icon <mat-icon
class="icon-size-5" class="icon-size-5"
[svgIcon]="notification.icon"> [svgIcon]="notification.icon"
>
</mat-icon> </mat-icon>
</div> </div>
</ng-container> </ng-container>
<!-- Image --> <!-- Image -->
<ng-container *ngIf="notification.image"> <ng-container *ngIf="notification.image">
<img <img
class="shrink-0 w-8 h-8 mr-4 rounded-full overflow-hidden object-cover object-center" class="mr-4 h-8 w-8 shrink-0 overflow-hidden rounded-full object-cover object-center"
[src]="notification.image" [src]="notification.image"
[alt]="'Notification image'"> [alt]="'Notification image'"
/>
</ng-container> </ng-container>
<!-- Title, description & time --> <!-- Title, description & time -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<ng-container *ngIf="notification.title"> <ng-container *ngIf="notification.title">
<div <div
class="font-semibold line-clamp-1" class="line-clamp-1 font-semibold"
[innerHTML]="notification.title"></div> [innerHTML]="notification.title"
></div>
</ng-container> </ng-container>
<ng-container *ngIf="notification.description"> <ng-container *ngIf="notification.description">
<div <div
class="line-clamp-2" class="line-clamp-2"
[innerHTML]="notification.description"></div> [innerHTML]="notification.description"
></div>
</ng-container> </ng-container>
<div class="mt-2 text-sm leading-none text-secondary"> <div class="text-secondary mt-2 text-sm leading-none">
{{ notification.time | date: 'MMM dd, h:mm a' }} {{ notification.time | date: 'MMM dd, h:mm a' }}
</div> </div>
</div> </div>
@ -145,19 +179,27 @@
<!-- No notifications --> <!-- No notifications -->
<ng-container *ngIf="!notifications || !notifications.length"> <ng-container *ngIf="!notifications || !notifications.length">
<div class="flex flex-col flex-auto items-center justify-center sm:justify-start py-12 px-8"> <div
<div class="flex flex-0 items-center justify-center w-14 h-14 rounded-full bg-primary-100 dark:bg-primary-600"> class="flex flex-auto flex-col items-center justify-center px-8 py-12 sm:justify-start"
>
<div
class="flex h-14 w-14 flex-0 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-600"
>
<mat-icon <mat-icon
class="text-primary-700 dark:text-primary-50" class="text-primary-700 dark:text-primary-50"
[svgIcon]="'heroicons_outline:bell'"></mat-icon> [svgIcon]="'heroicons_outline:bell'"
></mat-icon>
</div>
<div class="mt-5 text-2xl font-semibold tracking-tight">
No notifications
</div>
<div
class="text-secondary mt-1 w-full max-w-60 text-center text-md"
>
When you have notifications, they will appear here.
</div> </div>
<div class="mt-5 text-2xl font-semibold tracking-tight">No notifications</div>
<div class="w-full max-w-60 mt-1 text-md text-center text-secondary">When you have notifications, they will appear here.</div>
</div> </div>
</ng-container> </ng-container>
</div> </div>
</div> </div>
</ng-template> </ng-template>

View File

@ -1,7 +1,23 @@
import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { DatePipe, NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import {
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; DatePipe,
NgClass,
NgFor,
NgIf,
NgTemplateOutlet,
} from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
import { MatButton, MatButtonModule } from '@angular/material/button'; import { MatButton, MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
@ -17,12 +33,22 @@ import { Subject, takeUntil } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'notifications', exportAs: 'notifications',
standalone: true, standalone: true,
imports : [MatButtonModule, NgIf, MatIconModule, MatTooltipModule, NgFor, NgClass, NgTemplateOutlet, RouterLink, DatePipe], imports: [
MatButtonModule,
NgIf,
MatIconModule,
MatTooltipModule,
NgFor,
NgClass,
NgTemplateOutlet,
RouterLink,
DatePipe,
],
}) })
export class NotificationsComponent implements OnInit, OnDestroy export class NotificationsComponent implements OnInit, OnDestroy {
{
@ViewChild('notificationsOrigin') private _notificationsOrigin: MatButton; @ViewChild('notificationsOrigin') private _notificationsOrigin: MatButton;
@ViewChild('notificationsPanel') private _notificationsPanel: TemplateRef<any>; @ViewChild('notificationsPanel')
private _notificationsPanel: TemplateRef<any>;
notifications: Notification[]; notifications: Notification[];
unreadCount: number = 0; unreadCount: number = 0;
@ -36,10 +62,8 @@ export class NotificationsComponent implements OnInit, OnDestroy
private _changeDetectorRef: ChangeDetectorRef, private _changeDetectorRef: ChangeDetectorRef,
private _notificationsService: NotificationsService, private _notificationsService: NotificationsService,
private _overlay: Overlay, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef, private _viewContainerRef: ViewContainerRef
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -48,13 +72,11 @@ export class NotificationsComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to notification changes // Subscribe to notification changes
this._notificationsService.notifications$ this._notificationsService.notifications$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((notifications: Notification[]) => .subscribe((notifications: Notification[]) => {
{
// Load the notifications // Load the notifications
this.notifications = notifications; this.notifications = notifications;
@ -69,15 +91,13 @@ export class NotificationsComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
// Dispose the overlay // Dispose the overlay
if ( this._overlayRef ) if (this._overlayRef) {
{
this._overlayRef.dispose(); this._overlayRef.dispose();
} }
} }
@ -89,37 +109,34 @@ export class NotificationsComponent implements OnInit, OnDestroy
/** /**
* Open the notifications panel * Open the notifications panel
*/ */
openPanel(): void openPanel(): void {
{
// Return if the notifications panel or its origin is not defined // Return if the notifications panel or its origin is not defined
if ( !this._notificationsPanel || !this._notificationsOrigin ) if (!this._notificationsPanel || !this._notificationsOrigin) {
{
return; return;
} }
// Create the overlay if it doesn't exist // Create the overlay if it doesn't exist
if ( !this._overlayRef ) if (!this._overlayRef) {
{
this._createOverlay(); this._createOverlay();
} }
// Attach the portal to the overlay // Attach the portal to the overlay
this._overlayRef.attach(new TemplatePortal(this._notificationsPanel, this._viewContainerRef)); this._overlayRef.attach(
new TemplatePortal(this._notificationsPanel, this._viewContainerRef)
);
} }
/** /**
* Close the notifications panel * Close the notifications panel
*/ */
closePanel(): void closePanel(): void {
{
this._overlayRef.detach(); this._overlayRef.detach();
} }
/** /**
* Mark all notifications as read * Mark all notifications as read
*/ */
markAllAsRead(): void markAllAsRead(): void {
{
// Mark all as read // Mark all as read
this._notificationsService.markAllAsRead().subscribe(); this._notificationsService.markAllAsRead().subscribe();
} }
@ -127,20 +144,20 @@ export class NotificationsComponent implements OnInit, OnDestroy
/** /**
* Toggle read status of the given notification * Toggle read status of the given notification
*/ */
toggleRead(notification: Notification): void toggleRead(notification: Notification): void {
{
// Toggle the read status // Toggle the read status
notification.read = !notification.read; notification.read = !notification.read;
// Update the notification // Update the notification
this._notificationsService.update(notification.id, notification).subscribe(); this._notificationsService
.update(notification.id, notification)
.subscribe();
} }
/** /**
* Delete the given notification * Delete the given notification
*/ */
delete(notification: Notification): void delete(notification: Notification): void {
{
// Delete the notification // Delete the notification
this._notificationsService.delete(notification.id).subscribe(); this._notificationsService.delete(notification.id).subscribe();
} }
@ -151,8 +168,7 @@ export class NotificationsComponent implements OnInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
@ -163,15 +179,17 @@ export class NotificationsComponent implements OnInit, OnDestroy
/** /**
* Create the overlay * Create the overlay
*/ */
private _createOverlay(): void private _createOverlay(): void {
{
// Create the overlay // Create the overlay
this._overlayRef = this._overlay.create({ this._overlayRef = this._overlay.create({
hasBackdrop: true, hasBackdrop: true,
backdropClass: 'fuse-backdrop-on-mobile', backdropClass: 'fuse-backdrop-on-mobile',
scrollStrategy: this._overlay.scrollStrategies.block(), scrollStrategy: this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position() positionStrategy: this._overlay
.flexibleConnectedTo(this._notificationsOrigin._elementRef.nativeElement) .position()
.flexibleConnectedTo(
this._notificationsOrigin._elementRef.nativeElement
)
.withLockedPosition(true) .withLockedPosition(true)
.withPush(true) .withPush(true)
.withPositions([ .withPositions([
@ -203,8 +221,7 @@ export class NotificationsComponent implements OnInit, OnDestroy
}); });
// Detach the overlay from the portal on backdrop click // Detach the overlay from the portal on backdrop click
this._overlayRef.backdropClick().subscribe(() => this._overlayRef.backdropClick().subscribe(() => {
{
this._overlayRef.detach(); this._overlayRef.detach();
}); });
} }
@ -214,13 +231,13 @@ export class NotificationsComponent implements OnInit, OnDestroy
* *
* @private * @private
*/ */
private _calculateUnreadCount(): void private _calculateUnreadCount(): void {
{
let count = 0; let count = 0;
if ( this.notifications && this.notifications.length ) if (this.notifications && this.notifications.length) {
{ count = this.notifications.filter(
count = this.notifications.filter(notification => !notification.read).length; (notification) => !notification.read
).length;
} }
this.unreadCount = count; this.unreadCount = count;

View File

@ -4,16 +4,15 @@ import { Notification } from 'app/layout/common/notifications/notifications.type
import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs'; import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NotificationsService export class NotificationsService {
{ private _notifications: ReplaySubject<Notification[]> = new ReplaySubject<
private _notifications: ReplaySubject<Notification[]> = new ReplaySubject<Notification[]>(1); Notification[]
>(1);
/** /**
* Constructor * Constructor
*/ */
constructor(private _httpClient: HttpClient) constructor(private _httpClient: HttpClient) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -22,8 +21,7 @@ export class NotificationsService
/** /**
* Getter for notifications * Getter for notifications
*/ */
get notifications$(): Observable<Notification[]> get notifications$(): Observable<Notification[]> {
{
return this._notifications.asObservable(); return this._notifications.asObservable();
} }
@ -34,13 +32,13 @@ export class NotificationsService
/** /**
* Get all notifications * Get all notifications
*/ */
getAll(): Observable<Notification[]> getAll(): Observable<Notification[]> {
{ return this._httpClient
return this._httpClient.get<Notification[]>('api/common/notifications').pipe( .get<Notification[]>('api/common/notifications')
tap((notifications) => .pipe(
{ tap((notifications) => {
this._notifications.next(notifications); this._notifications.next(notifications);
}), })
); );
} }
@ -49,20 +47,27 @@ export class NotificationsService
* *
* @param notification * @param notification
*/ */
create(notification: Notification): Observable<Notification> create(notification: Notification): Observable<Notification> {
{
return this.notifications$.pipe( return this.notifications$.pipe(
take(1), take(1),
switchMap(notifications => this._httpClient.post<Notification>('api/common/notifications', {notification}).pipe( switchMap((notifications) =>
map((newNotification) => this._httpClient
{ .post<Notification>('api/common/notifications', {
notification,
})
.pipe(
map((newNotification) => {
// Update the notifications with the new notification // Update the notifications with the new notification
this._notifications.next([...notifications, newNotification]); this._notifications.next([
...notifications,
newNotification,
]);
// Return the new notification from observable // Return the new notification from observable
return newNotification; return newNotification;
}), })
)), )
)
); );
} }
@ -72,18 +77,21 @@ export class NotificationsService
* @param id * @param id
* @param notification * @param notification
*/ */
update(id: string, notification: Notification): Observable<Notification> update(id: string, notification: Notification): Observable<Notification> {
{
return this.notifications$.pipe( return this.notifications$.pipe(
take(1), take(1),
switchMap(notifications => this._httpClient.patch<Notification>('api/common/notifications', { switchMap((notifications) =>
this._httpClient
.patch<Notification>('api/common/notifications', {
id, id,
notification, notification,
}).pipe( })
map((updatedNotification: Notification) => .pipe(
{ map((updatedNotification: Notification) => {
// Find the index of the updated notification // Find the index of the updated notification
const index = notifications.findIndex(item => item.id === id); const index = notifications.findIndex(
(item) => item.id === id
);
// Update the notification // Update the notification
notifications[index] = updatedNotification; notifications[index] = updatedNotification;
@ -93,8 +101,9 @@ export class NotificationsService
// Return the updated notification // Return the updated notification
return updatedNotification; return updatedNotification;
}), })
)), )
)
); );
} }
@ -103,15 +112,20 @@ export class NotificationsService
* *
* @param id * @param id
*/ */
delete(id: string): Observable<boolean> delete(id: string): Observable<boolean> {
{
return this.notifications$.pipe( return this.notifications$.pipe(
take(1), take(1),
switchMap(notifications => this._httpClient.delete<boolean>('api/common/notifications', {params: {id}}).pipe( switchMap((notifications) =>
map((isDeleted: boolean) => this._httpClient
{ .delete<boolean>('api/common/notifications', {
params: { id },
})
.pipe(
map((isDeleted: boolean) => {
// Find the index of the deleted notification // Find the index of the deleted notification
const index = notifications.findIndex(item => item.id === id); const index = notifications.findIndex(
(item) => item.id === id
);
// Delete the notification // Delete the notification
notifications.splice(index, 1); notifications.splice(index, 1);
@ -121,24 +135,25 @@ export class NotificationsService
// Return the deleted status // Return the deleted status
return isDeleted; return isDeleted;
}), })
)), )
)
); );
} }
/** /**
* Mark all notifications as read * Mark all notifications as read
*/ */
markAllAsRead(): Observable<boolean> markAllAsRead(): Observable<boolean> {
{
return this.notifications$.pipe( return this.notifications$.pipe(
take(1), take(1),
switchMap(notifications => this._httpClient.get<boolean>('api/common/notifications/mark-all-as-read').pipe( switchMap((notifications) =>
map((isUpdated: boolean) => this._httpClient
{ .get<boolean>('api/common/notifications/mark-all-as-read')
.pipe(
map((isUpdated: boolean) => {
// Go through all notifications and set them as read // Go through all notifications and set them as read
notifications.forEach((notification, index) => notifications.forEach((notification, index) => {
{
notifications[index].read = true; notifications[index].read = true;
}); });
@ -147,8 +162,9 @@ export class NotificationsService
// Return the updated status // Return the updated status
return isUpdated; return isUpdated;
}), })
)), )
)
); );
} }
} }

View File

@ -1,5 +1,4 @@
export interface Notification export interface Notification {
{
id: string; id: string;
icon?: string; icon?: string;
image?: string; image?: string;

View File

@ -1,85 +1,120 @@
<div class="fixed lg:sticky top-0 bottom-0 lg:left-full w-full sm:w-96 lg:w-16 lg:h-screen lg:shadow">
<div <div
class="flex flex-col w-full sm:w-96 h-full transition-transform duration-400 ease-drawer bg-card" class="fixed bottom-0 top-0 w-full sm:w-96 lg:sticky lg:left-full lg:h-screen lg:w-16 lg:shadow"
[ngClass]="{'-translate-x-full sm:-translate-x-96 lg:-translate-x-80 shadow': opened, 'translate-x-0': !opened}"> >
<div
class="bg-card flex h-full w-full flex-col transition-transform duration-400 ease-drawer sm:w-96"
[ngClass]="{
'-translate-x-full shadow sm:-translate-x-96 lg:-translate-x-80':
opened,
'translate-x-0': !opened
}"
>
<!-- Header --> <!-- Header -->
<div <div
class="quick-chat-header flex flex-0 items-center justify-start cursor-pointer" class="quick-chat-header flex flex-0 cursor-pointer items-center justify-start"
(click)="toggle()"> (click)="toggle()"
>
<!-- Toggle --> <!-- Toggle -->
<ng-container *ngIf="!opened || (opened && !selectedChat)"> <ng-container *ngIf="!opened || (opened && !selectedChat)">
<div class="flex flex-auto items-center justify-center"> <div class="flex flex-auto items-center justify-center">
<div class="flex flex-0 items-center justify-center w-16"> <div class="flex w-16 flex-0 items-center justify-center">
<mat-icon <mat-icon
class="icon-size-6" class="icon-size-6"
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> [svgIcon]="
'heroicons_outline:chat-bubble-left-right'
"
></mat-icon>
</div> </div>
<div class="text-lg font-medium text-secondary">Team Chat</div> <div class="text-secondary text-lg font-medium">
<button Team Chat
class="ml-auto mr-4" </div>
mat-icon-button> <button class="ml-auto mr-4" mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:x-mark'"></mat-icon> <mat-icon
[svgIcon]="'heroicons_outline:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
</ng-container> </ng-container>
<!-- Contact info --> <!-- Contact info -->
<ng-container *ngIf="opened && selectedChat"> <ng-container *ngIf="opened && selectedChat">
<div class="flex flex-auto items-center ml-3"> <div class="ml-3 flex flex-auto items-center">
<div class="relative flex flex-0 items-center justify-center w-10 h-10"> <div
class="relative flex h-10 w-10 flex-0 items-center justify-center"
>
<ng-container *ngIf="chat.contact.avatar"> <ng-container *ngIf="chat.contact.avatar">
<img <img
class="w-full h-full rounded-full object-cover" class="h-full w-full rounded-full object-cover"
[src]="chat.contact.avatar" [src]="chat.contact.avatar"
alt="Contact avatar"/> alt="Contact avatar"
/>
</ng-container> </ng-container>
<ng-container *ngIf="!chat.contact.avatar"> <ng-container *ngIf="!chat.contact.avatar">
<div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200"> <div
class="flex h-full w-full items-center justify-center rounded-full bg-gray-200 text-lg uppercase text-gray-600 dark:bg-gray-700 dark:text-gray-200"
>
{{ chat.contact.name.charAt(0) }} {{ chat.contact.name.charAt(0) }}
</div> </div>
</ng-container> </ng-container>
</div> </div>
<div class="ml-4 text-lg font-medium leading-5 truncate">{{chat.contact.name}}</div> <div class="ml-4 truncate text-lg font-medium leading-5">
<button {{ chat.contact.name }}
class="ml-auto mr-4" </div>
mat-icon-button> <button class="ml-auto mr-4" mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:x-mark'"></mat-icon> <mat-icon
[svgIcon]="'heroicons_outline:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
</ng-container> </ng-container>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-auto border-t overflow-hidden"> <div class="flex flex-auto overflow-hidden border-t">
<!-- Chat list --> <!-- Chat list -->
<div <div
class="flex-0 w-16 h-full overflow-y-auto overscroll-y-contain sm:overflow-hidden sm:overscroll-auto" class="h-full w-16 flex-0 overflow-y-auto overscroll-y-contain sm:overflow-hidden sm:overscroll-auto"
fuseScrollbar fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false}"> [fuseScrollbarOptions]="{ wheelPropagation: false }"
>
<div class="flex-auto"> <div class="flex-auto">
<ng-container *ngFor="let chat of chats; trackBy: trackByFn"> <ng-container
*ngFor="let chat of chats; trackBy: trackByFn"
>
<div <div
class="flex items-center py-3 px-4 cursor-pointer" class="flex cursor-pointer items-center px-4 py-3"
[ngClass]="{'hover:bg-gray-100 dark:hover:bg-hover': !selectedChat || selectedChat.id !== chat.id, [ngClass]="{
'bg-primary-50 dark:bg-hover': selectedChat && selectedChat.id === chat.id}" 'dark:hover:bg-hover hover:bg-gray-100':
(click)="selectChat(chat.id)"> !selectedChat ||
<div class="relative flex flex-0 items-center justify-center w-8 h-8"> selectedChat.id !== chat.id,
'bg-primary-50 dark:bg-hover':
selectedChat && selectedChat.id === chat.id
}"
(click)="selectChat(chat.id)"
>
<div
class="relative flex h-8 w-8 flex-0 items-center justify-center"
>
<ng-container *ngIf="chat.unreadCount > 0"> <ng-container *ngIf="chat.unreadCount > 0">
<div <div
class="absolute bottom-0 right-0 flex-0 w-2 h-2 -ml-0.5 rounded-full ring-2 ring-bg-card dark:ring-gray-900 bg-primary dark:bg-primary-500 text-on-primary" class="ring-bg-card absolute bottom-0 right-0 -ml-0.5 h-2 w-2 flex-0 rounded-full bg-primary text-on-primary ring-2 dark:bg-primary-500 dark:ring-gray-900"
[class.ring-primary-50]="selectedChat && selectedChat.id === chat.id"></div> [class.ring-primary-50]="
selectedChat &&
selectedChat.id === chat.id
"
></div>
</ng-container> </ng-container>
<ng-container *ngIf="chat.contact.avatar"> <ng-container *ngIf="chat.contact.avatar">
<img <img
class="w-full h-full rounded-full object-cover" class="h-full w-full rounded-full object-cover"
[src]="chat.contact.avatar" [src]="chat.contact.avatar"
alt="Contact avatar"/> alt="Contact avatar"
/>
</ng-container> </ng-container>
<ng-container *ngIf="!chat.contact.avatar"> <ng-container *ngIf="!chat.contact.avatar">
<div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200"> <div
class="flex h-full w-full items-center justify-center rounded-full bg-gray-200 text-lg uppercase text-gray-600 dark:bg-gray-700 dark:text-gray-200"
>
{{ chat.contact.name.charAt(0) }} {{ chat.contact.name.charAt(0) }}
</div> </div>
</ng-container> </ng-container>
@ -90,58 +125,124 @@
</div> </div>
<!-- Conversation --> <!-- Conversation -->
<div class="flex flex-col flex-auto border-l overflow-hidden bg-gray-50 dark:bg-transparent"> <div
class="flex flex-auto flex-col overflow-hidden border-l bg-gray-50 dark:bg-transparent"
>
<ng-container *ngIf="chat; else selectChatOrStartNew"> <ng-container *ngIf="chat; else selectChatOrStartNew">
<div class="flex flex-col-reverse overflow-y-auto overscroll-y-contain"> <div
<div class="flex flex-col flex-auto shrink p-6"> class="flex flex-col-reverse overflow-y-auto overscroll-y-contain"
<ng-container *ngFor="let message of chat.messages; let i = index; let first = first; let last = last; trackBy: trackByFn"> >
<div class="flex flex-auto shrink flex-col p-6">
<ng-container
*ngFor="
let message of chat.messages;
let i = index;
let first = first;
let last = last;
trackBy: trackByFn
"
>
<!-- Start of the day --> <!-- Start of the day -->
<ng-container *ngIf="first || (chat.messages[i - 1].createdAt | date:'d') !== (message.createdAt | date:'d')"> <ng-container
<div class="flex items-center justify-center my-3 -mx-6"> *ngIf="
first ||
(chat.messages[i - 1].createdAt
| date: 'd') !==
(message.createdAt | date: 'd')
"
>
<div
class="-mx-6 my-3 flex items-center justify-center"
>
<div class="flex-auto border-b"></div> <div class="flex-auto border-b"></div>
<div class="flex-0 mx-4 text-sm font-medium leading-5 text-secondary"> <div
{{message.createdAt | date: 'longDate'}} class="text-secondary mx-4 flex-0 text-sm font-medium leading-5"
>
{{
message.createdAt
| date: 'longDate'
}}
</div> </div>
<div class="flex-auto border-b"></div> <div class="flex-auto border-b"></div>
</div> </div>
</ng-container> </ng-container>
<div <div
class="flex flex-col" class="flex flex-col"
[ngClass]="{'items-end': message.isMine, [ngClass]="{
'items-end': message.isMine,
'items-start': !message.isMine, 'items-start': !message.isMine,
'mt-0.5': i > 0 && chat.messages[i - 1].isMine === message.isMine, 'mt-0.5':
'mt-3': i > 0 && chat.messages[i - 1].isMine !== message.isMine}"> i > 0 &&
chat.messages[i - 1].isMine ===
message.isMine,
'mt-3':
i > 0 &&
chat.messages[i - 1].isMine !==
message.isMine
}"
>
<!-- Bubble --> <!-- Bubble -->
<div <div
class="relative max-w-3/4 px-3 py-2 rounded-lg" class="relative max-w-3/4 rounded-lg px-3 py-2"
[ngClass]="{'bg-blue-500 text-blue-50': message.isMine, [ngClass]="{
'bg-gray-500 text-gray-50': !message.isMine}"> 'bg-blue-500 text-blue-50':
message.isMine,
'bg-gray-500 text-gray-50':
!message.isMine
}"
>
<!-- Speech bubble tail --> <!-- Speech bubble tail -->
<ng-container *ngIf="last || chat.messages[i + 1].isMine !== message.isMine"> <ng-container
*ngIf="
last ||
chat.messages[i + 1].isMine !==
message.isMine
"
>
<div <div
class="absolute bottom-0 w-3" class="absolute bottom-0 w-3"
[ngClass]="{'text-blue-500 -right-1 -mr-px mb-px': message.isMine, [ngClass]="{
'text-gray-500 -left-1 -ml-px mb-px -scale-x-1': !message.isMine}"> '-right-1 -mr-px mb-px text-blue-500':
<ng-container *ngTemplateOutlet="speechBubbleExtension"></ng-container> message.isMine,
'-left-1 -ml-px mb-px -scale-x-1 text-gray-500':
!message.isMine
}"
>
<ng-container
*ngTemplateOutlet="
speechBubbleExtension
"
></ng-container>
</div> </div>
</ng-container> </ng-container>
<!-- Message --> <!-- Message -->
<div <div
class="min-w-4 leading-5" class="min-w-4 leading-5"
[innerHTML]="message.value"> [innerHTML]="message.value"
</div> ></div>
</div> </div>
<!-- Time --> <!-- Time -->
<ng-container <ng-container
*ngIf="first *ngIf="
|| last first ||
|| chat.messages[i + 1].isMine !== message.isMine last ||
|| chat.messages[i + 1].createdAt !== message.createdAt"> chat.messages[i + 1].isMine !==
message.isMine ||
chat.messages[i + 1].createdAt !==
message.createdAt
"
>
<div <div
class="my-0.5 text-sm font-medium text-secondary" class="text-secondary my-0.5 text-sm font-medium"
[ngClass]="{'mr-3': message.isMine, [ngClass]="{
'ml-3': !message.isMine}"> 'mr-3': message.isMine,
{{message.createdAt | date:'HH:mm'}} 'ml-3': !message.isMine
}"
>
{{
message.createdAt
| date: 'HH:mm'
}}
</div> </div>
</ng-container> </ng-container>
</div> </div>
@ -150,19 +251,26 @@
</div> </div>
<!-- Message field --> <!-- Message field -->
<div class="flex items-end p-4 border-t bg-gray-50 dark:bg-transparent"> <div
class="flex items-end border-t bg-gray-50 p-4 dark:bg-transparent"
>
<mat-form-field <mat-form-field
class="fuse-mat-dense fuse-mat-rounded fuse-mat-bold w-full" class="fuse-mat-dense fuse-mat-rounded fuse-mat-bold w-full"
[subscriptSizing]="'dynamic'"> [subscriptSizing]="'dynamic'"
>
<textarea <textarea
matInput matInput
cdkTextareaAutosize cdkTextareaAutosize
#messageInput></textarea> #messageInput
></textarea>
</mat-form-field> </mat-form-field>
<div class="flex items-center h-11 my-px ml-4"> <div class="my-px ml-4 flex h-11 items-center">
<button <button mat-icon-button>
mat-icon-button> <mat-icon
<mat-icon [svgIcon]="'heroicons_outline:paper-airplane'"></mat-icon> [svgIcon]="
'heroicons_outline:paper-airplane'
"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
@ -174,16 +282,23 @@
<!-- Select chat or start new template --> <!-- Select chat or start new template -->
<ng-template #selectChatOrStartNew> <ng-template #selectChatOrStartNew>
<div class="flex flex-col flex-auto items-center justify-center w-full h-full p-4"> <div
class="flex h-full w-full flex-auto flex-col items-center justify-center p-4"
>
<mat-icon <mat-icon
class="icon-size-24" class="icon-size-24"
[svgIcon]="'heroicons_outline:chat-bubble-bottom-center-text'"></mat-icon> [svgIcon]="'heroicons_outline:chat-bubble-bottom-center-text'"
<div class="mt-4 text-xl text-center font-medium tracking-tight text-secondary">Select a conversation</div> ></mat-icon>
<div
class="text-secondary mt-4 text-center text-xl font-medium tracking-tight"
>
Select a conversation
</div>
</div> </div>
</ng-template> </ng-template>
<!-- Speech bubble tail SVG --> <!-- Speech bubble tail SVG -->
<!-- @formatter:off --> <!-- prettier-ignore -->
<ng-template #speechBubbleExtension> <ng-template #speechBubbleExtension>
<svg width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"> <svg width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
@ -191,4 +306,3 @@
</g> </g>
</svg> </svg>
</ng-template> </ng-template>
<!-- @formatter:on -->

View File

@ -6,14 +6,12 @@ quick-chat {
} }
&.quick-chat-opened { &.quick-chat-opened {
> div { > div {
overflow: visible; overflow: visible;
} }
} }
&:not(.quick-chat-opened) { &:not(.quick-chat-opened) {
> div { > div {
overflow: visible; overflow: visible;
animation: addOverflowHidden 1ms linear 400ms; animation: addOverflowHidden 1ms linear 400ms;
@ -32,7 +30,6 @@ quick-chat {
} }
} }
/* Overlay */ /* Overlay */
.quick-chat-overlay { .quick-chat-overlay {
position: fixed; position: fixed;
@ -47,7 +44,7 @@ quick-chat {
@keyframes addOverflowHidden { @keyframes addOverflowHidden {
0% { 0% {
overflow: visible overflow: visible;
} }
99% { 99% {
overflow: visible; overflow: visible;

View File

@ -1,7 +1,27 @@
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay'; import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
import { TextFieldModule } from '@angular/cdk/text-field'; import { TextFieldModule } from '@angular/cdk/text-field';
import { DatePipe, DOCUMENT, NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import {
import { AfterViewInit, Component, ElementRef, HostBinding, HostListener, Inject, NgZone, OnDestroy, OnInit, Renderer2, ViewChild, ViewEncapsulation } from '@angular/core'; DOCUMENT,
DatePipe,
NgClass,
NgFor,
NgIf,
NgTemplateOutlet,
} from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
HostBinding,
HostListener,
Inject,
NgZone,
OnDestroy,
OnInit,
Renderer2,
ViewChild,
ViewEncapsulation,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@ -18,17 +38,29 @@ import { Subject, takeUntil } from 'rxjs';
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
exportAs: 'quickChat', exportAs: 'quickChat',
standalone: true, standalone: true,
imports : [NgClass, NgIf, MatIconModule, MatButtonModule, FuseScrollbarDirective, NgFor, NgTemplateOutlet, MatFormFieldModule, MatInputModule, TextFieldModule, DatePipe], imports: [
NgClass,
NgIf,
MatIconModule,
MatButtonModule,
FuseScrollbarDirective,
NgFor,
NgTemplateOutlet,
MatFormFieldModule,
MatInputModule,
TextFieldModule,
DatePipe,
],
}) })
export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy {
{
@ViewChild('messageInput') messageInput: ElementRef; @ViewChild('messageInput') messageInput: ElementRef;
chat: Chat; chat: Chat;
chats: Chat[]; chats: Chat[];
opened: boolean = false; opened: boolean = false;
selectedChat: Chat; selectedChat: Chat;
private _mutationObserver: MutationObserver; private _mutationObserver: MutationObserver;
private _scrollStrategy: ScrollStrategy = this._scrollStrategyOptions.block(); private _scrollStrategy: ScrollStrategy =
this._scrollStrategyOptions.block();
private _overlay: HTMLElement; private _overlay: HTMLElement;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -41,10 +73,8 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
private _renderer2: Renderer2, private _renderer2: Renderer2,
private _ngZone: NgZone, private _ngZone: NgZone,
private _quickChatService: QuickChatService, private _quickChatService: QuickChatService,
private _scrollStrategyOptions: ScrollStrategyOptions, private _scrollStrategyOptions: ScrollStrategyOptions
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Decorated methods // @ Decorated methods
@ -53,8 +83,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* Host binding for component classes * Host binding for component classes
*/ */
@HostBinding('class') get classList(): any @HostBinding('class') get classList(): any {
{
return { return {
'quick-chat-opened': this.opened, 'quick-chat-opened': this.opened,
}; };
@ -67,13 +96,10 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
*/ */
@HostListener('input') @HostListener('input')
@HostListener('ngModelChange') @HostListener('ngModelChange')
private _resizeMessageInput(): void private _resizeMessageInput(): void {
{
// This doesn't need to trigger Angular's change detection by itself // This doesn't need to trigger Angular's change detection by itself
this._ngZone.runOutsideAngular(() => this._ngZone.runOutsideAngular(() => {
{ setTimeout(() => {
setTimeout(() =>
{
// Set the height to 'auto' so we can correctly read the scrollHeight // Set the height to 'auto' so we can correctly read the scrollHeight
this.messageInput.nativeElement.style.height = 'auto'; this.messageInput.nativeElement.style.height = 'auto';
@ -90,29 +116,25 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Chat // Chat
this._quickChatService.chat$ this._quickChatService.chat$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((chat: Chat) => .subscribe((chat: Chat) => {
{
this.chat = chat; this.chat = chat;
}); });
// Chats // Chats
this._quickChatService.chats$ this._quickChatService.chats$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((chats: Chat[]) => .subscribe((chats: Chat[]) => {
{
this.chats = chats; this.chats = chats;
}); });
// Selected chat // Selected chat
this._quickChatService.chat$ this._quickChatService.chat$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((chat: Chat) => .subscribe((chat: Chat) => {
{
this.selectedChat = chat; this.selectedChat = chat;
}); });
} }
@ -120,29 +142,34 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* After view init * After view init
*/ */
ngAfterViewInit(): void ngAfterViewInit(): void {
{
// Fix for Firefox. // Fix for Firefox.
// //
// Because 'position: sticky' doesn't work correctly inside a 'position: fixed' parent, // Because 'position: sticky' doesn't work correctly inside a 'position: fixed' parent,
// adding the '.cdk-global-scrollblock' to the html element breaks the navigation's position. // adding the '.cdk-global-scrollblock' to the html element breaks the navigation's position.
// This fixes the problem by reading the 'top' value from the html element and adding it as a // This fixes the problem by reading the 'top' value from the html element and adding it as a
// 'marginTop' to the navigation itself. // 'marginTop' to the navigation itself.
this._mutationObserver = new MutationObserver((mutations) => this._mutationObserver = new MutationObserver((mutations) => {
{ mutations.forEach((mutation) => {
mutations.forEach((mutation) =>
{
const mutationTarget = mutation.target as HTMLElement; const mutationTarget = mutation.target as HTMLElement;
if ( mutation.attributeName === 'class' ) if (mutation.attributeName === 'class') {
{ if (
if ( mutationTarget.classList.contains('cdk-global-scrollblock') ) mutationTarget.classList.contains(
{ 'cdk-global-scrollblock'
)
) {
const top = parseInt(mutationTarget.style.top, 10); const top = parseInt(mutationTarget.style.top, 10);
this._renderer2.setStyle(this._elementRef.nativeElement, 'margin-top', `${Math.abs(top)}px`); this._renderer2.setStyle(
} this._elementRef.nativeElement,
else 'margin-top',
{ `${Math.abs(top)}px`
this._renderer2.setStyle(this._elementRef.nativeElement, 'margin-top', null); );
} else {
this._renderer2.setStyle(
this._elementRef.nativeElement,
'margin-top',
null
);
} }
} }
}); });
@ -156,8 +183,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Disconnect the mutation observer // Disconnect the mutation observer
this._mutationObserver.disconnect(); this._mutationObserver.disconnect();
@ -173,11 +199,9 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* Open the panel * Open the panel
*/ */
open(): void open(): void {
{
// Return if the panel has already opened // Return if the panel has already opened
if ( this.opened ) if (this.opened) {
{
return; return;
} }
@ -188,11 +212,9 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* Close the panel * Close the panel
*/ */
close(): void close(): void {
{
// Return if the panel has already closed // Return if the panel has already closed
if ( !this.opened ) if (!this.opened) {
{
return; return;
} }
@ -203,14 +225,10 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
/** /**
* Toggle the panel * Toggle the panel
*/ */
toggle(): void toggle(): void {
{ if (this.opened) {
if ( this.opened )
{
this.close(); this.close();
} } else {
else
{
this.open(); this.open();
} }
} }
@ -220,8 +238,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
* *
* @param id * @param id
*/ */
selectChat(id: string): void selectChat(id: string): void {
{
// Open the panel // Open the panel
this._toggleOpened(true); this._toggleOpened(true);
@ -235,8 +252,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
@ -249,8 +265,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
* *
* @private * @private
*/ */
private _showOverlay(): void private _showOverlay(): void {
{
// Try hiding the overlay in case there is one already opened // Try hiding the overlay in case there is one already opened
this._hideOverlay(); this._hideOverlay();
@ -258,8 +273,7 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
this._overlay = this._renderer2.createElement('div'); this._overlay = this._renderer2.createElement('div');
// Return if overlay couldn't be create for some reason // Return if overlay couldn't be create for some reason
if ( !this._overlay ) if (!this._overlay) {
{
return; return;
} }
@ -267,14 +281,16 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
this._overlay.classList.add('quick-chat-overlay'); this._overlay.classList.add('quick-chat-overlay');
// Append the backdrop to the parent of the panel // Append the backdrop to the parent of the panel
this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay); this._renderer2.appendChild(
this._elementRef.nativeElement.parentElement,
this._overlay
);
// Enable block scroll strategy // Enable block scroll strategy
this._scrollStrategy.enable(); this._scrollStrategy.enable();
// Add an event listener to the overlay // Add an event listener to the overlay
this._overlay.addEventListener('click', () => this._overlay.addEventListener('click', () => {
{
this.close(); this.close();
}); });
} }
@ -284,16 +300,13 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
* *
* @private * @private
*/ */
private _hideOverlay(): void private _hideOverlay(): void {
{ if (!this._overlay) {
if ( !this._overlay )
{
return; return;
} }
// If the backdrop still exists... // If the backdrop still exists...
if ( this._overlay ) if (this._overlay) {
{
// Remove the backdrop // Remove the backdrop
this._overlay.parentNode.removeChild(this._overlay); this._overlay.parentNode.removeChild(this._overlay);
this._overlay = null; this._overlay = null;
@ -309,19 +322,16 @@ export class QuickChatComponent implements OnInit, AfterViewInit, OnDestroy
* @param open * @param open
* @private * @private
*/ */
private _toggleOpened(open: boolean): void private _toggleOpened(open: boolean): void {
{
// Set the opened // Set the opened
this.opened = open; this.opened = open;
// If the panel opens, show the overlay // If the panel opens, show the overlay
if ( open ) if (open) {
{
this._showOverlay(); this._showOverlay();
} }
// Otherwise, hide the overlay // Otherwise, hide the overlay
else else {
{
this._hideOverlay(); this._hideOverlay();
} }
} }

View File

@ -1,20 +1,25 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Chat } from 'app/layout/common/quick-chat/quick-chat.types'; import { Chat } from 'app/layout/common/quick-chat/quick-chat.types';
import { BehaviorSubject, map, Observable, of, switchMap, tap, throwError } from 'rxjs'; import {
BehaviorSubject,
map,
Observable,
of,
switchMap,
tap,
throwError,
} from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class QuickChatService export class QuickChatService {
{
private _chat: BehaviorSubject<Chat> = new BehaviorSubject(null); private _chat: BehaviorSubject<Chat> = new BehaviorSubject(null);
private _chats: BehaviorSubject<Chat[]> = new BehaviorSubject<Chat[]>(null); private _chats: BehaviorSubject<Chat[]> = new BehaviorSubject<Chat[]>(null);
/** /**
* Constructor * Constructor
*/ */
constructor(private _httpClient: HttpClient) constructor(private _httpClient: HttpClient) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -23,16 +28,14 @@ export class QuickChatService
/** /**
* Getter for chat * Getter for chat
*/ */
get chat$(): Observable<Chat> get chat$(): Observable<Chat> {
{
return this._chat.asObservable(); return this._chat.asObservable();
} }
/** /**
* Getter for chat * Getter for chat
*/ */
get chats$(): Observable<Chat[]> get chats$(): Observable<Chat[]> {
{
return this._chats.asObservable(); return this._chats.asObservable();
} }
@ -43,13 +46,11 @@ export class QuickChatService
/** /**
* Get chats * Get chats
*/ */
getChats(): Observable<any> getChats(): Observable<any> {
{
return this._httpClient.get<Chat[]>('api/apps/chat/chats').pipe( return this._httpClient.get<Chat[]>('api/apps/chat/chats').pipe(
tap((response: Chat[]) => tap((response: Chat[]) => {
{
this._chats.next(response); this._chats.next(response);
}), })
); );
} }
@ -58,26 +59,26 @@ export class QuickChatService
* *
* @param id * @param id
*/ */
getChatById(id: string): Observable<any> getChatById(id: string): Observable<any> {
{ return this._httpClient
return this._httpClient.get<Chat>('api/apps/chat/chat', {params: {id}}).pipe( .get<Chat>('api/apps/chat/chat', { params: { id } })
map((chat) => .pipe(
{ map((chat) => {
// Update the chat // Update the chat
this._chat.next(chat); this._chat.next(chat);
// Return the chat // Return the chat
return chat; return chat;
}), }),
switchMap((chat) => switchMap((chat) => {
{ if (!chat) {
if ( !chat ) return throwError(
{ 'Could not found chat with id of ' + id + '!'
return throwError('Could not found chat with id of ' + id + '!'); );
} }
return of(chat); return of(chat);
}), })
); );
} }
} }

View File

@ -1,5 +1,4 @@
export interface Chat export interface Chat {
{
id?: string; id?: string;
contactId?: string; contactId?: string;
contact?: Contact; contact?: Contact;
@ -17,8 +16,7 @@ export interface Chat
}[]; }[];
} }
export interface Contact export interface Contact {
{
id?: string; id?: string;
avatar?: string; avatar?: string;
name?: string; name?: string;

View File

@ -1,65 +1,91 @@
<!-- Bar search --> <!-- Bar search -->
<ng-container *ngIf="appearance === 'bar'"> <ng-container *ngIf="appearance === 'bar'">
<button <button mat-icon-button *ngIf="!opened" (click)="open()">
mat-icon-button
*ngIf="!opened"
(click)="open()">
<mat-icon [svgIcon]="'heroicons_outline:magnifying-glass'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:magnifying-glass'"></mat-icon>
</button> </button>
<div <div
class="absolute inset-0 flex items-center shrink-0 z-99 bg-card" class="bg-card absolute inset-0 z-99 flex shrink-0 items-center"
*ngIf="opened" *ngIf="opened"
@slideInTop @slideInTop
@slideOutTop> @slideOutTop
>
<mat-icon <mat-icon
class="absolute ml-6 sm:ml-8" class="absolute ml-6 sm:ml-8"
[svgIcon]="'heroicons_outline:magnifying-glass'"></mat-icon> [svgIcon]="'heroicons_outline:magnifying-glass'"
></mat-icon>
<input <input
class="w-full h-full px-16 sm:px-18" class="h-full w-full px-16 sm:px-18"
[formControl]="searchControl" [formControl]="searchControl"
[matAutocomplete]="matAutocomplete" [matAutocomplete]="matAutocomplete"
[placeholder]="'Search...'" [placeholder]="'Search...'"
(keydown)="onKeydown($event)" (keydown)="onKeydown($event)"
#barSearchInput> #barSearchInput
/>
<mat-autocomplete <mat-autocomplete
class="max-h-128 sm:px-2 border-t rounded-b shadow-md" class="max-h-128 rounded-b border-t shadow-md sm:px-2"
[autoSelectActiveOption]="true" [autoSelectActiveOption]="true"
[disableRipple]="true" [disableRipple]="true"
#matAutocomplete="matAutocomplete"> #matAutocomplete="matAutocomplete"
>
<mat-option <mat-option
class="py-0 px-6 text-md pointer-events-none text-secondary bg-transparent" class="text-secondary pointer-events-none bg-transparent px-6 py-0 text-md"
*ngIf="resultSets && !resultSets.length"> *ngIf="resultSets && !resultSets.length"
>
No results found! No results found!
</mat-option> </mat-option>
<ng-container *ngFor="let resultSet of resultSets; trackBy: trackByFn"> <ng-container
<mat-optgroup class="flex items-center mt-2 px-2"> *ngFor="let resultSet of resultSets; trackBy: trackByFn"
<span class="text-sm font-semibold tracking-wider text-secondary">{{resultSet.label.toUpperCase()}}</span> >
<mat-optgroup class="mt-2 flex items-center px-2">
<span
class="text-secondary text-sm font-semibold tracking-wider"
>{{ resultSet.label.toUpperCase() }}</span
>
</mat-optgroup> </mat-optgroup>
<ng-container *ngFor="let result of resultSet.results; trackBy: trackByFn"> <ng-container
*ngFor="let result of resultSet.results; trackBy: trackByFn"
>
<mat-option <mat-option
class="group relative mb-1 py-0 px-6 text-md rounded-md hover:bg-gray-100 dark:hover:bg-hover" class="group relative mb-1 rounded-md px-6 py-0 text-md dark:hover:bg-hover hover:bg-gray-100"
[routerLink]="result.link" [routerLink]="result.link"
[value]="result.value"> [value]="result.value"
>
<!-- Contacts --> <!-- Contacts -->
<ng-container *ngIf="resultSet.id === 'contacts'"> <ng-container *ngIf="resultSet.id === 'contacts'">
<ng-container *ngTemplateOutlet="contactResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
contactResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
<!-- Pages --> <!-- Pages -->
<ng-container *ngIf="resultSet.id === 'pages'"> <ng-container *ngIf="resultSet.id === 'pages'">
<ng-container *ngTemplateOutlet="pageResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
pageResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
<!-- Tasks --> <!-- Tasks -->
<ng-container *ngIf="resultSet.id === 'tasks'"> <ng-container *ngIf="resultSet.id === 'tasks'">
<ng-container *ngTemplateOutlet="taskResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
taskResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
</mat-option> </mat-option>
</ng-container> </ng-container>
</ng-container> </ng-container>
</mat-autocomplete> </mat-autocomplete>
<button <button
class="absolute top-1/2 right-5 sm:right-7 shrink-0 w-10 h-10 -mt-5" class="absolute right-5 top-1/2 -mt-5 h-10 w-10 shrink-0 sm:right-7"
mat-icon-button mat-icon-button
(click)="close()"> (click)="close()"
>
<mat-icon [svgIcon]="'heroicons_outline:x-mark'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:x-mark'"></mat-icon>
</button> </button>
</div> </div>
@ -68,49 +94,74 @@
<!-- Basic search --> <!-- Basic search -->
<ng-container *ngIf="appearance === 'basic'"> <ng-container *ngIf="appearance === 'basic'">
<div class="w-full sm:min-w-80"> <div class="w-full sm:min-w-80">
<mat-form-field <mat-form-field class="w-full" [subscriptSizing]="'dynamic'">
class="w-full"
[subscriptSizing]="'dynamic'">
<mat-icon <mat-icon
matPrefix matPrefix
[svgIcon]="'heroicons_outline:magnifying-glass'"></mat-icon> [svgIcon]="'heroicons_outline:magnifying-glass'"
></mat-icon>
<input <input
matInput matInput
[formControl]="searchControl" [formControl]="searchControl"
[matAutocomplete]="matAutocomplete" [matAutocomplete]="matAutocomplete"
[placeholder]="'Search...'" [placeholder]="'Search...'"
(keydown)="onKeydown($event)"> (keydown)="onKeydown($event)"
/>
</mat-form-field> </mat-form-field>
<mat-autocomplete <mat-autocomplete
class="max-h-128 mt-1 rounded" class="mt-1 max-h-128 rounded"
[autoSelectActiveOption]="true" [autoSelectActiveOption]="true"
[disableRipple]="true" [disableRipple]="true"
#matAutocomplete="matAutocomplete"> #matAutocomplete="matAutocomplete"
>
<mat-option <mat-option
class="py-0 px-6 text-md pointer-events-none text-secondary bg-transparent" class="text-secondary pointer-events-none bg-transparent px-6 py-0 text-md"
*ngIf="resultSets && !resultSets.length"> *ngIf="resultSets && !resultSets.length"
>
No results found! No results found!
</mat-option> </mat-option>
<ng-container *ngFor="let resultSet of resultSets; trackBy: trackByFn"> <ng-container
<mat-optgroup class="flex items-center mt-2 px-2"> *ngFor="let resultSet of resultSets; trackBy: trackByFn"
<span class="text-sm font-semibold tracking-wider text-secondary">{{resultSet.label.toUpperCase()}}</span> >
<mat-optgroup class="mt-2 flex items-center px-2">
<span
class="text-secondary text-sm font-semibold tracking-wider"
>{{ resultSet.label.toUpperCase() }}</span
>
</mat-optgroup> </mat-optgroup>
<ng-container *ngFor="let result of resultSet.results; trackBy: trackByFn"> <ng-container
*ngFor="let result of resultSet.results; trackBy: trackByFn"
>
<mat-option <mat-option
class="group relative mb-1 py-0 px-6 text-md rounded-md hover:bg-gray-100 dark:hover:bg-hover" class="group relative mb-1 rounded-md px-6 py-0 text-md dark:hover:bg-hover hover:bg-gray-100"
[routerLink]="result.link" [routerLink]="result.link"
[value]="result.value"> [value]="result.value"
>
<!-- Contacts --> <!-- Contacts -->
<ng-container *ngIf="resultSet.id === 'contacts'"> <ng-container *ngIf="resultSet.id === 'contacts'">
<ng-container *ngTemplateOutlet="contactResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
contactResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
<!-- Pages --> <!-- Pages -->
<ng-container *ngIf="resultSet.id === 'pages'"> <ng-container *ngIf="resultSet.id === 'pages'">
<ng-container *ngTemplateOutlet="pageResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
pageResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
<!-- Tasks --> <!-- Tasks -->
<ng-container *ngIf="resultSet.id === 'tasks'"> <ng-container *ngIf="resultSet.id === 'tasks'">
<ng-container *ngTemplateOutlet="taskResult; context: {$implicit: result}"></ng-container> <ng-container
*ngTemplateOutlet="
taskResult;
context: { $implicit: result }
"
></ng-container>
</ng-container> </ng-container>
</mat-option> </mat-option>
</ng-container> </ng-container>
@ -120,18 +171,17 @@
</ng-container> </ng-container>
<!-- Contact result template --> <!-- Contact result template -->
<ng-template <ng-template #contactResult let-result>
#contactResult
let-result>
<div class="flex items-center"> <div class="flex items-center">
<div class="flex shrink-0 items-center justify-center w-8 h-8 rounded-full overflow-hidden bg-primary-100 dark:bg-primary-800"> <div
<img class="flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary-100 dark:bg-primary-800"
*ngIf="result.avatar" >
[src]="result.avatar"> <img *ngIf="result.avatar" [src]="result.avatar" />
<mat-icon <mat-icon
class="m-0 icon-size-5 text-primary dark:text-primary-400" class="m-0 text-primary icon-size-5 dark:text-primary-400"
*ngIf="!result.avatar" *ngIf="!result.avatar"
[svgIcon]="'heroicons_outline:user-circle'"></mat-icon> [svgIcon]="'heroicons_outline:user-circle'"
></mat-icon>
</div> </div>
<div class="ml-3 truncate"> <div class="ml-3 truncate">
<span [innerHTML]="result.name"></span> <span [innerHTML]="result.name"></span>
@ -140,37 +190,34 @@
</ng-template> </ng-template>
<!-- Page result template --> <!-- Page result template -->
<ng-template <ng-template #pageResult let-result>
#pageResult
let-result>
<div class="flex flex-col"> <div class="flex flex-col">
<div <div class="truncate leading-normal" [innerHTML]="result.title"></div>
class="truncate leading-normal" <div class="text-secondary truncate text-sm leading-normal">
[innerHTML]="result.title"></div>
<div class="truncate leading-normal text-sm text-secondary">
{{ result.link }} {{ result.link }}
</div> </div>
</div> </div>
</ng-template> </ng-template>
<!-- Task result template --> <!-- Task result template -->
<ng-template <ng-template #taskResult let-result>
#taskResult
let-result>
<div class="flex items-center"> <div class="flex items-center">
<ng-container *ngIf="result.completed"> <ng-container *ngIf="result.completed">
<mat-icon <mat-icon
class="mr-0 text-primary dark:text-primary-400" class="mr-0 text-primary dark:text-primary-400"
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon> [svgIcon]="'heroicons_outline:check-circle'"
></mat-icon>
</ng-container> </ng-container>
<ng-container *ngIf="!result.completed"> <ng-container *ngIf="!result.completed">
<mat-icon <mat-icon
class="mr-0 text-hint" class="text-hint mr-0"
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon> [svgIcon]="'heroicons_outline:check-circle'"
></mat-icon>
</ng-container> </ng-container>
<div <div
class="ml-3 truncate leading-normal" class="ml-3 truncate leading-normal"
[ngClass]="{'line-through text-hint': result.completed}" [ngClass]="{ 'text-hint line-through': result.completed }"
[innerHTML]="result.title"></div> [innerHTML]="result.title"
></div>
</div> </div>
</ng-template> </ng-template>

View File

@ -1,9 +1,32 @@
import { Overlay } from '@angular/cdk/overlay'; import { Overlay } from '@angular/cdk/overlay';
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, EventEmitter, HostBinding, inject, Input, OnChanges, OnDestroy, OnInit, Output, Renderer2, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; import {
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; Component,
import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY, MatAutocomplete, MatAutocompleteModule } from '@angular/material/autocomplete'; ElementRef,
EventEmitter,
HostBinding,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
Renderer2,
SimpleChanges,
ViewChild,
ViewEncapsulation,
inject,
} from '@angular/core';
import {
FormsModule,
ReactiveFormsModule,
UntypedFormControl,
} from '@angular/forms';
import {
MAT_AUTOCOMPLETE_SCROLL_STRATEGY,
MatAutocomplete,
MatAutocompleteModule,
} from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core'; import { MatOptionModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
@ -11,7 +34,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { fuseAnimations } from '@fuse/animations/public-api'; import { fuseAnimations } from '@fuse/animations/public-api';
import { debounceTime, filter, map, Subject, takeUntil } from 'rxjs'; import { Subject, debounceTime, filter, map, takeUntil } from 'rxjs';
@Component({ @Component({
selector: 'search', selector: 'search',
@ -20,20 +43,32 @@ import { debounceTime, filter, map, Subject, takeUntil } from 'rxjs';
exportAs: 'fuseSearch', exportAs: 'fuseSearch',
animations: fuseAnimations, animations: fuseAnimations,
standalone: true, standalone: true,
imports : [NgIf, MatButtonModule, MatIconModule, FormsModule, MatAutocompleteModule, ReactiveFormsModule, MatOptionModule, NgFor, RouterLink, NgTemplateOutlet, MatFormFieldModule, MatInputModule, NgClass], imports: [
NgIf,
MatButtonModule,
MatIconModule,
FormsModule,
MatAutocompleteModule,
ReactiveFormsModule,
MatOptionModule,
NgFor,
RouterLink,
NgTemplateOutlet,
MatFormFieldModule,
MatInputModule,
NgClass,
],
providers: [ providers: [
{ {
provide: MAT_AUTOCOMPLETE_SCROLL_STRATEGY, provide: MAT_AUTOCOMPLETE_SCROLL_STRATEGY,
useFactory: () => useFactory: () => {
{
const overlay = inject(Overlay); const overlay = inject(Overlay);
return () => overlay.scrollStrategies.block(); return () => overlay.scrollStrategies.block();
}, },
}, },
], ],
}) })
export class SearchComponent implements OnChanges, OnInit, OnDestroy export class SearchComponent implements OnChanges, OnInit, OnDestroy {
{
@Input() appearance: 'basic' | 'bar' = 'basic'; @Input() appearance: 'basic' | 'bar' = 'basic';
@Input() debounce: number = 300; @Input() debounce: number = 300;
@Input() minLength: number = 2; @Input() minLength: number = 2;
@ -51,10 +86,8 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
constructor( constructor(
private _elementRef: ElementRef, private _elementRef: ElementRef,
private _httpClient: HttpClient, private _httpClient: HttpClient,
private _renderer2: Renderer2, private _renderer2: Renderer2
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -63,8 +96,7 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
/** /**
* Host binding for component classes * Host binding for component classes
*/ */
@HostBinding('class') get classList(): any @HostBinding('class') get classList(): any {
{
return { return {
'search-appearance-bar': this.appearance === 'bar', 'search-appearance-bar': this.appearance === 'bar',
'search-appearance-basic': this.appearance === 'basic', 'search-appearance-basic': this.appearance === 'basic',
@ -78,15 +110,12 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* @param value * @param value
*/ */
@ViewChild('barSearchInput') @ViewChild('barSearchInput')
set barSearchInput(value: ElementRef) set barSearchInput(value: ElementRef) {
{
// If the value exists, it means that the search input // If the value exists, it means that the search input
// is now in the DOM, and we can focus on the input.. // is now in the DOM, and we can focus on the input..
if ( value ) if (value) {
{
// Give Angular time to complete the change detection cycle // Give Angular time to complete the change detection cycle
setTimeout(() => setTimeout(() => {
{
// Focus to the input element // Focus to the input element
value.nativeElement.focus(); value.nativeElement.focus();
}); });
@ -99,8 +128,7 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* @param value * @param value
*/ */
@ViewChild('matAutocomplete') @ViewChild('matAutocomplete')
set matAutocomplete(value: MatAutocomplete) set matAutocomplete(value: MatAutocomplete) {
{
this._matAutocomplete = value; this._matAutocomplete = value;
} }
@ -113,11 +141,9 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* *
* @param changes * @param changes
*/ */
ngOnChanges(changes: SimpleChanges): void ngOnChanges(changes: SimpleChanges): void {
{
// Appearance // Appearance
if ( 'appearance' in changes ) if ('appearance' in changes) {
{
// To prevent any issues, close the // To prevent any issues, close the
// search after changing the appearance // search after changing the appearance
this.close(); this.close();
@ -127,20 +153,17 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to the search field value changes // Subscribe to the search field value changes
this.searchControl.valueChanges this.searchControl.valueChanges
.pipe( .pipe(
debounceTime(this.debounce), debounceTime(this.debounce),
takeUntil(this._unsubscribeAll), takeUntil(this._unsubscribeAll),
map((value) => map((value) => {
{
// Set the resultSets to null if there is no value or // Set the resultSets to null if there is no value or
// the length of the value is smaller than the minLength // the length of the value is smaller than the minLength
// so the autocomplete panel can be closed // so the autocomplete panel can be closed
if ( !value || value.length < this.minLength ) if (!value || value.length < this.minLength) {
{
this.resultSets = null; this.resultSets = null;
} }
@ -149,13 +172,12 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
}), }),
// Filter out undefined/null/false statements and also // Filter out undefined/null/false statements and also
// filter out the values that are smaller than minLength // filter out the values that are smaller than minLength
filter(value => value && value.length >= this.minLength), filter((value) => value && value.length >= this.minLength)
) )
.subscribe((value) => .subscribe((value) => {
{ this._httpClient
this._httpClient.post('api/common/search', {query: value}) .post('api/common/search', { query: value })
.subscribe((resultSets: any) => .subscribe((resultSets: any) => {
{
// Store the result sets // Store the result sets
this.resultSets = resultSets; this.resultSets = resultSets;
@ -168,8 +190,7 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -184,14 +205,11 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* *
* @param event * @param event
*/ */
onKeydown(event: KeyboardEvent): void onKeydown(event: KeyboardEvent): void {
{
// Escape // Escape
if ( event.code === 'Escape' ) if (event.code === 'Escape') {
{
// If the appearance is 'bar' and the mat-autocomplete is not open, close the search // If the appearance is 'bar' and the mat-autocomplete is not open, close the search
if ( this.appearance === 'bar' && !this._matAutocomplete.isOpen ) if (this.appearance === 'bar' && !this._matAutocomplete.isOpen) {
{
this.close(); this.close();
} }
} }
@ -201,11 +219,9 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* Open the search * Open the search
* Used in 'bar' * Used in 'bar'
*/ */
open(): void open(): void {
{
// Return if it's already opened // Return if it's already opened
if ( this.opened ) if (this.opened) {
{
return; return;
} }
@ -217,11 +233,9 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* Close the search * Close the search
* * Used in 'bar' * * Used in 'bar'
*/ */
close(): void close(): void {
{
// Return if it's already closed // Return if it's already closed
if ( !this.opened ) if (!this.opened) {
{
return; return;
} }
@ -238,8 +252,7 @@ export class SearchComponent implements OnChanges, OnInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,13 @@ import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FuseDrawerComponent } from '@fuse/components/drawer'; import { FuseDrawerComponent } from '@fuse/components/drawer';
import { FuseConfig, FuseConfigService, Scheme, Theme, Themes } from '@fuse/services/config'; import {
FuseConfig,
FuseConfigService,
Scheme,
Theme,
Themes,
} from '@fuse/services/config';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
@ -22,7 +28,6 @@ import { Subject, takeUntil } from 'rxjs';
} }
@media (screen and min-width: 1280px) { @media (screen and min-width: 1280px) {
empty-layout + settings .settings-cog { empty-layout + settings .settings-cog {
right: 0 !important; right: 0 !important;
} }
@ -31,10 +36,16 @@ import { Subject, takeUntil } from 'rxjs';
], ],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [MatIconModule, FuseDrawerComponent, MatButtonModule, NgFor, NgClass, MatTooltipModule], imports: [
MatIconModule,
FuseDrawerComponent,
MatButtonModule,
NgFor,
NgClass,
MatTooltipModule,
],
}) })
export class SettingsComponent implements OnInit, OnDestroy export class SettingsComponent implements OnInit, OnDestroy {
{
config: FuseConfig; config: FuseConfig;
layout: string; layout: string;
scheme: 'dark' | 'light'; scheme: 'dark' | 'light';
@ -47,10 +58,8 @@ export class SettingsComponent implements OnInit, OnDestroy
*/ */
constructor( constructor(
private _router: Router, private _router: Router,
private _fuseConfigService: FuseConfigService, private _fuseConfigService: FuseConfigService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -59,13 +68,11 @@ export class SettingsComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to config changes // Subscribe to config changes
this._fuseConfigService.config$ this._fuseConfigService.config$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((config: FuseConfig) => .subscribe((config: FuseConfig) => {
{
// Store the config // Store the config
this.config = config; this.config = config;
}); });
@ -74,8 +81,7 @@ export class SettingsComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -90,16 +96,16 @@ export class SettingsComponent implements OnInit, OnDestroy
* *
* @param layout * @param layout
*/ */
setLayout(layout: string): void setLayout(layout: string): void {
{
// Clear the 'layout' query param to allow layout changes // Clear the 'layout' query param to allow layout changes
this._router.navigate([], { this._router
.navigate([], {
queryParams: { queryParams: {
layout: null, layout: null,
}, },
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
}).then(() => })
{ .then(() => {
// Set the config // Set the config
this._fuseConfigService.config = { layout }; this._fuseConfigService.config = { layout };
}); });
@ -110,8 +116,7 @@ export class SettingsComponent implements OnInit, OnDestroy
* *
* @param scheme * @param scheme
*/ */
setScheme(scheme: Scheme): void setScheme(scheme: Scheme): void {
{
this._fuseConfigService.config = { scheme }; this._fuseConfigService.config = { scheme };
} }
@ -120,8 +125,7 @@ export class SettingsComponent implements OnInit, OnDestroy
* *
* @param theme * @param theme
*/ */
setTheme(theme: Theme): void setTheme(theme: Theme): void {
{
this._fuseConfigService.config = { theme }; this._fuseConfigService.config = { theme };
} }
} }

View File

@ -1,56 +1,63 @@
<!-- Shortcuts toggle --> <!-- Shortcuts toggle -->
<button <button mat-icon-button (click)="openPanel()" #shortcutsOrigin>
mat-icon-button
(click)="openPanel()"
#shortcutsOrigin>
<mat-icon [svgIcon]="'heroicons_outline:squares-plus'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:squares-plus'"></mat-icon>
</button> </button>
<!-- Shortcuts panel --> <!-- Shortcuts panel -->
<ng-template #shortcutsPanel> <ng-template #shortcutsPanel>
<div class="fixed inset-0 sm:static sm:inset-auto flex flex-col sm:min-w-90 sm:w-90 sm:rounded-2xl overflow-hidden shadow-lg"> <div
class="fixed inset-0 flex flex-col overflow-hidden shadow-lg sm:static sm:inset-auto sm:w-90 sm:min-w-90 sm:rounded-2xl"
>
<!-- Header --> <!-- Header -->
<div class="flex shrink-0 items-center py-4 pr-4 pl-6 bg-primary text-on-primary"> <div
<div class="sm:hidden -ml-1 mr-3"> class="flex shrink-0 items-center bg-primary py-4 pl-6 pr-4 text-on-primary"
<button >
mat-icon-button <div class="-ml-1 mr-3 sm:hidden">
(click)="closePanel()"> <button mat-icon-button (click)="closePanel()">
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:x-mark'"></mat-icon> [svgIcon]="'heroicons_solid:x-mark'"
></mat-icon>
</button> </button>
</div> </div>
<div class="flex items-center text-lg font-medium leading-10"> <div class="flex items-center text-lg font-medium leading-10">
<span class="">Shortcuts</span> <span class="">Shortcuts</span>
<ng-container *ngIf="mode !== 'view'"> <ng-container *ngIf="mode !== 'view'">
<span class="ml-1"> <span class="ml-1">
<ng-container *ngIf="mode === 'add'">- Add new</ng-container> <ng-container *ngIf="mode === 'add'"
<ng-container *ngIf="mode === 'modify' || mode === 'edit'">- Editing</ng-container> >- Add new</ng-container
>
<ng-container
*ngIf="mode === 'modify' || mode === 'edit'"
>- Editing</ng-container
>
</span> </span>
</ng-container> </ng-container>
</div> </div>
<div class="ml-auto"> <div class="ml-auto">
<!-- View mode --> <!-- View mode -->
<ng-container *ngIf="mode === 'view'"> <ng-container *ngIf="mode === 'view'">
<!-- Enter 'modify' mode --> <!-- Enter 'modify' mode -->
<button <button
mat-icon-button mat-icon-button
(click)="changeMode('modify')" (click)="changeMode('modify')"
[matTooltip]="'Enter edit mode'"> [matTooltip]="'Enter edit mode'"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:pencil-square'"></mat-icon> [svgIcon]="'heroicons_solid:pencil-square'"
></mat-icon>
</button> </button>
<!-- Enter 'add' mode --> <!-- Enter 'add' mode -->
<button <button
mat-icon-button mat-icon-button
(click)="newShortcut()" (click)="newShortcut()"
[matTooltip]="'Add shortcut'"> [matTooltip]="'Add shortcut'"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:plus-circle'"></mat-icon> [svgIcon]="'heroicons_solid:plus-circle'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
@ -60,10 +67,12 @@
<button <button
mat-icon-button mat-icon-button
(click)="changeMode('view')" (click)="changeMode('view')"
[matTooltip]="'Exit edit mode'"> [matTooltip]="'Exit edit mode'"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:check-circle'"></mat-icon> [svgIcon]="'heroicons_solid:check-circle'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
@ -73,10 +82,12 @@
<button <button
mat-icon-button mat-icon-button
(click)="changeMode('view')" (click)="changeMode('view')"
[matTooltip]="'Cancel'"> [matTooltip]="'Cancel'"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:x-circle'"></mat-icon> [svgIcon]="'heroicons_solid:x-circle'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
@ -86,60 +97,87 @@
<button <button
mat-icon-button mat-icon-button
(click)="changeMode('modify')" (click)="changeMode('modify')"
[matTooltip]="'Cancel'"> [matTooltip]="'Cancel'"
>
<mat-icon <mat-icon
class="icon-size-5 text-current" class="text-current icon-size-5"
[svgIcon]="'heroicons_solid:x-circle'"></mat-icon> [svgIcon]="'heroicons_solid:x-circle'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
</div> </div>
</div> </div>
<div class="relative flex flex-col flex-auto sm:max-h-120 -mb-px overflow-y-auto bg-card"> <div
class="bg-card relative -mb-px flex flex-auto flex-col overflow-y-auto sm:max-h-120"
>
<!-- View mode --> <!-- View mode -->
<ng-container *ngIf="mode === 'view' || mode === 'modify'"> <ng-container *ngIf="mode === 'view' || mode === 'modify'">
<!-- Shortcuts --> <!-- Shortcuts -->
<div class="grid grid-cols-2 grid-flow-row"> <div class="grid grid-flow-row grid-cols-2">
<!-- Shortcut --> <!-- Shortcut -->
<ng-container *ngFor="let shortcut of shortcuts; trackBy: trackByFn"> <ng-container
<div class="relative group flex flex-col overflow-hidden bg-card border-r border-b even:border-r-0 hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"> *ngFor="let shortcut of shortcuts; trackBy: trackByFn"
>
<div
class="group bg-card relative flex flex-col overflow-hidden border-b border-r even:border-r-0 hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
>
<ng-container *ngIf="mode === 'modify'"> <ng-container *ngIf="mode === 'modify'">
<div <div
class="absolute inset-0 z-99 cursor-pointer" class="absolute inset-0 z-99 cursor-pointer"
(click)="editShortcut(shortcut)"> (click)="editShortcut(shortcut)"
</div> ></div>
</ng-container> </ng-container>
<!-- Normal links --> <!-- Normal links -->
<a <a
class="flex flex-col items-center justify-center w-full h-full py-6 no-underline" class="flex h-full w-full flex-col items-center justify-center py-6 no-underline"
*ngIf="!shortcut.useRouter" *ngIf="!shortcut.useRouter"
[ngClass]="{'pointer-events-none': mode === 'modify'}" [ngClass]="{
[href]="shortcut.link"> 'pointer-events-none': mode === 'modify'
<ng-container *ngTemplateOutlet="linkContent"></ng-container> }"
[href]="shortcut.link"
>
<ng-container
*ngTemplateOutlet="linkContent"
></ng-container>
</a> </a>
<!-- Router links --> <!-- Router links -->
<a <a
class="flex flex-col items-center justify-center w-full h-full py-6 no-underline" class="flex h-full w-full flex-col items-center justify-center py-6 no-underline"
*ngIf="shortcut.useRouter" *ngIf="shortcut.useRouter"
[ngClass]="{'pointer-events-none': mode === 'modify'}" [ngClass]="{
[routerLink]="shortcut.link"> 'pointer-events-none': mode === 'modify'
<ng-container *ngTemplateOutlet="linkContent"></ng-container> }"
[routerLink]="shortcut.link"
>
<ng-container
*ngTemplateOutlet="linkContent"
></ng-container>
</a> </a>
<!-- Link content template --> <!-- Link content template -->
<ng-template #linkContent> <ng-template #linkContent>
<div class="relative flex shrink-0 items-center justify-center w-12 h-12 mb-3 rounded-full bg-gray-100 dark:bg-gray-700"> <div
class="relative mb-3 flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-700"
>
<mat-icon <mat-icon
class="absolute opacity-0 group-hover:opacity-100 z-20 icon-size-5" class="absolute z-20 opacity-0 icon-size-5 group-hover:opacity-100"
*ngIf="mode === 'modify'" *ngIf="mode === 'modify'"
[svgIcon]="'heroicons_solid:pencil'"></mat-icon> [svgIcon]="'heroicons_solid:pencil'"
></mat-icon>
<mat-icon <mat-icon
class="z-10" class="z-10"
[ngClass]="{'group-hover:opacity-0': mode === 'modify'}" [ngClass]="{
[svgIcon]="shortcut.icon"></mat-icon> 'group-hover:opacity-0':
mode === 'modify'
}"
[svgIcon]="shortcut.icon"
></mat-icon>
</div>
<div class="text-center font-medium">
{{ shortcut.label }}
</div>
<div class="text-secondary text-center text-md">
{{ shortcut.description }}
</div> </div>
<div class="font-medium text-center">{{shortcut.label}}</div>
<div class="text-md text-center text-secondary">{{shortcut.description}}</div>
</ng-template> </ng-template>
</div> </div>
</ng-container> </ng-container>
@ -147,63 +185,63 @@
<!-- No shortcuts --> <!-- No shortcuts -->
<ng-container *ngIf="!shortcuts || !shortcuts.length"> <ng-container *ngIf="!shortcuts || !shortcuts.length">
<div class="flex flex-col flex-auto items-center justify-center sm:justify-start py-12 px-8"> <div
<div class="flex flex-0 items-center justify-center w-14 h-14 rounded-full bg-primary-100 dark:bg-primary-600"> class="flex flex-auto flex-col items-center justify-center px-8 py-12 sm:justify-start"
>
<div
class="flex h-14 w-14 flex-0 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-600"
>
<mat-icon <mat-icon
class="text-primary-700 dark:text-primary-50" class="text-primary-700 dark:text-primary-50"
[svgIcon]="'heroicons_outline:bookmark'"></mat-icon> [svgIcon]="'heroicons_outline:bookmark'"
></mat-icon>
</div>
<div class="mt-5 text-2xl font-semibold tracking-tight">
No shortcuts
</div>
<div
class="text-secondary mt-1 w-full max-w-60 text-center text-md"
>
When you have shortcuts, they will appear here.
</div> </div>
<div class="mt-5 text-2xl font-semibold tracking-tight">No shortcuts</div>
<div class="w-full max-w-60 mt-1 text-md text-center text-secondary">When you have shortcuts, they will appear here.</div>
</div> </div>
</ng-container> </ng-container>
</ng-container> </ng-container>
<!-- Add/Edit mode --> <!-- Add/Edit mode -->
<ng-container *ngIf="mode === 'add' || mode === 'edit'"> <ng-container *ngIf="mode === 'add' || mode === 'edit'">
<form <form class="p-6" [formGroup]="shortcutForm">
class="p-6"
[formGroup]="shortcutForm">
<mat-form-field class="w-full"> <mat-form-field class="w-full">
<mat-label>Label</mat-label> <mat-label>Label</mat-label>
<input <input matInput [formControlName]="'label'" required />
matInput
[formControlName]="'label'"
required>
</mat-form-field> </mat-form-field>
<mat-form-field class="w-full"> <mat-form-field class="w-full">
<mat-label>Description</mat-label> <mat-label>Description</mat-label>
<input <input matInput [formControlName]="'description'" />
matInput
[formControlName]="'description'">
</mat-form-field> </mat-form-field>
<mat-form-field class="w-full"> <mat-form-field class="w-full">
<mat-label>Icon</mat-label> <mat-label>Icon</mat-label>
<input <input matInput [formControlName]="'icon'" required />
matInput
[formControlName]="'icon'"
required>
</mat-form-field> </mat-form-field>
<mat-form-field class="w-full"> <mat-form-field class="w-full">
<mat-label>Link</mat-label> <mat-label>Link</mat-label>
<input <input matInput [formControlName]="'link'" required />
matInput
[formControlName]="'link'"
required>
</mat-form-field> </mat-form-field>
<mat-slide-toggle <mat-slide-toggle
[color]="'primary'" [color]="'primary'"
[formControlName]="'useRouter'"> [formControlName]="'useRouter'"
>
Use router for the link Use router for the link
</mat-slide-toggle> </mat-slide-toggle>
<!-- Actions --> <!-- Actions -->
<div class="flex items-center justify-end mt-4"> <div class="mt-4 flex items-center justify-end">
<button <button
class="mr-2" class="mr-2"
*ngIf="mode === 'edit'" *ngIf="mode === 'edit'"
mat-flat-button mat-flat-button
type="button" type="button"
(click)="delete()"> (click)="delete()"
>
Delete Delete
</button> </button>
<button <button
@ -211,9 +249,14 @@
[color]="'primary'" [color]="'primary'"
[disabled]="!shortcutForm.valid" [disabled]="!shortcutForm.valid"
type="button" type="button"
(click)="save()"> (click)="save()"
<ng-container *ngIf="mode === 'add'">Add</ng-container> >
<ng-container *ngIf="mode === 'edit'">Update</ng-container> <ng-container *ngIf="mode === 'add'"
>Add</ng-container
>
<ng-container *ngIf="mode === 'edit'"
>Update</ng-container
>
</button> </button>
</div> </div>
</form> </form>

View File

@ -1,8 +1,24 @@
import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common'; import { NgClass, NgFor, NgIf, NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; import {
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
import {
FormsModule,
ReactiveFormsModule,
UntypedFormBuilder,
UntypedFormGroup,
Validators,
} from '@angular/forms';
import { MatButton, MatButtonModule } from '@angular/material/button'; import { MatButton, MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@ -21,10 +37,23 @@ import { Subject, takeUntil } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'shortcuts', exportAs: 'shortcuts',
standalone: true, standalone: true,
imports : [MatButtonModule, MatIconModule, NgIf, MatTooltipModule, NgFor, NgClass, NgTemplateOutlet, RouterLink, FormsModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatSlideToggleModule], imports: [
MatButtonModule,
MatIconModule,
NgIf,
MatTooltipModule,
NgFor,
NgClass,
NgTemplateOutlet,
RouterLink,
FormsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatSlideToggleModule,
],
}) })
export class ShortcutsComponent implements OnInit, OnDestroy export class ShortcutsComponent implements OnInit, OnDestroy {
{
@ViewChild('shortcutsOrigin') private _shortcutsOrigin: MatButton; @ViewChild('shortcutsOrigin') private _shortcutsOrigin: MatButton;
@ViewChild('shortcutsPanel') private _shortcutsPanel: TemplateRef<any>; @ViewChild('shortcutsPanel') private _shortcutsPanel: TemplateRef<any>;
@ -42,10 +71,8 @@ export class ShortcutsComponent implements OnInit, OnDestroy
private _formBuilder: UntypedFormBuilder, private _formBuilder: UntypedFormBuilder,
private _shortcutsService: ShortcutsService, private _shortcutsService: ShortcutsService,
private _overlay: Overlay, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef, private _viewContainerRef: ViewContainerRef
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -54,8 +81,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Initialize the form // Initialize the form
this.shortcutForm = this._formBuilder.group({ this.shortcutForm = this._formBuilder.group({
id: [null], id: [null],
@ -69,8 +95,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
// Get the shortcuts // Get the shortcuts
this._shortcutsService.shortcuts$ this._shortcutsService.shortcuts$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((shortcuts: Shortcut[]) => .subscribe((shortcuts: Shortcut[]) => {
{
// Load the shortcuts // Load the shortcuts
this.shortcuts = shortcuts; this.shortcuts = shortcuts;
@ -82,15 +107,13 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
// Dispose the overlay // Dispose the overlay
if ( this._overlayRef ) if (this._overlayRef) {
{
this._overlayRef.dispose(); this._overlayRef.dispose();
} }
} }
@ -102,11 +125,9 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Open the shortcuts panel * Open the shortcuts panel
*/ */
openPanel(): void openPanel(): void {
{
// Return if the shortcuts panel or its origin is not defined // Return if the shortcuts panel or its origin is not defined
if ( !this._shortcutsPanel || !this._shortcutsOrigin ) if (!this._shortcutsPanel || !this._shortcutsOrigin) {
{
return; return;
} }
@ -114,28 +135,27 @@ export class ShortcutsComponent implements OnInit, OnDestroy
this.mode = 'view'; this.mode = 'view';
// Create the overlay if it doesn't exist // Create the overlay if it doesn't exist
if ( !this._overlayRef ) if (!this._overlayRef) {
{
this._createOverlay(); this._createOverlay();
} }
// Attach the portal to the overlay // Attach the portal to the overlay
this._overlayRef.attach(new TemplatePortal(this._shortcutsPanel, this._viewContainerRef)); this._overlayRef.attach(
new TemplatePortal(this._shortcutsPanel, this._viewContainerRef)
);
} }
/** /**
* Close the shortcuts panel * Close the shortcuts panel
*/ */
closePanel(): void closePanel(): void {
{
this._overlayRef.detach(); this._overlayRef.detach();
} }
/** /**
* Change the mode * Change the mode
*/ */
changeMode(mode: 'view' | 'modify' | 'add' | 'edit'): void changeMode(mode: 'view' | 'modify' | 'add' | 'edit'): void {
{
// Change the mode // Change the mode
this.mode = mode; this.mode = mode;
} }
@ -143,8 +163,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Prepare for a new shortcut * Prepare for a new shortcut
*/ */
newShortcut(): void newShortcut(): void {
{
// Reset the form // Reset the form
this.shortcutForm.reset(); this.shortcutForm.reset();
@ -155,8 +174,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Edit a shortcut * Edit a shortcut
*/ */
editShortcut(shortcut: Shortcut): void editShortcut(shortcut: Shortcut): void {
{
// Reset the form with the shortcut // Reset the form with the shortcut
this.shortcutForm.reset(shortcut); this.shortcutForm.reset(shortcut);
@ -167,19 +185,16 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Save shortcut * Save shortcut
*/ */
save(): void save(): void {
{
// Get the data from the form // Get the data from the form
const shortcut = this.shortcutForm.value; const shortcut = this.shortcutForm.value;
// If there is an id, update it... // If there is an id, update it...
if ( shortcut.id ) if (shortcut.id) {
{
this._shortcutsService.update(shortcut.id, shortcut).subscribe(); this._shortcutsService.update(shortcut.id, shortcut).subscribe();
} }
// Otherwise, create a new shortcut... // Otherwise, create a new shortcut...
else else {
{
this._shortcutsService.create(shortcut).subscribe(); this._shortcutsService.create(shortcut).subscribe();
} }
@ -190,8 +205,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Delete shortcut * Delete shortcut
*/ */
delete(): void delete(): void {
{
// Get the data from the form // Get the data from the form
const shortcut = this.shortcutForm.value; const shortcut = this.shortcutForm.value;
@ -208,8 +222,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
* @param index * @param index
* @param item * @param item
*/ */
trackByFn(index: number, item: any): any trackByFn(index: number, item: any): any {
{
return item.id || index; return item.id || index;
} }
@ -220,15 +233,17 @@ export class ShortcutsComponent implements OnInit, OnDestroy
/** /**
* Create the overlay * Create the overlay
*/ */
private _createOverlay(): void private _createOverlay(): void {
{
// Create the overlay // Create the overlay
this._overlayRef = this._overlay.create({ this._overlayRef = this._overlay.create({
hasBackdrop: true, hasBackdrop: true,
backdropClass: 'fuse-backdrop-on-mobile', backdropClass: 'fuse-backdrop-on-mobile',
scrollStrategy: this._overlay.scrollStrategies.block(), scrollStrategy: this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position() positionStrategy: this._overlay
.flexibleConnectedTo(this._shortcutsOrigin._elementRef.nativeElement) .position()
.flexibleConnectedTo(
this._shortcutsOrigin._elementRef.nativeElement
)
.withLockedPosition(true) .withLockedPosition(true)
.withPush(true) .withPush(true)
.withPositions([ .withPositions([
@ -260,8 +275,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
}); });
// Detach the overlay from the portal on backdrop click // Detach the overlay from the portal on backdrop click
this._overlayRef.backdropClick().subscribe(() => this._overlayRef.backdropClick().subscribe(() => {
{
this._overlayRef.detach(); this._overlayRef.detach();
}); });
} }

View File

@ -4,16 +4,15 @@ import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types';
import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs'; import { map, Observable, ReplaySubject, switchMap, take, tap } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ShortcutsService export class ShortcutsService {
{ private _shortcuts: ReplaySubject<Shortcut[]> = new ReplaySubject<
private _shortcuts: ReplaySubject<Shortcut[]> = new ReplaySubject<Shortcut[]>(1); Shortcut[]
>(1);
/** /**
* Constructor * Constructor
*/ */
constructor(private _httpClient: HttpClient) constructor(private _httpClient: HttpClient) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -22,8 +21,7 @@ export class ShortcutsService
/** /**
* Getter for shortcuts * Getter for shortcuts
*/ */
get shortcuts$(): Observable<Shortcut[]> get shortcuts$(): Observable<Shortcut[]> {
{
return this._shortcuts.asObservable(); return this._shortcuts.asObservable();
} }
@ -34,13 +32,11 @@ export class ShortcutsService
/** /**
* Get all messages * Get all messages
*/ */
getAll(): Observable<Shortcut[]> getAll(): Observable<Shortcut[]> {
{
return this._httpClient.get<Shortcut[]>('api/common/shortcuts').pipe( return this._httpClient.get<Shortcut[]>('api/common/shortcuts').pipe(
tap((shortcuts) => tap((shortcuts) => {
{
this._shortcuts.next(shortcuts); this._shortcuts.next(shortcuts);
}), })
); );
} }
@ -49,20 +45,22 @@ export class ShortcutsService
* *
* @param shortcut * @param shortcut
*/ */
create(shortcut: Shortcut): Observable<Shortcut> create(shortcut: Shortcut): Observable<Shortcut> {
{
return this.shortcuts$.pipe( return this.shortcuts$.pipe(
take(1), take(1),
switchMap(shortcuts => this._httpClient.post<Shortcut>('api/common/shortcuts', {shortcut}).pipe( switchMap((shortcuts) =>
map((newShortcut) => this._httpClient
{ .post<Shortcut>('api/common/shortcuts', { shortcut })
.pipe(
map((newShortcut) => {
// Update the shortcuts with the new shortcut // Update the shortcuts with the new shortcut
this._shortcuts.next([...shortcuts, newShortcut]); this._shortcuts.next([...shortcuts, newShortcut]);
// Return the new shortcut from observable // Return the new shortcut from observable
return newShortcut; return newShortcut;
}), })
)), )
)
); );
} }
@ -72,18 +70,21 @@ export class ShortcutsService
* @param id * @param id
* @param shortcut * @param shortcut
*/ */
update(id: string, shortcut: Shortcut): Observable<Shortcut> update(id: string, shortcut: Shortcut): Observable<Shortcut> {
{
return this.shortcuts$.pipe( return this.shortcuts$.pipe(
take(1), take(1),
switchMap(shortcuts => this._httpClient.patch<Shortcut>('api/common/shortcuts', { switchMap((shortcuts) =>
this._httpClient
.patch<Shortcut>('api/common/shortcuts', {
id, id,
shortcut, shortcut,
}).pipe( })
map((updatedShortcut: Shortcut) => .pipe(
{ map((updatedShortcut: Shortcut) => {
// Find the index of the updated shortcut // Find the index of the updated shortcut
const index = shortcuts.findIndex(item => item.id === id); const index = shortcuts.findIndex(
(item) => item.id === id
);
// Update the shortcut // Update the shortcut
shortcuts[index] = updatedShortcut; shortcuts[index] = updatedShortcut;
@ -93,8 +94,9 @@ export class ShortcutsService
// Return the updated shortcut // Return the updated shortcut
return updatedShortcut; return updatedShortcut;
}), })
)), )
)
); );
} }
@ -103,15 +105,18 @@ export class ShortcutsService
* *
* @param id * @param id
*/ */
delete(id: string): Observable<boolean> delete(id: string): Observable<boolean> {
{
return this.shortcuts$.pipe( return this.shortcuts$.pipe(
take(1), take(1),
switchMap(shortcuts => this._httpClient.delete<boolean>('api/common/shortcuts', {params: {id}}).pipe( switchMap((shortcuts) =>
map((isDeleted: boolean) => this._httpClient
{ .delete<boolean>('api/common/shortcuts', { params: { id } })
.pipe(
map((isDeleted: boolean) => {
// Find the index of the deleted shortcut // Find the index of the deleted shortcut
const index = shortcuts.findIndex(item => item.id === id); const index = shortcuts.findIndex(
(item) => item.id === id
);
// Delete the shortcut // Delete the shortcut
shortcuts.splice(index, 1); shortcuts.splice(index, 1);
@ -121,8 +126,9 @@ export class ShortcutsService
// Return the deleted status // Return the deleted status
return isDeleted; return isDeleted;
}), })
)), )
)
); );
} }
} }

View File

@ -1,5 +1,4 @@
export interface Shortcut export interface Shortcut {
{
id: string; id: string;
label: string; label: string;
description?: string; description?: string;

View File

@ -1,29 +1,29 @@
<!-- Button --> <!-- Button -->
<button <button mat-icon-button [matMenuTriggerFor]="userActions">
mat-icon-button
[matMenuTriggerFor]="userActions">
<span class="relative"> <span class="relative">
<img <img
class="w-7 h-7 rounded-full" class="h-7 w-7 rounded-full"
*ngIf="showAvatar && user.avatar" *ngIf="showAvatar && user.avatar"
[src]="user.avatar"> [src]="user.avatar"
/>
<mat-icon <mat-icon
*ngIf="!showAvatar || !user.avatar" *ngIf="!showAvatar || !user.avatar"
[svgIcon]="'heroicons_outline:user-circle'"></mat-icon> [svgIcon]="'heroicons_outline:user-circle'"
></mat-icon>
<span <span
class="absolute right-0 bottom-0 w-2 h-2 rounded-full" class="absolute bottom-0 right-0 h-2 w-2 rounded-full"
[ngClass]="{'mr-px mb-px': !showAvatar || !user.avatar, [ngClass]="{
'mb-px mr-px': !showAvatar || !user.avatar,
'bg-green-500': user.status === 'online', 'bg-green-500': user.status === 'online',
'bg-amber-500': user.status === 'away', 'bg-amber-500': user.status === 'away',
'bg-red-500': user.status === 'busy', 'bg-red-500': user.status === 'busy',
'bg-gray-400': user.status === 'not-visible'}" 'bg-gray-400': user.status === 'not-visible'
}"
></span> ></span>
</span> </span>
</button> </button>
<mat-menu <mat-menu [xPosition]="'before'" #userActions="matMenu">
[xPosition]="'before'"
#userActions="matMenu">
<button mat-menu-item> <button mat-menu-item>
<span class="flex flex-col leading-none"> <span class="flex flex-col leading-none">
<span>Signed in as</span> <span>Signed in as</span>
@ -39,46 +39,36 @@
<mat-icon [svgIcon]="'heroicons_outline:cog-8-tooth'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:cog-8-tooth'"></mat-icon>
<span>Settings</span> <span>Settings</span>
</button> </button>
<button <button mat-menu-item [matMenuTriggerFor]="userStatus">
mat-menu-item <mat-icon
[matMenuTriggerFor]="userStatus"> [svgIcon]="'heroicons_outline:ellipsis-horizontal-circle'"
<mat-icon [svgIcon]="'heroicons_outline:ellipsis-horizontal-circle'"></mat-icon> ></mat-icon>
<span>Status</span> <span>Status</span>
</button> </button>
<mat-divider class="my-2"></mat-divider> <mat-divider class="my-2"></mat-divider>
<button <button mat-menu-item (click)="signOut()">
mat-menu-item <mat-icon
(click)="signOut()"> [svgIcon]="'heroicons_outline:arrow-right-on-rectangle'"
<mat-icon [svgIcon]="'heroicons_outline:arrow-right-on-rectangle'"></mat-icon> ></mat-icon>
<span>Sign out</span> <span>Sign out</span>
</button> </button>
</mat-menu> </mat-menu>
<mat-menu <mat-menu class="user-status-menu" #userStatus="matMenu">
class="user-status-menu" <button mat-menu-item (click)="updateUserStatus('online')">
#userStatus="matMenu"> <span class="mr-3 h-4 w-4 rounded-full bg-green-500"></span>
<button
mat-menu-item
(click)="updateUserStatus('online')">
<span class="w-4 h-4 mr-3 rounded-full bg-green-500"></span>
<span>Online</span> <span>Online</span>
</button> </button>
<button <button mat-menu-item (click)="updateUserStatus('away')">
mat-menu-item <span class="mr-3 h-4 w-4 rounded-full bg-amber-500"></span>
(click)="updateUserStatus('away')">
<span class="w-4 h-4 mr-3 rounded-full bg-amber-500"></span>
<span>Away</span> <span>Away</span>
</button> </button>
<button <button mat-menu-item (click)="updateUserStatus('busy')">
mat-menu-item <span class="mr-3 h-4 w-4 rounded-full bg-red-500"></span>
(click)="updateUserStatus('busy')">
<span class="w-4 h-4 mr-3 rounded-full bg-red-500"></span>
<span>Busy</span> <span>Busy</span>
</button> </button>
<button <button mat-menu-item (click)="updateUserStatus('not-visible')">
mat-menu-item <span class="mr-3 h-4 w-4 rounded-full bg-gray-400"></span>
(click)="updateUserStatus('not-visible')">
<span class="w-4 h-4 mr-3 rounded-full bg-gray-400"></span>
<span>Invisible</span> <span>Invisible</span>
</button> </button>
</mat-menu> </mat-menu>

View File

@ -1,6 +1,14 @@
import { BooleanInput } from '@angular/cdk/coercion'; import { BooleanInput } from '@angular/cdk/coercion';
import { NgClass, NgIf } from '@angular/common'; import { NgClass, NgIf } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
OnDestroy,
OnInit,
ViewEncapsulation,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@ -17,10 +25,16 @@ import { Subject, takeUntil } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'user', exportAs: 'user',
standalone: true, standalone: true,
imports : [MatButtonModule, MatMenuModule, NgIf, MatIconModule, NgClass, MatDividerModule], imports: [
MatButtonModule,
MatMenuModule,
NgIf,
MatIconModule,
NgClass,
MatDividerModule,
],
}) })
export class UserComponent implements OnInit, OnDestroy export class UserComponent implements OnInit, OnDestroy {
{
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_showAvatar: BooleanInput; static ngAcceptInputType_showAvatar: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */ /* eslint-enable @typescript-eslint/naming-convention */
@ -36,10 +50,8 @@ export class UserComponent implements OnInit, OnDestroy
constructor( constructor(
private _changeDetectorRef: ChangeDetectorRef, private _changeDetectorRef: ChangeDetectorRef,
private _router: Router, private _router: Router,
private _userService: UserService, private _userService: UserService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -48,13 +60,11 @@ export class UserComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to user changes // Subscribe to user changes
this._userService.user$ this._userService.user$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((user: User) => .subscribe((user: User) => {
{
this.user = user; this.user = user;
// Mark for check // Mark for check
@ -65,8 +75,7 @@ export class UserComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -81,26 +90,25 @@ export class UserComponent implements OnInit, OnDestroy
* *
* @param status * @param status
*/ */
updateUserStatus(status: string): void updateUserStatus(status: string): void {
{
// Return if user is not available // Return if user is not available
if ( !this.user ) if (!this.user) {
{
return; return;
} }
// Update the user // Update the user
this._userService.update({ this._userService
.update({
...this.user, ...this.user,
status, status,
}).subscribe(); })
.subscribe();
} }
/** /**
* Sign out * Sign out
*/ */
signOut(): void signOut(): void {
{
this._router.navigate(['/sign-out']); this._router.navigate(['/sign-out']);
} }
} }

View File

@ -15,7 +15,6 @@ layout {
/* Base styles for components that load as a route */ /* Base styles for components that load as a route */
router-outlet { router-outlet {
+ * { + * {
position: relative; position: relative;
display: flex; display: flex;

View File

@ -1,11 +1,18 @@
import { DOCUMENT, NgIf } from '@angular/common'; import { DOCUMENT, NgIf } from '@angular/common';
import { Component, Inject, OnDestroy, OnInit, Renderer2, ViewEncapsulation } from '@angular/core'; import {
Component,
Inject,
OnDestroy,
OnInit,
Renderer2,
ViewEncapsulation,
} from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { FuseConfig, FuseConfigService } from '@fuse/services/config'; import { FuseConfig, FuseConfigService } from '@fuse/services/config';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FusePlatformService } from '@fuse/services/platform'; import { FusePlatformService } from '@fuse/services/platform';
import { FUSE_VERSION } from '@fuse/version'; import { FUSE_VERSION } from '@fuse/version';
import { combineLatest, filter, map, Subject, takeUntil } from 'rxjs'; import { Subject, combineLatest, filter, map, takeUntil } from 'rxjs';
import { SettingsComponent } from './common/settings/settings.component'; import { SettingsComponent } from './common/settings/settings.component';
import { EmptyLayoutComponent } from './layouts/empty/empty.component'; import { EmptyLayoutComponent } from './layouts/empty/empty.component';
import { CenteredLayoutComponent } from './layouts/horizontal/centered/centered.component'; import { CenteredLayoutComponent } from './layouts/horizontal/centered/centered.component';
@ -25,10 +32,23 @@ import { ThinLayoutComponent } from './layouts/vertical/thin/thin.component';
styleUrls: ['./layout.component.scss'], styleUrls: ['./layout.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [NgIf, EmptyLayoutComponent, CenteredLayoutComponent, EnterpriseLayoutComponent, MaterialLayoutComponent, ModernLayoutComponent, ClassicLayoutComponent, ClassyLayoutComponent, CompactLayoutComponent, DenseLayoutComponent, FuturisticLayoutComponent, ThinLayoutComponent, SettingsComponent], imports: [
NgIf,
EmptyLayoutComponent,
CenteredLayoutComponent,
EnterpriseLayoutComponent,
MaterialLayoutComponent,
ModernLayoutComponent,
ClassicLayoutComponent,
ClassyLayoutComponent,
CompactLayoutComponent,
DenseLayoutComponent,
FuturisticLayoutComponent,
ThinLayoutComponent,
SettingsComponent,
],
}) })
export class LayoutComponent implements OnInit, OnDestroy export class LayoutComponent implements OnInit, OnDestroy {
{
config: FuseConfig; config: FuseConfig;
layout: string; layout: string;
scheme: 'dark' | 'light'; scheme: 'dark' | 'light';
@ -45,10 +65,8 @@ export class LayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _fuseConfigService: FuseConfigService, private _fuseConfigService: FuseConfigService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fusePlatformService: FusePlatformService, private _fusePlatformService: FusePlatformService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -57,32 +75,37 @@ export class LayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Set the theme and scheme based on the configuration // Set the theme and scheme based on the configuration
combineLatest([ combineLatest([
this._fuseConfigService.config$, this._fuseConfigService.config$,
this._fuseMediaWatcherService.onMediaQueryChange$(['(prefers-color-scheme: dark)', '(prefers-color-scheme: light)']), this._fuseMediaWatcherService.onMediaQueryChange$([
]).pipe( '(prefers-color-scheme: dark)',
'(prefers-color-scheme: light)',
]),
])
.pipe(
takeUntil(this._unsubscribeAll), takeUntil(this._unsubscribeAll),
map(([config, mql]) => map(([config, mql]) => {
{
const options = { const options = {
scheme: config.scheme, scheme: config.scheme,
theme: config.theme, theme: config.theme,
}; };
// If the scheme is set to 'auto'... // If the scheme is set to 'auto'...
if ( config.scheme === 'auto' ) if (config.scheme === 'auto') {
{
// Decide the scheme using the media query // Decide the scheme using the media query
options.scheme = mql.breakpoints['(prefers-color-scheme: dark)'] ? 'dark' : 'light'; options.scheme = mql.breakpoints[
'(prefers-color-scheme: dark)'
]
? 'dark'
: 'light';
} }
return options; return options;
}), })
).subscribe((options) => )
{ .subscribe((options) => {
// Store the options // Store the options
this.scheme = options.scheme; this.scheme = options.scheme;
this.theme = options.theme; this.theme = options.theme;
@ -95,8 +118,7 @@ export class LayoutComponent implements OnInit, OnDestroy
// Subscribe to config changes // Subscribe to config changes
this._fuseConfigService.config$ this._fuseConfigService.config$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((config: FuseConfig) => .subscribe((config: FuseConfig) => {
{
// Store the config // Store the config
this.config = config; this.config = config;
@ -105,27 +127,34 @@ export class LayoutComponent implements OnInit, OnDestroy
}); });
// Subscribe to NavigationEnd event // Subscribe to NavigationEnd event
this._router.events.pipe( this._router.events
filter(event => event instanceof NavigationEnd), .pipe(
takeUntil(this._unsubscribeAll), filter((event) => event instanceof NavigationEnd),
).subscribe(() => takeUntil(this._unsubscribeAll)
{ )
.subscribe(() => {
// Update the layout // Update the layout
this._updateLayout(); this._updateLayout();
}); });
// Set the app version // Set the app version
this._renderer2.setAttribute(this._document.querySelector('[ng-version]'), 'fuse-version', FUSE_VERSION); this._renderer2.setAttribute(
this._document.querySelector('[ng-version]'),
'fuse-version',
FUSE_VERSION
);
// Set the OS name // Set the OS name
this._renderer2.addClass(this._document.body, this._fusePlatformService.osName); this._renderer2.addClass(
this._document.body,
this._fusePlatformService.osName
);
} }
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -138,12 +167,10 @@ export class LayoutComponent implements OnInit, OnDestroy
/** /**
* Update the selected layout * Update the selected layout
*/ */
private _updateLayout(): void private _updateLayout(): void {
{
// Get the current activated route // Get the current activated route
let route = this._activatedRoute; let route = this._activatedRoute;
while ( route.firstChild ) while (route.firstChild) {
{
route = route.firstChild; route = route.firstChild;
} }
@ -153,11 +180,9 @@ export class LayoutComponent implements OnInit, OnDestroy
// 2. Get the query parameter from the current route and // 2. Get the query parameter from the current route and
// set the layout and save the layout to the config // set the layout and save the layout to the config
const layoutFromQueryParam = route.snapshot.queryParamMap.get('layout'); const layoutFromQueryParam = route.snapshot.queryParamMap.get('layout');
if ( layoutFromQueryParam ) if (layoutFromQueryParam) {
{
this.layout = layoutFromQueryParam; this.layout = layoutFromQueryParam;
if ( this.config ) if (this.config) {
{
this.config.layout = layoutFromQueryParam; this.config.layout = layoutFromQueryParam;
} }
} }
@ -179,11 +204,13 @@ export class LayoutComponent implements OnInit, OnDestroy
// Also, this will allow overriding the layout in any time so we // Also, this will allow overriding the layout in any time so we
// can have different layouts for different routes. // can have different layouts for different routes.
const paths = route.pathFromRoot; const paths = route.pathFromRoot;
paths.forEach((path) => paths.forEach((path) => {
{
// Check if there is a 'layout' data // Check if there is a 'layout' data
if ( path.routeConfig && path.routeConfig.data && path.routeConfig.data.layout ) if (
{ path.routeConfig &&
path.routeConfig.data &&
path.routeConfig.data.layout
) {
// Set the layout // Set the layout
this.layout = path.routeConfig.data.layout; this.layout = path.routeConfig.data.layout;
} }
@ -195,8 +222,7 @@ export class LayoutComponent implements OnInit, OnDestroy
* *
* @private * @private
*/ */
private _updateScheme(): void private _updateScheme(): void {
{
// Remove class names for all schemes // Remove class names for all schemes
this._document.body.classList.remove('light', 'dark'); this._document.body.classList.remove('light', 'dark');
@ -209,14 +235,14 @@ export class LayoutComponent implements OnInit, OnDestroy
* *
* @private * @private
*/ */
private _updateTheme(): void private _updateTheme(): void {
{
// Find the class name for the previously selected theme and remove it // Find the class name for the previously selected theme and remove it
this._document.body.classList.forEach((className: string) => this._document.body.classList.forEach((className: string) => {
{ if (className.startsWith('theme-')) {
if ( className.startsWith('theme-') ) this._document.body.classList.remove(
{ className,
this._document.body.classList.remove(className, className.split('-')[1]); className.split('-')[1]
);
} }
}); });

View File

@ -2,13 +2,11 @@
<fuse-loading-bar></fuse-loading-bar> <fuse-loading-bar></fuse-loading-bar>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full"> <div class="flex w-full flex-auto flex-col">
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
</div> </div>

View File

@ -11,16 +11,13 @@ import { Subject } from 'rxjs';
standalone: true, standalone: true,
imports: [FuseLoadingBarComponent, NgIf, RouterOutlet], imports: [FuseLoadingBarComponent, NgIf, RouterOutlet],
}) })
export class EmptyLayoutComponent implements OnDestroy export class EmptyLayoutComponent implements OnDestroy {
{
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
/** /**
* Constructor * Constructor
*/ */
constructor() constructor() {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
@ -29,8 +26,7 @@ export class EmptyLayoutComponent implements OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();

View File

@ -1,8 +1,9 @@
<!-- Loading bar --> <!-- Loading bar -->
<fuse-loading-bar></fuse-loading-bar> <fuse-loading-bar></fuse-loading-bar>
<div class="flex flex-auto justify-center w-full sm:p-4 md:p-8 bg-gray-200 dark:bg-card"> <div
class="flex w-full flex-auto justify-center bg-gray-200 dark:bg-card sm:p-4 md:p-8"
>
<!-- Navigation --> <!-- Navigation -->
<ng-container *ngIf="isScreenSmall"> <ng-container *ngIf="isScreenSmall">
<fuse-vertical-navigation <fuse-vertical-navigation
@ -10,62 +11,69 @@
[mode]="'over'" [mode]="'over'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="false"> [opened]="false"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 pt-6 px-8"> <div class="flex h-20 items-center px-8 pt-6">
<img <img class="w-30" src="images/logo/logo-text-on-dark.svg" />
class="w-30"
src="images/logo/logo-text-on-dark.svg">
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
</ng-container> </ng-container>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col items-center flex-auto min-w-0 max-w-360 sm:rounded-xl shadow-2xl dark:shadow-none overflow-hidden"> <div
class="flex min-w-0 max-w-360 flex-auto flex-col items-center overflow-hidden shadow-2xl dark:shadow-none sm:rounded-xl"
>
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 sm:h-20 px-4 md:px-6 z-49 bg-card border-b dark:bg-default print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center border-b px-4 dark:bg-default sm:h-20 md:px-6 print:hidden"
>
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<!-- Logo --> <!-- Logo -->
<div class="flex items-center mx-2 lg:mr-8"> <div class="mx-2 flex items-center lg:mr-8">
<div class="hidden lg:flex"> <div class="hidden lg:flex">
<!-- Light version --> <!-- Light version -->
<img <img
class="dark:hidden w-24" class="w-24 dark:hidden"
src="images/logo/logo-text.svg" src="images/logo/logo-text.svg"
alt="Logo image"> alt="Logo image"
/>
<!-- Dark version --> <!-- Dark version -->
<img <img
class="hidden dark:flex w-24" class="hidden w-24 dark:flex"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
<!-- Small version --> <!-- Small version -->
<img <img
class="flex lg:hidden w-8" class="flex w-8 lg:hidden"
src="images/logo/logo.svg" src="images/logo/logo.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
<!-- Horizontal navigation --> <!-- Horizontal navigation -->
<fuse-horizontal-navigation <fuse-horizontal-navigation
class="mr-2" class="mr-2"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.horizontal"></fuse-horizontal-navigation> [navigation]="navigation.horizontal"
></fuse-horizontal-navigation>
</ng-container> </ng-container>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall"> <ng-container *ngIf="isScreenSmall">
<button <button
class="mr-2" class="mr-2"
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
>
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
</ng-container> </ng-container>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2"> <div class="ml-auto flex items-center space-x-1 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -77,17 +85,19 @@
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto w-full bg-default"> <div class="bg-default flex w-full flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-16 sm:h-20 px-6 sm:px-8 z-49 bg-card border-t dark:bg-default print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-16 w-full flex-0 items-center justify-start border-t px-6 dark:bg-default sm:h-20 sm:px-8 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,11 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseHorizontalNavigationComponent, FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseHorizontalNavigationComponent,
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -22,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './centered.component.html', templateUrl: './centered.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, NgIf, FuseVerticalNavigationComponent, FuseHorizontalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, RouterOutlet], imports: [
FuseLoadingBarComponent,
NgIf,
FuseVerticalNavigationComponent,
FuseHorizontalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
RouterOutlet,
],
}) })
export class CenteredLayoutComponent implements OnInit, OnDestroy export class CenteredLayoutComponent implements OnInit, OnDestroy {
{
navigation: Navigation; navigation: Navigation;
isScreenSmall: boolean; isScreenSmall: boolean;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -38,10 +56,8 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -50,8 +66,7 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -62,21 +77,18 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -85,8 +97,7 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -101,13 +112,14 @@ export class CenteredLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,47 +8,60 @@
[mode]="'over'" [mode]="'over'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="false"> [opened]="false"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 pt-6 px-8"> <div class="flex h-20 items-center px-8 pt-6">
<img <img
class="w-24" class="w-24"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
</ng-container> </ng-container>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto items-center w-full min-w-0 bg-gray-200 dark:bg-card"> <div
class="flex w-full min-w-0 flex-auto flex-col items-center bg-gray-200 dark:bg-card"
>
<!-- Header --> <!-- Header -->
<div class="relative flex flex-col flex-0 justify-center w-full h-16 sm:h-20 md:h-36 overflow-hidden z-49 shadow dark:shadow-none print:hidden"> <div
class="relative z-49 flex h-16 w-full flex-0 flex-col justify-center overflow-hidden shadow dark:shadow-none sm:h-20 md:h-36 print:hidden"
>
<!-- Top bar --> <!-- Top bar -->
<div class="relative dark flex flex-auto justify-center w-full px-4 md:px-8 bg-gray-800 dark:bg-gray-900"> <div
<div class="flex items-center w-full max-w-360 h-16 sm:h-20"> class="dark relative flex w-full flex-auto justify-center bg-gray-800 px-4 dark:bg-gray-900 md:px-8"
>
<div class="flex h-16 w-full max-w-360 items-center sm:h-20">
<!-- Logo --> <!-- Logo -->
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<div class="flex items-center"> <div class="flex items-center">
<img <img
class="w-24" class="w-24"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall"> <ng-container *ngIf="isScreenSmall">
<button <button
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:bars-3'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div
class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2"
>
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -58,8 +71,13 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="
'heroicons_outline:chat-bubble-left-right'
"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
@ -67,20 +85,25 @@
</div> </div>
<!-- Bottom bar --> <!-- Bottom bar -->
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<div class="flex flex-auto justify-center px-4 md:px-8 bg-card dark:bg-gray-700"> <div
<div class="relative flex items-center w-full max-w-360 h-16"> class="bg-card flex flex-auto justify-center px-4 dark:bg-gray-700 md:px-8"
>
<div class="relative flex h-16 w-full max-w-360 items-center">
<fuse-horizontal-navigation <fuse-horizontal-navigation
class="-mx-4" class="-mx-4"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.horizontal"></fuse-horizontal-navigation> [navigation]="navigation.horizontal"
></fuse-horizontal-navigation>
</div> </div>
</div> </div>
</ng-container> </ng-container>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-auto justify-center w-full sm:p-6 md:p-8"> <div class="flex w-full flex-auto justify-center sm:p-6 md:p-8">
<div class="flex flex-col flex-auto w-full sm:max-w-360 sm:shadow-lg sm:rounded-lg sm:overflow-hidden bg-default"> <div
class="bg-default flex w-full flex-auto flex-col sm:max-w-360 sm:overflow-hidden sm:rounded-lg sm:shadow-lg"
>
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
@ -88,12 +111,15 @@
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 justify-center w-full px-6 md:px-8 z-49 border-t bg-card print:hidden"> <div
<div class="flex items-center w-full max-w-360 h-14 sm:h-20"> class="bg-card relative z-49 flex w-full flex-0 justify-center border-t px-6 md:px-8 print:hidden"
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> >
<div class="flex h-14 w-full max-w-360 items-center sm:h-20">
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,11 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseHorizontalNavigationComponent, FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseHorizontalNavigationComponent,
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +27,25 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './enterprise.component.html', templateUrl: './enterprise.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, NgIf, FuseVerticalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, FuseHorizontalNavigationComponent, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
NgIf,
FuseVerticalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
FuseHorizontalNavigationComponent,
RouterOutlet,
QuickChatComponent,
],
}) })
export class EnterpriseLayoutComponent implements OnInit, OnDestroy export class EnterpriseLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -39,10 +58,8 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -51,8 +68,7 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -63,21 +79,18 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -86,8 +99,7 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -102,13 +114,14 @@ export class EnterpriseLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,53 +8,69 @@
[mode]="'over'" [mode]="'over'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="false"> [opened]="false"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 pt-6 px-8"> <div class="flex h-20 items-center px-8 pt-6">
<img <img
class="w-24" class="w-24"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
</ng-container> </ng-container>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto items-center w-full min-w-0 bg-gray-200 dark:bg-card"> <div
class="flex w-full min-w-0 flex-auto flex-col items-center bg-gray-200 dark:bg-card"
>
<!-- Header --> <!-- Header -->
<div class="relative flex justify-center w-full overflow-hidden z-49 bg-primary-700 print:hidden"> <div
<div class="max-w-360 w-full sm:py-3 sm:m-8 sm:mb-0 md:mt-12 md:mx-8 md:pt-4 md:pb-3 sm:rounded-t-xl border-b sm:shadow-2xl overflow-hidden bg-card"> class="relative z-49 flex w-full justify-center overflow-hidden bg-primary-700 print:hidden"
>
<div
class="bg-card w-full max-w-360 overflow-hidden border-b sm:m-8 sm:mb-0 sm:rounded-t-xl sm:py-3 sm:shadow-2xl md:mx-8 md:mt-12 md:pb-3 md:pt-4"
>
<!-- Top bar --> <!-- Top bar -->
<div class="relative flex flex-auto flex-0 items-center h-16 px-4 md:px-6"> <div
class="relative flex h-16 flex-0 flex-auto items-center px-4 md:px-6"
>
<!-- Logo --> <!-- Logo -->
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<div class="flex items-center mx-2"> <div class="mx-2 flex items-center">
<!-- Light version --> <!-- Light version -->
<img <img
class="w-24 dark:hidden" class="w-24 dark:hidden"
src="images/logo/logo-text.svg" src="images/logo/logo-text.svg"
alt="Logo image"> alt="Logo image"
/>
<!-- Dark version --> <!-- Dark version -->
<img <img
class="hidden dark:flex w-24" class="hidden w-24 dark:flex"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall"> <ng-container *ngIf="isScreenSmall">
<button <button
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:bars-3'"
></mat-icon>
</button> </button>
</ng-container> </ng-container>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2"> <div
class="ml-auto flex items-center space-x-1 pl-2 sm:space-x-2"
>
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -66,18 +82,23 @@
</div> </div>
<!-- Bottom bar --> <!-- Bottom bar -->
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<div class="relative flex flex-auto flex-0 items-center h-16 px-4"> <div
class="relative flex h-16 flex-0 flex-auto items-center px-4"
>
<fuse-horizontal-navigation <fuse-horizontal-navigation
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.horizontal"></fuse-horizontal-navigation> [navigation]="navigation.horizontal"
></fuse-horizontal-navigation>
</div> </div>
</ng-container> </ng-container>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-auto justify-center w-full sm:px-8"> <div class="flex w-full flex-auto justify-center sm:px-8">
<div class="flex flex-col flex-auto w-full sm:max-w-360 sm:shadow-xl sm:overflow-hidden bg-default"> <div
class="bg-default flex w-full flex-auto flex-col sm:max-w-360 sm:overflow-hidden sm:shadow-xl"
>
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
@ -85,10 +106,13 @@
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex justify-center w-full z-49 print:hidden"> <div class="relative z-49 flex w-full justify-center print:hidden">
<div class="flex items-center max-w-360 w-full h-14 sm:h-20 px-6 md:px-8 sm:shadow-xl border-t bg-card dark:bg-default"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card flex h-14 w-full max-w-360 items-center border-t px-6 dark:bg-default sm:h-20 sm:shadow-xl md:px-8"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,11 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseHorizontalNavigationComponent, FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseHorizontalNavigationComponent,
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -22,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './material.component.html', templateUrl: './material.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, NgIf, FuseVerticalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, FuseHorizontalNavigationComponent, RouterOutlet], imports: [
FuseLoadingBarComponent,
NgIf,
FuseVerticalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
FuseHorizontalNavigationComponent,
RouterOutlet,
],
}) })
export class MaterialLayoutComponent implements OnInit, OnDestroy export class MaterialLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -38,10 +56,8 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -50,8 +66,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -62,21 +77,18 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -85,8 +97,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -101,13 +112,14 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,56 +8,61 @@
[mode]="'over'" [mode]="'over'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="false"> [opened]="false"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 pt-6 px-8"> <div class="flex h-20 items-center px-8 pt-6">
<img <img
class="w-24" class="w-24"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
</ng-container> </ng-container>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 sm:h-20 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none sm:h-20 md:px-6 print:hidden"
>
<ng-container *ngIf="!isScreenSmall"> <ng-container *ngIf="!isScreenSmall">
<!-- Logo --> <!-- Logo -->
<div class="flex items-center mx-2 lg:mr-8"> <div class="mx-2 flex items-center lg:mr-8">
<div class="hidden lg:flex"> <div class="hidden lg:flex">
<img <img
class="dark:hidden w-24" class="w-24 dark:hidden"
src="images/logo/logo-text.svg"> src="images/logo/logo-text.svg"
/>
<img <img
class="hidden dark:flex w-24" class="hidden w-24 dark:flex"
src="images/logo/logo-text-on-dark.svg"> src="images/logo/logo-text-on-dark.svg"
/>
</div> </div>
<img <img class="flex w-8 lg:hidden" src="images/logo/logo.svg" />
class="flex lg:hidden w-8"
src="images/logo/logo.svg">
</div> </div>
<!-- Horizontal navigation --> <!-- Horizontal navigation -->
<fuse-horizontal-navigation <fuse-horizontal-navigation
class="mr-2" class="mr-2"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.horizontal"></fuse-horizontal-navigation> [navigation]="navigation.horizontal"
></fuse-horizontal-navigation>
</ng-container> </ng-container>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<ng-container *ngIf="isScreenSmall"> <ng-container *ngIf="isScreenSmall">
<button <button
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
>
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
</ng-container> </ng-container>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -67,25 +72,31 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto w-full"> <div class="flex w-full flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center w-full h-14 sm:h-20 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center border-t px-4 dark:bg-transparent sm:h-20 md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,11 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseHorizontalNavigationComponent, FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseHorizontalNavigationComponent,
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +27,25 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './modern.component.html', templateUrl: './modern.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, NgIf, FuseVerticalNavigationComponent, FuseHorizontalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
NgIf,
FuseVerticalNavigationComponent,
FuseHorizontalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
RouterOutlet,
QuickChatComponent,
],
}) })
export class ModernLayoutComponent implements OnInit, OnDestroy export class ModernLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -39,10 +58,8 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -51,8 +68,7 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -63,21 +79,18 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -86,8 +99,7 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -102,13 +114,14 @@ export class ModernLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -7,38 +7,40 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 p-6 pb-0"> <div class="flex h-20 items-center p-6 pb-0">
<!-- Light version --> <!-- Light version -->
<img <img
class="dark:hidden w-30" class="w-30 dark:hidden"
src="images/logo/logo-text.svg" src="images/logo/logo-text.svg"
alt="Logo image"> alt="Logo image"
/>
<!-- Dark version --> <!-- Dark version -->
<img <img
class="hidden dark:flex w-30" class="hidden w-30 dark:flex"
src="images/logo/logo-text-on-dark.svg" src="images/logo/logo-text-on-dark.svg"
alt="Logo image"> alt="Logo image"
/>
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button mat-icon-button (click)="toggleNavigation('mainNavigation')">
mat-icon-button
(click)="toggleNavigation('mainNavigation')">
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -48,25 +50,31 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center justify-start border-t px-4 dark:bg-transparent md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './classic.component.html', templateUrl: './classic.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, FuseVerticalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, NgIf, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
FuseVerticalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
NgIf,
RouterOutlet,
QuickChatComponent,
],
}) })
export class ClassicLayoutComponent implements OnInit, OnDestroy export class ClassicLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -39,10 +56,8 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -51,8 +66,7 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -63,21 +77,18 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -86,8 +97,7 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -102,13 +112,14 @@ export class ClassicLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -7,40 +7,45 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<div class="flex items-center w-full p-4 pl-6"> <div class="flex w-full items-center p-4 pl-6">
<!-- Logo --> <!-- Logo -->
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<img <img class="w-8" src="images/logo/logo.svg" />
class="w-8"
src="images/logo/logo.svg">
</div> </div>
<!-- Components --> <!-- Components -->
<div class="flex items-center ml-auto"> <div class="ml-auto flex items-center">
<notifications></notifications> <notifications></notifications>
<user [showAvatar]="false"></user> <user [showAvatar]="false"></user>
</div> </div>
</div> </div>
<!-- User --> <!-- User -->
<div class="flex flex-col items-center w-full p-4"> <div class="flex w-full flex-col items-center p-4">
<div class="relative w-24 h-24"> <div class="relative h-24 w-24">
<img <img
class="w-full h-full rounded-full" class="h-full w-full rounded-full"
*ngIf="user.avatar" *ngIf="user.avatar"
[src]="user.avatar" [src]="user.avatar"
alt="User avatar"> alt="User avatar"
/>
<mat-icon <mat-icon
class="icon-size-24" class="icon-size-24"
*ngIf="!user.avatar" *ngIf="!user.avatar"
[svgIcon]="'heroicons_solid:user-circle'"></mat-icon> [svgIcon]="'heroicons_solid:user-circle'"
></mat-icon>
</div> </div>
<div class="flex flex-col items-center justify-center w-full mt-6"> <div class="mt-6 flex w-full flex-col items-center justify-center">
<div class="w-full whitespace-nowrap text-ellipsis overflow-hidden text-center leading-normal font-medium"> <div
class="w-full overflow-hidden text-ellipsis whitespace-nowrap text-center font-medium leading-normal"
>
{{ user.name }} {{ user.name }}
</div> </div>
<div class="w-full mt-0.5 whitespace-nowrap text-ellipsis overflow-hidden text-center text-md leading-normal font-medium text-secondary"> <div
class="text-secondary mt-0.5 w-full overflow-hidden text-ellipsis whitespace-nowrap text-center text-md font-medium leading-normal"
>
{{ user.email }} {{ user.email }}
</div> </div>
</div> </div>
@ -48,27 +53,26 @@
</ng-container> </ng-container>
<!-- Navigation footer hook --> <!-- Navigation footer hook -->
<ng-container fuseVerticalNavigationContentFooter> <ng-container fuseVerticalNavigationContentFooter>
<div class="flex flex-0 items-center justify-center h-16 pr-6 pl-2 mt-2 mb-4 opacity-12"> <div
<img class="mb-4 mt-2 flex h-16 flex-0 items-center justify-center pl-2 pr-6 opacity-12"
class="max-w-36" >
src="images/logo/logo-text-on-dark.svg"> <img class="max-w-36" src="images/logo/logo-text-on-dark.svg" />
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button mat-icon-button (click)="toggleNavigation('mainNavigation')">
mat-icon-button
(click)="toggleNavigation('mainNavigation')">
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -77,14 +81,17 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
@ -94,7 +101,6 @@
<!--<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <!--<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden">
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> <span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span>
</div>--> </div>-->
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -25,10 +28,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './classy.component.html', templateUrl: './classy.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, FuseVerticalNavigationComponent, NotificationsComponent, UserComponent, NgIf, MatIconModule, MatButtonModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
FuseVerticalNavigationComponent,
NotificationsComponent,
UserComponent,
NgIf,
MatIconModule,
MatButtonModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
RouterOutlet,
QuickChatComponent,
],
}) })
export class ClassyLayoutComponent implements OnInit, OnDestroy export class ClassyLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
user: User; user: User;
@ -43,10 +60,8 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _userService: UserService, private _userService: UserService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -55,8 +70,7 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -67,29 +81,25 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to the user service // Subscribe to the user service
this._userService.user$ this._userService.user$
.pipe((takeUntil(this._unsubscribeAll))) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((user: User) => .subscribe((user: User) => {
{
this.user = user; this.user = user;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -98,8 +108,7 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -114,13 +123,14 @@ export class ClassyLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,32 +8,29 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.compact" [navigation]="navigation.compact"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center justify-center h-20 mt-3 mb-4"> <div class="mb-4 mt-3 flex h-20 items-center justify-center">
<img <img class="w-10" src="images/logo/logo.svg" alt="Logo image" />
class="w-10"
src="images/logo/logo.svg"
alt="Logo image">
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button mat-icon-button (click)="toggleNavigation('mainNavigation')">
mat-icon-button
(click)="toggleNavigation('mainNavigation')">
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -43,25 +40,31 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center justify-start border-t px-4 dark:bg-transparent md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './compact.component.html', templateUrl: './compact.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, NgIf, RouterOutlet, QuickChatComponent, FuseVerticalNavigationComponent], imports: [
FuseLoadingBarComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
NgIf,
RouterOutlet,
QuickChatComponent,
FuseVerticalNavigationComponent,
],
}) })
export class CompactLayoutComponent implements OnInit, OnDestroy export class CompactLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -39,10 +56,8 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -51,8 +66,7 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -63,21 +77,18 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -86,8 +97,7 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -102,13 +112,14 @@ export class CompactLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,41 +8,44 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.default" [navigation]="navigation.default"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center justify-center h-20"> <div class="flex h-20 items-center justify-center">
<img <img class="w-8" src="images/logo/logo.svg" alt="Logo image" />
class="w-8"
src="images/logo/logo.svg"
alt="Logo image">
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
<div class="flex items-center pr-2 space-x-2"> class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<div class="flex items-center space-x-2 pr-2">
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
>
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Navigation appearance toggle button --> <!-- Navigation appearance toggle button -->
<button <button
class="hidden md:inline-flex" class="hidden md:inline-flex"
mat-icon-button mat-icon-button
(click)="toggleNavigationAppearance()"> (click)="toggleNavigationAppearance()"
<mat-icon [svgIcon]="'heroicons_outline:arrows-right-left'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:arrows-right-left'"
></mat-icon>
</button> </button>
</div> </div>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -52,25 +55,31 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center justify-start border-t px-4 dark:bg-transparent md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './dense.component.html', templateUrl: './dense.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, FuseVerticalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, NgIf, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
FuseVerticalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
NgIf,
RouterOutlet,
QuickChatComponent,
],
}) })
export class DenseLayoutComponent implements OnInit, OnDestroy export class DenseLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
navigationAppearance: 'default' | 'dense' = 'dense'; navigationAppearance: 'default' | 'dense' = 'dense';
@ -40,10 +57,8 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -52,8 +67,7 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -64,34 +78,32 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
// Change the navigation appearance // Change the navigation appearance
this.navigationAppearance = this.isScreenSmall ? 'default' : 'dense'; this.navigationAppearance = this.isScreenSmall
? 'default'
: 'dense';
}); });
} }
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -106,13 +118,14 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }
@ -121,8 +134,8 @@ export class DenseLayoutComponent implements OnInit, OnDestroy
/** /**
* Toggle the navigation appearance * Toggle the navigation appearance
*/ */
toggleNavigationAppearance(): void toggleNavigationAppearance(): void {
{ this.navigationAppearance =
this.navigationAppearance = (this.navigationAppearance === 'default' ? 'dense' : 'default'); this.navigationAppearance === 'default' ? 'dense' : 'default';
} }
} }

View File

@ -7,26 +7,29 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.futuristic" [navigation]="navigation.futuristic"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationHeader> <ng-container fuseVerticalNavigationHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center h-20 p-6 pb-0"> <div class="flex h-20 items-center p-6 pb-0">
<img <img class="w-30" src="images/logo/logo-text-on-dark.svg" />
class="w-30"
src="images/logo/logo-text-on-dark.svg">
</div> </div>
</ng-container> </ng-container>
<!-- Navigation footer hook --> <!-- Navigation footer hook -->
<ng-container fuseVerticalNavigationFooter> <ng-container fuseVerticalNavigationFooter>
<!-- User --> <!-- User -->
<div class="flex items-center w-full px-6 py-8 border-t"> <div class="flex w-full items-center border-t px-6 py-8">
<user></user> <user></user>
<div class="flex flex-col w-full ml-4 overflow-hidden"> <div class="ml-4 flex w-full flex-col overflow-hidden">
<div class="w-full whitespace-nowrap text-ellipsis overflow-hidden leading-normal text-current opacity-80"> <div
class="w-full overflow-hidden text-ellipsis whitespace-nowrap leading-normal text-current opacity-80"
>
{{ user.name }} {{ user.name }}
</div> </div>
<div class="w-full mt-0.5 whitespace-nowrap text-sm text-ellipsis overflow-hidden leading-normal text-current opacity-50"> <div
class="mt-0.5 w-full overflow-hidden text-ellipsis whitespace-nowrap text-sm leading-normal text-current opacity-50"
>
brian.hughes&#64;company.com brian.hughes&#64;company.com
</div> </div>
</div> </div>
@ -35,19 +38,21 @@
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button
class="mr-2" class="mr-2"
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
>
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -57,24 +62,30 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center justify-start border-t px-4 dark:bg-transparent md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -25,10 +28,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './futuristic.component.html', templateUrl: './futuristic.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, FuseVerticalNavigationComponent, UserComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, NgIf, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
FuseVerticalNavigationComponent,
UserComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
NgIf,
RouterOutlet,
QuickChatComponent,
],
}) })
export class FuturisticLayoutComponent implements OnInit, OnDestroy export class FuturisticLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
user: User; user: User;
@ -43,10 +60,8 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _userService: UserService, private _userService: UserService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -55,8 +70,7 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -67,29 +81,25 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to the user service // Subscribe to the user service
this._userService.user$ this._userService.user$
.pipe((takeUntil(this._unsubscribeAll))) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((user: User) => .subscribe((user: User) => {
{
this.user = user; this.user = user;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -98,8 +108,7 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -114,13 +123,14 @@ export class FuturisticLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -8,33 +8,33 @@
[mode]="isScreenSmall ? 'over' : 'side'" [mode]="isScreenSmall ? 'over' : 'side'"
[name]="'mainNavigation'" [name]="'mainNavigation'"
[navigation]="navigation.compact" [navigation]="navigation.compact"
[opened]="!isScreenSmall"> [opened]="!isScreenSmall"
>
<!-- Navigation header hook --> <!-- Navigation header hook -->
<ng-container fuseVerticalNavigationContentHeader> <ng-container fuseVerticalNavigationContentHeader>
<!-- Logo --> <!-- Logo -->
<div class="flex items-center justify-center h-20"> <div class="flex h-20 items-center justify-center">
<img <img class="w-8" src="images/logo/logo.svg" alt="Logo image" />
class="w-8"
src="images/logo/logo.svg"
alt="Logo image">
</div> </div>
</ng-container> </ng-container>
</fuse-vertical-navigation> </fuse-vertical-navigation>
<!-- Wrapper --> <!-- Wrapper -->
<div class="flex flex-col flex-auto w-full min-w-0"> <div class="flex w-full min-w-0 flex-auto flex-col">
<!-- Header --> <!-- Header -->
<div class="relative flex flex-0 items-center w-full h-16 px-4 md:px-6 z-49 shadow dark:shadow-none dark:border-b bg-card dark:bg-transparent print:hidden"> <div
class="bg-card relative z-49 flex h-16 w-full flex-0 items-center px-4 shadow dark:border-b dark:bg-transparent dark:shadow-none md:px-6 print:hidden"
>
<!-- Navigation toggle button --> <!-- Navigation toggle button -->
<button <button
class="mr-2" class="mr-2"
mat-icon-button mat-icon-button
(click)="toggleNavigation('mainNavigation')"> (click)="toggleNavigation('mainNavigation')"
>
<mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:bars-3'"></mat-icon>
</button> </button>
<!-- Components --> <!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2"> <div class="ml-auto flex items-center space-x-0.5 pl-2 sm:space-x-2">
<languages></languages> <languages></languages>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen> <fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search> <search [appearance]="'bar'"></search>
@ -44,25 +44,31 @@
<button <button
class="lg:hidden" class="lg:hidden"
mat-icon-button mat-icon-button
(click)="quickChat.toggle()"> (click)="quickChat.toggle()"
<mat-icon [svgIcon]="'heroicons_outline:chat-bubble-left-right'"></mat-icon> >
<mat-icon
[svgIcon]="'heroicons_outline:chat-bubble-left-right'"
></mat-icon>
</button> </button>
<user></user> <user></user>
</div> </div>
</div> </div>
<!-- Content --> <!-- Content -->
<div class="flex flex-col flex-auto"> <div class="flex flex-auto flex-col">
<!-- *ngIf="true" hack is required here for router-outlet to work correctly. <!-- *ngIf="true" hack is required here for router-outlet to work correctly.
Otherwise, layout changes won't be registered and the view won't be updated! --> Otherwise, layout changes won't be registered and the view won't be updated! -->
<router-outlet *ngIf="true"></router-outlet> <router-outlet *ngIf="true"></router-outlet>
</div> </div>
<!-- Footer --> <!-- Footer -->
<div class="relative flex flex-0 items-center justify-start w-full h-14 px-4 md:px-6 z-49 border-t bg-card dark:bg-transparent print:hidden"> <div
<span class="font-medium text-secondary">Fuse &copy; {{currentYear}}</span> class="bg-card relative z-49 flex h-14 w-full flex-0 items-center justify-start border-t px-4 dark:bg-transparent md:px-6 print:hidden"
>
<span class="text-secondary font-medium"
>Fuse &copy; {{ currentYear }}</span
>
</div> </div>
</div> </div>
<!-- Quick chat --> <!-- Quick chat -->

View File

@ -5,7 +5,10 @@ import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen';
import { FuseLoadingBarComponent } from '@fuse/components/loading-bar'; import { FuseLoadingBarComponent } from '@fuse/components/loading-bar';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation'; import {
FuseNavigationService,
FuseVerticalNavigationComponent,
} from '@fuse/components/navigation';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { NavigationService } from 'app/core/navigation/navigation.service';
import { Navigation } from 'app/core/navigation/navigation.types'; import { Navigation } from 'app/core/navigation/navigation.types';
@ -23,10 +26,24 @@ import { Subject, takeUntil } from 'rxjs';
templateUrl: './thin.component.html', templateUrl: './thin.component.html',
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
standalone: true, standalone: true,
imports : [FuseLoadingBarComponent, FuseVerticalNavigationComponent, MatButtonModule, MatIconModule, LanguagesComponent, FuseFullscreenComponent, SearchComponent, ShortcutsComponent, MessagesComponent, NotificationsComponent, UserComponent, NgIf, RouterOutlet, QuickChatComponent], imports: [
FuseLoadingBarComponent,
FuseVerticalNavigationComponent,
MatButtonModule,
MatIconModule,
LanguagesComponent,
FuseFullscreenComponent,
SearchComponent,
ShortcutsComponent,
MessagesComponent,
NotificationsComponent,
UserComponent,
NgIf,
RouterOutlet,
QuickChatComponent,
],
}) })
export class ThinLayoutComponent implements OnInit, OnDestroy export class ThinLayoutComponent implements OnInit, OnDestroy {
{
isScreenSmall: boolean; isScreenSmall: boolean;
navigation: Navigation; navigation: Navigation;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@ -39,10 +56,8 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
private _router: Router, private _router: Router,
private _navigationService: NavigationService, private _navigationService: NavigationService,
private _fuseMediaWatcherService: FuseMediaWatcherService, private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {}
{
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Accessors // @ Accessors
@ -51,8 +66,7 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
/** /**
* Getter for current year * Getter for current year
*/ */
get currentYear(): number get currentYear(): number {
{
return new Date().getFullYear(); return new Date().getFullYear();
} }
@ -63,21 +77,18 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
/** /**
* On init * On init
*/ */
ngOnInit(): void ngOnInit(): void {
{
// Subscribe to navigation data // Subscribe to navigation data
this._navigationService.navigation$ this._navigationService.navigation$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe((navigation: Navigation) => .subscribe((navigation: Navigation) => {
{
this.navigation = navigation; this.navigation = navigation;
}); });
// Subscribe to media changes // Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$ this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll)) .pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => .subscribe(({ matchingAliases }) => {
{
// Check if the screen is small // Check if the screen is small
this.isScreenSmall = !matchingAliases.includes('md'); this.isScreenSmall = !matchingAliases.includes('md');
}); });
@ -86,8 +97,7 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
/** /**
* On destroy * On destroy
*/ */
ngOnDestroy(): void ngOnDestroy(): void {
{
// Unsubscribe from all subscriptions // Unsubscribe from all subscriptions
this._unsubscribeAll.next(null); this._unsubscribeAll.next(null);
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
@ -102,13 +112,14 @@ export class ThinLayoutComponent implements OnInit, OnDestroy
* *
* @param name * @param name
*/ */
toggleNavigation(name: string): void toggleNavigation(name: string): void {
{
// Get the navigation // Get the navigation
const navigation = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(name); const navigation =
this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>(
name
);
if ( navigation ) if (navigation) {
{
// Toggle the opened status // Toggle the opened status
navigation.toggle(); navigation.toggle();
} }

View File

@ -1,11 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service'; import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';
import { categories as categoriesData, courses as coursesData, demoCourseSteps as demoCourseStepsData } from 'app/mock-api/apps/academy/data'; import {
categories as categoriesData,
courses as coursesData,
demoCourseSteps as demoCourseStepsData,
} from 'app/mock-api/apps/academy/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AcademyMockApi export class AcademyMockApi {
{
private _categories: any[] = categoriesData; private _categories: any[] = categoriesData;
private _courses: any[] = coursesData; private _courses: any[] = coursesData;
private _demoCourseSteps: any[] = demoCourseStepsData; private _demoCourseSteps: any[] = demoCourseStepsData;
@ -13,8 +16,7 @@ export class AcademyMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -26,15 +28,13 @@ export class AcademyMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Categories - GET // @ Categories - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/academy/categories') .onGet('api/apps/academy/categories')
.reply(() => .reply(() => {
{
// Clone the categories // Clone the categories
const categories = cloneDeep(this._categories); const categories = cloneDeep(this._categories);
@ -47,10 +47,7 @@ export class AcademyMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Courses - GET // @ Courses - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/academy/courses').reply(() => {
.onGet('api/apps/academy/courses')
.reply(() =>
{
// Clone the courses // Clone the courses
const courses = cloneDeep(this._courses); const courses = cloneDeep(this._courses);
@ -62,8 +59,7 @@ export class AcademyMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/academy/courses/course') .onGet('api/apps/academy/courses/course')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id from the params // Get the id from the params
const id = request.params.get('id'); const id = request.params.get('id');
@ -72,16 +68,12 @@ export class AcademyMockApi
const steps = cloneDeep(this._demoCourseSteps); const steps = cloneDeep(this._demoCourseSteps);
// Find the course and attach steps to it // Find the course and attach steps to it
const course = courses.find(item => item.id === id); const course = courses.find((item) => item.id === id);
if ( course ) if (course) {
{
course.steps = steps; course.steps = steps;
} }
return [ return [200, course];
200,
course,
];
}); });
} }
} }

View File

@ -99,7 +99,7 @@ export const courses = [
}, },
{ {
id: 'fad2ab23-1011-4028-9a54-e52179ac4a50', id: 'fad2ab23-1011-4028-9a54-e52179ac4a50',
title : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge', title: "Manage Your Pivotal Cloud Foundry App's Using Apigee Edge",
slug: 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge', slug: 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge',
description: 'Introductory course for Pivotal Cloud Foundry App', description: 'Introductory course for Pivotal Cloud Foundry App',
category: 'cloud', category: 'cloud',
@ -176,7 +176,8 @@ export const courses = [
id: 'fcbfedbf-6187-4b3b-89d3-1a7cb4e11616', id: 'fcbfedbf-6187-4b3b-89d3-1a7cb4e11616',
title: 'Personalize Your iOS App with Firebase User Management', title: 'Personalize Your iOS App with Firebase User Management',
slug: 'personalize-your-ios-app-with-firebase-user-management', slug: 'personalize-your-ios-app-with-firebase-user-management',
description: 'Dive deep into User Management on iOS apps using Firebase', description:
'Dive deep into User Management on iOS apps using Firebase',
category: 'firebase', category: 'firebase',
duration: 90, duration: 90,
totalSteps: 11, totalSteps: 11,
@ -206,7 +207,8 @@ export const courses = [
id: '02992ac9-d1a3-4167-b70e-8a1d5b5ba253', id: '02992ac9-d1a3-4167-b70e-8a1d5b5ba253',
title: 'Building Beautiful UIs with Flutter', title: 'Building Beautiful UIs with Flutter',
slug: 'building-beautiful-uis-with-flutter', slug: 'building-beautiful-uis-with-flutter',
description: 'Dive deep into Flutter\'s hidden secrets for creating beautiful UIs', description:
"Dive deep into Flutter's hidden secrets for creating beautiful UIs",
category: 'web', category: 'web',
duration: 90, duration: 90,
totalSteps: 11, totalSteps: 11,
@ -236,7 +238,8 @@ export const courses = [
id: '65e0a0e0-d8c0-4117-a3cb-eb74f8e28809', id: '65e0a0e0-d8c0-4117-a3cb-eb74f8e28809',
title: 'Simulating a Thread Network Using OpenThread', title: 'Simulating a Thread Network Using OpenThread',
slug: 'simulating-a-thread-network-using-openthread', slug: 'simulating-a-thread-network-using-openthread',
description: 'Introductory course for OpenThread and Simulating a Thread Network', description:
'Introductory course for OpenThread and Simulating a Thread Network',
category: 'web', category: 'web',
duration: 45, duration: 45,
totalSteps: 11, totalSteps: 11,
@ -665,7 +668,8 @@ export const demoCourseSteps = [
{ {
order: 2, order: 2,
title: 'Create a Firebase project and Set up your app', title: 'Create a Firebase project and Set up your app',
subtitle: 'How to create a basic Firebase project and how to run it locally', subtitle:
'How to create a basic Firebase project and how to run it locally',
content: `<h2 class="text-2xl sm:text-3xl">Create a Firebase project and Set up your app</h1> ${demoCourseContent}`, content: `<h2 class="text-2xl sm:text-3xl">Create a Firebase project and Set up your app</h1> ${demoCourseContent}`,
}, },
{ {
@ -689,7 +693,8 @@ export const demoCourseSteps = [
{ {
order: 6, order: 6,
title: 'Import the Cloud Functions and Firebase Admin modules', title: 'Import the Cloud Functions and Firebase Admin modules',
subtitle: 'Create your first Function and run it to administer your app', subtitle:
'Create your first Function and run it to administer your app',
content: `<h2 class="text-2xl sm:text-3xl">Import the Cloud Functions and Firebase Admin modules</h1> ${demoCourseContent}`, content: `<h2 class="text-2xl sm:text-3xl">Import the Cloud Functions and Firebase Admin modules</h1> ${demoCourseContent}`,
}, },
{ {

View File

@ -1,11 +1,15 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService } from '@fuse/lib/mock-api'; import { FuseMockApiService } from '@fuse/lib/mock-api';
import { chats as chatsData, contacts as contactsData, messages as messagesData, profile as profileData } from 'app/mock-api/apps/chat/data'; import {
chats as chatsData,
contacts as contactsData,
messages as messagesData,
profile as profileData,
} from 'app/mock-api/apps/chat/data';
import { assign, cloneDeep, omit } from 'lodash-es'; import { assign, cloneDeep, omit } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ChatMockApi export class ChatMockApi {
{
private _chats: any[] = chatsData; private _chats: any[] = chatsData;
private _contacts: any[] = contactsData; private _contacts: any[] = contactsData;
private _messages: any[] = messagesData; private _messages: any[] = messagesData;
@ -14,21 +18,25 @@ export class ChatMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
// Modify the chats array to attach certain data to it // Modify the chats array to attach certain data to it
this._chats = this._chats.map(chat => ({ this._chats = this._chats.map((chat) => ({
...chat, ...chat,
// Get the actual contact object from the id and attach it to the chat // Get the actual contact object from the id and attach it to the chat
contact: this._contacts.find(contact => contact.id === chat.contactId), contact: this._contacts.find(
(contact) => contact.id === chat.contactId
),
// Since we use same set of messages on all chats, we assign them here. // Since we use same set of messages on all chats, we assign them here.
messages: this._messages.map(message => ({ messages: this._messages.map((message) => ({
...message, ...message,
chatId: chat.id, chatId: chat.id,
contactId: message.contactId === 'me' ? this._profile.id : chat.contactId, contactId:
message.contactId === 'me'
? this._profile.id
: chat.contactId,
isMine: message.contactId === 'me', isMine: message.contactId === 'me',
})), })),
})); }));
@ -41,15 +49,11 @@ export class ChatMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Chats - GET // @ Chats - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/chat/chats').reply(() => {
.onGet('api/apps/chat/chats')
.reply(() =>
{
// Clone the chats // Clone the chats
const chats = cloneDeep(this._chats); const chats = cloneDeep(this._chats);
@ -62,8 +66,7 @@ export class ChatMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/chat/chat') .onGet('api/apps/chat/chat')
.reply(({request}) => .reply(({ request }) => {
{
// Get the chat id // Get the chat id
const id = request.params.get('id'); const id = request.params.get('id');
@ -71,7 +74,7 @@ export class ChatMockApi
const chats = cloneDeep(this._chats); const chats = cloneDeep(this._chats);
// Find the chat we need // Find the chat we need
const chat = chats.find(item => item.id === id); const chat = chats.find((item) => item.id === id);
// Return the response // Return the response
return [200, chat]; return [200, chat];
@ -82,8 +85,7 @@ export class ChatMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/chat/chat') .onPatch('api/apps/chat/chat')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and chat // Get the id and chat
const id = request.body.id; const id = request.body.id;
const chat = cloneDeep(request.body.chat); const chat = cloneDeep(request.body.chat);
@ -92,10 +94,8 @@ export class ChatMockApi
let updatedChat = null; let updatedChat = null;
// Find the chat and update it // Find the chat and update it
this._chats.forEach((item, index, chats) => this._chats.forEach((item, index, chats) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the chat // Update the chat
chats[index] = assign({}, chats[index], chat); chats[index] = assign({}, chats[index], chat);
@ -111,10 +111,7 @@ export class ChatMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Contacts - GET // @ Contacts - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/chat/contacts').reply(() => {
.onGet('api/apps/chat/contacts')
.reply(() =>
{
// Clone the contacts // Clone the contacts
let contacts = cloneDeep(this._contacts); let contacts = cloneDeep(this._contacts);
@ -122,7 +119,9 @@ export class ChatMockApi
contacts.sort((a, b) => a.name.localeCompare(b.name)); contacts.sort((a, b) => a.name.localeCompare(b.name));
// Omit details and attachments from contacts // Omit details and attachments from contacts
contacts = contacts.map(contact => omit(contact, ['details', 'attachments'])); contacts = contacts.map((contact) =>
omit(contact, ['details', 'attachments'])
);
// Return the response // Return the response
return [200, contacts]; return [200, contacts];
@ -133,8 +132,7 @@ export class ChatMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/chat/contact') .onGet('api/apps/chat/contact')
.reply(({request}) => .reply(({ request }) => {
{
// Get the contact id // Get the contact id
const id = request.params.get('id'); const id = request.params.get('id');
@ -142,7 +140,7 @@ export class ChatMockApi
const contacts = cloneDeep(this._contacts); const contacts = cloneDeep(this._contacts);
// Find the contact // Find the contact
const contact = contacts.find(item => item.id === id); const contact = contacts.find((item) => item.id === id);
// Return the response // Return the response
return [200, contact]; return [200, contact];
@ -151,10 +149,7 @@ export class ChatMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Profile - GET // @ Profile - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/chat/profile').reply(() => {
.onGet('api/apps/chat/profile')
.reply(() =>
{
// Clone the profile // Clone the profile
const profile = cloneDeep(this._profile); const profile = cloneDeep(this._profile);

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,15 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import { contacts as contactsData, countries as countriesData, tags as tagsData } from 'app/mock-api/apps/contacts/data'; import {
contacts as contactsData,
countries as countriesData,
tags as tagsData,
} from 'app/mock-api/apps/contacts/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
import { from, map } from 'rxjs'; import { from, map } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ContactsMockApi export class ContactsMockApi {
{
private _contacts: any[] = contactsData; private _contacts: any[] = contactsData;
private _countries: any[] = countriesData; private _countries: any[] = countriesData;
private _tags: any[] = tagsData; private _tags: any[] = tagsData;
@ -14,8 +17,7 @@ export class ContactsMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -27,15 +29,11 @@ export class ContactsMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Contacts - GET // @ Contacts - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/contacts/all').reply(() => {
.onGet('api/apps/contacts/all')
.reply(() =>
{
// Clone the contacts // Clone the contacts
const contacts = cloneDeep(this._contacts); const contacts = cloneDeep(this._contacts);
@ -51,8 +49,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/contacts/search') .onGet('api/apps/contacts/search')
.reply(({request}) => .reply(({ request }) => {
{
// Get the search query // Get the search query
const query = request.params.get('query'); const query = request.params.get('query');
@ -60,10 +57,15 @@ export class ContactsMockApi
let contacts = cloneDeep(this._contacts); let contacts = cloneDeep(this._contacts);
// If the query exists... // If the query exists...
if ( query ) if (query) {
{
// Filter the contacts // Filter the contacts
contacts = contacts.filter(contact => contact.name && contact.name.toLowerCase().includes(query.toLowerCase())); contacts = contacts.filter(
(contact) =>
contact.name &&
contact.name
.toLowerCase()
.includes(query.toLowerCase())
);
} }
// Sort the contacts by the name field by default // Sort the contacts by the name field by default
@ -78,8 +80,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/contacts/contact') .onGet('api/apps/contacts/contact')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id from the params // Get the id from the params
const id = request.params.get('id'); const id = request.params.get('id');
@ -87,7 +88,7 @@ export class ContactsMockApi
const contacts = cloneDeep(this._contacts); const contacts = cloneDeep(this._contacts);
// Find the contact // Find the contact
const contact = contacts.find(item => item.id === id); const contact = contacts.find((item) => item.id === id);
// Return the response // Return the response
return [200, contact]; return [200, contact];
@ -98,8 +99,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/contacts/contact') .onPost('api/apps/contacts/contact')
.reply(() => .reply(() => {
{
// Generate a new contact // Generate a new contact
const newContact = { const newContact = {
id: FuseMockApiUtils.guid(), id: FuseMockApiUtils.guid(),
@ -129,8 +129,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/contacts/contact') .onPatch('api/apps/contacts/contact')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and contact // Get the id and contact
const id = request.body.id; const id = request.body.id;
const contact = cloneDeep(request.body.contact); const contact = cloneDeep(request.body.contact);
@ -139,10 +138,8 @@ export class ContactsMockApi
let updatedContact = null; let updatedContact = null;
// Find the contact and update it // Find the contact and update it
this._contacts.forEach((item, index, contacts) => this._contacts.forEach((item, index, contacts) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the contact // Update the contact
contacts[index] = assign({}, contacts[index], contact); contacts[index] = assign({}, contacts[index], contact);
@ -160,16 +157,13 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/contacts/contact') .onDelete('api/apps/contacts/contact')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the contact and delete it // Find the contact and delete it
this._contacts.forEach((item, index) => this._contacts.forEach((item, index) => {
{ if (item.id === id) {
if ( item.id === id )
{
this._contacts.splice(index, 1); this._contacts.splice(index, 1);
} }
}); });
@ -197,8 +191,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/contacts/tag') .onPost('api/apps/contacts/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the tag // Get the tag
const newTag = cloneDeep(request.body.tag); const newTag = cloneDeep(request.body.tag);
@ -217,8 +210,7 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/contacts/tag') .onPatch('api/apps/contacts/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and tag // Get the id and tag
const id = request.body.id; const id = request.body.id;
const tag = cloneDeep(request.body.tag); const tag = cloneDeep(request.body.tag);
@ -227,10 +219,8 @@ export class ContactsMockApi
let updatedTag = null; let updatedTag = null;
// Find the tag and update it // Find the tag and update it
this._tags.forEach((item, index, tags) => this._tags.forEach((item, index, tags) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the tag // Update the tag
tags[index] = assign({}, tags[index], tag); tags[index] = assign({}, tags[index], tag);
@ -248,26 +238,24 @@ export class ContactsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/contacts/tag') .onDelete('api/apps/contacts/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the tag and delete it // Find the tag and delete it
this._tags.forEach((item, index) => this._tags.forEach((item, index) => {
{ if (item.id === id) {
if ( item.id === id )
{
this._tags.splice(index, 1); this._tags.splice(index, 1);
} }
}); });
// Get the contacts that have the tag // Get the contacts that have the tag
const contactsWithTag = this._contacts.filter(contact => contact.tags.indexOf(id) > -1); const contactsWithTag = this._contacts.filter(
(contact) => contact.tags.indexOf(id) > -1
);
// Iterate through them and delete the tag // Iterate through them and delete the tag
contactsWithTag.forEach((contact) => contactsWithTag.forEach((contact) => {
{
contact.tags.splice(contact.tags.indexOf(id), 1); contact.tags.splice(contact.tags.indexOf(id), 1);
}); });
@ -285,34 +273,27 @@ export class ContactsMockApi
* @param file * @param file
*/ */
const readAsDataURL = (file: File): Promise<any> => const readAsDataURL = (file: File): Promise<any> =>
// Return a new promise // Return a new promise
new Promise((resolve, reject) => new Promise((resolve, reject) => {
{
// Create a new reader // Create a new reader
const reader = new FileReader(); const reader = new FileReader();
// Resolve the promise on success // Resolve the promise on success
reader.onload = (): void => reader.onload = (): void => {
{
resolve(reader.result); resolve(reader.result);
}; };
// Reject the promise on error // Reject the promise on error
reader.onerror = (e): void => reader.onerror = (e): void => {
{
reject(e); reject(e);
}; };
// Read the file as the // Read the file as the
reader.readAsDataURL(file); reader.readAsDataURL(file);
}) });
;
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/contacts/avatar') .onPost('api/apps/contacts/avatar')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and avatar // Get the id and avatar
const id = request.body.id; const id = request.body.id;
const avatar = request.body.avatar; const avatar = request.body.avatar;
@ -327,13 +308,10 @@ export class ContactsMockApi
// the src attribute of the img tag works with both image urls // the src attribute of the img tag works with both image urls
// and encoded images. // and encoded images.
return from(readAsDataURL(avatar)).pipe( return from(readAsDataURL(avatar)).pipe(
map((path) => map((path) => {
{
// Find the contact and update it // Find the contact and update it
this._contacts.forEach((item, index, contacts) => this._contacts.forEach((item, index, contacts) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the avatar // Update the avatar
contacts[index].avatar = path; contacts[index].avatar = path;
@ -344,7 +322,7 @@ export class ContactsMockApi
// Return the response // Return the response
return [200, updatedContact]; return [200, updatedContact];
}), })
); );
}); });
} }

View File

@ -32,9 +32,7 @@ export const contacts = [
birthday: '1975-01-10T12:00:00.000Z', birthday: '1975-01-10T12:00:00.000Z',
address: '279 Independence Avenue, Calvary, Guam, PO4127', address: '279 Independence Avenue, Calvary, Guam, PO4127',
notes: '<p>Do incididunt cillum duis eu pariatur enim proident minim officia amet proident consequat consequat qui consequat magna magna occaecat aliquip culpa pariatur velit nisi nostrud irure eu ullamco exercitation sint.</p><p>Cillum deserunt laborum laborum quis nisi enim et aliquip labore excepteur in excepteur labore amet in ipsum ipsum nostrud deserunt lorem nisi voluptate dolor minim enim ut eu cupidatat enim.</p>', notes: '<p>Do incididunt cillum duis eu pariatur enim proident minim officia amet proident consequat consequat qui consequat magna magna occaecat aliquip culpa pariatur velit nisi nostrud irure eu ullamco exercitation sint.</p><p>Cillum deserunt laborum laborum quis nisi enim et aliquip labore excepteur in excepteur labore amet in ipsum ipsum nostrud deserunt lorem nisi voluptate dolor minim enim ut eu cupidatat enim.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'beec5287-ed50-4504-858a-5dc3f8ce6935', id: 'beec5287-ed50-4504-858a-5dc3f8ce6935',
@ -63,9 +61,7 @@ export const contacts = [
birthday: '1994-12-05T12:00:00.000Z', birthday: '1994-12-05T12:00:00.000Z',
address: '856 Woodside Avenue, Alfarata, Iowa, PO4992', address: '856 Woodside Avenue, Alfarata, Iowa, PO4992',
notes: '<p>Consequat duis ullamco sint elit pariatur esse dolore nostrud consequat lorem duis sunt veniam ipsum exercitation eiusmod consequat nisi quis voluptate quis officia irure fugiat ex duis eu amet ex.</p><p>Irure est nisi dolor culpa sunt nulla irure lorem adipisicing non do consequat deserunt et ea eu non reprehenderit fugiat ex elit nulla sunt quis voluptate enim nulla aliquip veniam.</p>', notes: '<p>Consequat duis ullamco sint elit pariatur esse dolore nostrud consequat lorem duis sunt veniam ipsum exercitation eiusmod consequat nisi quis voluptate quis officia irure fugiat ex duis eu amet ex.</p><p>Irure est nisi dolor culpa sunt nulla irure lorem adipisicing non do consequat deserunt et ea eu non reprehenderit fugiat ex elit nulla sunt quis voluptate enim nulla aliquip veniam.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '9d3f0e7f-dcbd-4e56-a5e8-87b8154e9edf', id: '9d3f0e7f-dcbd-4e56-a5e8-87b8154e9edf',
@ -94,9 +90,7 @@ export const contacts = [
birthday: '1988-05-26T12:00:00.000Z', birthday: '1988-05-26T12:00:00.000Z',
address: '943 Adler Place, Hamilton, South Dakota, PO5592', address: '943 Adler Place, Hamilton, South Dakota, PO5592',
notes: '<p>Est amet in adipisicing ex excepteur ullamco est lorem adipisicing veniam reprehenderit elit commodo cillum commodo eu officia fugiat id reprehenderit sunt mollit eiusmod dolor fugiat ad do esse aliquip.</p><p>Mollit amet adipisicing enim est est commodo sint et eu nulla in laboris ipsum aliqua elit aliqua adipisicing ea nulla nulla consectetur velit laborum labore ullamco eu sit consectetur velit.</p>', notes: '<p>Est amet in adipisicing ex excepteur ullamco est lorem adipisicing veniam reprehenderit elit commodo cillum commodo eu officia fugiat id reprehenderit sunt mollit eiusmod dolor fugiat ad do esse aliquip.</p><p>Mollit amet adipisicing enim est est commodo sint et eu nulla in laboris ipsum aliqua elit aliqua adipisicing ea nulla nulla consectetur velit laborum labore ullamco eu sit consectetur velit.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '42a5da95-5e6d-42fd-a09d-de755d123a47', id: '42a5da95-5e6d-42fd-a09d-de755d123a47',
@ -134,9 +128,7 @@ export const contacts = [
birthday: '1968-08-13T12:00:00.000Z', birthday: '1968-08-13T12:00:00.000Z',
address: '334 Sandford Street, Savage, Virgin Islands, PO1858', address: '334 Sandford Street, Savage, Virgin Islands, PO1858',
notes: '<p>Consequat eu aliquip dolor non consequat laborum ad non labore cillum consectetur quis dolore do ea nulla incididunt proident ea eiusmod in do qui eiusmod et irure dolor ea adipisicing.</p><p>Reprehenderit occaecat nostrud ad aliquip commodo amet velit id ut minim dolor mollit mollit in eiusmod voluptate lorem nisi labore culpa elit proident laborum ipsum occaecat esse sint nostrud esse.</p>', notes: '<p>Consequat eu aliquip dolor non consequat laborum ad non labore cillum consectetur quis dolore do ea nulla incididunt proident ea eiusmod in do qui eiusmod et irure dolor ea adipisicing.</p><p>Reprehenderit occaecat nostrud ad aliquip commodo amet velit id ut minim dolor mollit mollit in eiusmod voluptate lorem nisi labore culpa elit proident laborum ipsum occaecat esse sint nostrud esse.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'a7806ced-03f1-4197-8b30-00bdd463366b', id: 'a7806ced-03f1-4197-8b30-00bdd463366b',
@ -166,9 +158,7 @@ export const contacts = [
birthday: '1983-12-22T12:00:00.000Z', birthday: '1983-12-22T12:00:00.000Z',
address: '487 Hamilton Walk, Bergoo, American Samoa, PO5616', address: '487 Hamilton Walk, Bergoo, American Samoa, PO5616',
notes: '<p>Id eiusmod deserunt amet lorem commodo consequat nostrud magna aliquip ex et pariatur labore non elit ad ad nulla culpa reprehenderit enim magna aliqua enim pariatur occaecat sint do lorem.</p><p>Adipisicing ut est nulla nisi cupidatat consequat aliqua et esse in voluptate amet eiusmod ut esse ea do irure commodo aute culpa amet consequat id adipisicing et incididunt ut duis.</p>', notes: '<p>Id eiusmod deserunt amet lorem commodo consequat nostrud magna aliquip ex et pariatur labore non elit ad ad nulla culpa reprehenderit enim magna aliqua enim pariatur occaecat sint do lorem.</p><p>Adipisicing ut est nulla nisi cupidatat consequat aliqua et esse in voluptate amet eiusmod ut esse ea do irure commodo aute culpa amet consequat id adipisicing et incididunt ut duis.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: 'f4ad15d9-5a24-463a-88ea-6189d6bb3a53', id: 'f4ad15d9-5a24-463a-88ea-6189d6bb3a53',
@ -207,9 +197,7 @@ export const contacts = [
birthday: '1963-08-24T12:00:00.000Z', birthday: '1963-08-24T12:00:00.000Z',
address: '610 Harbor Lane, Cascades, Minnesota, PO8639', address: '610 Harbor Lane, Cascades, Minnesota, PO8639',
notes: '<p>Cillum enim eiusmod dolor aliqua ipsum exercitation sint aliqua lorem dolore id velit sint velit labore cupidatat minim cupidatat elit est magna eu proident eiusmod non pariatur est esse pariatur.</p><p>Sint do enim officia velit pariatur excepteur commodo adipisicing labore elit velit velit id exercitation excepteur veniam reprehenderit sint nulla duis ad incididunt cillum in in labore laboris magna esse.</p>', notes: '<p>Cillum enim eiusmod dolor aliqua ipsum exercitation sint aliqua lorem dolore id velit sint velit labore cupidatat minim cupidatat elit est magna eu proident eiusmod non pariatur est esse pariatur.</p><p>Sint do enim officia velit pariatur excepteur commodo adipisicing labore elit velit velit id exercitation excepteur veniam reprehenderit sint nulla duis ad incididunt cillum in in labore laboris magna esse.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '780d0111-5e5c-4694-8d1d-0ea421971fbf', id: '780d0111-5e5c-4694-8d1d-0ea421971fbf',
@ -239,9 +227,7 @@ export const contacts = [
birthday: '1973-09-25T12:00:00.000Z', birthday: '1973-09-25T12:00:00.000Z',
address: '428 Newport Street, Neahkahnie, Arkansas, PO8324', address: '428 Newport Street, Neahkahnie, Arkansas, PO8324',
notes: '<p>Incididunt lorem proident est anim amet nulla do nulla ea anim ullamco ea amet voluptate laboris do elit elit consequat in esse in dolor enim irure ut irure ad commodo.</p><p>Aliqua dolore nulla sunt ad nostrud aute labore occaecat non amet nulla adipisicing sint eu lorem velit sint do sint adipisicing esse adipisicing anim culpa quis dolor non magna ea.</p>', notes: '<p>Incididunt lorem proident est anim amet nulla do nulla ea anim ullamco ea amet voluptate laboris do elit elit consequat in esse in dolor enim irure ut irure ad commodo.</p><p>Aliqua dolore nulla sunt ad nostrud aute labore occaecat non amet nulla adipisicing sint eu lorem velit sint do sint adipisicing esse adipisicing anim culpa quis dolor non magna ea.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'bf172879-423a-4fd6-8df3-6d1938bbfe1f', id: 'bf172879-423a-4fd6-8df3-6d1938bbfe1f',
@ -280,9 +266,7 @@ export const contacts = [
birthday: '1988-07-27T12:00:00.000Z', birthday: '1988-07-27T12:00:00.000Z',
address: '384 Polhemus Place, Dalton, Palau, PO6038', address: '384 Polhemus Place, Dalton, Palau, PO6038',
notes: '<p>Eu veniam consectetur eiusmod anim sint anim consectetur do consectetur aliqua cillum proident fugiat do in aliqua ipsum id consequat commodo qui officia adipisicing ullamco occaecat laboris proident incididunt exercitation.</p><p>Velit ullamco magna aute proident irure ut magna ullamco labore dolor deserunt deserunt tempor fugiat ex ullamco do sunt veniam reprehenderit officia elit duis sint ut proident pariatur est reprehenderit.</p>', notes: '<p>Eu veniam consectetur eiusmod anim sint anim consectetur do consectetur aliqua cillum proident fugiat do in aliqua ipsum id consequat commodo qui officia adipisicing ullamco occaecat laboris proident incididunt exercitation.</p><p>Velit ullamco magna aute proident irure ut magna ullamco labore dolor deserunt deserunt tempor fugiat ex ullamco do sunt veniam reprehenderit officia elit duis sint ut proident pariatur est reprehenderit.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '1eaa3213-ece2-4ba6-8e15-eb36ca388f50', id: '1eaa3213-ece2-4ba6-8e15-eb36ca388f50',
@ -311,9 +295,7 @@ export const contacts = [
birthday: '1989-12-15T12:00:00.000Z', birthday: '1989-12-15T12:00:00.000Z',
address: '945 Jerome Avenue, Riceville, North Carolina, PO1625', address: '945 Jerome Avenue, Riceville, North Carolina, PO1625',
notes: '<p>Excepteur ullamco aute aliqua reprehenderit ullamco do anim ut ut veniam et ut et ut commodo aliqua consequat occaecat fugiat dolor labore proident ipsum ad culpa est cillum aliqua reprehenderit.</p><p>Amet aliqua sint laboris in aute nostrud voluptate tempor ea tempor laborum tempor culpa dolore aliqua nulla dolore ad enim id cupidatat nostrud nostrud amet non velit id fugiat lorem.</p>', notes: '<p>Excepteur ullamco aute aliqua reprehenderit ullamco do anim ut ut veniam et ut et ut commodo aliqua consequat occaecat fugiat dolor labore proident ipsum ad culpa est cillum aliqua reprehenderit.</p><p>Amet aliqua sint laboris in aute nostrud voluptate tempor ea tempor laborum tempor culpa dolore aliqua nulla dolore ad enim id cupidatat nostrud nostrud amet non velit id fugiat lorem.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: 'abd9e78b-9e96-428f-b3ff-4d934c401bee', id: 'abd9e78b-9e96-428f-b3ff-4d934c401bee',
@ -347,9 +329,7 @@ export const contacts = [
birthday: '1980-06-28T12:00:00.000Z', birthday: '1980-06-28T12:00:00.000Z',
address: '428 Varanda Place, Veyo, Oklahoma, PO6188', address: '428 Varanda Place, Veyo, Oklahoma, PO6188',
notes: '<p>Laboris commodo consequat duis dolor ullamco nisi sunt ipsum nisi elit dolore aute sint tempor qui ad sit aliqua laboris consequat dolore aliqua est deserunt irure cillum tempor ut veniam.</p><p>Eiusmod nulla ex esse in deserunt consectetur non qui cillum reprehenderit magna sit ipsum lorem aute consequat sint magna id laboris velit adipisicing non ipsum ipsum sint velit ex non.</p>', notes: '<p>Laboris commodo consequat duis dolor ullamco nisi sunt ipsum nisi elit dolore aute sint tempor qui ad sit aliqua laboris consequat dolore aliqua est deserunt irure cillum tempor ut veniam.</p><p>Eiusmod nulla ex esse in deserunt consectetur non qui cillum reprehenderit magna sit ipsum lorem aute consequat sint magna id laboris velit adipisicing non ipsum ipsum sint velit ex non.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: 'efae92cc-3bd1-4c6a-a395-b6760c69bd55', id: 'efae92cc-3bd1-4c6a-a395-b6760c69bd55',
@ -384,9 +364,7 @@ export const contacts = [
birthday: '1990-07-26T12:00:00.000Z', birthday: '1990-07-26T12:00:00.000Z',
address: '609 Greenpoint Avenue, Beason, Vermont, PO5229', address: '609 Greenpoint Avenue, Beason, Vermont, PO5229',
notes: '<p>Exercitation tempor laboris dolor deserunt nulla et nisi ullamco minim duis sint nulla sint deserunt irure excepteur nostrud ipsum duis enim sit exercitation eiusmod tempor commodo excepteur mollit cupidatat fugiat.</p><p>Deserunt est dolore nulla laborum consequat veniam elit lorem do exercitation incididunt ea ad laboris lorem ipsum ex incididunt nostrud ipsum laborum et nostrud minim aute velit incididunt quis quis.</p>', notes: '<p>Exercitation tempor laboris dolor deserunt nulla et nisi ullamco minim duis sint nulla sint deserunt irure excepteur nostrud ipsum duis enim sit exercitation eiusmod tempor commodo excepteur mollit cupidatat fugiat.</p><p>Deserunt est dolore nulla laborum consequat veniam elit lorem do exercitation incididunt ea ad laboris lorem ipsum ex incididunt nostrud ipsum laborum et nostrud minim aute velit incididunt quis quis.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: 'bde636a7-c3d2-4bff-939a-aab11df1516b', id: 'bde636a7-c3d2-4bff-939a-aab11df1516b',
@ -421,9 +399,7 @@ export const contacts = [
birthday: '1994-01-10T12:00:00.000Z', birthday: '1994-01-10T12:00:00.000Z',
address: '183 Crosby Avenue, Blanco, Mississippi, PO3463', address: '183 Crosby Avenue, Blanco, Mississippi, PO3463',
notes: '<p>Mollit qui amet in esse ipsum nostrud cupidatat occaecat proident aliquip non mollit commodo ex labore enim culpa dolor aute occaecat cillum sit excepteur tempor culpa nostrud nulla qui commodo.</p><p>Labore nulla id excepteur non velit adipisicing tempor reprehenderit cillum sint do consectetur laboris ut proident pariatur quis aute ad dolor quis labore labore nostrud sunt elit proident enim aliqua.</p>', notes: '<p>Mollit qui amet in esse ipsum nostrud cupidatat occaecat proident aliquip non mollit commodo ex labore enim culpa dolor aute occaecat cillum sit excepteur tempor culpa nostrud nulla qui commodo.</p><p>Labore nulla id excepteur non velit adipisicing tempor reprehenderit cillum sint do consectetur laboris ut proident pariatur quis aute ad dolor quis labore labore nostrud sunt elit proident enim aliqua.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '6519600a-5eaa-45f8-8bed-c46fddb3b26a', id: '6519600a-5eaa-45f8-8bed-c46fddb3b26a',
@ -457,9 +433,7 @@ export const contacts = [
birthday: '1980-12-03T12:00:00.000Z', birthday: '1980-12-03T12:00:00.000Z',
address: '736 Glen Street, Kaka, West Virginia, PO9350', address: '736 Glen Street, Kaka, West Virginia, PO9350',
notes: '<p>Laboris consequat est anim quis quis eiusmod ipsum non quis fugiat anim culpa non elit mollit pariatur veniam nisi irure velit dolore dolor proident nisi deserunt culpa nisi et laborum.</p><p>Eiusmod eu esse ipsum voluptate excepteur ipsum et proident cupidatat sint sunt aliquip lorem culpa esse et dolor fugiat sit est id consectetur sint et ea pariatur occaecat nulla irure.</p>', notes: '<p>Laboris consequat est anim quis quis eiusmod ipsum non quis fugiat anim culpa non elit mollit pariatur veniam nisi irure velit dolore dolor proident nisi deserunt culpa nisi et laborum.</p><p>Eiusmod eu esse ipsum voluptate excepteur ipsum et proident cupidatat sint sunt aliquip lorem culpa esse et dolor fugiat sit est id consectetur sint et ea pariatur occaecat nulla irure.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '6d80a6f6-2884-4ac4-9c73-06b82c220017', id: '6d80a6f6-2884-4ac4-9c73-06b82c220017',
@ -498,9 +472,7 @@ export const contacts = [
birthday: '1975-08-31T12:00:00.000Z', birthday: '1975-08-31T12:00:00.000Z',
address: '547 Revere Place, Hoehne, New Hampshire, PO2125', address: '547 Revere Place, Hoehne, New Hampshire, PO2125',
notes: '<p>Duis incididunt minim nisi sit qui dolor aliquip quis ipsum id amet occaecat sit ullamco minim velit est eiusmod anim proident consectetur non reprehenderit ea reprehenderit dolore in nisi eiusmod.</p><p>Ut commodo aliqua non ut proident velit et commodo voluptate eu mollit dolor veniam ipsum velit aute esse est adipisicing id aliqua nostrud nostrud nisi enim officia eiusmod in enim.</p>', notes: '<p>Duis incididunt minim nisi sit qui dolor aliquip quis ipsum id amet occaecat sit ullamco minim velit est eiusmod anim proident consectetur non reprehenderit ea reprehenderit dolore in nisi eiusmod.</p><p>Ut commodo aliqua non ut proident velit et commodo voluptate eu mollit dolor veniam ipsum velit aute esse est adipisicing id aliqua nostrud nostrud nisi enim officia eiusmod in enim.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '35190d23-036e-44ef-b545-cc744c626edd', id: '35190d23-036e-44ef-b545-cc744c626edd',
@ -535,9 +507,7 @@ export const contacts = [
birthday: '1994-09-07T12:00:00.000Z', birthday: '1994-09-07T12:00:00.000Z',
address: '480 Chase Court, Edinburg, Kansas, PO5357', address: '480 Chase Court, Edinburg, Kansas, PO5357',
notes: '<p>Lorem ex amet anim anim qui consequat ullamco consectetur et voluptate in velit dolore culpa pariatur amet enim ut non magna duis qui excepteur esse ullamco velit fugiat aute dolor.</p><p>Reprehenderit ullamco veniam sit laborum nulla sunt excepteur eiusmod anim eu ullamco tempor est qui adipisicing sit fugiat voluptate minim non incididunt quis ipsum et exercitation officia laborum incididunt nostrud.</p>', notes: '<p>Lorem ex amet anim anim qui consequat ullamco consectetur et voluptate in velit dolore culpa pariatur amet enim ut non magna duis qui excepteur esse ullamco velit fugiat aute dolor.</p><p>Reprehenderit ullamco veniam sit laborum nulla sunt excepteur eiusmod anim eu ullamco tempor est qui adipisicing sit fugiat voluptate minim non incididunt quis ipsum et exercitation officia laborum incididunt nostrud.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'b018c194-68ec-4915-ab56-e9f3bd2d98db', id: 'b018c194-68ec-4915-ab56-e9f3bd2d98db',
@ -576,9 +546,7 @@ export const contacts = [
birthday: '1993-12-31T12:00:00.000Z', birthday: '1993-12-31T12:00:00.000Z',
address: '595 Howard Place, Convent, Rhode Island, PO6993', address: '595 Howard Place, Convent, Rhode Island, PO6993',
notes: '<p>Lorem nostrud cillum non cillum nisi eu labore anim ipsum consequat consectetur sunt ipsum ipsum ad culpa laborum in ea exercitation quis voluptate velit id elit labore cillum cillum consectetur.</p><p>Ullamco ullamco nostrud aute pariatur nulla officia proident magna laborum dolor reprehenderit ullamco in reprehenderit veniam aliqua elit magna voluptate amet ut minim in labore irure culpa consequat sit pariatur.</p>', notes: '<p>Lorem nostrud cillum non cillum nisi eu labore anim ipsum consequat consectetur sunt ipsum ipsum ad culpa laborum in ea exercitation quis voluptate velit id elit labore cillum cillum consectetur.</p><p>Ullamco ullamco nostrud aute pariatur nulla officia proident magna laborum dolor reprehenderit ullamco in reprehenderit veniam aliqua elit magna voluptate amet ut minim in labore irure culpa consequat sit pariatur.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'b7c355e9-e003-467e-82d2-4f6978c1a696', id: 'b7c355e9-e003-467e-82d2-4f6978c1a696',
@ -607,9 +575,7 @@ export const contacts = [
birthday: '1976-09-30T12:00:00.000Z', birthday: '1976-09-30T12:00:00.000Z',
address: '971 Conover Street, Statenville, Louisiana, PO6622', address: '971 Conover Street, Statenville, Louisiana, PO6622',
notes: '<p>Pariatur fugiat labore aliquip aute in adipisicing veniam et consequat magna nulla laboris eiusmod eu esse cupidatat ipsum amet sint est anim lorem consequat eiusmod sit aliquip consequat nisi duis.</p><p>Est esse excepteur non amet reprehenderit cillum ullamco ex excepteur laboris excepteur dolor magna enim consequat lorem commodo ipsum elit ea veniam non quis id nisi esse tempor enim ut.</p>', notes: '<p>Pariatur fugiat labore aliquip aute in adipisicing veniam et consequat magna nulla laboris eiusmod eu esse cupidatat ipsum amet sint est anim lorem consequat eiusmod sit aliquip consequat nisi duis.</p><p>Est esse excepteur non amet reprehenderit cillum ullamco ex excepteur laboris excepteur dolor magna enim consequat lorem commodo ipsum elit ea veniam non quis id nisi esse tempor enim ut.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: 'cfa07b7c-93d1-42e7-9592-493d9efc78ae', id: 'cfa07b7c-93d1-42e7-9592-493d9efc78ae',
@ -643,9 +609,7 @@ export const contacts = [
birthday: '1976-06-14T12:00:00.000Z', birthday: '1976-06-14T12:00:00.000Z',
address: '197 Marconi Place, Welda, Delaware, PO6061', address: '197 Marconi Place, Welda, Delaware, PO6061',
notes: '<p>Aliqua ea dolor est enim ipsum esse pariatur tempor nulla excepteur aliquip irure fugiat reprehenderit adipisicing ex tempor proident voluptate dolore ea dolore nostrud id incididunt culpa in do occaecat.</p><p>Aute fugiat magna velit enim in duis duis elit ipsum excepteur reprehenderit do ipsum qui cillum aliquip ut occaecat do ea et adipisicing cupidatat voluptate non elit ad aliqua ad.</p>', notes: '<p>Aliqua ea dolor est enim ipsum esse pariatur tempor nulla excepteur aliquip irure fugiat reprehenderit adipisicing ex tempor proident voluptate dolore ea dolore nostrud id incididunt culpa in do occaecat.</p><p>Aute fugiat magna velit enim in duis duis elit ipsum excepteur reprehenderit do ipsum qui cillum aliquip ut occaecat do ea et adipisicing cupidatat voluptate non elit ad aliqua ad.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: '00feeb63-c83a-4655-a37e-a07da10cfa1c', id: '00feeb63-c83a-4655-a37e-a07da10cfa1c',
@ -680,9 +644,7 @@ export const contacts = [
birthday: '1967-11-28T12:00:00.000Z', birthday: '1967-11-28T12:00:00.000Z',
address: '775 Dahill Road, Iberia, California, PO2169', address: '775 Dahill Road, Iberia, California, PO2169',
notes: '<p>Ut occaecat tempor deserunt proident enim ex ullamco ex aliquip mollit aute reprehenderit in occaecat anim aliquip ea laboris anim laboris do non aute aute ea laboris magna sunt sit.</p><p>Ullamco in in minim culpa eiusmod amet consequat consequat magna nisi cillum occaecat irure officia voluptate et eu duis officia nostrud culpa non eiusmod anim sint et anim enim voluptate.</p>', notes: '<p>Ut occaecat tempor deserunt proident enim ex ullamco ex aliquip mollit aute reprehenderit in occaecat anim aliquip ea laboris anim laboris do non aute aute ea laboris magna sunt sit.</p><p>Ullamco in in minim culpa eiusmod amet consequat consequat magna nisi cillum occaecat irure officia voluptate et eu duis officia nostrud culpa non eiusmod anim sint et anim enim voluptate.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '142abf21-e635-4a7d-9330-e57f66adcdbe', id: '142abf21-e635-4a7d-9330-e57f66adcdbe',
@ -717,9 +679,7 @@ export const contacts = [
birthday: '1976-02-15T12:00:00.000Z', birthday: '1976-02-15T12:00:00.000Z',
address: '305 Columbia Street, Dupuyer, Puerto Rico, PO8744', address: '305 Columbia Street, Dupuyer, Puerto Rico, PO8744',
notes: '<p>Proident nulla culpa magna nostrud do aliqua ullamco sit culpa ullamco eu amet culpa laborum enim fugiat non ad quis esse pariatur exercitation lorem incididunt exercitation aliquip labore minim adipisicing.</p><p>Sint ea voluptate tempor irure consequat aute laboris exercitation id minim voluptate aliquip tempor occaecat elit incididunt laboris enim labore sit aute sunt cillum ipsum ad laboris nostrud dolor excepteur.</p>', notes: '<p>Proident nulla culpa magna nostrud do aliqua ullamco sit culpa ullamco eu amet culpa laborum enim fugiat non ad quis esse pariatur exercitation lorem incididunt exercitation aliquip labore minim adipisicing.</p><p>Sint ea voluptate tempor irure consequat aute laboris exercitation id minim voluptate aliquip tempor occaecat elit incididunt laboris enim labore sit aute sunt cillum ipsum ad laboris nostrud dolor excepteur.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: 'e4f255a3-b5dd-45a7-975f-c399604a399a', id: 'e4f255a3-b5dd-45a7-975f-c399604a399a',
@ -749,9 +709,7 @@ export const contacts = [
birthday: '1995-02-16T12:00:00.000Z', birthday: '1995-02-16T12:00:00.000Z',
address: '195 Brooklyn Road, Jeff, Marshall Islands, PO2943', address: '195 Brooklyn Road, Jeff, Marshall Islands, PO2943',
notes: '<p>Ex nulla nisi do cillum consequat amet incididunt eu minim eu ut excepteur ad anim minim aliquip ullamco fugiat labore esse aliquip ea incididunt incididunt nisi officia consectetur dolore minim.</p><p>Et dolor consectetur anim deserunt laborum eu lorem et in nisi et officia nostrud fugiat deserunt aute irure ullamco officia fugiat voluptate exercitation ut deserunt officia nostrud tempor velit pariatur.</p>', notes: '<p>Ex nulla nisi do cillum consequat amet incididunt eu minim eu ut excepteur ad anim minim aliquip ullamco fugiat labore esse aliquip ea incididunt incididunt nisi officia consectetur dolore minim.</p><p>Et dolor consectetur anim deserunt laborum eu lorem et in nisi et officia nostrud fugiat deserunt aute irure ullamco officia fugiat voluptate exercitation ut deserunt officia nostrud tempor velit pariatur.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'ab4f712d-d712-41a8-b567-be4c66c349a3', id: 'ab4f712d-d712-41a8-b567-be4c66c349a3',
@ -781,9 +739,7 @@ export const contacts = [
birthday: '1973-12-19T12:00:00.000Z', birthday: '1973-12-19T12:00:00.000Z',
address: '964 Henry Street, Eureka, Indiana, PO1035', address: '964 Henry Street, Eureka, Indiana, PO1035',
notes: '<p>Non proident pariatur nostrud dolor incididunt occaecat amet officia sunt magna anim dolor labore culpa ut laborum id incididunt officia amet mollit anim ea proident laboris non incididunt incididunt sint.</p><p>Nulla minim consectetur nostrud magna anim irure consectetur labore cupidatat laborum reprehenderit et et adipisicing in qui elit ipsum reprehenderit esse nisi non ipsum exercitation sunt eu elit velit fugiat.</p>', notes: '<p>Non proident pariatur nostrud dolor incididunt occaecat amet officia sunt magna anim dolor labore culpa ut laborum id incididunt officia amet mollit anim ea proident laboris non incididunt incididunt sint.</p><p>Nulla minim consectetur nostrud magna anim irure consectetur labore cupidatat laborum reprehenderit et et adipisicing in qui elit ipsum reprehenderit esse nisi non ipsum exercitation sunt eu elit velit fugiat.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '5d067800-c301-46c6-a7f7-28dc89d9a554', id: '5d067800-c301-46c6-a7f7-28dc89d9a554',
@ -812,9 +768,7 @@ export const contacts = [
birthday: '1985-10-22T12:00:00.000Z', birthday: '1985-10-22T12:00:00.000Z',
address: '622 Dodworth Street, Rose, Arizona, PO9530', address: '622 Dodworth Street, Rose, Arizona, PO9530',
notes: '<p>Lorem laboris excepteur magna pariatur occaecat voluptate pariatur cillum exercitation anim enim elit laborum reprehenderit laboris ad velit ut ipsum irure id ullamco minim sint ipsum occaecat esse tempor ea.</p><p>Pariatur non labore cillum consectetur aute voluptate sint adipisicing nisi laborum culpa nisi elit et amet dolor incididunt velit ex laboris ea reprehenderit eiusmod qui esse veniam labore ea sit.</p>', notes: '<p>Lorem laboris excepteur magna pariatur occaecat voluptate pariatur cillum exercitation anim enim elit laborum reprehenderit laboris ad velit ut ipsum irure id ullamco minim sint ipsum occaecat esse tempor ea.</p><p>Pariatur non labore cillum consectetur aute voluptate sint adipisicing nisi laborum culpa nisi elit et amet dolor incididunt velit ex laboris ea reprehenderit eiusmod qui esse veniam labore ea sit.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: 'c500255a-1173-47d0-a0e4-4944d48fc12a', id: 'c500255a-1173-47d0-a0e4-4944d48fc12a',
@ -839,9 +793,7 @@ export const contacts = [
birthday: '1969-09-05T12:00:00.000Z', birthday: '1969-09-05T12:00:00.000Z',
address: '579 Pooles Lane, Belleview, Montana, PO4106', address: '579 Pooles Lane, Belleview, Montana, PO4106',
notes: '<p>Incididunt labore sunt ullamco in deserunt dolore labore voluptate adipisicing eu id duis eiusmod elit ea ad cillum culpa excepteur labore fugiat excepteur ea culpa labore sit id dolor ullamco.</p><p>Eu eu ex dolore proident nostrud et minim lorem nulla lorem nulla duis velit voluptate nisi cillum anim minim amet dolore officia id cillum in cupidatat ipsum veniam velit dolor.</p>', notes: '<p>Incididunt labore sunt ullamco in deserunt dolore labore voluptate adipisicing eu id duis eiusmod elit ea ad cillum culpa excepteur labore fugiat excepteur ea culpa labore sit id dolor ullamco.</p><p>Eu eu ex dolore proident nostrud et minim lorem nulla lorem nulla duis velit voluptate nisi cillum anim minim amet dolore officia id cillum in cupidatat ipsum veniam velit dolor.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'b62359fd-f2a8-46e6-904e-31052d1cd675', id: 'b62359fd-f2a8-46e6-904e-31052d1cd675',
@ -870,9 +822,7 @@ export const contacts = [
birthday: '1991-09-08T12:00:00.000Z', birthday: '1991-09-08T12:00:00.000Z',
address: '844 Ellery Street, Hondah, Texas, PO1272', address: '844 Ellery Street, Hondah, Texas, PO1272',
notes: '<p>Excepteur consequat magna laborum dolore ut laborum ea excepteur ad officia mollit exercitation sunt tempor amet ex ipsum aliquip cillum mollit amet laborum voluptate ipsum sit esse duis eiusmod adipisicing.</p><p>Non tempor ad pariatur adipisicing excepteur est pariatur aute et velit lorem ut est eu voluptate pariatur ea consectetur excepteur sunt reprehenderit id irure aliqua tempor anim id voluptate culpa.</p>', notes: '<p>Excepteur consequat magna laborum dolore ut laborum ea excepteur ad officia mollit exercitation sunt tempor amet ex ipsum aliquip cillum mollit amet laborum voluptate ipsum sit esse duis eiusmod adipisicing.</p><p>Non tempor ad pariatur adipisicing excepteur est pariatur aute et velit lorem ut est eu voluptate pariatur ea consectetur excepteur sunt reprehenderit id irure aliqua tempor anim id voluptate culpa.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '16b9e696-ea95-4dd8-86c4-3caf705a1dc6', id: '16b9e696-ea95-4dd8-86c4-3caf705a1dc6',
@ -897,9 +847,7 @@ export const contacts = [
birthday: '1982-01-23T12:00:00.000Z', birthday: '1982-01-23T12:00:00.000Z',
address: '614 Herkimer Court, Darrtown, Nebraska, PO9308', address: '614 Herkimer Court, Darrtown, Nebraska, PO9308',
notes: '<p>Culpa labore ullamco veniam est ullamco ipsum culpa excepteur esse esse aliqua nulla ullamco nulla amet consequat tempor aute exercitation do eu do ullamco elit excepteur est anim nisi excepteur.</p><p>Cillum eiusmod cupidatat officia ipsum ullamco adipisicing cillum adipisicing sint exercitation non enim consectetur est esse tempor fugiat sit eiusmod in exercitation enim quis duis dolor amet consequat pariatur dolor.</p>', notes: '<p>Culpa labore ullamco veniam est ullamco ipsum culpa excepteur esse esse aliqua nulla ullamco nulla amet consequat tempor aute exercitation do eu do ullamco elit excepteur est anim nisi excepteur.</p><p>Cillum eiusmod cupidatat officia ipsum ullamco adipisicing cillum adipisicing sint exercitation non enim consectetur est esse tempor fugiat sit eiusmod in exercitation enim quis duis dolor amet consequat pariatur dolor.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '19662ecf-0686-4aad-a46c-24b552eb2ff5', id: '19662ecf-0686-4aad-a46c-24b552eb2ff5',
@ -924,9 +872,7 @@ export const contacts = [
birthday: '1992-03-29T12:00:00.000Z', birthday: '1992-03-29T12:00:00.000Z',
address: '663 Drew Street, Juntura, Georgia, PO9857', address: '663 Drew Street, Juntura, Georgia, PO9857',
notes: '<p>Mollit et amet qui incididunt officia anim est in consectetur qui anim qui labore ea mollit veniam adipisicing ex magna commodo mollit adipisicing sunt commodo laboris labore aliquip deserunt est.</p><p>Cupidatat ut cillum anim reprehenderit ea magna enim fugiat proident anim esse lorem lorem commodo cupidatat pariatur qui commodo nulla aliqua nisi labore in adipisicing minim excepteur do eu amet.</p>', notes: '<p>Mollit et amet qui incididunt officia anim est in consectetur qui anim qui labore ea mollit veniam adipisicing ex magna commodo mollit adipisicing sunt commodo laboris labore aliquip deserunt est.</p><p>Cupidatat ut cillum anim reprehenderit ea magna enim fugiat proident anim esse lorem lorem commodo cupidatat pariatur qui commodo nulla aliqua nisi labore in adipisicing minim excepteur do eu amet.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '26dfe954-8bf3-45ee-b285-1d0a88c8d3ea', id: '26dfe954-8bf3-45ee-b285-1d0a88c8d3ea',
@ -951,9 +897,7 @@ export const contacts = [
birthday: '1961-06-07T12:00:00.000Z', birthday: '1961-06-07T12:00:00.000Z',
address: '762 Troutman Street, Drummond, Oregon, PO6973', address: '762 Troutman Street, Drummond, Oregon, PO6973',
notes: '<p>Laboris dolor incididunt eiusmod deserunt officia labore eu est nulla velit id ex veniam qui fugiat velit irure reprehenderit dolor proident aliquip culpa nisi magna occaecat do nostrud cillum lorem.</p><p>Sit consequat laboris culpa quis laborum lorem ullamco occaecat labore duis ea et consequat pariatur reprehenderit excepteur excepteur exercitation sunt enim amet adipisicing laborum incididunt dolor aliquip culpa ea laboris.</p>', notes: '<p>Laboris dolor incididunt eiusmod deserunt officia labore eu est nulla velit id ex veniam qui fugiat velit irure reprehenderit dolor proident aliquip culpa nisi magna occaecat do nostrud cillum lorem.</p><p>Sit consequat laboris culpa quis laborum lorem ullamco occaecat labore duis ea et consequat pariatur reprehenderit excepteur excepteur exercitation sunt enim amet adipisicing laborum incididunt dolor aliquip culpa ea laboris.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: 'd6462af2-c488-4de7-9b26-3845bd2983f9', id: 'd6462af2-c488-4de7-9b26-3845bd2983f9',
@ -986,11 +930,10 @@ export const contacts = [
title: 'Hotel Manager', title: 'Hotel Manager',
company: 'Xleen', company: 'Xleen',
birthday: '1972-09-13T12:00:00.000Z', birthday: '1972-09-13T12:00:00.000Z',
address : '674 Bryant Street, Grahamtown, Federated States Of Micronesia, PO2757', address:
'674 Bryant Street, Grahamtown, Federated States Of Micronesia, PO2757',
notes: '<p>Velit consequat elit anim qui eu elit aliquip consectetur aliqua cupidatat lorem laboris dolor qui ad laborum adipisicing adipisicing consequat et nostrud ullamco consequat dolore deserunt irure do aliquip non.</p><p>Ipsum commodo voluptate qui ex ullamco amet do ex dolore quis cupidatat ut anim sunt dolore excepteur anim do dolor aliqua ex aute esse eiusmod sint laborum consequat laboris cillum.</p>', notes: '<p>Velit consequat elit anim qui eu elit aliquip consectetur aliqua cupidatat lorem laboris dolor qui ad laborum adipisicing adipisicing consequat et nostrud ullamco consequat dolore deserunt irure do aliquip non.</p><p>Ipsum commodo voluptate qui ex ullamco amet do ex dolore quis cupidatat ut anim sunt dolore excepteur anim do dolor aliqua ex aute esse eiusmod sint laborum consequat laboris cillum.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'a1723c04-69fe-4573-a135-6645658afe76', id: 'a1723c04-69fe-4573-a135-6645658afe76',
@ -1019,9 +962,7 @@ export const contacts = [
birthday: '1979-10-21T12:00:00.000Z', birthday: '1979-10-21T12:00:00.000Z',
address: '869 Seton Place, Chemung, Maine, PO8109', address: '869 Seton Place, Chemung, Maine, PO8109',
notes: '<p>Amet non anim ex ullamco pariatur ullamco laboris eiusmod ut magna nisi amet incididunt sunt anim nisi qui ut ex sunt adipisicing consequat deserunt qui mollit duis anim quis veniam.</p><p>Magna ut id duis qui ea proident quis officia lorem commodo et et proident dolore qui quis incididunt nulla incididunt ut aliqua veniam est adipisicing adipisicing reprehenderit ad velit incididunt.</p>', notes: '<p>Amet non anim ex ullamco pariatur ullamco laboris eiusmod ut magna nisi amet incididunt sunt anim nisi qui ut ex sunt adipisicing consequat deserunt qui mollit duis anim quis veniam.</p><p>Magna ut id duis qui ea proident quis officia lorem commodo et et proident dolore qui quis incididunt nulla incididunt ut aliqua veniam est adipisicing adipisicing reprehenderit ad velit incididunt.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '823e6166-c0c8-4373-9270-8a0d17489a08', id: '823e6166-c0c8-4373-9270-8a0d17489a08',
@ -1056,9 +997,7 @@ export const contacts = [
birthday: '1964-03-05T12:00:00.000Z', birthday: '1964-03-05T12:00:00.000Z',
address: '854 Hanover Place, Harleigh, New Jersey, PO9459', address: '854 Hanover Place, Harleigh, New Jersey, PO9459',
notes: '<p>Ea occaecat nisi cillum officia in velit ipsum reprehenderit ex fugiat fugiat ad velit pariatur ullamco sint in elit quis aute id cupidatat nostrud quis culpa aliquip id officia excepteur.</p><p>Ea ut consequat sit ullamco do pariatur quis officia ad ipsum quis nisi in nulla incididunt esse pariatur amet qui ullamco consectetur dolor voluptate sit qui mollit reprehenderit reprehenderit amet.</p>', notes: '<p>Ea occaecat nisi cillum officia in velit ipsum reprehenderit ex fugiat fugiat ad velit pariatur ullamco sint in elit quis aute id cupidatat nostrud quis culpa aliquip id officia excepteur.</p><p>Ea ut consequat sit ullamco do pariatur quis officia ad ipsum quis nisi in nulla incididunt esse pariatur amet qui ullamco consectetur dolor voluptate sit qui mollit reprehenderit reprehenderit amet.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '2c37ed00-427a-46d7-8f8f-d711c768d1ee', id: '2c37ed00-427a-46d7-8f8f-d711c768d1ee',
@ -1088,9 +1027,7 @@ export const contacts = [
birthday: '1980-04-29T12:00:00.000Z', birthday: '1980-04-29T12:00:00.000Z',
address: '137 Bridge Street, Sisquoc, District Of Columbia, PO4105', address: '137 Bridge Street, Sisquoc, District Of Columbia, PO4105',
notes: '<p>Ipsum velit est do velit do deserunt cupidatat officia duis laborum veniam sunt in ex reprehenderit esse ex ad aute anim duis ut sunt reprehenderit occaecat ut nostrud eu minim.</p><p>Aliqua consequat adipisicing adipisicing aliquip voluptate fugiat eu amet nostrud id proident non nisi fugiat velit nostrud ea officia non laboris magna cillum exercitation culpa eiusmod mollit fugiat et lorem.</p>', notes: '<p>Ipsum velit est do velit do deserunt cupidatat officia duis laborum veniam sunt in ex reprehenderit esse ex ad aute anim duis ut sunt reprehenderit occaecat ut nostrud eu minim.</p><p>Aliqua consequat adipisicing adipisicing aliquip voluptate fugiat eu amet nostrud id proident non nisi fugiat velit nostrud ea officia non laboris magna cillum exercitation culpa eiusmod mollit fugiat et lorem.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '944764c0-b261-4428-9188-bbd3022d66a8', id: '944764c0-b261-4428-9188-bbd3022d66a8',
@ -1129,9 +1066,7 @@ export const contacts = [
birthday: '1981-06-09T12:00:00.000Z', birthday: '1981-06-09T12:00:00.000Z',
address: '528 Glenmore Avenue, Elrama, Illinois, PO2952', address: '528 Glenmore Avenue, Elrama, Illinois, PO2952',
notes: '<p>Ea enim exercitation lorem excepteur officia nulla culpa culpa nisi veniam quis non duis exercitation labore commodo et occaecat reprehenderit ex velit exercitation commodo cupidatat amet veniam mollit magna consectetur.</p><p>Voluptate consectetur eu id eiusmod anim reprehenderit incididunt duis veniam tempor cillum ea esse tempor do laborum dolore sint ea duis incididunt in do aliqua voluptate incididunt officia excepteur do.</p>', notes: '<p>Ea enim exercitation lorem excepteur officia nulla culpa culpa nisi veniam quis non duis exercitation labore commodo et occaecat reprehenderit ex velit exercitation commodo cupidatat amet veniam mollit magna consectetur.</p><p>Voluptate consectetur eu id eiusmod anim reprehenderit incididunt duis veniam tempor cillum ea esse tempor do laborum dolore sint ea duis incididunt in do aliqua voluptate incididunt officia excepteur do.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'f2b3c756-5ad2-4d4b-aee5-b32c91457128', id: 'f2b3c756-5ad2-4d4b-aee5-b32c91457128',
@ -1161,9 +1096,7 @@ export const contacts = [
birthday: '1968-12-07T12:00:00.000Z', birthday: '1968-12-07T12:00:00.000Z',
address: '277 Coventry Road, Fairforest, Nevada, PO6031', address: '277 Coventry Road, Fairforest, Nevada, PO6031',
notes: '<p>Lorem mollit dolore nostrud sunt id anim veniam labore duis eiusmod duis fugiat aliqua occaecat do labore culpa consectetur consectetur sunt amet tempor incididunt tempor esse sunt id elit non.</p><p>Laborum mollit ullamco quis ad culpa nisi sit nisi veniam minim adipisicing sint eiusmod velit amet minim aliquip nulla eiusmod nulla laboris quis proident in adipisicing aute et ea anim.</p>', notes: '<p>Lorem mollit dolore nostrud sunt id anim veniam labore duis eiusmod duis fugiat aliqua occaecat do labore culpa consectetur consectetur sunt amet tempor incididunt tempor esse sunt id elit non.</p><p>Laborum mollit ullamco quis ad culpa nisi sit nisi veniam minim adipisicing sint eiusmod velit amet minim aliquip nulla eiusmod nulla laboris quis proident in adipisicing aute et ea anim.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: '54b1c201-4b2b-4be0-ad70-a6413e9628cd', id: '54b1c201-4b2b-4be0-ad70-a6413e9628cd',
@ -1188,9 +1121,7 @@ export const contacts = [
birthday: '1983-11-07T12:00:00.000Z', birthday: '1983-11-07T12:00:00.000Z',
address: '557 Monroe Street, Mayfair, Maryland, PO7200', address: '557 Monroe Street, Mayfair, Maryland, PO7200',
notes: '<p>Fugiat mollit sunt aliquip consectetur ipsum ut aliqua id ex laboris labore id elit nulla irure id aute pariatur do officia proident eiusmod proident reprehenderit dolor non dolor laborum nulla.</p><p>Pariatur reprehenderit incididunt voluptate enim aliqua laborum anim veniam pariatur irure exercitation non dolore velit et ex culpa lorem ipsum mollit eu sint duis aliquip elit amet consectetur velit minim.</p>', notes: '<p>Fugiat mollit sunt aliquip consectetur ipsum ut aliqua id ex laboris labore id elit nulla irure id aute pariatur do officia proident eiusmod proident reprehenderit dolor non dolor laborum nulla.</p><p>Pariatur reprehenderit incididunt voluptate enim aliqua laborum anim veniam pariatur irure exercitation non dolore velit et ex culpa lorem ipsum mollit eu sint duis aliquip elit amet consectetur velit minim.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'faf979c7-a13b-445a-b30a-08845f5fa90e', id: 'faf979c7-a13b-445a-b30a-08845f5fa90e',
@ -1220,9 +1151,7 @@ export const contacts = [
birthday: '1984-05-04T12:00:00.000Z', birthday: '1984-05-04T12:00:00.000Z',
address: '219 Village Court, Keyport, Alabama, PO7776', address: '219 Village Court, Keyport, Alabama, PO7776',
notes: '<p>Velit enim anim est aliqua consequat exercitation velit quis magna est incididunt ipsum minim minim nulla adipisicing ad eiusmod id veniam eiusmod sit elit est pariatur velit ea laborum anim.</p><p>Ad lorem ea nisi irure id consequat ullamco nisi nostrud dolore officia ipsum veniam velit minim pariatur culpa culpa esse minim adipisicing sit labore commodo aute excepteur non do in.</p>', notes: '<p>Velit enim anim est aliqua consequat exercitation velit quis magna est incididunt ipsum minim minim nulla adipisicing ad eiusmod id veniam eiusmod sit elit est pariatur velit ea laborum anim.</p><p>Ad lorem ea nisi irure id consequat ullamco nisi nostrud dolore officia ipsum veniam velit minim pariatur culpa culpa esse minim adipisicing sit labore commodo aute excepteur non do in.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14', id: '2bfa2be5-7688-48d5-b5ac-dc0d9ac97f14',
@ -1252,9 +1181,7 @@ export const contacts = [
birthday: '1973-10-06T12:00:00.000Z', birthday: '1973-10-06T12:00:00.000Z',
address: '448 Berriman Street, Reinerton, Washington, PO6704', address: '448 Berriman Street, Reinerton, Washington, PO6704',
notes: '<p>Esse sint lorem exercitation velit tempor tempor voluptate nulla proident excepteur magna tempor consectetur aliquip qui nisi mollit cupidatat est adipisicing ipsum sint et excepteur sit labore velit dolore labore.</p><p>Duis nisi adipisicing lorem do excepteur magna consequat labore magna ut consectetur eu enim occaecat id nulla laboris minim officia est id nisi mollit ullamco irure ut dolore esse aliqua.</p>', notes: '<p>Esse sint lorem exercitation velit tempor tempor voluptate nulla proident excepteur magna tempor consectetur aliquip qui nisi mollit cupidatat est adipisicing ipsum sint et excepteur sit labore velit dolore labore.</p><p>Duis nisi adipisicing lorem do excepteur magna consequat labore magna ut consectetur eu enim occaecat id nulla laboris minim officia est id nisi mollit ullamco irure ut dolore esse aliqua.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '77a4383b-b5a5-4943-bc46-04c3431d1566', id: '77a4383b-b5a5-4943-bc46-04c3431d1566',
@ -1283,9 +1210,7 @@ export const contacts = [
birthday: '1987-06-07T12:00:00.000Z', birthday: '1987-06-07T12:00:00.000Z',
address: '578 Tampa Court, Wescosville, Ohio, PO4108', address: '578 Tampa Court, Wescosville, Ohio, PO4108',
notes: '<p>Lorem do deserunt nulla nostrud incididunt et laboris labore eu nisi ut ullamco veniam deserunt do non labore commodo amet aliquip exercitation ea occaecat amet non eiusmod ut minim fugiat.</p><p>Esse eu ex irure pariatur qui cillum labore nulla quis officia consequat commodo consequat fugiat culpa nostrud labore eu adipisicing magna irure aliquip est amet irure eiusmod esse reprehenderit mollit.</p>', notes: '<p>Lorem do deserunt nulla nostrud incididunt et laboris labore eu nisi ut ullamco veniam deserunt do non labore commodo amet aliquip exercitation ea occaecat amet non eiusmod ut minim fugiat.</p><p>Esse eu ex irure pariatur qui cillum labore nulla quis officia consequat commodo consequat fugiat culpa nostrud labore eu adipisicing magna irure aliquip est amet irure eiusmod esse reprehenderit mollit.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '8bb0f597-673a-47ca-8c77-2f83219cb9af', id: '8bb0f597-673a-47ca-8c77-2f83219cb9af',
@ -1320,9 +1245,7 @@ export const contacts = [
birthday: '1980-09-15T12:00:00.000Z', birthday: '1980-09-15T12:00:00.000Z',
address: '931 Bristol Street, Why, South Carolina, PO9700', address: '931 Bristol Street, Why, South Carolina, PO9700',
notes: '<p>Dolore laboris aute officia reprehenderit cupidatat aliquip duis labore aliquip officia est nostrud nisi voluptate eiusmod ad aute et ea cillum aliqua elit ipsum officia cillum laborum minim labore sit.</p><p>Exercitation labore ut pariatur occaecat ullamco non occaecat aliqua amet nostrud aliquip ipsum ad do ullamco enim laborum commodo minim elit ut quis laboris elit laborum proident sunt ullamco sit.</p>', notes: '<p>Dolore laboris aute officia reprehenderit cupidatat aliquip duis labore aliquip officia est nostrud nisi voluptate eiusmod ad aute et ea cillum aliqua elit ipsum officia cillum laborum minim labore sit.</p><p>Exercitation labore ut pariatur occaecat ullamco non occaecat aliqua amet nostrud aliquip ipsum ad do ullamco enim laborum commodo minim elit ut quis laboris elit laborum proident sunt ullamco sit.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb', id: 'c318e31f-1d74-49c5-8dae-2bc5805e2fdb',
@ -1357,9 +1280,7 @@ export const contacts = [
birthday: '1977-04-12T12:00:00.000Z', birthday: '1977-04-12T12:00:00.000Z',
address: '268 Hutchinson Court, Drytown, Florida, PO3041', address: '268 Hutchinson Court, Drytown, Florida, PO3041',
notes: '<p>Eu ipsum nisi eu lorem cupidatat mollit exercitation elit ea culpa enim qui culpa ad aliqua exercitation tempor nulla excepteur fugiat ipsum quis amet occaecat adipisicing ullamco duis dolore occaecat.</p><p>Non eu et elit ea labore lorem adipisicing voluptate incididunt ut officia aute minim incididunt lorem qui adipisicing mollit magna nisi consectetur cillum sit exercitation eiusmod qui eu nisi sunt.</p>', notes: '<p>Eu ipsum nisi eu lorem cupidatat mollit exercitation elit ea culpa enim qui culpa ad aliqua exercitation tempor nulla excepteur fugiat ipsum quis amet occaecat adipisicing ullamco duis dolore occaecat.</p><p>Non eu et elit ea labore lorem adipisicing voluptate incididunt ut officia aute minim incididunt lorem qui adipisicing mollit magna nisi consectetur cillum sit exercitation eiusmod qui eu nisi sunt.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '0a8bc517-631a-4a93-aacc-000fa2e8294c', id: '0a8bc517-631a-4a93-aacc-000fa2e8294c',
@ -1384,9 +1305,7 @@ export const contacts = [
birthday: '1976-09-09T12:00:00.000Z', birthday: '1976-09-09T12:00:00.000Z',
address: '946 Remsen Street, Caroline, New Mexico, PO3247', address: '946 Remsen Street, Caroline, New Mexico, PO3247',
notes: '<p>Amet dolore elit irure in commodo in et eu eu nulla labore elit sunt et nisi quis officia nostrud et mollit dolor aute fugiat sunt reprehenderit quis sint minim ipsum.</p><p>Laboris ut sunt nisi aute incididunt reprehenderit mollit culpa velit exercitation reprehenderit irure id sunt officia magna est ea labore consectetur incididunt cillum qui tempor ea ullamco quis pariatur aliquip.</p>', notes: '<p>Amet dolore elit irure in commodo in et eu eu nulla labore elit sunt et nisi quis officia nostrud et mollit dolor aute fugiat sunt reprehenderit quis sint minim ipsum.</p><p>Laboris ut sunt nisi aute incididunt reprehenderit mollit culpa velit exercitation reprehenderit irure id sunt officia magna est ea labore consectetur incididunt cillum qui tempor ea ullamco quis pariatur aliquip.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'a4c9945a-757b-40b0-8942-d20e0543cabd', id: 'a4c9945a-757b-40b0-8942-d20e0543cabd',
@ -1416,9 +1335,7 @@ export const contacts = [
birthday: '1989-06-21T12:00:00.000Z', birthday: '1989-06-21T12:00:00.000Z',
address: '397 Vandalia Avenue, Rockingham, Michigan, PO8089', address: '397 Vandalia Avenue, Rockingham, Michigan, PO8089',
notes: '<p>Velit sunt sunt commodo ex amet laboris voluptate eu lorem aliqua minim occaecat cupidatat aliqua ipsum nisi velit id reprehenderit exercitation velit fugiat minim nisi deserunt voluptate anim cillum commodo.</p><p>Cillum velit nostrud cupidatat ex sit culpa deserunt cillum cupidatat cillum aute cupidatat exercitation ullamco sunt incididunt non magna sint lorem et incididunt laborum culpa qui sint sunt duis fugiat.</p>', notes: '<p>Velit sunt sunt commodo ex amet laboris voluptate eu lorem aliqua minim occaecat cupidatat aliqua ipsum nisi velit id reprehenderit exercitation velit fugiat minim nisi deserunt voluptate anim cillum commodo.</p><p>Cillum velit nostrud cupidatat ex sit culpa deserunt cillum cupidatat cillum aute cupidatat exercitation ullamco sunt incididunt non magna sint lorem et incididunt laborum culpa qui sint sunt duis fugiat.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645', id: 'b8258ccf-48b5-46a2-9c95-e0bd7580c645',
@ -1448,9 +1365,7 @@ export const contacts = [
birthday: '1976-09-10T12:00:00.000Z', birthday: '1976-09-10T12:00:00.000Z',
address: '821 Beverly Road, Tyro, Colorado, PO4248', address: '821 Beverly Road, Tyro, Colorado, PO4248',
notes: '<p>Incididunt non est consequat qui sit sunt aliquip sit quis minim laboris ullamco est culpa velit culpa cupidatat veniam incididunt non quis elit reprehenderit et officia cillum magna aliqua occaecat.</p><p>Cupidatat amet incididunt id pariatur minim veniam id dolor nisi labore cillum ea officia cupidatat do culpa aliqua consequat deserunt aliquip sit ea excepteur eiusmod labore tempor reprehenderit commodo exercitation.</p>', notes: '<p>Incididunt non est consequat qui sit sunt aliquip sit quis minim laboris ullamco est culpa velit culpa cupidatat veniam incididunt non quis elit reprehenderit et officia cillum magna aliqua occaecat.</p><p>Cupidatat amet incididunt id pariatur minim veniam id dolor nisi labore cillum ea officia cupidatat do culpa aliqua consequat deserunt aliquip sit ea excepteur eiusmod labore tempor reprehenderit commodo exercitation.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d', id: 'f004ea79-98fc-436c-9ba5-6cfe32fe583d',
@ -1484,9 +1399,7 @@ export const contacts = [
birthday: '1973-11-08T12:00:00.000Z', birthday: '1973-11-08T12:00:00.000Z',
address: '364 Porter Avenue, Delshire, Missouri, PO8911', address: '364 Porter Avenue, Delshire, Missouri, PO8911',
notes: '<p>Velit fugiat minim sit nisi esse laboris ad velit proident non et cillum labore sint excepteur nisi eu amet voluptate duis duis id enim ea anim adipisicing consectetur id consectetur.</p><p>Ex eiusmod id magna in non lorem sunt sunt officia do adipisicing officia mollit occaecat sunt laborum aliquip adipisicing ullamco in sit proident et quis incididunt pariatur fugiat mollit anim.</p>', notes: '<p>Velit fugiat minim sit nisi esse laboris ad velit proident non et cillum labore sint excepteur nisi eu amet voluptate duis duis id enim ea anim adipisicing consectetur id consectetur.</p><p>Ex eiusmod id magna in non lorem sunt sunt officia do adipisicing officia mollit occaecat sunt laborum aliquip adipisicing ullamco in sit proident et quis incididunt pariatur fugiat mollit anim.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37', id: '8b69fe2d-d7cc-4a3d-983d-559173e37d37',
@ -1515,9 +1428,7 @@ export const contacts = [
birthday: '1969-08-10T12:00:00.000Z', birthday: '1969-08-10T12:00:00.000Z',
address: '101 Sackett Street, Naomi, Tennessee, PO6335', address: '101 Sackett Street, Naomi, Tennessee, PO6335',
notes: '<p>Ut cupidatat sint minim consectetur cupidatat aute ut anim consequat fugiat laboris quis sint sit nulla irure nulla officia eiusmod consequat ex quis ad ex ullamco et ut labore tempor.</p><p>Deserunt minim dolore voluptate aute aliqua est elit mollit ut ut consequat in esse est do ex officia nostrud aute id fugiat reprehenderit quis cillum fugiat id fugiat minim tempor.</p>', notes: '<p>Ut cupidatat sint minim consectetur cupidatat aute ut anim consequat fugiat laboris quis sint sit nulla irure nulla officia eiusmod consequat ex quis ad ex ullamco et ut labore tempor.</p><p>Deserunt minim dolore voluptate aute aliqua est elit mollit ut ut consequat in esse est do ex officia nostrud aute id fugiat reprehenderit quis cillum fugiat id fugiat minim tempor.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: 'cdcc62e4-1520-4ccc-803d-52868c7e01ba', id: 'cdcc62e4-1520-4ccc-803d-52868c7e01ba',
@ -1552,9 +1463,7 @@ export const contacts = [
birthday: '1996-06-17T12:00:00.000Z', birthday: '1996-06-17T12:00:00.000Z',
address: '956 Pierrepont Street, Crumpler, Hawaii, PO3299', address: '956 Pierrepont Street, Crumpler, Hawaii, PO3299',
notes: '<p>Esse excepteur ad aliquip amet elit reprehenderit ut nostrud magna ex esse dolore magna excepteur irure esse incididunt sunt enim laborum ex mollit magna elit quis ullamco aute minim veniam.</p><p>Duis id ullamco laboris elit ea ea dolore tempor est eu esse aliqua quis quis ut laborum mollit cillum proident deserunt fugiat ipsum elit exercitation quis mollit eiusmod officia non.</p>', notes: '<p>Esse excepteur ad aliquip amet elit reprehenderit ut nostrud magna ex esse dolore magna excepteur irure esse incididunt sunt enim laborum ex mollit magna elit quis ullamco aute minim veniam.</p><p>Duis id ullamco laboris elit ea ea dolore tempor est eu esse aliqua quis quis ut laborum mollit cillum proident deserunt fugiat ipsum elit exercitation quis mollit eiusmod officia non.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'e2946946-b4b5-4fd7-bab4-62c38cdff2f1', id: 'e2946946-b4b5-4fd7-bab4-62c38cdff2f1',
@ -1579,9 +1488,7 @@ export const contacts = [
birthday: '1972-02-04T12:00:00.000Z', birthday: '1972-02-04T12:00:00.000Z',
address: '384 Love Lane, Dyckesville, New York, PO4115', address: '384 Love Lane, Dyckesville, New York, PO4115',
notes: '<p>Consectetur eu et ea anim magna occaecat anim labore velit nulla non magna laboris duis sit adipisicing commodo laboris consequat id quis aliqua est culpa quis in ex est culpa.</p><p>Sunt qui excepteur reprehenderit nostrud voluptate eu laborum laborum id esse occaecat irure esse elit magna tempor ad est elit non labore tempor laborum deserunt voluptate cupidatat excepteur sunt sint.</p>', notes: '<p>Consectetur eu et ea anim magna occaecat anim labore velit nulla non magna laboris duis sit adipisicing commodo laboris consequat id quis aliqua est culpa quis in ex est culpa.</p><p>Sunt qui excepteur reprehenderit nostrud voluptate eu laborum laborum id esse occaecat irure esse elit magna tempor ad est elit non labore tempor laborum deserunt voluptate cupidatat excepteur sunt sint.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'fdc77706-6ba2-4397-b2f8-a9a0b6495153', id: 'fdc77706-6ba2-4397-b2f8-a9a0b6495153',
@ -1611,9 +1518,7 @@ export const contacts = [
birthday: '1988-05-22T12:00:00.000Z', birthday: '1988-05-22T12:00:00.000Z',
address: '725 Arlington Avenue, Mathews, Wyoming, PO4562', address: '725 Arlington Avenue, Mathews, Wyoming, PO4562',
notes: '<p>Eiusmod ullamco laboris tempor reprehenderit culpa non sunt ea consequat velit id ipsum commodo eiusmod exercitation laboris aliqua magna reprehenderit culpa tempor mollit pariatur consectetur amet aliqua cillum voluptate exercitation.</p><p>Qui cillum consectetur qui proident adipisicing id qui esse aute velit excepteur pariatur ea excepteur sunt velit nostrud esse mollit sint ex irure sunt aliquip velit consequat minim do officia.</p>', notes: '<p>Eiusmod ullamco laboris tempor reprehenderit culpa non sunt ea consequat velit id ipsum commodo eiusmod exercitation laboris aliqua magna reprehenderit culpa tempor mollit pariatur consectetur amet aliqua cillum voluptate exercitation.</p><p>Qui cillum consectetur qui proident adipisicing id qui esse aute velit excepteur pariatur ea excepteur sunt velit nostrud esse mollit sint ex irure sunt aliquip velit consequat minim do officia.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '12148fa2-e0a4-49fb-b3c5-daeecdb5180a', id: '12148fa2-e0a4-49fb-b3c5-daeecdb5180a',
@ -1652,9 +1557,7 @@ export const contacts = [
birthday: '1971-08-13T12:00:00.000Z', birthday: '1971-08-13T12:00:00.000Z',
address: '253 Beard Street, Staples, Massachusetts, PO8089', address: '253 Beard Street, Staples, Massachusetts, PO8089',
notes: '<p>Proident est est et in commodo incididunt anim fugiat laboris pariatur eu enim dolor eiusmod dolor voluptate officia eiusmod excepteur culpa aute do do anim pariatur irure incididunt incididunt est.</p><p>Sint duis mollit dolor laborum ex non esse consequat anim et qui est nostrud incididunt fugiat anim veniam sunt cupidatat ut voluptate commodo non ex tempor ullamco magna culpa culpa.</p>', notes: '<p>Proident est est et in commodo incididunt anim fugiat laboris pariatur eu enim dolor eiusmod dolor voluptate officia eiusmod excepteur culpa aute do do anim pariatur irure incididunt incididunt est.</p><p>Sint duis mollit dolor laborum ex non esse consequat anim et qui est nostrud incididunt fugiat anim veniam sunt cupidatat ut voluptate commodo non ex tempor ullamco magna culpa culpa.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '07dd64eb-8b8f-4765-a16c-8db083c45096', id: '07dd64eb-8b8f-4765-a16c-8db083c45096',
@ -1689,9 +1592,7 @@ export const contacts = [
birthday: '1967-06-10T12:00:00.000Z', birthday: '1967-06-10T12:00:00.000Z',
address: '962 Whitney Avenue, Sussex, North Dakota, PO5796', address: '962 Whitney Avenue, Sussex, North Dakota, PO5796',
notes: '<p>Nulla nisi officia quis aliquip voluptate mollit ut anim eu et quis tempor incididunt consectetur exercitation cupidatat in nisi exercitation est culpa nostrud sit elit sit sunt do ipsum eu.</p><p>Enim voluptate ad ullamco tempor voluptate culpa et ut ullamco eu consequat est esse excepteur est nostrud velit enim culpa dolore non quis occaecat eiusmod velit ex mollit tempor labore.</p>', notes: '<p>Nulla nisi officia quis aliquip voluptate mollit ut anim eu et quis tempor incididunt consectetur exercitation cupidatat in nisi exercitation est culpa nostrud sit elit sit sunt do ipsum eu.</p><p>Enim voluptate ad ullamco tempor voluptate culpa et ut ullamco eu consequat est esse excepteur est nostrud velit enim culpa dolore non quis occaecat eiusmod velit ex mollit tempor labore.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '81fdc48c-5572-4123-8a73-71b7892120de', id: '81fdc48c-5572-4123-8a73-71b7892120de',
@ -1720,9 +1621,7 @@ export const contacts = [
birthday: '1960-11-13T12:00:00.000Z', birthday: '1960-11-13T12:00:00.000Z',
address: '981 Kingston Avenue, Topaz, Connecticut, PO6866', address: '981 Kingston Avenue, Topaz, Connecticut, PO6866',
notes: '<p>Adipisicing fugiat magna eiusmod consectetur id commodo incididunt ullamco ut sint minim nulla in do aute in sit pariatur irure dolor magna pariatur ad officia excepteur duis ullamco dolor sunt.</p><p>Dolor laborum proident voluptate eu esse lorem adipisicing enim consectetur veniam nisi pariatur aliquip sit laborum sunt adipisicing anim labore eiusmod nostrud irure irure nisi ipsum dolor aliquip ex exercitation.</p>', notes: '<p>Adipisicing fugiat magna eiusmod consectetur id commodo incididunt ullamco ut sint minim nulla in do aute in sit pariatur irure dolor magna pariatur ad officia excepteur duis ullamco dolor sunt.</p><p>Dolor laborum proident voluptate eu esse lorem adipisicing enim consectetur veniam nisi pariatur aliquip sit laborum sunt adipisicing anim labore eiusmod nostrud irure irure nisi ipsum dolor aliquip ex exercitation.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: 'f8bbf6be-d49a-41a3-bb80-3d51df84c12b', id: 'f8bbf6be-d49a-41a3-bb80-3d51df84c12b',
@ -1761,9 +1660,7 @@ export const contacts = [
birthday: '1980-02-26T12:00:00.000Z', birthday: '1980-02-26T12:00:00.000Z',
address: '802 Preston Court, Waikele, Pennsylvania, PO7421', address: '802 Preston Court, Waikele, Pennsylvania, PO7421',
notes: '<p>Aliqua sint aute in cillum deserunt enim fugiat tempor est pariatur irure commodo commodo deserunt eu nulla laboris enim occaecat incididunt voluptate enim est reprehenderit qui anim veniam sint adipisicing.</p><p>Commodo veniam occaecat ex et laborum minim fugiat sunt commodo velit dolor labore excepteur fugiat ipsum eiusmod in esse ex nulla deserunt minim consectetur in est sunt eu commodo fugiat.</p>', notes: '<p>Aliqua sint aute in cillum deserunt enim fugiat tempor est pariatur irure commodo commodo deserunt eu nulla laboris enim occaecat incididunt voluptate enim est reprehenderit qui anim veniam sint adipisicing.</p><p>Commodo veniam occaecat ex et laborum minim fugiat sunt commodo velit dolor labore excepteur fugiat ipsum eiusmod in esse ex nulla deserunt minim consectetur in est sunt eu commodo fugiat.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: 'cd482941-3eaf-4560-ac37-56a9296025df', id: 'cd482941-3eaf-4560-ac37-56a9296025df',
@ -1788,9 +1685,7 @@ export const contacts = [
birthday: '1988-04-27T12:00:00.000Z', birthday: '1988-04-27T12:00:00.000Z',
address: '935 Guider Avenue, Kipp, Wisconsin, PO5282', address: '935 Guider Avenue, Kipp, Wisconsin, PO5282',
notes: '<p>Magna et culpa cillum sint labore consequat aute aliqua amet ea consequat ut ullamco nisi commodo lorem enim amet dolor sit nisi dolor do sit lorem cillum esse reprehenderit ut.</p><p>Quis veniam anim nulla adipisicing veniam fugiat elit duis pariatur anim irure adipisicing elit labore eu aute exercitation qui exercitation commodo exercitation ipsum tempor non et ex eu aute proident.</p>', notes: '<p>Magna et culpa cillum sint labore consequat aute aliqua amet ea consequat ut ullamco nisi commodo lorem enim amet dolor sit nisi dolor do sit lorem cillum esse reprehenderit ut.</p><p>Quis veniam anim nulla adipisicing veniam fugiat elit duis pariatur anim irure adipisicing elit labore eu aute exercitation qui exercitation commodo exercitation ipsum tempor non et ex eu aute proident.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '22f18d47-ff8d-440e-888d-a1747c093052', id: '22f18d47-ff8d-440e-888d-a1747c093052',
@ -1825,9 +1720,7 @@ export const contacts = [
birthday: '1985-09-17T12:00:00.000Z', birthday: '1985-09-17T12:00:00.000Z',
address: '387 Holt Court, Thomasville, Alaska, PO2867', address: '387 Holt Court, Thomasville, Alaska, PO2867',
notes: '<p>Adipisicing exercitation dolor nisi ipsum nostrud anim dolore sint veniam consequat lorem sit ex commodo nostrud occaecat elit magna magna commodo incididunt laborum ad irure pariatur et sit ullamco adipisicing.</p><p>Ullamco in dolore amet est quis consectetur fugiat non nisi incididunt id laborum adipisicing dolor proident velit ut quis aliquip dolore id anim sit adipisicing nisi incididunt enim amet pariatur.</p>', notes: '<p>Adipisicing exercitation dolor nisi ipsum nostrud anim dolore sint veniam consequat lorem sit ex commodo nostrud occaecat elit magna magna commodo incididunt laborum ad irure pariatur et sit ullamco adipisicing.</p><p>Ullamco in dolore amet est quis consectetur fugiat non nisi incididunt id laborum adipisicing dolor proident velit ut quis aliquip dolore id anim sit adipisicing nisi incididunt enim amet pariatur.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: 'a9a9f382-e4c3-42fb-9fe9-65aa534732b5', id: 'a9a9f382-e4c3-42fb-9fe9-65aa534732b5',
@ -1861,9 +1754,7 @@ export const contacts = [
birthday: '1966-08-14T12:00:00.000Z', birthday: '1966-08-14T12:00:00.000Z',
address: '733 Delmonico Place, Belvoir, Virginia, PO7102', address: '733 Delmonico Place, Belvoir, Virginia, PO7102',
notes: '<p>Voluptate nisi adipisicing ex magna mollit non cillum dolor in magna duis exercitation irure elit duis eiusmod deserunt lorem nulla sunt laboris quis voluptate ullamco labore adipisicing quis minim ipsum.</p><p>Id ut esse elit proident mollit nulla exercitation magna voluptate sit eiusmod labore velit commodo exercitation dolore anim est eiusmod occaecat et consequat eiusmod culpa ipsum deserunt lorem non incididunt.</p>', notes: '<p>Voluptate nisi adipisicing ex magna mollit non cillum dolor in magna duis exercitation irure elit duis eiusmod deserunt lorem nulla sunt laboris quis voluptate ullamco labore adipisicing quis minim ipsum.</p><p>Id ut esse elit proident mollit nulla exercitation magna voluptate sit eiusmod labore velit commodo exercitation dolore anim est eiusmod occaecat et consequat eiusmod culpa ipsum deserunt lorem non incididunt.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: '0222b24b-c288-48d1-b356-0f087fa172f8', id: '0222b24b-c288-48d1-b356-0f087fa172f8',
@ -1902,9 +1793,7 @@ export const contacts = [
birthday: '1977-02-23T12:00:00.000Z', birthday: '1977-02-23T12:00:00.000Z',
address: '713 Fane Court, Lemoyne, Kentucky, PO3601', address: '713 Fane Court, Lemoyne, Kentucky, PO3601',
notes: '<p>Sint tempor consectetur ullamco ullamco consequat exercitation ea occaecat eiusmod cupidatat anim pariatur nisi pariatur excepteur ut labore anim excepteur sit eu consequat do enim pariatur et dolore in irure.</p><p>Commodo ut non minim sunt nisi tempor culpa duis anim ipsum qui irure lorem est voluptate voluptate officia occaecat lorem labore elit officia laboris mollit et eiusmod esse laborum nisi.</p>', notes: '<p>Sint tempor consectetur ullamco ullamco consequat exercitation ea occaecat eiusmod cupidatat anim pariatur nisi pariatur excepteur ut labore anim excepteur sit eu consequat do enim pariatur et dolore in irure.</p><p>Commodo ut non minim sunt nisi tempor culpa duis anim ipsum qui irure lorem est voluptate voluptate officia occaecat lorem labore elit officia laboris mollit et eiusmod esse laborum nisi.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '0630f1ca-cdb9-405d-b134-68f733334089', id: '0630f1ca-cdb9-405d-b134-68f733334089',
@ -1938,9 +1827,7 @@ export const contacts = [
birthday: '1963-04-07T12:00:00.000Z', birthday: '1963-04-07T12:00:00.000Z',
address: '698 Brooklyn Avenue, Dixonville, Utah, PO2712', address: '698 Brooklyn Avenue, Dixonville, Utah, PO2712',
notes: '<p>Pariatur velit ea ad quis elit pariatur consectetur eiusmod veniam non incididunt ex ex et nulla voluptate fugiat esse sit dolore voluptate in dolor nulla laborum irure consequat sit pariatur.</p><p>Dolore ex officia incididunt pariatur ea amet sunt enim aute labore cupidatat laboris eiusmod enim lorem labore nostrud ea consectetur et eu sunt exercitation dolore consequat fugiat anim in exercitation.</p>', notes: '<p>Pariatur velit ea ad quis elit pariatur consectetur eiusmod veniam non incididunt ex ex et nulla voluptate fugiat esse sit dolore voluptate in dolor nulla laborum irure consequat sit pariatur.</p><p>Dolore ex officia incididunt pariatur ea amet sunt enim aute labore cupidatat laboris eiusmod enim lorem labore nostrud ea consectetur et eu sunt exercitation dolore consequat fugiat anim in exercitation.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '999c24f3-7bb8-4a01-85ca-2fca7863c57e', id: '999c24f3-7bb8-4a01-85ca-2fca7863c57e',
@ -1979,9 +1866,7 @@ export const contacts = [
birthday: '1960-01-26T12:00:00.000Z', birthday: '1960-01-26T12:00:00.000Z',
address: '923 Ivan Court, Hatteras, Idaho, PO7573', address: '923 Ivan Court, Hatteras, Idaho, PO7573',
notes: '<p>Est duis sint ullamco nulla do tempor do dolore laboris in sint ad duis est eu consequat nisi esse irure tempor sunt pariatur qui mollit ipsum quis esse ex ipsum.</p><p>Dolore anim irure quis ipsum adipisicing sint et incididunt aute nisi minim aliquip consectetur duis tempor laborum nostrud exercitation do mollit irure anim lorem non excepteur commodo laborum dolore dolor.</p>', notes: '<p>Est duis sint ullamco nulla do tempor do dolore laboris in sint ad duis est eu consequat nisi esse irure tempor sunt pariatur qui mollit ipsum quis esse ex ipsum.</p><p>Dolore anim irure quis ipsum adipisicing sint et incididunt aute nisi minim aliquip consectetur duis tempor laborum nostrud exercitation do mollit irure anim lorem non excepteur commodo laborum dolore dolor.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '7e8e1f1e-d19f-45c7-86bd-6fef599dae71', id: '7e8e1f1e-d19f-45c7-86bd-6fef599dae71',
@ -2020,9 +1905,7 @@ export const contacts = [
birthday: '1975-08-31T12:00:00.000Z', birthday: '1975-08-31T12:00:00.000Z',
address: '539 Rockaway Avenue, Whitmer, Guam, PO4871', address: '539 Rockaway Avenue, Whitmer, Guam, PO4871',
notes: '<p>Sunt quis officia elit laborum excepteur consequat amet cillum labore deserunt cillum cillum labore exercitation minim laboris anim incididunt voluptate minim duis enim eu duis veniam labore nisi culpa duis.</p><p>Pariatur irure sunt et commodo reprehenderit consectetur duis et ullamco fugiat occaecat culpa enim incididunt officia minim aliqua sit amet do dolore pariatur fugiat et adipisicing labore dolor id dolore.</p>', notes: '<p>Sunt quis officia elit laborum excepteur consequat amet cillum labore deserunt cillum cillum labore exercitation minim laboris anim incididunt voluptate minim duis enim eu duis veniam labore nisi culpa duis.</p><p>Pariatur irure sunt et commodo reprehenderit consectetur duis et ullamco fugiat occaecat culpa enim incididunt officia minim aliqua sit amet do dolore pariatur fugiat et adipisicing labore dolor id dolore.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: 'bedcb6a2-da83-4631-866a-77d10d239477', id: 'bedcb6a2-da83-4631-866a-77d10d239477',
@ -2047,9 +1930,7 @@ export const contacts = [
birthday: '1985-12-08T12:00:00.000Z', birthday: '1985-12-08T12:00:00.000Z',
address: '233 Willmohr Street, Cressey, Iowa, PO1962', address: '233 Willmohr Street, Cressey, Iowa, PO1962',
notes: '<p>In amet voluptate ad eiusmod cupidatat nulla sunt eu amet occaecat qui cillum occaecat tempor minim nostrud ullamco amet elit aliquip est nisi officia lorem occaecat ea lorem officia veniam.</p><p>Nulla tempor id excepteur irure do do veniam eiusmod esse ipsum sint dolore commodo enim officia nulla nulla proident in dolor et aliquip sit nulla sit proident duis aute deserunt.</p>', notes: '<p>In amet voluptate ad eiusmod cupidatat nulla sunt eu amet occaecat qui cillum occaecat tempor minim nostrud ullamco amet elit aliquip est nisi officia lorem occaecat ea lorem officia veniam.</p><p>Nulla tempor id excepteur irure do do veniam eiusmod esse ipsum sint dolore commodo enim officia nulla nulla proident in dolor et aliquip sit nulla sit proident duis aute deserunt.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '66f9de1b-f842-4d4c-bb59-f97e91db0462', id: '66f9de1b-f842-4d4c-bb59-f97e91db0462',
@ -2088,9 +1969,7 @@ export const contacts = [
birthday: '1993-06-01T12:00:00.000Z', birthday: '1993-06-01T12:00:00.000Z',
address: '916 Cobek Court, Morningside, South Dakota, PO2019', address: '916 Cobek Court, Morningside, South Dakota, PO2019',
notes: '<p>Laboris consequat labore nisi aute voluptate minim amet nulla elit tempor dolor nulla do et consequat esse dolore fugiat laboris deserunt velit minim laboris voluptate enim ut non laboris nisi.</p><p>Magna pariatur voluptate veniam nostrud irure magna pariatur ut quis reprehenderit voluptate aute duis sunt laboris consequat lorem eu pariatur nulla incididunt quis lorem consectetur ex lorem commodo magna dolore.</p>', notes: '<p>Laboris consequat labore nisi aute voluptate minim amet nulla elit tempor dolor nulla do et consequat esse dolore fugiat laboris deserunt velit minim laboris voluptate enim ut non laboris nisi.</p><p>Magna pariatur voluptate veniam nostrud irure magna pariatur ut quis reprehenderit voluptate aute duis sunt laboris consequat lorem eu pariatur nulla incididunt quis lorem consectetur ex lorem commodo magna dolore.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '9cb0ea57-3461-4182-979b-593b0c1ec6c3', id: '9cb0ea57-3461-4182-979b-593b0c1ec6c3',
@ -2129,9 +2008,7 @@ export const contacts = [
birthday: '1976-04-27T12:00:00.000Z', birthday: '1976-04-27T12:00:00.000Z',
address: '405 Canarsie Road, Richville, Virgin Islands, PO2744', address: '405 Canarsie Road, Richville, Virgin Islands, PO2744',
notes: '<p>Occaecat do excepteur non ipsum labore consequat id eu sunt minim aliquip elit occaecat velit ut aute cupidatat irure ex eiusmod fugiat ea ea cupidatat nulla dolor labore consectetur amet.</p><p>Mollit enim dolore deserunt tempor aliqua velit nostrud nostrud id consectetur lorem in enim excepteur nisi laborum ex commodo sint ea et culpa lorem esse culpa ad officia do amet.</p>', notes: '<p>Occaecat do excepteur non ipsum labore consequat id eu sunt minim aliquip elit occaecat velit ut aute cupidatat irure ex eiusmod fugiat ea ea cupidatat nulla dolor labore consectetur amet.</p><p>Mollit enim dolore deserunt tempor aliqua velit nostrud nostrud id consectetur lorem in enim excepteur nisi laborum ex commodo sint ea et culpa lorem esse culpa ad officia do amet.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '2fb89a90-5622-4b5b-8df3-d49b85905392', id: '2fb89a90-5622-4b5b-8df3-d49b85905392',
@ -2166,9 +2043,7 @@ export const contacts = [
birthday: '1968-03-11T12:00:00.000Z', birthday: '1968-03-11T12:00:00.000Z',
address: '540 Metrotech Courtr, Garfield, American Samoa, PO2290', address: '540 Metrotech Courtr, Garfield, American Samoa, PO2290',
notes: '<p>Ullamco dolore ipsum exercitation officia dolore sit consequat nisi consequat occaecat et ipsum veniam anim tempor pariatur sunt in adipisicing aliqua non dolor laborum veniam nisi dolore quis sunt incididunt.</p><p>Incididunt ullamco sunt magna reprehenderit velit dolor qui anim eiusmod nostrud commodo exercitation velit incididunt exercitation nulla ad aute eiusmod est amet exercitation est nostrud sit esse esse ad irure.</p>', notes: '<p>Ullamco dolore ipsum exercitation officia dolore sit consequat nisi consequat occaecat et ipsum veniam anim tempor pariatur sunt in adipisicing aliqua non dolor laborum veniam nisi dolore quis sunt incididunt.</p><p>Incididunt ullamco sunt magna reprehenderit velit dolor qui anim eiusmod nostrud commodo exercitation velit incididunt exercitation nulla ad aute eiusmod est amet exercitation est nostrud sit esse esse ad irure.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: '8141dd08-3a6e-4770-912c-59d0ed06dde6', id: '8141dd08-3a6e-4770-912c-59d0ed06dde6',
@ -2197,9 +2072,7 @@ export const contacts = [
birthday: '1970-07-15T12:00:00.000Z', birthday: '1970-07-15T12:00:00.000Z',
address: '825 Cherry Street, Foscoe, Minnesota, PO7290', address: '825 Cherry Street, Foscoe, Minnesota, PO7290',
notes: '<p>Fugiat in exercitation nostrud labore labore irure ex magna ex aliquip veniam sit irure irure deserunt occaecat tempor cillum aliqua dolore ea tempor dolore laboris est amet quis consequat quis.</p><p>Esse officia velit consectetur ullamco ea pariatur mollit sit consectetur sint mollit commodo anim anim ea amet consectetur eiusmod aliqua excepteur elit laborum magna non fugiat nisi pariatur ut velit.</p>', notes: '<p>Fugiat in exercitation nostrud labore labore irure ex magna ex aliquip veniam sit irure irure deserunt occaecat tempor cillum aliqua dolore ea tempor dolore laboris est amet quis consequat quis.</p><p>Esse officia velit consectetur ullamco ea pariatur mollit sit consectetur sint mollit commodo anim anim ea amet consectetur eiusmod aliqua excepteur elit laborum magna non fugiat nisi pariatur ut velit.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '7585015c-ada2-4f88-998d-9646865d1ad2', id: '7585015c-ada2-4f88-998d-9646865d1ad2',
@ -2234,9 +2107,7 @@ export const contacts = [
birthday: '1968-10-16T12:00:00.000Z', birthday: '1968-10-16T12:00:00.000Z',
address: '315 Albemarle Road, Allison, Arkansas, PO6008', address: '315 Albemarle Road, Allison, Arkansas, PO6008',
notes: '<p>Eiusmod deserunt aliqua dolore ipsum cillum veniam minim dolore nulla aute aliqua voluptate labore sint cillum excepteur nulla nostrud do cupidatat eu adipisicing reprehenderit deserunt elit qui mollit adipisicing eu.</p><p>Proident commodo magna eu voluptate eiusmod aliqua laborum eu ea elit quis ullamco ullamco magna minim enim amet dolore sit lorem aliqua officia amet officia non magna enim cillum sit.</p>', notes: '<p>Eiusmod deserunt aliqua dolore ipsum cillum veniam minim dolore nulla aute aliqua voluptate labore sint cillum excepteur nulla nostrud do cupidatat eu adipisicing reprehenderit deserunt elit qui mollit adipisicing eu.</p><p>Proident commodo magna eu voluptate eiusmod aliqua laborum eu ea elit quis ullamco ullamco magna minim enim amet dolore sit lorem aliqua officia amet officia non magna enim cillum sit.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '32c73a6a-67f2-48a9-b2a1-b23da83187bb', id: '32c73a6a-67f2-48a9-b2a1-b23da83187bb',
@ -2270,9 +2141,7 @@ export const contacts = [
birthday: '1968-09-08T12:00:00.000Z', birthday: '1968-09-08T12:00:00.000Z',
address: '818 Aviation Road, Geyserville, Palau, PO9655', address: '818 Aviation Road, Geyserville, Palau, PO9655',
notes: '<p>Cupidatat lorem tempor commodo do eu ea dolor eiusmod do nisi occaecat fugiat labore non esse aliquip ullamco laboris adipisicing pariatur nostrud enim minim do fugiat culpa exercitation lorem duis.</p><p>Pariatur cupidatat tempor est et nostrud in amet aliquip sint nulla amet ea lorem irure sint sit ea aliquip voluptate id laboris fugiat cillum cillum dolore deserunt fugiat ad tempor.</p>', notes: '<p>Cupidatat lorem tempor commodo do eu ea dolor eiusmod do nisi occaecat fugiat labore non esse aliquip ullamco laboris adipisicing pariatur nostrud enim minim do fugiat culpa exercitation lorem duis.</p><p>Pariatur cupidatat tempor est et nostrud in amet aliquip sint nulla amet ea lorem irure sint sit ea aliquip voluptate id laboris fugiat cillum cillum dolore deserunt fugiat ad tempor.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '114642a2-ccb7-4cb1-ad2b-5e9b6a0c1d2e', id: '114642a2-ccb7-4cb1-ad2b-5e9b6a0c1d2e',
@ -2307,9 +2176,7 @@ export const contacts = [
birthday: '1967-03-02T12:00:00.000Z', birthday: '1967-03-02T12:00:00.000Z',
address: '386 Vernon Avenue, Dragoon, North Carolina, PO4559', address: '386 Vernon Avenue, Dragoon, North Carolina, PO4559',
notes: '<p>Esse amet ex duis esse aliqua non tempor ullamco dolore et aliquip nisi pariatur qui laborum id consequat tempor sint eiusmod exercitation velit aliquip occaecat tempor nisi aute magna sint.</p><p>Deserunt veniam voluptate dolore eiusmod eu consequat dolor sit pariatur laboris anim excepteur consequat nulla officia exercitation magna sint ea excepteur qui eu officia pariatur culpa sint elit nulla officia.</p>', notes: '<p>Esse amet ex duis esse aliqua non tempor ullamco dolore et aliquip nisi pariatur qui laborum id consequat tempor sint eiusmod exercitation velit aliquip occaecat tempor nisi aute magna sint.</p><p>Deserunt veniam voluptate dolore eiusmod eu consequat dolor sit pariatur laboris anim excepteur consequat nulla officia exercitation magna sint ea excepteur qui eu officia pariatur culpa sint elit nulla officia.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '310ece7d-dbb0-45d6-9e69-14c24e50fe3d', id: '310ece7d-dbb0-45d6-9e69-14c24e50fe3d',
@ -2339,9 +2206,7 @@ export const contacts = [
birthday: '1992-09-04T12:00:00.000Z', birthday: '1992-09-04T12:00:00.000Z',
address: '238 Rochester Avenue, Lydia, Oklahoma, PO3914', address: '238 Rochester Avenue, Lydia, Oklahoma, PO3914',
notes: '<p>Excepteur do ullamco voluptate deserunt tempor ullamco enim non incididunt adipisicing sunt sint sit qui occaecat occaecat id laboris et duis amet reprehenderit cupidatat aliquip dolore ea eu ea nulla.</p><p>Cillum nulla deserunt laboris eu sint dolor non laboris cupidatat aute nisi amet mollit ipsum cillum excepteur consequat tempor exercitation consequat nostrud ipsum qui excepteur eiusmod nostrud laboris pariatur sint.</p>', notes: '<p>Excepteur do ullamco voluptate deserunt tempor ullamco enim non incididunt adipisicing sunt sint sit qui occaecat occaecat id laboris et duis amet reprehenderit cupidatat aliquip dolore ea eu ea nulla.</p><p>Cillum nulla deserunt laboris eu sint dolor non laboris cupidatat aute nisi amet mollit ipsum cillum excepteur consequat tempor exercitation consequat nostrud ipsum qui excepteur eiusmod nostrud laboris pariatur sint.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: 'dcc673f6-de59-4715-94ed-8f64663d449b', id: 'dcc673f6-de59-4715-94ed-8f64663d449b',
@ -2376,9 +2241,7 @@ export const contacts = [
birthday: '1984-09-08T12:00:00.000Z', birthday: '1984-09-08T12:00:00.000Z',
address: '112 Tillary Street, Camptown, Vermont, PO8827', address: '112 Tillary Street, Camptown, Vermont, PO8827',
notes: '<p>Pariatur tempor laborum deserunt commodo eiusmod adipisicing amet anim irure fugiat laboris velit do velit elit aute deserunt officia fugiat nulla ullamco est elit veniam officia sit veniam velit commodo.</p><p>Laboris duis eu adipisicing esse fugiat voluptate enim sint in voluptate lorem laboris eiusmod commodo nostrud dolor qui incididunt non fugiat culpa aliquip minim voluptate lorem sint sunt velit eiusmod.</p>', notes: '<p>Pariatur tempor laborum deserunt commodo eiusmod adipisicing amet anim irure fugiat laboris velit do velit elit aute deserunt officia fugiat nulla ullamco est elit veniam officia sit veniam velit commodo.</p><p>Laboris duis eu adipisicing esse fugiat voluptate enim sint in voluptate lorem laboris eiusmod commodo nostrud dolor qui incididunt non fugiat culpa aliquip minim voluptate lorem sint sunt velit eiusmod.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '3e4ca731-d39b-4ad9-b6e0-f84e67f4b74a', id: '3e4ca731-d39b-4ad9-b6e0-f84e67f4b74a',
@ -2412,9 +2275,7 @@ export const contacts = [
birthday: '1988-11-11T12:00:00.000Z', birthday: '1988-11-11T12:00:00.000Z',
address: '951 Hampton Avenue, Bartonsville, Mississippi, PO4232', address: '951 Hampton Avenue, Bartonsville, Mississippi, PO4232',
notes: '<p>Ad lorem id irure aute ipsum ex occaecat commodo dolore eu dolor exercitation anim quis officia deserunt lorem sunt officia eu sit aliquip laborum id duis aliqua quis aute magna.</p><p>Do do lorem est amet aliqua ex excepteur nisi cupidatat esse consequat ipsum in ad eiusmod proident cupidatat dolore anim ut pariatur sint do elit incididunt officia adipisicing amet eu.</p>', notes: '<p>Ad lorem id irure aute ipsum ex occaecat commodo dolore eu dolor exercitation anim quis officia deserunt lorem sunt officia eu sit aliquip laborum id duis aliqua quis aute magna.</p><p>Do do lorem est amet aliqua ex excepteur nisi cupidatat esse consequat ipsum in ad eiusmod proident cupidatat dolore anim ut pariatur sint do elit incididunt officia adipisicing amet eu.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
{ {
id: '2012d4a5-19e4-444d-aaff-1d8b1d853650', id: '2012d4a5-19e4-444d-aaff-1d8b1d853650',
@ -2449,9 +2310,7 @@ export const contacts = [
birthday: '1987-05-17T12:00:00.000Z', birthday: '1987-05-17T12:00:00.000Z',
address: '157 Woodhull Street, Rutherford, West Virginia, PO6646', address: '157 Woodhull Street, Rutherford, West Virginia, PO6646',
notes: '<p>Duis laboris consectetur et anim eiusmod laborum aute mollit ut officia ipsum dolore eiusmod ex eu elit officia est amet aliquip ullamco veniam proident id aliquip duis qui voluptate fugiat.</p><p>Sunt aliquip nulla amet sint culpa laboris quis proident qui veniam excepteur ullamco irure non eu occaecat est enim ut velit dolore sit tempor cillum reprehenderit proident velit lorem ad.</p>', notes: '<p>Duis laboris consectetur et anim eiusmod laborum aute mollit ut officia ipsum dolore eiusmod ex eu elit officia est amet aliquip ullamco veniam proident id aliquip duis qui voluptate fugiat.</p><p>Sunt aliquip nulla amet sint culpa laboris quis proident qui veniam excepteur ullamco irure non eu occaecat est enim ut velit dolore sit tempor cillum reprehenderit proident velit lorem ad.</p>',
tags : [ tags: ['2026ce08-d08f-4b4f-9506-b10cdb5b104f'],
'2026ce08-d08f-4b4f-9506-b10cdb5b104f',
],
}, },
{ {
id: '012b8219-74bf-447c-af2c-66904d90a956', id: '012b8219-74bf-447c-af2c-66904d90a956',
@ -2480,9 +2339,7 @@ export const contacts = [
birthday: '1963-08-10T12:00:00.000Z', birthday: '1963-08-10T12:00:00.000Z',
address: '604 Merit Court, Wyano, New Hampshire, PO1641', address: '604 Merit Court, Wyano, New Hampshire, PO1641',
notes: '<p>Dolor anim fugiat aliquip eiusmod lorem nisi adipisicing ea deserunt est quis non sit nulla voluptate deserunt magna eiusmod irure labore fugiat consectetur laboris velit voluptate exercitation aute magna sit.</p><p>Sunt ullamco quis qui ea ullamco quis sit ex nisi deserunt fugiat qui culpa minim proident dolor veniam lorem nulla amet do dolor proident sunt ex incididunt ipsum cillum non.</p>', notes: '<p>Dolor anim fugiat aliquip eiusmod lorem nisi adipisicing ea deserunt est quis non sit nulla voluptate deserunt magna eiusmod irure labore fugiat consectetur laboris velit voluptate exercitation aute magna sit.</p><p>Sunt ullamco quis qui ea ullamco quis sit ex nisi deserunt fugiat qui culpa minim proident dolor veniam lorem nulla amet do dolor proident sunt ex incididunt ipsum cillum non.</p>',
tags : [ tags: ['c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309'],
'c31e9e5d-e0cb-4574-a13f-8a6ee5ff8309',
],
}, },
{ {
id: '8b1befd2-66a7-4981-ae52-77f01b382d18', id: '8b1befd2-66a7-4981-ae52-77f01b382d18',
@ -2521,9 +2378,7 @@ export const contacts = [
birthday: '1975-09-02T12:00:00.000Z', birthday: '1975-09-02T12:00:00.000Z',
address: '100 Menahan Street, Snyderville, Kansas, PO1006', address: '100 Menahan Street, Snyderville, Kansas, PO1006',
notes: '<p>Sint anim sint tempor proident irure proident exercitation dolor enim in sint non occaecat tempor mollit dolore ea labore ipsum sunt in incididunt proident excepteur id in velit et quis.</p><p>Amet mollit ut nostrud cupidatat ut culpa irure in ex occaecat aute aliqua tempor incididunt elit nulla irure aliqua ea do amet ex elit incididunt minim eu fugiat elit pariatur.</p>', notes: '<p>Sint anim sint tempor proident irure proident exercitation dolor enim in sint non occaecat tempor mollit dolore ea labore ipsum sunt in incididunt proident excepteur id in velit et quis.</p><p>Amet mollit ut nostrud cupidatat ut culpa irure in ex occaecat aute aliqua tempor incididunt elit nulla irure aliqua ea do amet ex elit incididunt minim eu fugiat elit pariatur.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '844668c3-5e20-4fed-9e3a-7d274f696e61', id: '844668c3-5e20-4fed-9e3a-7d274f696e61',
@ -2552,9 +2407,7 @@ export const contacts = [
birthday: '1986-03-15T12:00:00.000Z', birthday: '1986-03-15T12:00:00.000Z',
address: '283 Albany Avenue, Jennings, Rhode Island, PO1646', address: '283 Albany Avenue, Jennings, Rhode Island, PO1646',
notes: '<p>Id est dolore nostrud consectetur ullamco aliquip dolore nisi consectetur cupidatat consectetur ut lorem exercitation laborum est culpa qui aliquip fugiat fugiat laborum minim sint sit laborum elit consectetur occaecat.</p><p>Cillum eu aliquip ex enim dolore enim ea pariatur elit voluptate in eu magna eu voluptate est cupidatat aliqua cupidatat ex eu dolor voluptate velit fugiat ipsum labore labore aliqua.</p>', notes: '<p>Id est dolore nostrud consectetur ullamco aliquip dolore nisi consectetur cupidatat consectetur ut lorem exercitation laborum est culpa qui aliquip fugiat fugiat laborum minim sint sit laborum elit consectetur occaecat.</p><p>Cillum eu aliquip ex enim dolore enim ea pariatur elit voluptate in eu magna eu voluptate est cupidatat aliqua cupidatat ex eu dolor voluptate velit fugiat ipsum labore labore aliqua.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '5a01e870-8be1-45a5-b58a-ec09c06e8f28', id: '5a01e870-8be1-45a5-b58a-ec09c06e8f28',
@ -2584,9 +2437,7 @@ export const contacts = [
birthday: '1975-04-22T12:00:00.000Z', birthday: '1975-04-22T12:00:00.000Z',
address: '560 Dooley Street, Ellerslie, Louisiana, PO1005', address: '560 Dooley Street, Ellerslie, Louisiana, PO1005',
notes: '<p>Pariatur esse ex laborum ex dolor laborum proident enim consectetur occaecat magna adipisicing magna dolore officia aute et dolor aliquip enim adipisicing culpa reprehenderit aliqua officia qui pariatur aliquip occaecat.</p><p>Excepteur est nisi officia eiusmod et duis mollit labore minim duis officia lorem ipsum duis deserunt cupidatat excepteur nostrud incididunt non cillum fugiat adipisicing anim consectetur nostrud aliquip labore cupidatat.</p>', notes: '<p>Pariatur esse ex laborum ex dolor laborum proident enim consectetur occaecat magna adipisicing magna dolore officia aute et dolor aliquip enim adipisicing culpa reprehenderit aliqua officia qui pariatur aliquip occaecat.</p><p>Excepteur est nisi officia eiusmod et duis mollit labore minim duis officia lorem ipsum duis deserunt cupidatat excepteur nostrud incididunt non cillum fugiat adipisicing anim consectetur nostrud aliquip labore cupidatat.</p>',
tags : [ tags: ['56ddbd47-4078-4ddd-8448-73c5e88d5f59'],
'56ddbd47-4078-4ddd-8448-73c5e88d5f59',
],
}, },
{ {
id: '5ac1f193-f150-45f9-bfe4-b7b4e1a83ff9', id: '5ac1f193-f150-45f9-bfe4-b7b4e1a83ff9',
@ -2621,9 +2472,7 @@ export const contacts = [
birthday: '1965-08-02T12:00:00.000Z', birthday: '1965-08-02T12:00:00.000Z',
address: '445 Remsen Avenue, Ruckersville, Delaware, PO2712', address: '445 Remsen Avenue, Ruckersville, Delaware, PO2712',
notes: '<p>Pariatur do nisi labore culpa minim aliquip excepteur voluptate id id aute eu aliquip adipisicing nulla laboris consectetur dolore ullamco ut exercitation fugiat excepteur veniam ex cillum cupidatat ad adipisicing.</p><p>Dolor culpa dolor magna incididunt voluptate sunt amet dolor cillum ut nostrud nisi quis ex pariatur enim dolore sunt sunt cupidatat id non lorem magna esse amet commodo minim id.</p>', notes: '<p>Pariatur do nisi labore culpa minim aliquip excepteur voluptate id id aute eu aliquip adipisicing nulla laboris consectetur dolore ullamco ut exercitation fugiat excepteur veniam ex cillum cupidatat ad adipisicing.</p><p>Dolor culpa dolor magna incididunt voluptate sunt amet dolor cillum ut nostrud nisi quis ex pariatur enim dolore sunt sunt cupidatat id non lorem magna esse amet commodo minim id.</p>',
tags : [ tags: ['cbde2486-5033-4e09-838e-e901b108cd41'],
'cbde2486-5033-4e09-838e-e901b108cd41',
],
}, },
{ {
id: '995df091-d78a-4bb7-840c-ba6a7d14a1bd', id: '995df091-d78a-4bb7-840c-ba6a7d14a1bd',
@ -2653,9 +2502,7 @@ export const contacts = [
birthday: '1978-03-22T12:00:00.000Z', birthday: '1978-03-22T12:00:00.000Z',
address: '911 Lois Avenue, Epworth, California, PO6557', address: '911 Lois Avenue, Epworth, California, PO6557',
notes: '<p>Veniam deserunt aliquip culpa commodo et est ea cillum ea pariatur reprehenderit dolore adipisicing voluptate dolor eiusmod tempor exercitation reprehenderit nostrud labore nostrud do nulla commodo officia qui culpa ea.</p><p>Velit deserunt do ut esse tempor minim cupidatat amet qui consequat enim duis elit veniam sunt sit aliquip irure cillum irure sunt officia incididunt cupidatat commodo amet non qui anim.</p>', notes: '<p>Veniam deserunt aliquip culpa commodo et est ea cillum ea pariatur reprehenderit dolore adipisicing voluptate dolor eiusmod tempor exercitation reprehenderit nostrud labore nostrud do nulla commodo officia qui culpa ea.</p><p>Velit deserunt do ut esse tempor minim cupidatat amet qui consequat enim duis elit veniam sunt sit aliquip irure cillum irure sunt officia incididunt cupidatat commodo amet non qui anim.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: '7184be71-a28f-4f2b-8c45-15f78cf2f825', id: '7184be71-a28f-4f2b-8c45-15f78cf2f825',
@ -2680,9 +2527,7 @@ export const contacts = [
birthday: '1993-10-19T12:00:00.000Z', birthday: '1993-10-19T12:00:00.000Z',
address: '514 Sutter Avenue, Shindler, Puerto Rico, PO3862', address: '514 Sutter Avenue, Shindler, Puerto Rico, PO3862',
notes: '<p>Ullamco ut aute reprehenderit velit incididunt veniam consequat ut ipsum sint laborum duis officia pariatur mollit enim nulla reprehenderit dolor aliquip labore ex aute in sunt dolor nulla reprehenderit dolor.</p><p>Ad enim ex non minim commodo culpa culpa ex est anim aute adipisicing proident ut ex et aliquip amet exercitation lorem tempor laborum quis reprehenderit veniam proident ullamco id eiusmod.</p>', notes: '<p>Ullamco ut aute reprehenderit velit incididunt veniam consequat ut ipsum sint laborum duis officia pariatur mollit enim nulla reprehenderit dolor aliquip labore ex aute in sunt dolor nulla reprehenderit dolor.</p><p>Ad enim ex non minim commodo culpa culpa ex est anim aute adipisicing proident ut ex et aliquip amet exercitation lorem tempor laborum quis reprehenderit veniam proident ullamco id eiusmod.</p>',
tags : [ tags: ['3eaab175-ec0d-4db7-bc3b-efc633c769be'],
'3eaab175-ec0d-4db7-bc3b-efc633c769be',
],
}, },
{ {
id: '325d508c-ca49-42bf-b0d5-c4a6b8da3d5c', id: '325d508c-ca49-42bf-b0d5-c4a6b8da3d5c',
@ -2707,9 +2552,7 @@ export const contacts = [
birthday: '1967-01-05T12:00:00.000Z', birthday: '1967-01-05T12:00:00.000Z',
address: '569 Clermont Avenue, Movico, Marshall Islands, PO7293', address: '569 Clermont Avenue, Movico, Marshall Islands, PO7293',
notes: '<p>Duis laborum magna ipsum officia cillum ea ut commodo anim exercitation incididunt id ipsum nisi consectetur aute officia culpa anim in veniam ad officia consequat qui ullamco ea laboris ad.</p><p>Ad ea excepteur ea veniam nostrud est labore ea consectetur laboris cupidatat aute pariatur aute mollit dolor do deserunt nisi mollit fugiat qui officia ullamco est officia est ullamco consequat.</p>', notes: '<p>Duis laborum magna ipsum officia cillum ea ut commodo anim exercitation incididunt id ipsum nisi consectetur aute officia culpa anim in veniam ad officia consequat qui ullamco ea laboris ad.</p><p>Ad ea excepteur ea veniam nostrud est labore ea consectetur laboris cupidatat aute pariatur aute mollit dolor do deserunt nisi mollit fugiat qui officia ullamco est officia est ullamco consequat.</p>',
tags : [ tags: ['65930b5a-5d2a-4303-b11f-865d69e6fdb5'],
'65930b5a-5d2a-4303-b11f-865d69e6fdb5',
],
}, },
{ {
id: 'c674b6e1-b846-4bba-824b-0b4df0cdec48', id: 'c674b6e1-b846-4bba-824b-0b4df0cdec48',
@ -2748,9 +2591,7 @@ export const contacts = [
birthday: '1977-11-06T12:00:00.000Z', birthday: '1977-11-06T12:00:00.000Z',
address: '103 Chestnut Avenue, Glenbrook, Indiana, PO2578', address: '103 Chestnut Avenue, Glenbrook, Indiana, PO2578',
notes: '<p>Ad ipsum occaecat dolore ullamco labore ex sint est pariatur aliquip ea do esse do est dolore duis excepteur esse irure eiusmod pariatur elit nostrud laboris ad ex nostrud nostrud.</p><p>Occaecat proident magna elit ullamco ea incididunt fugiat est nulla reprehenderit in veniam esse qui minim aliqua tempor excepteur dolor et tempor occaecat in veniam esse qui exercitation laborum esse.</p>', notes: '<p>Ad ipsum occaecat dolore ullamco labore ex sint est pariatur aliquip ea do esse do est dolore duis excepteur esse irure eiusmod pariatur elit nostrud laboris ad ex nostrud nostrud.</p><p>Occaecat proident magna elit ullamco ea incididunt fugiat est nulla reprehenderit in veniam esse qui minim aliqua tempor excepteur dolor et tempor occaecat in veniam esse qui exercitation laborum esse.</p>',
tags : [ tags: ['a8991c76-2fda-4bbd-a718-df13d6478847'],
'a8991c76-2fda-4bbd-a718-df13d6478847',
],
}, },
]; ];
export const countries = [ export const countries = [

View File

@ -1,11 +1,16 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import { brands as brandsData, categories as categoriesData, products as productsData, tags as tagsData, vendors as vendorsData } from 'app/mock-api/apps/ecommerce/inventory/data'; import {
brands as brandsData,
categories as categoriesData,
products as productsData,
tags as tagsData,
vendors as vendorsData,
} from 'app/mock-api/apps/ecommerce/inventory/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ECommerceInventoryMockApi export class ECommerceInventoryMockApi {
{
private _categories: any[] = categoriesData; private _categories: any[] = categoriesData;
private _brands: any[] = brandsData; private _brands: any[] = brandsData;
private _products: any[] = productsData; private _products: any[] = productsData;
@ -15,8 +20,7 @@ export class ECommerceInventoryMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -28,8 +32,7 @@ export class ECommerceInventoryMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Categories - GET // @ Categories - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -49,8 +52,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/ecommerce/inventory/products', 300) .onGet('api/apps/ecommerce/inventory/products', 300)
.reply(({request}) => .reply(({ request }) => {
{
// Get available queries // Get available queries
const search = request.params.get('search'); const search = request.params.get('search');
const sort = request.params.get('sort') || 'name'; const sort = request.params.get('sort') || 'name';
@ -62,25 +64,30 @@ export class ECommerceInventoryMockApi
let products: any[] | null = cloneDeep(this._products); let products: any[] | null = cloneDeep(this._products);
// Sort the products // Sort the products
if ( sort === 'sku' || sort === 'name' || sort === 'active' ) if (sort === 'sku' || sort === 'name' || sort === 'active') {
{ products.sort((a, b) => {
products.sort((a, b) =>
{
const fieldA = a[sort].toString().toUpperCase(); const fieldA = a[sort].toString().toUpperCase();
const fieldB = b[sort].toString().toUpperCase(); const fieldB = b[sort].toString().toUpperCase();
return order === 'asc' ? fieldA.localeCompare(fieldB) : fieldB.localeCompare(fieldA); return order === 'asc'
? fieldA.localeCompare(fieldB)
: fieldB.localeCompare(fieldA);
}); });
} } else {
else products.sort((a, b) =>
{ order === 'asc' ? a[sort] - b[sort] : b[sort] - a[sort]
products.sort((a, b) => order === 'asc' ? a[sort] - b[sort] : b[sort] - a[sort]); );
} }
// If search exists... // If search exists...
if ( search ) if (search) {
{
// Filter the products // Filter the products
products = products.filter(contact => contact.name && contact.name.toLowerCase().includes(search.toLowerCase())); products = products.filter(
(contact) =>
contact.name &&
contact.name
.toLowerCase()
.includes(search.toLowerCase())
);
} }
// Paginate - Start // Paginate - Start
@ -88,7 +95,7 @@ export class ECommerceInventoryMockApi
// Calculate pagination details // Calculate pagination details
const begin = page * size; const begin = page * size;
const end = Math.min((size * (page + 1)), productsLength); const end = Math.min(size * (page + 1), productsLength);
const lastPage = Math.max(Math.ceil(productsLength / size), 1); const lastPage = Math.max(Math.ceil(productsLength / size), 1);
// Prepare the pagination object // Prepare the pagination object
@ -98,15 +105,12 @@ export class ECommerceInventoryMockApi
// the last possible page number, return null for // the last possible page number, return null for
// products but also send the last possible page so // products but also send the last possible page so
// the app can navigate to there // the app can navigate to there
if ( page > lastPage ) if (page > lastPage) {
{
products = null; products = null;
pagination = { pagination = {
lastPage, lastPage,
}; };
} } else {
else
{
// Paginate the results by size // Paginate the results by size
products = products.slice(begin, end); products = products.slice(begin, end);
@ -136,8 +140,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/ecommerce/inventory/product') .onGet('api/apps/ecommerce/inventory/product')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id from the params // Get the id from the params
const id = request.params.get('id'); const id = request.params.get('id');
@ -145,7 +148,7 @@ export class ECommerceInventoryMockApi
const products = cloneDeep(this._products); const products = cloneDeep(this._products);
// Find the product // Find the product
const product = products.find(item => item.id === id); const product = products.find((item) => item.id === id);
// Return the response // Return the response
return [200, product]; return [200, product];
@ -156,8 +159,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/ecommerce/inventory/product') .onPost('api/apps/ecommerce/inventory/product')
.reply(() => .reply(() => {
{
// Generate a new product // Generate a new product
const newProduct = { const newProduct = {
id: FuseMockApiUtils.guid(), id: FuseMockApiUtils.guid(),
@ -193,8 +195,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/ecommerce/inventory/product') .onPatch('api/apps/ecommerce/inventory/product')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and product // Get the id and product
const id = request.body.id; const id = request.body.id;
const product = cloneDeep(request.body.product); const product = cloneDeep(request.body.product);
@ -203,10 +204,8 @@ export class ECommerceInventoryMockApi
let updatedProduct = null; let updatedProduct = null;
// Find the product and update it // Find the product and update it
this._products.forEach((item, index, products) => this._products.forEach((item, index, products) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the product // Update the product
products[index] = assign({}, products[index], product); products[index] = assign({}, products[index], product);
@ -224,16 +223,13 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/ecommerce/inventory/product') .onDelete('api/apps/ecommerce/inventory/product')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the product and delete it // Find the product and delete it
this._products.forEach((item, index) => this._products.forEach((item, index) => {
{ if (item.id === id) {
if ( item.id === id )
{
this._products.splice(index, 1); this._products.splice(index, 1);
} }
}); });
@ -254,8 +250,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/ecommerce/inventory/tag') .onPost('api/apps/ecommerce/inventory/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the tag // Get the tag
const newTag = cloneDeep(request.body.tag); const newTag = cloneDeep(request.body.tag);
@ -274,8 +269,7 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/ecommerce/inventory/tag') .onPatch('api/apps/ecommerce/inventory/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and tag // Get the id and tag
const id = request.body.id; const id = request.body.id;
const tag = cloneDeep(request.body.tag); const tag = cloneDeep(request.body.tag);
@ -284,10 +278,8 @@ export class ECommerceInventoryMockApi
let updatedTag = null; let updatedTag = null;
// Find the tag and update it // Find the tag and update it
this._tags.forEach((item, index, tags) => this._tags.forEach((item, index, tags) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the tag // Update the tag
tags[index] = assign({}, tags[index], tag); tags[index] = assign({}, tags[index], tag);
@ -305,26 +297,24 @@ export class ECommerceInventoryMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/ecommerce/inventory/tag') .onDelete('api/apps/ecommerce/inventory/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the tag and delete it // Find the tag and delete it
this._tags.forEach((item, index) => this._tags.forEach((item, index) => {
{ if (item.id === id) {
if ( item.id === id )
{
this._tags.splice(index, 1); this._tags.splice(index, 1);
} }
}); });
// Get the products that have the tag // Get the products that have the tag
const productsWithTag = this._products.filter(product => product.tags.indexOf(id) > -1); const productsWithTag = this._products.filter(
(product) => product.tags.indexOf(id) > -1
);
// Iterate through them and delete the tag // Iterate through them and delete the tag
productsWithTag.forEach((product) => productsWithTag.forEach((product) => {
{
product.tags.splice(product.tags.indexOf(id), 1); product.tags.splice(product.tags.indexOf(id), 1);
}); });

View File

@ -110,7 +110,8 @@ export const products = [
id: '7eb7c859-1347-4317-96b6-9476a7e2ba3c', id: '7eb7c859-1347-4317-96b6-9476a7e2ba3c',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Capmia Mens Chronograph Watch 44mm 5 ATM', name: 'Capmia Mens Chronograph Watch 44mm 5 ATM',
description: 'Consequat esse in culpa commodo anim. Et ullamco anim amet est. Sunt dolore ex occaecat officia anim. In sit minim laborum nostrud. Consequat ex do velit voluptate do exercitation est adipisicing quis velit.', description:
'Consequat esse in culpa commodo anim. Et ullamco anim amet est. Sunt dolore ex occaecat officia anim. In sit minim laborum nostrud. Consequat ex do velit voluptate do exercitation est adipisicing quis velit.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -141,7 +142,8 @@ export const products = [
id: '00b0292f-3d50-4669-a0c4-7a9d85efc98d', id: '00b0292f-3d50-4669-a0c4-7a9d85efc98d',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Zeon Ladies Chronograph Watch 40mm 10 ATM', name: 'Zeon Ladies Chronograph Watch 40mm 10 ATM',
description: 'Nulla duis dolor fugiat culpa proident. Duis anim est excepteur occaecat adipisicing occaecat. Labore id laborum non elit proident est veniam officia eu. Labore aliqua nisi duis sint ex consequat nostrud excepteur duis ex incididunt adipisicing.', description:
'Nulla duis dolor fugiat culpa proident. Duis anim est excepteur occaecat adipisicing occaecat. Labore id laborum non elit proident est veniam officia eu. Labore aliqua nisi duis sint ex consequat nostrud excepteur duis ex incididunt adipisicing.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -172,7 +174,8 @@ export const products = [
id: '3f34e2fb-95bf-4f61-be28-956d2c7e4eb2', id: '3f34e2fb-95bf-4f61-be28-956d2c7e4eb2',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Benton Mens Automatic Watch 44mm 5 ATM', name: 'Benton Mens Automatic Watch 44mm 5 ATM',
description: 'Velit irure deserunt aliqua officia. Eiusmod quis sunt magna laboris aliquip non dolor consequat cupidatat dolore esse. Consectetur mollit officia laborum fugiat nulla duis ad excepteur do aliqua fugiat. Fugiat non laboris exercitation ipsum in incididunt.', description:
'Velit irure deserunt aliqua officia. Eiusmod quis sunt magna laboris aliquip non dolor consequat cupidatat dolore esse. Consectetur mollit officia laborum fugiat nulla duis ad excepteur do aliqua fugiat. Fugiat non laboris exercitation ipsum in incididunt.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -203,7 +206,8 @@ export const products = [
id: '8fcce528-d878-4cc8-99f7-bd3451ed5405', id: '8fcce528-d878-4cc8-99f7-bd3451ed5405',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Capmia Mens Chronograph Watch 44mm 10 ATM', name: 'Capmia Mens Chronograph Watch 44mm 10 ATM',
description: 'Velit nisi proident cupidatat exercitation occaecat et adipisicing nostrud id ex nostrud sint. Qui fugiat velit minim amet reprehenderit voluptate velit exercitation proident Lorem nisi culpa. Commodo quis officia officia eiusmod mollit aute fugiat duis quis minim culpa in. Exercitation laborum fugiat ex excepteur officia reprehenderit magna ipsum. Laboris dolore nostrud id labore sint consectetur aliqua tempor ea aute do.', description:
'Velit nisi proident cupidatat exercitation occaecat et adipisicing nostrud id ex nostrud sint. Qui fugiat velit minim amet reprehenderit voluptate velit exercitation proident Lorem nisi culpa. Commodo quis officia officia eiusmod mollit aute fugiat duis quis minim culpa in. Exercitation laborum fugiat ex excepteur officia reprehenderit magna ipsum. Laboris dolore nostrud id labore sint consectetur aliqua tempor ea aute do.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -234,7 +238,8 @@ export const products = [
id: '91d96e18-d3f5-4c32-a8bf-1fc525cb92c0', id: '91d96e18-d3f5-4c32-a8bf-1fc525cb92c0',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Benton Ladies Automatic Watch 40mm 5 ATM', name: 'Benton Ladies Automatic Watch 40mm 5 ATM',
description: 'Pariatur proident labore commodo consequat qui et. Ad labore fugiat consectetur ea magna dolore mollit consequat reprehenderit laborum ad mollit eiusmod. Esse laboris voluptate ullamco occaecat labore esse laboris enim ipsum aliquip ipsum. Ea ea proident eu enim anim mollit non consequat enim nulla.', description:
'Pariatur proident labore commodo consequat qui et. Ad labore fugiat consectetur ea magna dolore mollit consequat reprehenderit laborum ad mollit eiusmod. Esse laboris voluptate ullamco occaecat labore esse laboris enim ipsum aliquip ipsum. Ea ea proident eu enim anim mollit non consequat enim nulla.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -265,7 +270,8 @@ export const products = [
id: 'd7a47d7c-4cdf-4319-bbaa-37ade38c622c', id: 'd7a47d7c-4cdf-4319-bbaa-37ade38c622c',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Benton Mens Chronograph Watch 44mm 10 ATM', name: 'Benton Mens Chronograph Watch 44mm 10 ATM',
description: 'Nulla enim reprehenderit proident ut Lorem laborum cillum eiusmod est ex anim. Nisi non non laboris excepteur ullamco elit do duis anim esse labore aliqua adipisicing velit. Deserunt magna exercitation cillum amet.', description:
'Nulla enim reprehenderit proident ut Lorem laborum cillum eiusmod est ex anim. Nisi non non laboris excepteur ullamco elit do duis anim esse labore aliqua adipisicing velit. Deserunt magna exercitation cillum amet.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -296,7 +302,8 @@ export const products = [
id: 'ecf0b3df-38c3-45dc-972b-c509a3dc053e', id: 'ecf0b3df-38c3-45dc-972b-c509a3dc053e',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Benton Mens Chronograph Watch 44mm 10 ATM', name: 'Benton Mens Chronograph Watch 44mm 10 ATM',
description: 'Esse culpa ut ullamco dolore quis adipisicing. Minim veniam quis magna officia non. In pariatur nostrud nisi eiusmod minim anim id. Commodo ex incididunt dolor ad id aliqua incididunt minim in Lorem reprehenderit. Commodo ullamco consectetur aliqua Lorem cupidatat esse veniam consectetur sint veniam duis commodo.', description:
'Esse culpa ut ullamco dolore quis adipisicing. Minim veniam quis magna officia non. In pariatur nostrud nisi eiusmod minim anim id. Commodo ex incididunt dolor ad id aliqua incididunt minim in Lorem reprehenderit. Commodo ullamco consectetur aliqua Lorem cupidatat esse veniam consectetur sint veniam duis commodo.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -327,7 +334,8 @@ export const products = [
id: '5765080a-aaee-40b9-86be-c18b9d79c73c', id: '5765080a-aaee-40b9-86be-c18b9d79c73c',
category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9', category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9',
name: 'Benton Unisex Automatic Watch 40mm 10 ATM', name: 'Benton Unisex Automatic Watch 40mm 10 ATM',
description: 'Anim duis nisi ut ex amet reprehenderit cillum consequat pariatur ipsum elit voluptate excepteur non. Anim enim proident laboris pariatur mollit quis incididunt labore. Incididunt tempor aliquip ex labore ad consequat cillum est sunt anim dolor. Dolore adipisicing non nulla cillum Lorem deserunt. Nostrud incididunt amet sint velit.', description:
'Anim duis nisi ut ex amet reprehenderit cillum consequat pariatur ipsum elit voluptate excepteur non. Anim enim proident laboris pariatur mollit quis incididunt labore. Incididunt tempor aliquip ex labore ad consequat cillum est sunt anim dolor. Dolore adipisicing non nulla cillum Lorem deserunt. Nostrud incididunt amet sint velit.',
tags: [ tags: [
'8ec8f60d-552f-4216-9f11-462b95b1d306', '8ec8f60d-552f-4216-9f11-462b95b1d306',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -358,7 +366,8 @@ export const products = [
id: '6e71be88-b225-474c-91e5-111ced7d6220', id: '6e71be88-b225-474c-91e5-111ced7d6220',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Premera Ladies Chronograph Watch 40mm 5 ATM', name: 'Premera Ladies Chronograph Watch 40mm 5 ATM',
description: 'Velit fugiat adipisicing ut quis anim deserunt ex culpa nostrud laborum. Consectetur duis velit esse commodo voluptate magna dolor in enim exercitation. Ea aliquip cupidatat aute dolor tempor magna id laboris nulla eiusmod ut amet. Veniam irure ex incididunt officia commodo eiusmod nostrud ad consequat commodo ad voluptate.', description:
'Velit fugiat adipisicing ut quis anim deserunt ex culpa nostrud laborum. Consectetur duis velit esse commodo voluptate magna dolor in enim exercitation. Ea aliquip cupidatat aute dolor tempor magna id laboris nulla eiusmod ut amet. Veniam irure ex incididunt officia commodo eiusmod nostrud ad consequat commodo ad voluptate.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -389,7 +398,8 @@ export const products = [
id: '51242500-6983-4a78-bff3-d278eb4e3a57', id: '51242500-6983-4a78-bff3-d278eb4e3a57',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Lara Mens Automatic Watch 44mm 10 ATM', name: 'Lara Mens Automatic Watch 44mm 10 ATM',
description: 'Enim laboris ut non elit dolore est consectetur. Duis irure minim elit velit anim incididunt minim ipsum ullamco ad dolore sunt. Proident aute proident velit elit ex reprehenderit ut. Lorem laborum excepteur elit proident sunt ipsum incididunt id do. Occaecat proident proident qui aute officia cupidatat aliqua aliqua nostrud proident laboris est ad qui. Magna eiusmod amet ut pariatur esse nisi aliquip deserunt minim ad et ea occaecat. Sunt enim cupidatat id eiusmod ea aute quis excepteur irure commodo dolore excepteur.', description:
'Enim laboris ut non elit dolore est consectetur. Duis irure minim elit velit anim incididunt minim ipsum ullamco ad dolore sunt. Proident aute proident velit elit ex reprehenderit ut. Lorem laborum excepteur elit proident sunt ipsum incididunt id do. Occaecat proident proident qui aute officia cupidatat aliqua aliqua nostrud proident laboris est ad qui. Magna eiusmod amet ut pariatur esse nisi aliquip deserunt minim ad et ea occaecat. Sunt enim cupidatat id eiusmod ea aute quis excepteur irure commodo dolore excepteur.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -420,7 +430,8 @@ export const products = [
id: '844a4395-233f-4ffb-85bd-7baa0e490a88', id: '844a4395-233f-4ffb-85bd-7baa0e490a88',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Lara Mens Chronograph Watch 44mm 5 ATM', name: 'Lara Mens Chronograph Watch 44mm 5 ATM',
description: 'Labore irure qui sunt consectetur. Elit nulla id cillum duis. Nulla nulla eu occaecat eiusmod duis irure id do esse. Ad eu incididunt voluptate amet nostrud ullamco mollit dolore occaecat cupidatat nisi reprehenderit. Proident fugiat laborum sit velit ea voluptate.', description:
'Labore irure qui sunt consectetur. Elit nulla id cillum duis. Nulla nulla eu occaecat eiusmod duis irure id do esse. Ad eu incididunt voluptate amet nostrud ullamco mollit dolore occaecat cupidatat nisi reprehenderit. Proident fugiat laborum sit velit ea voluptate.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -451,7 +462,8 @@ export const products = [
id: '7520f1b6-3c45-46ef-a4d5-881971212d1e', id: '7520f1b6-3c45-46ef-a4d5-881971212d1e',
category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9', category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9',
name: 'Benton Unisex Automatic Watch 40mm 10 ATM', name: 'Benton Unisex Automatic Watch 40mm 10 ATM',
description: 'Esse nisi amet occaecat culpa aliqua est ad ea velit. Consectetur in voluptate sit pariatur eiusmod exercitation eu aute occaecat in duis. Voluptate consectetur eu commodo proident id sunt labore irure.', description:
'Esse nisi amet occaecat culpa aliqua est ad ea velit. Consectetur in voluptate sit pariatur eiusmod exercitation eu aute occaecat in duis. Voluptate consectetur eu commodo proident id sunt labore irure.',
tags: [ tags: [
'8ec8f60d-552f-4216-9f11-462b95b1d306', '8ec8f60d-552f-4216-9f11-462b95b1d306',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -482,7 +494,8 @@ export const products = [
id: '683e41d8-6ebc-4e6a-a7c1-9189ca52ef19', id: '683e41d8-6ebc-4e6a-a7c1-9189ca52ef19',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Zeon Mens Chronograph Watch 44mm 10 ATM', name: 'Zeon Mens Chronograph Watch 44mm 10 ATM',
description: 'Eu irure do cupidatat esse in. Aliqua laborum deserunt qui Lorem deserunt minim fugiat deserunt voluptate minim. Anim nulla tempor eiusmod ad exercitation reprehenderit officia. Nisi proident labore eu anim excepteur aliqua occaecat. Laboris nostrud ipsum commodo cupidatat.', description:
'Eu irure do cupidatat esse in. Aliqua laborum deserunt qui Lorem deserunt minim fugiat deserunt voluptate minim. Anim nulla tempor eiusmod ad exercitation reprehenderit officia. Nisi proident labore eu anim excepteur aliqua occaecat. Laboris nostrud ipsum commodo cupidatat.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -513,7 +526,8 @@ export const products = [
id: 'd4e52238-292d-462b-b9bb-1751030132e2', id: 'd4e52238-292d-462b-b9bb-1751030132e2',
category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9', category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9',
name: 'Lara Unisex Chronograph Watch 40mm 5 ATM', name: 'Lara Unisex Chronograph Watch 40mm 5 ATM',
description: 'Nulla nostrud aliquip consequat laborum ut enim exercitation. Aute dolor duis aliquip consequat minim officia. Nisi labore et magna et sunt consectetur id anim pariatur officia et esse ut. Ullamco dolor cillum consequat velit eiusmod consectetur. Ullamco reprehenderit tempor minim dolore officia do nisi cupidatat adipisicing fugiat velit.', description:
'Nulla nostrud aliquip consequat laborum ut enim exercitation. Aute dolor duis aliquip consequat minim officia. Nisi labore et magna et sunt consectetur id anim pariatur officia et esse ut. Ullamco dolor cillum consequat velit eiusmod consectetur. Ullamco reprehenderit tempor minim dolore officia do nisi cupidatat adipisicing fugiat velit.',
tags: [ tags: [
'8ec8f60d-552f-4216-9f11-462b95b1d306', '8ec8f60d-552f-4216-9f11-462b95b1d306',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -544,7 +558,8 @@ export const products = [
id: '98861dfc-0d21-4fd5-81aa-49785d003d95', id: '98861dfc-0d21-4fd5-81aa-49785d003d95',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Premera Mens Automatic Watch 44mm 10 ATM', name: 'Premera Mens Automatic Watch 44mm 10 ATM',
description: 'Veniam sint aliquip aliquip aliquip amet Lorem irure proident laborum et eiusmod aliqua. Aliquip deserunt voluptate magna ut quis magna dolor in dolore. Commodo adipisicing excepteur occaecat aute nisi in. Est aute ad ut incididunt anim ea commodo. Sunt excepteur duis sunt est laborum magna Lorem ullamco exercitation dolore irure.', description:
'Veniam sint aliquip aliquip aliquip amet Lorem irure proident laborum et eiusmod aliqua. Aliquip deserunt voluptate magna ut quis magna dolor in dolore. Commodo adipisicing excepteur occaecat aute nisi in. Est aute ad ut incididunt anim ea commodo. Sunt excepteur duis sunt est laborum magna Lorem ullamco exercitation dolore irure.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -575,7 +590,8 @@ export const products = [
id: 'a71f9b10-e884-4aad-9810-29fe10ce6d42', id: 'a71f9b10-e884-4aad-9810-29fe10ce6d42',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Lara Ladies Chronograph Watch 40mm 5 ATM', name: 'Lara Ladies Chronograph Watch 40mm 5 ATM',
description: 'Deserunt non deserunt ut do labore cupidatat duis veniam in non adipisicing officia esse id. Adipisicing Lorem sint excepteur culpa labore consequat incididunt nulla minim amet. Sint do et fugiat laborum exercitation reprehenderit ut non nostrud occaecat nisi et qui dolore. Amet eiusmod nulla est officia ad magna cillum non dolor ullamco officia incididunt.', description:
'Deserunt non deserunt ut do labore cupidatat duis veniam in non adipisicing officia esse id. Adipisicing Lorem sint excepteur culpa labore consequat incididunt nulla minim amet. Sint do et fugiat laborum exercitation reprehenderit ut non nostrud occaecat nisi et qui dolore. Amet eiusmod nulla est officia ad magna cillum non dolor ullamco officia incididunt.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -606,7 +622,8 @@ export const products = [
id: '149e6db5-4ecc-4021-bc56-08b27514a746', id: '149e6db5-4ecc-4021-bc56-08b27514a746',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Lara Ladies Chronograph Watch 40mm 5 ATM', name: 'Lara Ladies Chronograph Watch 40mm 5 ATM',
description: 'Occaecat proident fugiat consectetur ullamco est. Duis non minim eiusmod magna dolor reprehenderit ad deserunt et qui amet. Tempor cillum dolore veniam Lorem sit ad pariatur et sint. Sunt anim et cupidatat Lorem proident fugiat incididunt incididunt minim non sint. Eiusmod quis et ullamco cillum et veniam do tempor officia sint.', description:
'Occaecat proident fugiat consectetur ullamco est. Duis non minim eiusmod magna dolor reprehenderit ad deserunt et qui amet. Tempor cillum dolore veniam Lorem sit ad pariatur et sint. Sunt anim et cupidatat Lorem proident fugiat incididunt incididunt minim non sint. Eiusmod quis et ullamco cillum et veniam do tempor officia sint.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -637,7 +654,8 @@ export const products = [
id: '655287de-2e24-41f3-a82f-8b08548ecc39', id: '655287de-2e24-41f3-a82f-8b08548ecc39',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Zeon Mens Automatic Watch 44mm 10 ATM', name: 'Zeon Mens Automatic Watch 44mm 10 ATM',
description: 'Eiusmod magna tempor est est quis eu. Minim irure magna anim mollit non adipisicing aute. Nostrud aute consectetur eu in non laboris excepteur esse esse occaecat officia.', description:
'Eiusmod magna tempor est est quis eu. Minim irure magna anim mollit non adipisicing aute. Nostrud aute consectetur eu in non laboris excepteur esse esse occaecat officia.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -668,7 +686,8 @@ export const products = [
id: 'c215b427-d840-4537-aea1-a9bdfa49441b', id: 'c215b427-d840-4537-aea1-a9bdfa49441b',
category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9', category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9',
name: 'Lara Unisex Automatic Watch 40mm 10 ATM', name: 'Lara Unisex Automatic Watch 40mm 10 ATM',
description: 'Excepteur enim non qui consequat sunt exercitation laborum ipsum sunt. Sunt pariatur fugiat voluptate ipsum consectetur do magna culpa labore. Cupidatat non ex labore incididunt aliquip commodo est in. Consectetur mollit nisi aliquip cupidatat do laborum est ullamco velit aliqua fugiat qui adipisicing. Aute reprehenderit quis id sint nulla.', description:
'Excepteur enim non qui consequat sunt exercitation laborum ipsum sunt. Sunt pariatur fugiat voluptate ipsum consectetur do magna culpa labore. Cupidatat non ex labore incididunt aliquip commodo est in. Consectetur mollit nisi aliquip cupidatat do laborum est ullamco velit aliqua fugiat qui adipisicing. Aute reprehenderit quis id sint nulla.',
tags: [ tags: [
'8ec8f60d-552f-4216-9f11-462b95b1d306', '8ec8f60d-552f-4216-9f11-462b95b1d306',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -699,7 +718,8 @@ export const products = [
id: '8b1d9366-891e-49cd-aafb-ac65ce2741e2', id: '8b1d9366-891e-49cd-aafb-ac65ce2741e2',
category: '07986d93-d4eb-4de1-9448-2538407f7254', category: '07986d93-d4eb-4de1-9448-2538407f7254',
name: 'Zeon Ladies Automatic Watch 40mm 10 ATM', name: 'Zeon Ladies Automatic Watch 40mm 10 ATM',
description: 'Reprehenderit magna reprehenderit ex mollit Lorem labore ut. Duis consectetur aliqua cillum occaecat quis ex excepteur fugiat nulla nisi dolor minim. Elit voluptate exercitation nulla et ut adipisicing esse eu nisi amet eu. Ut cillum ipsum quis fugiat proident Lorem est aute ipsum sint dolore consequat.', description:
'Reprehenderit magna reprehenderit ex mollit Lorem labore ut. Duis consectetur aliqua cillum occaecat quis ex excepteur fugiat nulla nisi dolor minim. Elit voluptate exercitation nulla et ut adipisicing esse eu nisi amet eu. Ut cillum ipsum quis fugiat proident Lorem est aute ipsum sint dolore consequat.',
tags: [ tags: [
'3baea410-a7d6-4916-b79a-bdce50c37f95', '3baea410-a7d6-4916-b79a-bdce50c37f95',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',
@ -730,7 +750,8 @@ export const products = [
id: '54e29534-518b-4006-b72a-f21fac6c4d5e', id: '54e29534-518b-4006-b72a-f21fac6c4d5e',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Lara Mens Chronograph Watch 44mm 10 ATM', name: 'Lara Mens Chronograph Watch 44mm 10 ATM',
description: 'Officia eu magna eu amet fugiat qui ullamco eu. Occaecat dolore minim ad tempor consequat adipisicing non Lorem consequat. In nostrud incididunt adipisicing in. Irure occaecat aliquip deserunt minim officia ad excepteur do commodo magna.', description:
'Officia eu magna eu amet fugiat qui ullamco eu. Occaecat dolore minim ad tempor consequat adipisicing non Lorem consequat. In nostrud incididunt adipisicing in. Irure occaecat aliquip deserunt minim officia ad excepteur do commodo magna.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -761,7 +782,8 @@ export const products = [
id: '6a5726e8-c467-45ea-92ab-d83235a06405', id: '6a5726e8-c467-45ea-92ab-d83235a06405',
category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de', category: 'b899ec30-b85a-40ab-bb1f-18a596d5c6de',
name: 'Premera Mens Chronograph Watch 44mm 10 ATM', name: 'Premera Mens Chronograph Watch 44mm 10 ATM',
description: 'Duis id consequat ex officia nisi. Et reprehenderit tempor sunt nostrud. Duis dolore tempor anim non duis qui aute magna officia. Ullamco proident esse enim amet nostrud occaecat veniam. Nostrud ea eiusmod laborum id laborum veniam nulla. Voluptate proident ullamco exercitation id consequat dolore id pariatur esse nulla consectetur.', description:
'Duis id consequat ex officia nisi. Et reprehenderit tempor sunt nostrud. Duis dolore tempor anim non duis qui aute magna officia. Ullamco proident esse enim amet nostrud occaecat veniam. Nostrud ea eiusmod laborum id laborum veniam nulla. Voluptate proident ullamco exercitation id consequat dolore id pariatur esse nulla consectetur.',
tags: [ tags: [
'167190fa-51b4-45fc-a742-8ce1b33d24ea', '167190fa-51b4-45fc-a742-8ce1b33d24ea',
'7d6dd47e-7472-4f8b-93d4-46c114c44533', '7d6dd47e-7472-4f8b-93d4-46c114c44533',
@ -792,7 +814,8 @@ export const products = [
id: 'd7d1d6df-e91f-4c53-982a-2720bc2b4cdd', id: 'd7d1d6df-e91f-4c53-982a-2720bc2b4cdd',
category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9', category: 'ad12aa94-3863-47f8-acab-a638ef02a3e9',
name: 'Capmia Unisex Automatic Watch 40mm 10 ATM', name: 'Capmia Unisex Automatic Watch 40mm 10 ATM',
description: 'Voluptate consectetur nisi aliquip cupidatat sunt labore. Adipisicing voluptate tempor sunt eu irure cupidatat laboris. Enim aliquip aute sit non laborum Lorem in enim duis eu deserunt. Laboris magna irure aute ut proident fugiat laborum aliquip tempor nostrud id. Et esse cupidatat sunt ullamco reprehenderit enim dolore ea in do esse esse id.', description:
'Voluptate consectetur nisi aliquip cupidatat sunt labore. Adipisicing voluptate tempor sunt eu irure cupidatat laboris. Enim aliquip aute sit non laborum Lorem in enim duis eu deserunt. Laboris magna irure aute ut proident fugiat laborum aliquip tempor nostrud id. Et esse cupidatat sunt ullamco reprehenderit enim dolore ea in do esse esse id.',
tags: [ tags: [
'8ec8f60d-552f-4216-9f11-462b95b1d306', '8ec8f60d-552f-4216-9f11-462b95b1d306',
'0fc39efd-f640-41f8-95a5-3f1d749df200', '0fc39efd-f640-41f8-95a5-3f1d749df200',

View File

@ -4,15 +4,13 @@ import { items as itemsData } from 'app/mock-api/apps/file-manager/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class FileManagerMockApi export class FileManagerMockApi {
{
private _items: any[] = itemsData; private _items: any[] = itemsData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,29 +22,30 @@ export class FileManagerMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Items - GET // @ Items - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/file-manager') .onGet('api/apps/file-manager')
.reply(({request}) => .reply(({ request }) => {
{
// Clone the items // Clone the items
let items = cloneDeep(this._items); let items = cloneDeep(this._items);
// See if the folder id exist // See if the folder id exist
const folderId = request.params.get('folderId') === 'null' ? null : request.params.get('folderId'); const folderId =
request.params.get('folderId') === 'null'
? null
: request.params.get('folderId');
// Filter the items by folder id. If folder id is null, // Filter the items by folder id. If folder id is null,
// that means we want to root items which have folder id // that means we want to root items which have folder id
// of null // of null
items = items.filter(item => item.folderId === folderId); items = items.filter((item) => item.folderId === folderId);
// Separate the items by folders and files // Separate the items by folders and files
const folders = items.filter(item => item.type === 'folder'); const folders = items.filter((item) => item.type === 'folder');
const files = items.filter(item => item.type !== 'folder'); const files = items.filter((item) => item.type !== 'folder');
// Sort the folders and files alphabetically by filename // Sort the folders and files alphabetically by filename
folders.sort((a, b) => a.name.localeCompare(b.name)); folders.sort((a, b) => a.name.localeCompare(b.name));
@ -61,19 +60,20 @@ export class FileManagerMockApi
let currentFolder = null; let currentFolder = null;
// Get the current folder and add it as the first entry // Get the current folder and add it as the first entry
if ( folderId ) if (folderId) {
{ currentFolder = pathItems.find(
currentFolder = pathItems.find(item => item.id === folderId); (item) => item.id === folderId
);
path.push(currentFolder); path.push(currentFolder);
} }
// Start traversing and storing the folders as a path array // Start traversing and storing the folders as a path array
// until we hit null on the folder id // until we hit null on the folder id
while ( currentFolder?.folderId ) while (currentFolder?.folderId) {
{ currentFolder = pathItems.find(
currentFolder = pathItems.find(item => item.id === currentFolder.folderId); (item) => item.id === currentFolder.folderId
if ( currentFolder ) );
{ if (currentFolder) {
path.unshift(currentFolder); path.unshift(currentFolder);
} }
} }

View File

@ -10,7 +10,8 @@ export const items = [
size: '87 MB', size: '87 MB',
type: 'folder', type: 'folder',
contents: '57 files', contents: '57 files',
description: 'Personal documents such as insurance policies, tax papers and etc.', description:
'Personal documents such as insurance policies, tax papers and etc.',
}, },
{ {
id: '6da8747f-b474-4c9a-9eba-5ef212285500', id: '6da8747f-b474-4c9a-9eba-5ef212285500',

View File

@ -1,11 +1,16 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService } from '@fuse/lib/mock-api'; import { FuseMockApiService } from '@fuse/lib/mock-api';
import { faqCategories as faqCategoriesData, faqs as faqsData, guideCategories as guideCategoriesData, guideContent as guideContentData, guides as guidesData } from 'app/mock-api/apps/help-center/data'; import {
faqCategories as faqCategoriesData,
faqs as faqsData,
guideCategories as guideCategoriesData,
guideContent as guideContentData,
guides as guidesData,
} from 'app/mock-api/apps/help-center/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HelpCenterMockApi export class HelpCenterMockApi {
{
private _faqCategories: any[] = faqCategoriesData; private _faqCategories: any[] = faqCategoriesData;
private _faqs: any[] = faqsData; private _faqs: any[] = faqsData;
private _guideCategories: any[] = guideCategoriesData; private _guideCategories: any[] = guideCategoriesData;
@ -15,8 +20,7 @@ export class HelpCenterMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -28,15 +32,13 @@ export class HelpCenterMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ FAQs - GET // @ FAQs - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/help-center/faqs') .onGet('api/apps/help-center/faqs')
.reply(({request}) => .reply(({ request }) => {
{
// Get the category slug // Get the category slug
const slug = request.params.get('slug'); const slug = request.params.get('slug');
@ -50,32 +52,31 @@ export class HelpCenterMockApi
const categories = cloneDeep(this._faqCategories); const categories = cloneDeep(this._faqCategories);
// If slug is not provided... // If slug is not provided...
if ( !slug ) if (!slug) {
{
// Go through each category and set the results // Go through each category and set the results
categories.forEach((category) => categories.forEach((category) => {
{ results.push({
results.push(
{
...category, ...category,
faqs: faqs.filter(faq => faq.categoryId === category.id), faqs: faqs.filter(
}, (faq) => faq.categoryId === category.id
); ),
});
}); });
} }
// Otherwise... // Otherwise...
else else {
{
// Find the category by the slug // Find the category by the slug
const category = categories.find(item => item.slug === slug); const category = categories.find(
(item) => item.slug === slug
);
// Set the results // Set the results
results.push( results.push({
{
...category, ...category,
faqs: faqs.filter(faq => faq.categoryId === category.id), faqs: faqs.filter(
}, (faq) => faq.categoryId === category.id
); ),
});
} }
// Return the response // Return the response
@ -87,8 +88,7 @@ export class HelpCenterMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/help-center/guides') .onGet('api/apps/help-center/guides')
.reply(({request}) => .reply(({ request }) => {
{
// Get the slug & limit // Get the slug & limit
const slug = request.params.get('slug'); const slug = request.params.get('slug');
const limit = request.params.get('limit'); const limit = request.params.get('limit');
@ -103,37 +103,40 @@ export class HelpCenterMockApi
const categories = cloneDeep(this._guideCategories); const categories = cloneDeep(this._guideCategories);
// If slug is not provided... // If slug is not provided...
if ( !slug ) if (!slug) {
{
// Parse the limit as an integer // Parse the limit as an integer
const limitNum = parseInt(limit ?? '5', 10); const limitNum = parseInt(limit ?? '5', 10);
// Go through each category and set the results // Go through each category and set the results
categories.forEach((category) => categories.forEach((category) => {
{ results.push({
results.push(
{
...category, ...category,
visibleGuides: limitNum, visibleGuides: limitNum,
totalGuides : guides.filter(guide => guide.categoryId === category.id).length, totalGuides: guides.filter(
guides : guides.filter(guide => guide.categoryId === category.id).slice(0, limitNum), (guide) => guide.categoryId === category.id
}, ).length,
); guides: guides
.filter(
(guide) => guide.categoryId === category.id
)
.slice(0, limitNum),
});
}); });
} }
// Otherwise... // Otherwise...
else else {
{
// Find the category by the slug // Find the category by the slug
const category = categories.find(item => item.slug === slug); const category = categories.find(
(item) => item.slug === slug
);
// Set the results // Set the results
results.push( results.push({
{
...category, ...category,
guides: guides.filter(guide => guide.categoryId === category.id), guides: guides.filter(
}, (guide) => guide.categoryId === category.id
); ),
});
} }
// Return the response // Return the response
@ -145,8 +148,7 @@ export class HelpCenterMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/help-center/guide') .onGet('api/apps/help-center/guide')
.reply(({request}) => .reply(({ request }) => {
{
// Get the slugs // Get the slugs
const categorySlug = request.params.get('categorySlug'); const categorySlug = request.params.get('categorySlug');
const guideSlug = request.params.get('guideSlug'); const guideSlug = request.params.get('guideSlug');
@ -157,8 +159,10 @@ export class HelpCenterMockApi
// Prepare the result // Prepare the result
const result = { const result = {
...categories.find(category => category.slug === categorySlug), ...categories.find(
guides: [guides.find(guide => guide.slug === guideSlug)], (category) => category.slug === categorySlug
),
guides: [guides.find((guide) => guide.slug === guideSlug)],
}; };
// Add the content to the guide // Add the content to the guide

View File

@ -74,7 +74,7 @@ export const faqs = [
{ {
id: 'f55c023a-785e-4f0f-b5b7-47da75224deb', id: 'f55c023a-785e-4f0f-b5b7-47da75224deb',
categoryId: '395b0d41-b9a8-4cd6-8b5c-f07855e82d62', categoryId: '395b0d41-b9a8-4cd6-8b5c-f07855e82d62',
question : 'I\'ve forgotten my username or password', question: "I've forgotten my username or password",
answer: 'In exercitation sunt ad anim commodo sunt do in sunt est officia amet ex ullamco do nisi consectetur lorem proident lorem adipisicing incididunt consequat fugiat voluptate sint est anim officia.\n\nVelit sint aliquip elit culpa amet eu mollit veniam esse deserunt ex occaecat quis lorem minim occaecat culpa esse veniam enim duis excepteur ipsum esse ut ut velit cillum adipisicing.', answer: 'In exercitation sunt ad anim commodo sunt do in sunt est officia amet ex ullamco do nisi consectetur lorem proident lorem adipisicing incididunt consequat fugiat voluptate sint est anim officia.\n\nVelit sint aliquip elit culpa amet eu mollit veniam esse deserunt ex occaecat quis lorem minim occaecat culpa esse veniam enim duis excepteur ipsum esse ut ut velit cillum adipisicing.',
}, },
{ {
@ -197,7 +197,7 @@ export const faqs = [
{ {
id: '2fffd148-7644-466d-8737-7dde88c54154', id: '2fffd148-7644-466d-8737-7dde88c54154',
categoryId: 'bea49ee0-26da-46ad-97be-116cd7ab416d', categoryId: 'bea49ee0-26da-46ad-97be-116cd7ab416d',
question : 'I haven\'t received a response from the author', question: "I haven't received a response from the author",
answer: 'Velit commodo pariatur ullamco elit sunt dolor quis irure amet tempor laboris labore tempor nisi consectetur ea proident dolore culpa nostrud esse amet commodo do esse laboris laboris in magna.\n\nAute officia labore minim laborum irure cupidatat occaecat laborum ex labore ipsum aliqua cillum do exercitation esse et veniam excepteur mollit incididunt ut qui irure culpa qui deserunt nostrud tempor.', answer: 'Velit commodo pariatur ullamco elit sunt dolor quis irure amet tempor laboris labore tempor nisi consectetur ea proident dolore culpa nostrud esse amet commodo do esse laboris laboris in magna.\n\nAute officia labore minim laborum irure cupidatat occaecat laborum ex labore ipsum aliqua cillum do exercitation esse et veniam excepteur mollit incididunt ut qui irure culpa qui deserunt nostrud tempor.',
}, },
{ {
@ -241,28 +241,32 @@ export const guides = [
categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81', categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81',
slug: 'what-is-this-app', slug: 'what-is-this-app',
title: 'What is this app?', title: 'What is this app?',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '7643d388-12ab-4025-a2f1-5045ac7b1c4c', id: '7643d388-12ab-4025-a2f1-5045ac7b1c4c',
categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81', categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81',
slug: 'start-using-the-app', slug: 'start-using-the-app',
title: 'Start using the app', title: 'Start using the app',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '1fecee67-c4b4-413a-b0f2-949dcab73249', id: '1fecee67-c4b4-413a-b0f2-949dcab73249',
categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81', categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81',
slug: 'signing-in-to-the-dashboard', slug: 'signing-in-to-the-dashboard',
title: 'Signing in to the dashboard', title: 'Signing in to the dashboard',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'd2e2ea8f-5298-4ba2-898b-afc60c064bba', id: 'd2e2ea8f-5298-4ba2-898b-afc60c064bba',
categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81', categoryId: '0ee72de7-49c0-4880-9e89-b72a4edd6a81',
slug: 'navigating-within-the-app', slug: 'navigating-within-the-app',
title: 'Navigating within the app', title: 'Navigating within the app',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
// Projects // Projects
{ {
@ -270,56 +274,64 @@ export const guides = [
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'creating-a-project', slug: 'creating-a-project',
title: 'Creating a project', title: 'Creating a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '9ec3f4b9-a355-4f57-9e93-efa8611cc1c9', id: '9ec3f4b9-a355-4f57-9e93-efa8611cc1c9',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'renaming-a-project', slug: 'renaming-a-project',
title: 'Renaming a project', title: 'Renaming a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '1bc6e7f9-b046-4f4f-9b18-741c9d5429f6', id: '1bc6e7f9-b046-4f4f-9b18-741c9d5429f6',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'displaying-a-project', slug: 'displaying-a-project',
title: 'Displaying a project', title: 'Displaying a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'a005d5f1-938d-45c5-8ed4-d0cf8d02e533', id: 'a005d5f1-938d-45c5-8ed4-d0cf8d02e533',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'deleting-a-project', slug: 'deleting-a-project',
title: 'Deleting a project', title: 'Deleting a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '43837279-dce2-4dc0-beac-30b5ba829f14', id: '43837279-dce2-4dc0-beac-30b5ba829f14',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'changing-the-visibility-of-a-project', slug: 'changing-the-visibility-of-a-project',
title: 'Changing the visibility of a project', title: 'Changing the visibility of a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '4cf5a435-eaa0-463c-8d2b-efde193c7fb3', id: '4cf5a435-eaa0-463c-8d2b-efde193c7fb3',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'adding-media-to-a-project', slug: 'adding-media-to-a-project',
title: 'Adding media to a project', title: 'Adding media to a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'cd3fb87e-e138-4721-9e29-a5c751bfd949', id: 'cd3fb87e-e138-4721-9e29-a5c751bfd949',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'removing-a-media-from-a-project', slug: 'removing-a-media-from-a-project',
title: 'Removing a media from a project', title: 'Removing a media from a project',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'f26205c6-882e-4713-b067-c73758b45551', id: 'f26205c6-882e-4713-b067-c73758b45551',
categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc', categoryId: '07b8421f-20bf-45b6-90ee-169ebe3a5bcc',
slug: 'cropping-a-media', slug: 'cropping-a-media',
title: 'Cropping a media', title: 'Cropping a media',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
// Settings // Settings
{ {
@ -327,35 +339,40 @@ export const guides = [
categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b', categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b',
slug: 'general-settings', slug: 'general-settings',
title: 'General settings', title: 'General settings',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '98de7d4a-2ca2-4d47-bbe6-083ed26467db', id: '98de7d4a-2ca2-4d47-bbe6-083ed26467db',
categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b', categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b',
slug: 'project-settings', slug: 'project-settings',
title: 'Project settings', title: 'Project settings',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '145f497c-1fdb-47b5-a6c1-31f856403571', id: '145f497c-1fdb-47b5-a6c1-31f856403571',
categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b', categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b',
slug: 'media-settings', slug: 'media-settings',
title: 'Media settings', title: 'Media settings',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '0a007f59-a5ea-4875-991d-f22d6fd69898', id: '0a007f59-a5ea-4875-991d-f22d6fd69898',
categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b', categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b',
slug: 'domain-settings', slug: 'domain-settings',
title: 'Domain settings', title: 'Domain settings',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '4707c8eb-31f9-415c-bd07-86f226c75feb', id: '4707c8eb-31f9-415c-bd07-86f226c75feb',
categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b', categoryId: 'c88a1f54-360a-4b9b-a54b-2f92b7a1f63b',
slug: 'privacy-settings', slug: 'privacy-settings',
title: 'Privacy settings', title: 'Privacy settings',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
// Payments // Payments
{ {
@ -363,28 +380,32 @@ export const guides = [
categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add', categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add',
slug: 'subscriptions', slug: 'subscriptions',
title: 'Subscriptions', title: 'Subscriptions',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '3d7150d2-feb3-4f20-bd3f-8e525cef77a4', id: '3d7150d2-feb3-4f20-bd3f-8e525cef77a4',
categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add', categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add',
slug: 'discounts', slug: 'discounts',
title: 'Discounts', title: 'Discounts',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '79239bc4-4fb5-428b-b30d-62c5289b061d', id: '79239bc4-4fb5-428b-b30d-62c5289b061d',
categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add', categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add',
slug: 'payment-methods', slug: 'payment-methods',
title: 'Payment methods', title: 'Payment methods',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '8d68c5e6-5404-450c-9d5f-d9800c164041', id: '8d68c5e6-5404-450c-9d5f-d9800c164041',
categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add', categoryId: '7b25b38c-1ab3-4474-8569-65b3ea232add',
slug: 'overdue-payments', slug: 'overdue-payments',
title: 'Overdue payments', title: 'Overdue payments',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
// Your account // Your account
{ {
@ -392,42 +413,48 @@ export const guides = [
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'changing-your-username', slug: 'changing-your-username',
title: 'Changing your username', title: 'Changing your username',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '0a9c3321-1db3-42bc-92b6-7e257368123e', id: '0a9c3321-1db3-42bc-92b6-7e257368123e',
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'changing-your-email', slug: 'changing-your-email',
title: 'Changing your email', title: 'Changing your email',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '80ba5106-5f9c-4ed7-b8f3-8544035e3095', id: '80ba5106-5f9c-4ed7-b8f3-8544035e3095',
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'changing-your-password', slug: 'changing-your-password',
title: 'Changing your password', title: 'Changing your password',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'db2e97a6-d657-4e9d-9b6c-5f213ea3301c', id: 'db2e97a6-d657-4e9d-9b6c-5f213ea3301c',
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'closing-your-account', slug: 'closing-your-account',
title: 'Closing your account', title: 'Closing your account',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: '3374c887-2fb7-4223-9f40-7f2cbbf76795', id: '3374c887-2fb7-4223-9f40-7f2cbbf76795',
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'account-limits', slug: 'account-limits',
title: 'Account limits', title: 'Account limits',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
{ {
id: 'cc65f92a-7d46-4557-b15b-6f8f59a60576', id: 'cc65f92a-7d46-4557-b15b-6f8f59a60576',
categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928', categoryId: '41fdf071-aec4-49de-9dd4-b4f746596928',
slug: 'two-factor-authentication', slug: 'two-factor-authentication',
title: 'Two factor authentication', title: 'Two factor authentication',
subtitle : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt', subtitle:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt',
}, },
]; ];

View File

@ -1,11 +1,16 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import { filters as filtersData, folders as foldersData, labels as labelsData, mails as mailsData, settings as settingsData } from 'app/mock-api/apps/mailbox/data'; import {
filters as filtersData,
folders as foldersData,
labels as labelsData,
mails as mailsData,
settings as settingsData,
} from 'app/mock-api/apps/mailbox/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class MailboxMockApi export class MailboxMockApi {
{
private _filters: any[] = filtersData; private _filters: any[] = filtersData;
private _folders: any[] = foldersData; private _folders: any[] = foldersData;
private _mails: any[] = mailsData; private _mails: any[] = mailsData;
@ -15,8 +20,7 @@ export class MailboxMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -28,8 +32,7 @@ export class MailboxMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Settings - GET // @ Settings - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -42,8 +45,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/mailbox/settings') .onPatch('api/apps/mailbox/settings')
.reply(({request}) => .reply(({ request }) => {
{
// Get the settings // Get the settings
const settings = cloneDeep(request.body.settings); const settings = cloneDeep(request.body.settings);
@ -57,38 +59,35 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Folders - GET // @ Folders - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/mailbox/folders').reply(() => {
.onGet('api/apps/mailbox/folders')
.reply(() =>
{
let count = 0; let count = 0;
// Iterate through the folders // Iterate through the folders
this._folders.forEach((folder) => this._folders.forEach((folder) => {
{
// Get the mails of this folder // Get the mails of this folder
const mails = this._mails.filter(mail => mail.folder === folder.id); const mails = this._mails.filter(
(mail) => mail.folder === folder.id
);
// If we are counting the 'sent' or the 'trash' folder... // If we are counting the 'sent' or the 'trash' folder...
if ( folder.slug === 'sent' || folder.slug === 'trash' ) if (folder.slug === 'sent' || folder.slug === 'trash') {
{
// Always set the count to 0 // Always set the count to 0
count = 0; count = 0;
} }
// If we are counting the 'drafts' or the 'spam' folder... // If we are counting the 'drafts' or the 'spam' folder...
else if ( folder.slug === 'drafts' || folder.slug === 'trash' || folder.slug === 'spam' ) else if (
{ folder.slug === 'drafts' ||
folder.slug === 'trash' ||
folder.slug === 'spam'
) {
// Set the count to the count of all mails // Set the count to the count of all mails
count = mails.length; count = mails.length;
} }
// Otherwise ('inbox')... // Otherwise ('inbox')...
else else {
{
// Go through the mails and count the unread ones // Go through the mails and count the unread ones
mails.forEach((mail) => mails.forEach((mail) => {
{ if (mail.unread) {
if ( mail.unread )
{
count++; count++;
} }
}); });
@ -124,8 +123,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/mailbox/label') .onPost('api/apps/mailbox/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the label // Get the label
const label = cloneDeep(request.body.label); const label = cloneDeep(request.body.label);
@ -133,7 +131,8 @@ export class MailboxMockApi
label.id = FuseMockApiUtils.guid(); label.id = FuseMockApiUtils.guid();
// Generate a slug // Generate a slug
label.slug = label.title.toLowerCase() label.slug = label.title
.toLowerCase()
.replace(/ /g, '-') .replace(/ /g, '-')
.replace(/[-]+/g, '-') .replace(/[-]+/g, '-')
.replace(/[^\w-]+/g, ''); .replace(/[^\w-]+/g, '');
@ -144,17 +143,16 @@ export class MailboxMockApi
let sameSlug; let sameSlug;
let slugSuffix = 1; let slugSuffix = 1;
do do {
{ sameSlug = this._labels.filter(
sameSlug = this._labels.filter(item => item.slug === label.slug); (item) => item.slug === label.slug
);
if ( sameSlug.length > 0 ) if (sameSlug.length > 0) {
{
label.slug = originalSlug + '-' + slugSuffix; label.slug = originalSlug + '-' + slugSuffix;
slugSuffix++; slugSuffix++;
} }
} } while (sameSlug.length > 0);
while ( sameSlug.length > 0 );
// Add the label // Add the label
this._labels.push(label); this._labels.push(label);
@ -168,8 +166,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/mailbox/label') .onPatch('api/apps/mailbox/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and label // Get the id and label
const id = request.body.id; const id = request.body.id;
const label = cloneDeep(request.body.label); const label = cloneDeep(request.body.label);
@ -178,12 +175,11 @@ export class MailboxMockApi
let updatedLabel = null; let updatedLabel = null;
// Find the label and update it // Find the label and update it
this._labels.forEach((item, index, labels) => this._labels.forEach((item, index, labels) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the slug // Update the slug
label.slug = label.title.toLowerCase() label.slug = label.title
.toLowerCase()
.replace(/ /g, '-') .replace(/ /g, '-')
.replace(/[-]+/g, '-') .replace(/[-]+/g, '-')
.replace(/[^\w-]+/g, ''); .replace(/[^\w-]+/g, '');
@ -205,21 +201,21 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/mailbox/label') .onDelete('api/apps/mailbox/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the label and delete it // Find the label and delete it
const index = this._labels.findIndex(item => item.id === id); const index = this._labels.findIndex((item) => item.id === id);
this._labels.splice(index, 1); this._labels.splice(index, 1);
// Get all the mails that have the label // Get all the mails that have the label
const mailsWithLabel = this._mails.filter(mail => mail.labels.indexOf(id) > -1); const mailsWithLabel = this._mails.filter(
(mail) => mail.labels.indexOf(id) > -1
);
// Iterate through them and remove the label // Iterate through them and remove the label
mailsWithLabel.forEach((mail) => mailsWithLabel.forEach((mail) => {
{
mail.labels.splice(mail.labels.indexOf(id), 1); mail.labels.splice(mail.labels.indexOf(id), 1);
}); });
@ -232,8 +228,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/mailbox/mails', 625) .onGet('api/apps/mailbox/mails', 625)
.reply(({request}) => .reply(({ request }) => {
{
// First, decide if mails are requested by folder, filter or label // First, decide if mails are requested by folder, filter or label
const byFolder = request.params.get('folder'); const byFolder = request.params.get('folder');
const byFilter = request.params.get('filter'); const byFilter = request.params.get('filter');
@ -243,30 +238,36 @@ export class MailboxMockApi
let mails: any[] | null = cloneDeep(this._mails); let mails: any[] | null = cloneDeep(this._mails);
// Filter the mails depending on the requested by type // Filter the mails depending on the requested by type
mails = mails.filter((mail) => mails = mails.filter((mail) => {
{ if (byFolder) {
if ( byFolder ) return (
{ mail.folder ===
return mail.folder === this._folders.find(folder => folder.slug === byFolder).id; this._folders.find(
(folder) => folder.slug === byFolder
).id
);
} }
if ( byFilter ) if (byFilter) {
{
return mail[byFilter] === true; return mail[byFilter] === true;
} }
if ( byLabel ) if (byLabel) {
{ return mail.labels.includes(
return mail.labels.includes(this._labels.find(label => label.slug === byLabel).id); this._labels.find((label) => label.slug === byLabel)
.id
);
} }
}); });
// Sort by date - descending // Sort by date - descending
mails.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); mails.sort(
(a, b) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
);
// Figure out the cc and bcc counts // Figure out the cc and bcc counts
mails.forEach((mail) => mails.forEach((mail) => {
{
mail.ccCount = mail.cc ? mail.cc.length : 0; mail.ccCount = mail.cc ? mail.cc.length : 0;
mail.bccCount = mail.bcc ? mail.bcc.length : 0; mail.bccCount = mail.bcc ? mail.bcc.length : 0;
}); });
@ -280,8 +281,11 @@ export class MailboxMockApi
// Calculate pagination details // Calculate pagination details
const begin = (page - 1) * resultsPerPage; const begin = (page - 1) * resultsPerPage;
const end = Math.min((resultsPerPage * page), mailsLength); const end = Math.min(resultsPerPage * page, mailsLength);
const lastPage = Math.max(Math.ceil(mailsLength / resultsPerPage), 1); const lastPage = Math.max(
Math.ceil(mailsLength / resultsPerPage),
1
);
// Prepare the pagination object // Prepare the pagination object
let pagination = {}; let pagination = {};
@ -290,15 +294,12 @@ export class MailboxMockApi
// the last possible page number, return null for // the last possible page number, return null for
// mails but also send the last possible page so // mails but also send the last possible page so
// the app can navigate to there // the app can navigate to there
if ( page > lastPage ) if (page > lastPage) {
{
mails = null; mails = null;
pagination = { pagination = {
lastPage, lastPage,
}; };
} } else {
else
{
// Paginate the results by 10 // Paginate the results by 10
mails = mails.slice(begin, end); mails = mails.slice(begin, end);
@ -328,8 +329,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/mailbox/mail') .onGet('api/apps/mailbox/mail')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id from the params // Get the id from the params
const id = request.params.get('id'); const id = request.params.get('id');
@ -337,12 +337,9 @@ export class MailboxMockApi
const mails = cloneDeep(this._mails); const mails = cloneDeep(this._mails);
// Find the mail // Find the mail
const mail = mails.find(item => item.id === id); const mail = mails.find((item) => item.id === id);
return [ return [200, mail];
200,
mail,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -350,8 +347,7 @@ export class MailboxMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/mailbox/mail') .onPatch('api/apps/mailbox/mail')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and mail // Get the id and mail
const id = request.body.id; const id = request.body.id;
const mail = cloneDeep(request.body.mail); const mail = cloneDeep(request.body.mail);
@ -360,10 +356,8 @@ export class MailboxMockApi
let updatedMail = null; let updatedMail = null;
// Find the mail and update it // Find the mail and update it
this._mails.forEach((item, index, mails) => this._mails.forEach((item, index, mails) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the mail // Update the mail
mails[index] = assign({}, mails[index], mail); mails[index] = assign({}, mails[index], mail);

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,21 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiUtils } from '@fuse/lib/mock-api'; import { FuseMockApiUtils } from '@fuse/lib/mock-api';
import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service'; import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';
import { labels as labelsData, notes as notesData } from 'app/mock-api/apps/notes/data'; import {
labels as labelsData,
notes as notesData,
} from 'app/mock-api/apps/notes/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NotesMockApi export class NotesMockApi {
{
private _labels: any[] = labelsData; private _labels: any[] = labelsData;
private _notes: any[] = notesData; private _notes: any[] = notesData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -26,25 +27,20 @@ export class NotesMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Labels - GET // @ Labels - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/notes/labels') .onGet('api/apps/notes/labels')
.reply(() => [ .reply(() => [200, cloneDeep(this._labels)]);
200,
cloneDeep(this._labels),
]);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Labels - POST // @ Labels - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/notes/labels') .onPost('api/apps/notes/labels')
.reply(({request}) => .reply(({ request }) => {
{
// Create a new label // Create a new label
const label = { const label = {
id: FuseMockApiUtils.guid(), id: FuseMockApiUtils.guid(),
@ -54,10 +50,7 @@ export class NotesMockApi
// Update the labels // Update the labels
this._labels.push(label); this._labels.push(label);
return [ return [200, cloneDeep(this._labels)];
200,
cloneDeep(this._labels),
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -65,16 +58,13 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/notes/labels') .onPatch('api/apps/notes/labels')
.reply(({request}) => .reply(({ request }) => {
{
// Get label // Get label
const updatedLabel = request.body.label; const updatedLabel = request.body.label;
// Update the label // Update the label
this._labels = this._labels.map((label) => this._labels = this._labels.map((label) => {
{ if (label.id === updatedLabel.id) {
if ( label.id === updatedLabel.id )
{
return { return {
...label, ...label,
title: updatedLabel.title, title: updatedLabel.title,
@ -84,10 +74,7 @@ export class NotesMockApi
return label; return label;
}); });
return [ return [200, cloneDeep(this._labels)];
200,
cloneDeep(this._labels),
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -95,24 +82,20 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/notes/labels') .onDelete('api/apps/notes/labels')
.reply(({request}) => .reply(({ request }) => {
{
// Get label id // Get label id
const id = request.params.get('id'); const id = request.params.get('id');
// Delete the label // Delete the label
this._labels = this._labels.filter(label => label.id !== id); this._labels = this._labels.filter((label) => label.id !== id);
// Go through notes and delete the label // Go through notes and delete the label
this._notes = this._notes.map(note => ({ this._notes = this._notes.map((note) => ({
...note, ...note,
labels: note.labels.filter(item => item !== id), labels: note.labels.filter((item) => item !== id),
})); }));
return [ return [200, cloneDeep(this._labels)];
200,
cloneDeep(this._labels),
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -120,20 +103,16 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/notes/tasks') .onPost('api/apps/notes/tasks')
.reply(({request}) => .reply(({ request }) => {
{
// Get note and task // Get note and task
let updatedNote = request.body.note; let updatedNote = request.body.note;
const task = request.body.task; const task = request.body.task;
// Update the note // Update the note
this._notes = this._notes.map((note) => this._notes = this._notes.map((note) => {
{ if (note.id === updatedNote.id) {
if ( note.id === updatedNote.id )
{
// Update the tasks // Update the tasks
if ( !note.tasks ) if (!note.tasks) {
{
note.tasks = []; note.tasks = [];
} }
@ -154,35 +133,26 @@ export class NotesMockApi
return note; return note;
}); });
return [ return [200, updatedNote];
200,
updatedNote,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Notes - GET // @ Notes - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/notes/all').reply(() => {
.onGet('api/apps/notes/all')
.reply(() =>
{
// Clone the labels and notes // Clone the labels and notes
const labels = cloneDeep(this._labels); const labels = cloneDeep(this._labels);
let notes = cloneDeep(this._notes); let notes = cloneDeep(this._notes);
// Attach the labels to the notes // Attach the labels to the notes
notes = notes.map(note => ( notes = notes.map((note) => ({
{
...note, ...note,
labels: note.labels.map(labelId => labels.find(label => label.id === labelId)), labels: note.labels.map((labelId) =>
} labels.find((label) => label.id === labelId)
)); ),
}));
return [ return [200, notes];
200,
notes,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -190,8 +160,7 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/notes') .onPost('api/apps/notes')
.reply(({request}) => .reply(({ request }) => {
{
// Get note // Get note
const note = request.body.note; const note = request.body.note;
@ -201,10 +170,7 @@ export class NotesMockApi
// Push the note // Push the note
this._notes.push(note); this._notes.push(note);
return [ return [200, note];
200,
note,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -212,16 +178,13 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/notes') .onPatch('api/apps/notes')
.reply(({request}) => .reply(({ request }) => {
{
// Get note // Get note
const updatedNote = request.body.updatedNote; const updatedNote = request.body.updatedNote;
// Update the note // Update the note
this._notes = this._notes.map((note) => this._notes = this._notes.map((note) => {
{ if (note.id === updatedNote.id) {
if ( note.id === updatedNote.id )
{
return { return {
...updatedNote, ...updatedNote,
}; };
@ -230,10 +193,7 @@ export class NotesMockApi
return note; return note;
}); });
return [ return [200, updatedNote];
200,
updatedNote,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -241,16 +201,13 @@ export class NotesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/notes') .onDelete('api/apps/notes')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the note and delete it // Find the note and delete it
this._notes.forEach((item, index) => this._notes.forEach((item, index) => {
{ if (item.id === id) {
if ( item.id === id )
{
this._notes.splice(index, 1); this._notes.splice(index, 1);
} }
}); });

View File

@ -41,10 +41,13 @@ export const notes = [
reminder: null, reminder: null,
labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 10, hour: 10,
minute: 19, minute: 19,
}).minus({day: 98}).toISO(), })
.minus({ day: 98 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -59,10 +62,13 @@ export const notes = [
'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528',
], ],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 15, hour: 15,
minute: 37, minute: 37,
}).minus({day: 80}).toISO(), })
.minus({ day: 80 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -74,14 +80,20 @@ export const notes = [
reminder: null, reminder: null,
labels: ['6c288794-47eb-4605-8bdf-785b61a449d3'], labels: ['6c288794-47eb-4605-8bdf-785b61a449d3'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 19, hour: 19,
minute: 27, minute: 27,
}).minus({day: 74}).toISO(), })
updatedAt: now.set({ .minus({ day: 74 })
.toISO(),
updatedAt: now
.set({
hour: 15, hour: 15,
minute: 36, minute: 36,
}).minus({day: 50}).toISO(), })
.minus({ day: 50 })
.toISO(),
}, },
{ {
id: '89861bd4-0144-4bb4-8b39-332ca10371d5', id: '89861bd4-0144-4bb4-8b39-332ca10371d5',
@ -89,22 +101,29 @@ export const notes = [
content: 'Theming support for all apps', content: 'Theming support for all apps',
tasks: null, tasks: null,
image: null, image: null,
reminder : now.set({ reminder: now
.set({
hour: 12, hour: 12,
minute: 34, minute: 34,
}).plus({day: 50}).toISO(), })
.plus({ day: 50 })
.toISO(),
labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 12, hour: 12,
minute: 34, minute: 34,
}).minus({day: 59}).toISO(), })
.minus({ day: 59 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
id: 'ffd20f3c-2d43-4c6b-8021-278032fc9e92', id: 'ffd20f3c-2d43-4c6b-8021-278032fc9e92',
title: 'Gift Ideas', title: 'Gift Ideas',
content : 'Stephanie\'s birthday is coming and I need to pick a present for her. Take a look at the below list and buy one of them (or all of them)', content:
"Stephanie's birthday is coming and I need to pick a present for her. Take a look at the below list and buy one of them (or all of them)",
tasks: [ tasks: [
{ {
id: '330a924f-fb51-48f6-a374-1532b1dd353d', id: '330a924f-fb51-48f6-a374-1532b1dd353d',
@ -131,10 +150,13 @@ export const notes = [
reminder: null, reminder: null,
labels: ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], labels: ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 16, hour: 16,
minute: 4, minute: 4,
}).minus({day: 47}).toISO(), })
.minus({ day: 47 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -169,16 +191,22 @@ export const notes = [
}, },
], ],
image: null, image: null,
reminder : now.set({ reminder: now
.set({
hour: 10, hour: 10,
minute: 44, minute: 44,
}).minus({day: 35}).toISO(), })
.minus({ day: 35 })
.toISO(),
labels: ['b1cde9ee-e54d-4142-ad8b-cf55dafc9528'], labels: ['b1cde9ee-e54d-4142-ad8b-cf55dafc9528'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 10, hour: 10,
minute: 44, minute: 44,
}).minus({day: 35}).toISO(), })
.minus({ day: 35 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -213,37 +241,49 @@ export const notes = [
}, },
], ],
image: null, image: null,
reminder : now.set({ reminder: now
.set({
hour: 11, hour: 11,
minute: 27, minute: 27,
}).minus({day: 14}).toISO(), })
.minus({ day: 14 })
.toISO(),
labels: [ labels: [
'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528',
'e2f749f5-41ed-49d0-a92a-1c83d879e371', 'e2f749f5-41ed-49d0-a92a-1c83d879e371',
], ],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 11, hour: 11,
minute: 27, minute: 27,
}).minus({day: 24}).toISO(), })
.minus({ day: 24 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
id: 'd46dee8b-8761-4b6d-a1df-449d6e6feb6a', id: 'd46dee8b-8761-4b6d-a1df-449d6e6feb6a',
title: '', title: '',
content : 'Organize the dad\'s surprise retirement party', content: "Organize the dad's surprise retirement party",
tasks: null, tasks: null,
image: null, image: null,
reminder : now.set({ reminder: now
.set({
hour: 14, hour: 14,
minute: 56, minute: 56,
}).minus({day: 25}).toISO(), })
.minus({ day: 25 })
.toISO(),
labels: ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'], labels: ['f47c92e5-20b9-44d9-917f-9ff4ad25dfd0'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 14, hour: 14,
minute: 56, minute: 56,
}).minus({day: 20}).toISO(), })
.minus({ day: 20 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -258,14 +298,20 @@ export const notes = [
'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528',
], ],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 9, hour: 9,
minute: 32, minute: 32,
}).minus({day: 15}).toISO(), })
updatedAt: now.set({ .minus({ day: 15 })
.toISO(),
updatedAt: now
.set({
hour: 17, hour: 17,
minute: 6, minute: 6,
}).minus({day: 12}).toISO(), })
.minus({ day: 12 })
.toISO(),
}, },
{ {
id: '15188348-78aa-4ed6-b5c2-028a214ba987', id: '15188348-78aa-4ed6-b5c2-028a214ba987',
@ -276,10 +322,13 @@ export const notes = [
reminder: null, reminder: null,
labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 20, hour: 20,
minute: 5, minute: 5,
}).minus({day: 12}).toISO(), })
.minus({ day: 12 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -299,16 +348,22 @@ export const notes = [
}, },
], ],
image: null, image: null,
reminder : now.set({ reminder: now
.set({
hour: 13, hour: 13,
minute: 43, minute: 43,
}).minus({day: 2}).toISO(), })
.minus({ day: 2 })
.toISO(),
labels: ['bbc73458-940b-421c-8d5f-8dcd23a9b0d6'], labels: ['bbc73458-940b-421c-8d5f-8dcd23a9b0d6'],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 13, hour: 13,
minute: 43, minute: 43,
}).minus({day: 7}).toISO(), })
.minus({ day: 7 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -323,10 +378,13 @@ export const notes = [
'6c288794-47eb-4605-8bdf-785b61a449d3', '6c288794-47eb-4605-8bdf-785b61a449d3',
], ],
archived: false, archived: false,
createdAt: now.set({ createdAt: now
.set({
hour: 7, hour: 7,
minute: 12, minute: 12,
}).minus({day: 2}).toISO(), })
.minus({ day: 2 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -341,10 +399,13 @@ export const notes = [
'6c288794-47eb-4605-8bdf-785b61a449d3', '6c288794-47eb-4605-8bdf-785b61a449d3',
], ],
archived: true, archived: true,
createdAt: now.set({ createdAt: now
.set({
hour: 17, hour: 17,
minute: 14, minute: 14,
}).minus({day: 100}).toISO(), })
.minus({ day: 100 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -356,10 +417,13 @@ export const notes = [
reminder: null, reminder: null,
labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'], labels: ['e2f749f5-41ed-49d0-a92a-1c83d879e371'],
archived: true, archived: true,
createdAt: now.set({ createdAt: now
.set({
hour: 10, hour: 10,
minute: 29, minute: 29,
}).minus({day: 85}).toISO(), })
.minus({ day: 85 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
{ {
@ -374,10 +438,13 @@ export const notes = [
'b1cde9ee-e54d-4142-ad8b-cf55dafc9528', 'b1cde9ee-e54d-4142-ad8b-cf55dafc9528',
], ],
archived: true, archived: true,
createdAt: now.set({ createdAt: now
.set({
hour: 15, hour: 15,
minute: 30, minute: 30,
}).minus({day: 69}).toISO(), })
.minus({ day: 69 })
.toISO(),
updatedAt: null, updatedAt: null,
}, },
]; ];

View File

@ -1,11 +1,16 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import { boards as boardsData, cards as cardsData, labels as labelsData, lists as listsData, members as membersData } from 'app/mock-api/apps/scrumboard/data'; import {
boards as boardsData,
cards as cardsData,
labels as labelsData,
lists as listsData,
members as membersData,
} from 'app/mock-api/apps/scrumboard/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ScrumboardMockApi export class ScrumboardMockApi {
{
// Private // Private
private _boards: any[] = boardsData; private _boards: any[] = boardsData;
private _cards: any[] = cardsData; private _cards: any[] = cardsData;
@ -16,8 +21,7 @@ export class ScrumboardMockApi
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -29,28 +33,27 @@ export class ScrumboardMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Boards - GET // @ Boards - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/scrumboard/boards') .onGet('api/apps/scrumboard/boards')
.reply(({request}) => .reply(({ request }) => {
{
// Clone the boards // Clone the boards
let boards = cloneDeep(this._boards); let boards = cloneDeep(this._boards);
// Go through the boards and inject the members // Go through the boards and inject the members
boards = boards.map(board => ({ boards = boards.map((board) => ({
...board, ...board,
members: board.members.map(boardMember => this._members.find(member => boardMember === member.id)), members: board.members.map((boardMember) =>
this._members.find(
(member) => boardMember === member.id
)
),
})); }));
return [ return [200, boards];
200,
boards,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -58,39 +61,43 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/scrumboard/board') .onGet('api/apps/scrumboard/board')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the board // Find the board
const board = this._boards.find(item => item.id === id); const board = this._boards.find((item) => item.id === id);
// Attach the board lists // Attach the board lists
board.lists = this._lists.filter(item => item.boardId === id).sort((a, b) => a.position - b.position); board.lists = this._lists
.filter((item) => item.boardId === id)
.sort((a, b) => a.position - b.position);
// Grab all cards that belong to this board and attach labels to them // Grab all cards that belong to this board and attach labels to them
let cards = this._cards.filter(item => item.boardId === id); let cards = this._cards.filter((item) => item.boardId === id);
cards = cards.map(card => ( cards = cards.map((card) => ({
{
...card, ...card,
labels: card.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId)), labels: card.labels.map((cardLabelId) =>
} this._labels.find((label) => label.id === cardLabelId)
)); ),
}));
// Attach the board cards into corresponding lists // Attach the board cards into corresponding lists
board.lists.forEach((list, index, array) => board.lists.forEach((list, index, array) => {
{ array[index].cards = cards
array[index].cards = cards.filter(item => item.boardId === id && item.listId === list.id).sort((a, b) => a.position - b.position); .filter(
(item) =>
item.boardId === id && item.listId === list.id
)
.sort((a, b) => a.position - b.position);
}); });
// Attach the board labels // Attach the board labels
board.labels = this._labels.filter(item => item.boardId === id); board.labels = this._labels.filter(
(item) => item.boardId === id
);
return [ return [200, cloneDeep(board)];
200,
cloneDeep(board),
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -98,8 +105,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/scrumboard/board/list') .onPost('api/apps/scrumboard/board/list')
.reply(({request}) => .reply(({ request }) => {
{
// Get the list // Get the list
const newList = cloneDeep(request.body.list); const newList = cloneDeep(request.body.list);
@ -109,10 +115,7 @@ export class ScrumboardMockApi
// Store the new list // Store the new list
this._lists.push(newList); this._lists.push(newList);
return [ return [200, newList];
200,
newList,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -120,8 +123,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/list') .onPatch('api/apps/scrumboard/board/list')
.reply(({request}) => .reply(({ request }) => {
{
// Get the list // Get the list
const list = cloneDeep(request.body.list); const list = cloneDeep(request.body.list);
@ -129,10 +131,8 @@ export class ScrumboardMockApi
let updatedList = null; let updatedList = null;
// Find the list and update it // Find the list and update it
this._lists.forEach((item, index, lists) => this._lists.forEach((item, index, lists) => {
{ if (item.id === list.id) {
if ( item.id === list.id )
{
// Update the list // Update the list
lists[index] = assign({}, lists[index], list); lists[index] = assign({}, lists[index], list);
@ -141,10 +141,7 @@ export class ScrumboardMockApi
} }
}); });
return [ return [200, updatedList];
200,
updatedList,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -152,8 +149,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/lists') .onPatch('api/apps/scrumboard/board/lists')
.reply(({request}) => .reply(({ request }) => {
{
// Get the lists // Get the lists
const lists = cloneDeep(request.body.lists); const lists = cloneDeep(request.body.lists);
@ -161,10 +157,11 @@ export class ScrumboardMockApi
const updatedLists = []; const updatedLists = [];
// Go through the lists // Go through the lists
lists.forEach((item) => lists.forEach((item) => {
{
// Find the list // Find the list
const index = this._lists.findIndex(list => item.id === list.id); const index = this._lists.findIndex(
(list) => item.id === list.id
);
// Update the list // Update the list
this._lists[index] = assign({}, this._lists[index], item); this._lists[index] = assign({}, this._lists[index], item);
@ -173,10 +170,7 @@ export class ScrumboardMockApi
updatedLists.push(item); updatedLists.push(item);
}); });
return [ return [200, updatedLists];
200,
updatedLists,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -184,22 +178,18 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/list') .onDelete('api/apps/scrumboard/board/list')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the list and delete it // Find the list and delete it
const index = this._lists.findIndex(item => item.id === id); const index = this._lists.findIndex((item) => item.id === id);
this._lists.splice(index, 1); this._lists.splice(index, 1);
// Filter out the cards that belonged to the list to delete them // Filter out the cards that belonged to the list to delete them
this._cards = this._cards.filter(card => card.listId !== id); this._cards = this._cards.filter((card) => card.listId !== id);
return [ return [200, true];
200,
true,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -207,8 +197,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPut('api/apps/scrumboard/board/card') .onPut('api/apps/scrumboard/board/card')
.reply(({request}) => .reply(({ request }) => {
{
// Get the card // Get the card
const newCard = cloneDeep(request.body.card); const newCard = cloneDeep(request.body.card);
@ -218,10 +207,7 @@ export class ScrumboardMockApi
// Unshift the new card // Unshift the new card
this._cards.push(newCard); this._cards.push(newCard);
return [ return [200, newCard];
200,
newCard,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -229,8 +215,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/card') .onPatch('api/apps/scrumboard/board/card')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and card // Get the id and card
const id = request.body.id; const id = request.body.id;
const card = cloneDeep(request.body.card); const card = cloneDeep(request.body.card);
@ -239,13 +224,11 @@ export class ScrumboardMockApi
let updatedCard = null; let updatedCard = null;
// Go through the labels and leave only ids of them // Go through the labels and leave only ids of them
card.labels = card.labels.map(itemLabel => itemLabel.id); card.labels = card.labels.map((itemLabel) => itemLabel.id);
// Find the card and update it // Find the card and update it
this._cards.forEach((item, index, cards) => this._cards.forEach((item, index, cards) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the card // Update the card
cards[index] = assign({}, cards[index], card); cards[index] = assign({}, cards[index], card);
@ -255,12 +238,11 @@ export class ScrumboardMockApi
}); });
// Attach the labels of the card // Attach the labels of the card
updatedCard.labels = updatedCard.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId)); updatedCard.labels = updatedCard.labels.map((cardLabelId) =>
this._labels.find((label) => label.id === cardLabelId)
);
return [ return [200, updatedCard];
200,
updatedCard,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -268,8 +250,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/cards') .onPatch('api/apps/scrumboard/board/cards')
.reply(({request}) => .reply(({ request }) => {
{
// Get the cards // Get the cards
const cards = cloneDeep(request.body.cards); const cards = cloneDeep(request.body.cards);
@ -277,28 +258,28 @@ export class ScrumboardMockApi
const updatedCards = []; const updatedCards = [];
// Go through the cards // Go through the cards
cards.forEach((item) => cards.forEach((item) => {
{
// Find the card // Find the card
const index = this._cards.findIndex(card => item.id === card.id); const index = this._cards.findIndex(
(card) => item.id === card.id
);
// Go through the labels and leave only ids of them // Go through the labels and leave only ids of them
item.labels = item.labels.map(itemLabel => itemLabel.id); item.labels = item.labels.map((itemLabel) => itemLabel.id);
// Update the card // Update the card
this._cards[index] = assign({}, this._cards[index], item); this._cards[index] = assign({}, this._cards[index], item);
// Attach the labels of the card // Attach the labels of the card
item.labels = item.labels.map(cardLabelId => this._labels.find(label => label.id === cardLabelId)); item.labels = item.labels.map((cardLabelId) =>
this._labels.find((label) => label.id === cardLabelId)
);
// Store in the updated cards // Store in the updated cards
updatedCards.push(item); updatedCards.push(item);
}); });
return [ return [200, updatedCards];
200,
updatedCards,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -306,19 +287,15 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/card') .onDelete('api/apps/scrumboard/board/card')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the card and delete it // Find the card and delete it
const index = this._cards.findIndex(item => item.id === id); const index = this._cards.findIndex((item) => item.id === id);
this._cards.splice(index, 1); this._cards.splice(index, 1);
return [ return [200, true];
200,
true,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -326,26 +303,26 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/card/positions') .onPatch('api/apps/scrumboard/board/card/positions')
.reply(({request}) => .reply(({ request }) => {
{
// Get the cards // Get the cards
const cards = request.body.cards; const cards = request.body.cards;
// Go through the cards // Go through the cards
this._cards.forEach((card) => this._cards.forEach((card) => {
{
// Find this card's index within the cards array that comes with the request // Find this card's index within the cards array that comes with the request
// and assign that index as the new position number for the card // and assign that index as the new position number for the card
card.position = cards.findIndex(item => item.id === card.id && item.listId === card.listId && item.boardId === card.boardId); card.position = cards.findIndex(
(item) =>
item.id === card.id &&
item.listId === card.listId &&
item.boardId === card.boardId
);
}); });
// Clone the cards // Clone the cards
const updatedCards = cloneDeep(this._cards); const updatedCards = cloneDeep(this._cards);
return [ return [200, updatedCards];
200,
updatedCards,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -353,18 +330,16 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/scrumboard/board/labels') .onGet('api/apps/scrumboard/board/labels')
.reply(({request}) => .reply(({ request }) => {
{
// Get the board id // Get the board id
const boardId = request.params.get('boardId'); const boardId = request.params.get('boardId');
// Filter the labels // Filter the labels
const labels = this._labels.filter(item => item.boardId === boardId); const labels = this._labels.filter(
(item) => item.boardId === boardId
);
return [ return [200, cloneDeep(labels)];
200,
cloneDeep(labels),
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -372,8 +347,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPut('api/apps/scrumboard/board/label') .onPut('api/apps/scrumboard/board/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the label // Get the label
const newLabel = cloneDeep(request.body.label); const newLabel = cloneDeep(request.body.label);
@ -383,10 +357,7 @@ export class ScrumboardMockApi
// Unshift the new label // Unshift the new label
this._labels.unshift(newLabel); this._labels.unshift(newLabel);
return [ return [200, newLabel];
200,
newLabel,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -394,8 +365,7 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/scrumboard/board/label') .onPatch('api/apps/scrumboard/board/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and label // Get the id and label
const id = request.body.id; const id = request.body.id;
const label = cloneDeep(request.body.label); const label = cloneDeep(request.body.label);
@ -404,10 +374,8 @@ export class ScrumboardMockApi
let updatedLabel = null; let updatedLabel = null;
// Find the label and update it // Find the label and update it
this._labels.forEach((item, index, labels) => this._labels.forEach((item, index, labels) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the label // Update the label
labels[index] = assign({}, labels[index], label); labels[index] = assign({}, labels[index], label);
@ -416,10 +384,7 @@ export class ScrumboardMockApi
} }
}); });
return [ return [200, updatedLabel];
200,
updatedLabel,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -427,28 +392,25 @@ export class ScrumboardMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/scrumboard/board/label') .onDelete('api/apps/scrumboard/board/label')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the label and delete it // Find the label and delete it
const index = this._labels.findIndex(item => item.id === id); const index = this._labels.findIndex((item) => item.id === id);
this._labels.splice(index, 1); this._labels.splice(index, 1);
// Get the cards that have the label // Get the cards that have the label
const cardsWithLabel = this._cards.filter(card => card.labels.indexOf(id) > -1); const cardsWithLabel = this._cards.filter(
(card) => card.labels.indexOf(id) > -1
);
// Iterate through them and remove the label // Iterate through them and remove the label
cardsWithLabel.forEach((card) => cardsWithLabel.forEach((card) => {
{
card.tags.splice(card.tags.indexOf(id), 1); card.tags.splice(card.tags.indexOf(id), 1);
}); });
return [ return [200, true];
200,
true,
];
}); });
} }
} }

View File

@ -40,9 +40,7 @@ export const boards = [
description: 'Personal tasks around the house', description: 'Personal tasks around the house',
icon: 'heroicons_outline:home', icon: 'heroicons_outline:home',
lastActivity: now.startOf('day').minus({ week: 1 }).toISO(), lastActivity: now.startOf('day').minus({ week: 1 }).toISO(),
members : [ members: ['6f6a1c34-390b-4b2e-97c8-ff0e0d787839'],
'6f6a1c34-390b-4b2e-97c8-ff0e0d787839',
],
}, },
]; ];
export const lists = [ export const lists = [
@ -78,7 +76,8 @@ export const cards = [
listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37', listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 65536, position: 65536,
title: 'Example that showcase all of the available bits on the card with a fairly long title compared to other cards', title: 'Example that showcase all of the available bits on the card with a fairly long title compared to other cards',
description: 'Example that showcase all of the available bits on the card with a fairly long title compared to other cards. Example that showcase all of the available bits on the card with a fairly long title compared to other cards.', description:
'Example that showcase all of the available bits on the card with a fairly long title compared to other cards. Example that showcase all of the available bits on the card with a fairly long title compared to other cards.',
labels: [ labels: [
'e0175175-2784-48f1-a519-a1d2e397c9b3', 'e0175175-2784-48f1-a519-a1d2e397c9b3',
'51779701-818a-4a53-bc16-137c3bd7a564', '51779701-818a-4a53-bc16-137c3bd7a564',
@ -94,9 +93,7 @@ export const cards = [
listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37', listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 131072, position: 131072,
title: 'Do a research about most needed admin applications', title: 'Do a research about most needed admin applications',
labels : [ labels: ['e0175175-2784-48f1-a519-a1d2e397c9b3'],
'e0175175-2784-48f1-a519-a1d2e397c9b3',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -105,9 +102,7 @@ export const cards = [
listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37', listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 196608, position: 196608,
title: 'Implement the Project dashboard', title: 'Implement the Project dashboard',
labels : [ labels: ['caff9c9b-a198-4564-b1f4-8b3df1d345bb'],
'caff9c9b-a198-4564-b1f4-8b3df1d345bb',
],
dueDate: now.startOf('day').toISO(), dueDate: now.startOf('day').toISO(),
}, },
{ {
@ -116,9 +111,7 @@ export const cards = [
listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37', listId: 'a2df7786-519c-485a-a85f-c09a61cc5f37',
position: 262144, position: 262144,
title: 'Implement the Analytics dashboard', title: 'Implement the Analytics dashboard',
labels : [ labels: ['caff9c9b-a198-4564-b1f4-8b3df1d345bb'],
'caff9c9b-a198-4564-b1f4-8b3df1d345bb',
],
dueDate: now.startOf('day').minus({ day: 1 }).toISO(), dueDate: now.startOf('day').minus({ day: 1 }).toISO(),
}, },
{ {
@ -127,9 +120,7 @@ export const cards = [
listId: '83ca2a34-65af-49c0-a42e-94a34003fcf2', listId: '83ca2a34-65af-49c0-a42e-94a34003fcf2',
position: 65536, position: 65536,
title: 'Analytics dashboard design', title: 'Analytics dashboard design',
labels : [ labels: ['e8364d69-9595-46ce-a0f9-ce428632a0ac'],
'e8364d69-9595-46ce-a0f9-ce428632a0ac',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -138,9 +129,7 @@ export const cards = [
listId: '83ca2a34-65af-49c0-a42e-94a34003fcf2', listId: '83ca2a34-65af-49c0-a42e-94a34003fcf2',
position: 131072, position: 131072,
title: 'Project dashboard design', title: 'Project dashboard design',
labels : [ labels: ['e8364d69-9595-46ce-a0f9-ce428632a0ac'],
'e8364d69-9595-46ce-a0f9-ce428632a0ac',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -149,9 +138,7 @@ export const cards = [
listId: 'a85ea483-f8f7-42d9-a314-3fed6aac22ab', listId: 'a85ea483-f8f7-42d9-a314-3fed6aac22ab',
position: 65536, position: 65536,
title: 'JWT Auth implementation', title: 'JWT Auth implementation',
labels : [ labels: ['caff9c9b-a198-4564-b1f4-8b3df1d345bb'],
'caff9c9b-a198-4564-b1f4-8b3df1d345bb',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -178,9 +165,7 @@ export const cards = [
listId: '34cbef38-5687-4813-bd66-141a6df6d832', listId: '34cbef38-5687-4813-bd66-141a6df6d832',
position: 196608, position: 196608,
title: 'Collect information about most used admin layouts', title: 'Collect information about most used admin layouts',
labels : [ labels: ['e0175175-2784-48f1-a519-a1d2e397c9b3'],
'e0175175-2784-48f1-a519-a1d2e397c9b3',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -189,9 +174,7 @@ export const cards = [
listId: '34cbef38-5687-4813-bd66-141a6df6d832', listId: '34cbef38-5687-4813-bd66-141a6df6d832',
position: 262144, position: 262144,
title: 'Do a research about latest UI trends', title: 'Do a research about latest UI trends',
labels : [ labels: ['e0175175-2784-48f1-a519-a1d2e397c9b3'],
'e0175175-2784-48f1-a519-a1d2e397c9b3',
],
dueDate: null, dueDate: null,
}, },
{ {
@ -200,9 +183,7 @@ export const cards = [
listId: '34cbef38-5687-4813-bd66-141a6df6d832', listId: '34cbef38-5687-4813-bd66-141a6df6d832',
position: 327680, position: 327680,
title: 'Learn more about UX', title: 'Learn more about UX',
labels : [ labels: ['e0175175-2784-48f1-a519-a1d2e397c9b3'],
'e0175175-2784-48f1-a519-a1d2e397c9b3',
],
dueDate: null, dueDate: null,
}, },
]; ];

View File

@ -1,20 +1,21 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service'; import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';
import { FuseMockApiUtils } from '@fuse/lib/mock-api/mock-api.utils'; import { FuseMockApiUtils } from '@fuse/lib/mock-api/mock-api.utils';
import { tags as tagsData, tasks as tasksData } from 'app/mock-api/apps/tasks/data'; import {
tags as tagsData,
tasks as tasksData,
} from 'app/mock-api/apps/tasks/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TasksMockApi export class TasksMockApi {
{
private _tags: any[] = tagsData; private _tags: any[] = tagsData;
private _tasks: any[] = tasksData; private _tasks: any[] = tasksData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -26,25 +27,20 @@ export class TasksMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Tags - GET // @ Tags - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/tasks/tags') .onGet('api/apps/tasks/tags')
.reply(() => [ .reply(() => [200, cloneDeep(this._tags)]);
200,
cloneDeep(this._tags),
]);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Tags - POST // @ Tags - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/tasks/tag') .onPost('api/apps/tasks/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the tag // Get the tag
const newTag = cloneDeep(request.body.tag); const newTag = cloneDeep(request.body.tag);
@ -54,10 +50,7 @@ export class TasksMockApi
// Unshift the new tag // Unshift the new tag
this._tags.unshift(newTag); this._tags.unshift(newTag);
return [ return [200, newTag];
200,
newTag,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -65,8 +58,7 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/tasks/tag') .onPatch('api/apps/tasks/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and tag // Get the id and tag
const id = request.body.id; const id = request.body.id;
const tag = cloneDeep(request.body.tag); const tag = cloneDeep(request.body.tag);
@ -75,10 +67,8 @@ export class TasksMockApi
let updatedTag = null; let updatedTag = null;
// Find the tag and update it // Find the tag and update it
this._tags.forEach((item, index, tags) => this._tags.forEach((item, index, tags) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the tag // Update the tag
tags[index] = assign({}, tags[index], tag); tags[index] = assign({}, tags[index], tag);
@ -87,10 +77,7 @@ export class TasksMockApi
} }
}); });
return [ return [200, updatedTag];
200,
updatedTag,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -98,47 +85,38 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/tasks/tag') .onDelete('api/apps/tasks/tag')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the tag and delete it // Find the tag and delete it
const index = this._tags.findIndex(item => item.id === id); const index = this._tags.findIndex((item) => item.id === id);
this._tags.splice(index, 1); this._tags.splice(index, 1);
// Get the tasks that have the tag // Get the tasks that have the tag
const tasksWithTag = this._tasks.filter(task => task.tags.indexOf(id) > -1); const tasksWithTag = this._tasks.filter(
(task) => task.tags.indexOf(id) > -1
);
// Iterate through them and remove the tag // Iterate through them and remove the tag
tasksWithTag.forEach((task) => tasksWithTag.forEach((task) => {
{
task.tags.splice(task.tags.indexOf(id), 1); task.tags.splice(task.tags.indexOf(id), 1);
}); });
return [ return [200, true];
200,
true,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Tasks - GET // @ Tasks - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/apps/tasks/all').reply(() => {
.onGet('api/apps/tasks/all')
.reply(() =>
{
// Clone the tasks // Clone the tasks
const tasks = cloneDeep(this._tasks); const tasks = cloneDeep(this._tasks);
// Sort the tasks by order // Sort the tasks by order
tasks.sort((a, b) => a.order - b.order); tasks.sort((a, b) => a.order - b.order);
return [ return [200, tasks];
200,
tasks,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -146,8 +124,7 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/tasks/search') .onGet('api/apps/tasks/search')
.reply(({request}) => .reply(({ request }) => {
{
// Get the search query // Get the search query
const query = request.params.get('query'); const query = request.params.get('query');
@ -155,19 +132,34 @@ export class TasksMockApi
let results; let results;
// If the query exists... // If the query exists...
if ( query ) if (query) {
{
// Clone the tasks // Clone the tasks
let tasks = cloneDeep(this._tasks); let tasks = cloneDeep(this._tasks);
// Filter the tasks // Filter the tasks
tasks = tasks.filter(task => task.title && task.title.toLowerCase().includes(query.toLowerCase()) || task.notes && task.notes.toLowerCase() tasks = tasks.filter(
.includes(query.toLowerCase())); (task) =>
(task.title &&
task.title
.toLowerCase()
.includes(query.toLowerCase())) ||
(task.notes &&
task.notes
.toLowerCase()
.includes(query.toLowerCase()))
);
// Mark the found chars // Mark the found chars
tasks.forEach((task) => tasks.forEach((task) => {
{ const re = new RegExp(
const re = new RegExp('(' + query.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')', 'ig'); '(' +
query.replace(
/[-\/\\^$*+?.()|[\]{}]/g,
'\\$&'
) +
')',
'ig'
);
task.title = task.title.replace(re, '<mark>$1</mark>'); task.title = task.title.replace(re, '<mark>$1</mark>');
}); });
@ -175,15 +167,11 @@ export class TasksMockApi
results = tasks; results = tasks;
} }
// Otherwise, set the results to null // Otherwise, set the results to null
else else {
{
results = null; results = null;
} }
return [ return [200, results];
200,
results,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -191,26 +179,23 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/tasks/order') .onPatch('api/apps/tasks/order')
.reply(({request}) => .reply(({ request }) => {
{
// Get the tasks // Get the tasks
const tasks = request.body.tasks; const tasks = request.body.tasks;
// Go through the tasks // Go through the tasks
this._tasks.forEach((task) => this._tasks.forEach((task) => {
{
// Find this task's index within the tasks array that comes with the request // Find this task's index within the tasks array that comes with the request
// and assign that index as the new order number for the task // and assign that index as the new order number for the task
task.order = tasks.findIndex((item: any) => item.id === task.id); task.order = tasks.findIndex(
(item: any) => item.id === task.id
);
}); });
// Clone the tasks // Clone the tasks
const updatedTasks = cloneDeep(this._tasks); const updatedTasks = cloneDeep(this._tasks);
return [ return [200, updatedTasks];
200,
updatedTasks,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -218,8 +203,7 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/apps/tasks/task') .onGet('api/apps/tasks/task')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id from the params // Get the id from the params
const id = request.params.get('id'); const id = request.params.get('id');
@ -227,12 +211,9 @@ export class TasksMockApi
const tasks = cloneDeep(this._tasks); const tasks = cloneDeep(this._tasks);
// Find the task // Find the task
const task = tasks.find(item => item.id === id); const task = tasks.find((item) => item.id === id);
return [ return [200, task];
200,
task,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -240,8 +221,7 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/apps/tasks/task') .onPost('api/apps/tasks/task')
.reply(({request}) => .reply(({ request }) => {
{
// Generate a new task // Generate a new task
const newTask = { const newTask = {
id: FuseMockApiUtils.guid(), id: FuseMockApiUtils.guid(),
@ -259,15 +239,11 @@ export class TasksMockApi
this._tasks.unshift(newTask); this._tasks.unshift(newTask);
// Go through the tasks and update their order numbers // Go through the tasks and update their order numbers
this._tasks.forEach((task, index) => this._tasks.forEach((task, index) => {
{
task.order = index; task.order = index;
}); });
return [ return [200, newTask];
200,
newTask,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -275,8 +251,7 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/apps/tasks/task') .onPatch('api/apps/tasks/task')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and task // Get the id and task
const id = request.body.id; const id = request.body.id;
const task = cloneDeep(request.body.task); const task = cloneDeep(request.body.task);
@ -285,10 +260,8 @@ export class TasksMockApi
let updatedTask = null; let updatedTask = null;
// Find the task and update it // Find the task and update it
this._tasks.forEach((item, index, tasks) => this._tasks.forEach((item, index, tasks) => {
{ if (item.id === id) {
if ( item.id === id )
{
// Update the task // Update the task
tasks[index] = assign({}, tasks[index], task); tasks[index] = assign({}, tasks[index], task);
@ -297,10 +270,7 @@ export class TasksMockApi
} }
}); });
return [ return [200, updatedTask];
200,
updatedTask,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -308,19 +278,15 @@ export class TasksMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/apps/tasks/task') .onDelete('api/apps/tasks/task')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
// Find the task and delete it // Find the task and delete it
const index = this._tasks.findIndex(item => item.id === id); const index = this._tasks.findIndex((item) => item.id === id);
this._tasks.splice(index, 1); this._tasks.splice(index, 1);
return [ return [200, true];
200,
true,
];
}); });
} }
} }

View File

@ -204,9 +204,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: null, dueDate: null,
priority: 2, priority: 2,
tags : [ tags: ['a0bf42ca-c3a5-47be-8341-b9c0bb8ef270'],
'a0bf42ca-c3a5-47be-8341-b9c0bb8ef270',
],
assignedTo: null, assignedTo: null,
subTasks: [ subTasks: [
{ {
@ -240,9 +238,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: null, dueDate: null,
priority: 1, priority: 1,
tags : [ tags: ['51483dd3-cb98-4400-9128-4bd66b455807'],
'51483dd3-cb98-4400-9128-4bd66b455807',
],
assignedTo: '4678ad07-e057-48a9-a5d1-3cf98e722eeb', assignedTo: '4678ad07-e057-48a9-a5d1-3cf98e722eeb',
subTasks: [ subTasks: [
{ {
@ -266,9 +262,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2018-09-29T19:30:45.325Z', dueDate: '2018-09-29T19:30:45.325Z',
priority: 1, priority: 1,
tags : [ tags: ['c6058d0d-a4b0-4453-986a-9d249ec230b1'],
'c6058d0d-a4b0-4453-986a-9d249ec230b1',
],
assignedTo: '6617b0a3-0ccd-44ea-af78-c6633115d683', assignedTo: '6617b0a3-0ccd-44ea-af78-c6633115d683',
subTasks: [], subTasks: [],
order: 5, order: 5,
@ -377,9 +371,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2019-08-10T06:18:17.785Z', dueDate: '2019-08-10T06:18:17.785Z',
priority: 1, priority: 1,
tags : [ tags: ['a0bf42ca-c3a5-47be-8341-b9c0bb8ef270'],
'a0bf42ca-c3a5-47be-8341-b9c0bb8ef270',
],
assignedTo: '368aab1e-ebce-43ba-8925-4cf13937867b', assignedTo: '368aab1e-ebce-43ba-8925-4cf13937867b',
subTasks: [ subTasks: [
{ {
@ -398,9 +390,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2024-08-23T14:33:06.227Z', dueDate: '2024-08-23T14:33:06.227Z',
priority: 2, priority: 2,
tags : [ tags: ['91658b8a-f382-4b0c-a53f-e9390351c2c5'],
'91658b8a-f382-4b0c-a53f-e9390351c2c5',
],
assignedTo: '271e6a06-0d37-433d-bc8d-607b12bcbed9', assignedTo: '271e6a06-0d37-433d-bc8d-607b12bcbed9',
subTasks: [ subTasks: [
{ {
@ -482,9 +472,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: '2023-09-15T15:12:36.910Z', dueDate: '2023-09-15T15:12:36.910Z',
priority: 0, priority: 0,
tags : [ tags: ['2b884143-419a-45ca-a7f6-48f99f4e7798'],
'2b884143-419a-45ca-a7f6-48f99f4e7798',
],
assignedTo: '3a23baf7-2db8-4ef5-8d49-86d3e708dff5', assignedTo: '3a23baf7-2db8-4ef5-8d49-86d3e708dff5',
subTasks: [ subTasks: [
{ {
@ -557,9 +545,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2019-10-13T08:25:17.064Z', dueDate: '2019-10-13T08:25:17.064Z',
priority: 0, priority: 0,
tags : [ tags: ['2b884143-419a-45ca-a7f6-48f99f4e7798'],
'2b884143-419a-45ca-a7f6-48f99f4e7798',
],
assignedTo: 'b2e97a96-2f15-4e3d-aff5-4ddf2af924d4', assignedTo: 'b2e97a96-2f15-4e3d-aff5-4ddf2af924d4',
subTasks: [ subTasks: [
{ {
@ -588,9 +574,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: '2020-02-03T05:39:30.880Z', dueDate: '2020-02-03T05:39:30.880Z',
priority: 1, priority: 1,
tags : [ tags: ['2b884143-419a-45ca-a7f6-48f99f4e7798'],
'2b884143-419a-45ca-a7f6-48f99f4e7798',
],
assignedTo: '65e15136-5168-4655-8bbc-e3ad8a94bf67', assignedTo: '65e15136-5168-4655-8bbc-e3ad8a94bf67',
subTasks: [], subTasks: [],
order: 16, order: 16,
@ -666,9 +650,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2023-10-04T15:48:16.507Z', dueDate: '2023-10-04T15:48:16.507Z',
priority: 1, priority: 1,
tags : [ tags: ['d3ef4226-ef2c-43b0-a986-3e3e07f32799'],
'd3ef4226-ef2c-43b0-a986-3e3e07f32799',
],
assignedTo: null, assignedTo: null,
subTasks: [ subTasks: [
{ {
@ -692,9 +674,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: '2024-02-01T10:02:52.745Z', dueDate: '2024-02-01T10:02:52.745Z',
priority: 1, priority: 1,
tags : [ tags: ['a0bf42ca-c3a5-47be-8341-b9c0bb8ef270'],
'a0bf42ca-c3a5-47be-8341-b9c0bb8ef270',
],
assignedTo: '368aab1e-ebce-43ba-8925-4cf13937867b', assignedTo: '368aab1e-ebce-43ba-8925-4cf13937867b',
subTasks: [ subTasks: [
{ {
@ -821,9 +801,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: '2023-12-08T23:20:50.910Z', dueDate: '2023-12-08T23:20:50.910Z',
priority: 2, priority: 2,
tags : [ tags: ['a0bf42ca-c3a5-47be-8341-b9c0bb8ef270'],
'a0bf42ca-c3a5-47be-8341-b9c0bb8ef270',
],
assignedTo: null, assignedTo: null,
subTasks: [], subTasks: [],
order: 25, order: 25,
@ -875,9 +853,7 @@ export const tasks = [
completed: true, completed: true,
dueDate: '2020-06-08T00:23:24.051Z', dueDate: '2020-06-08T00:23:24.051Z',
priority: 1, priority: 1,
tags : [ tags: ['91658b8a-f382-4b0c-a53f-e9390351c2c5'],
'91658b8a-f382-4b0c-a53f-e9390351c2c5',
],
assignedTo: '65f1c421-83c5-4cdf-99da-d97794328679', assignedTo: '65f1c421-83c5-4cdf-99da-d97794328679',
subTasks: [ subTasks: [
{ {
@ -911,9 +887,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2024-01-27T11:17:52.198Z', dueDate: '2024-01-27T11:17:52.198Z',
priority: 1, priority: 1,
tags : [ tags: ['d3ef4226-ef2c-43b0-a986-3e3e07f32799'],
'd3ef4226-ef2c-43b0-a986-3e3e07f32799',
],
assignedTo: 'b2e97a96-2f15-4e3d-aff5-4ddf2af924d4', assignedTo: 'b2e97a96-2f15-4e3d-aff5-4ddf2af924d4',
subTasks: [ subTasks: [
{ {
@ -932,9 +906,7 @@ export const tasks = [
completed: false, completed: false,
dueDate: '2024-06-24T04:38:28.087Z', dueDate: '2024-06-24T04:38:28.087Z',
priority: 1, priority: 1,
tags : [ tags: ['51483dd3-cb98-4400-9128-4bd66b455807'],
'51483dd3-cb98-4400-9128-4bd66b455807',
],
assignedTo: '7f5db993-ec36-412f-9db3-16d076a98807', assignedTo: '7f5db993-ec36-412f-9db3-16d076a98807',
subTasks: [ subTasks: [
{ {

View File

@ -7,18 +7,17 @@ import HmacSHA256 from 'crypto-js/hmac-sha256';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthMockApi export class AuthMockApi {
{
private readonly _secret: any; private readonly _secret: any;
private _user: any = userData; private _user: any = userData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Set the mock-api // Set the mock-api
this._secret = 'YOUR_VERY_CONFIDENTIAL_SECRET_FOR_SIGNING_JWT_TOKENS!!!'; this._secret =
'YOUR_VERY_CONFIDENTIAL_SECRET_FOR_SIGNING_JWT_TOKENS!!!';
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
@ -31,42 +30,32 @@ export class AuthMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Forgot password - POST // @ Forgot password - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/auth/forgot-password', 1000) .onPost('api/auth/forgot-password', 1000)
.reply(() => .reply(() => [200, true]);
[
200,
true,
],
);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Reset password - POST // @ Reset password - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/auth/reset-password', 1000) .onPost('api/auth/reset-password', 1000)
.reply(() => .reply(() => [200, true]);
[
200,
true,
],
);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Sign in - POST // @ Sign in - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/auth/sign-in', 1500) .onPost('api/auth/sign-in', 1500)
.reply(({request}) => .reply(({ request }) => {
{
// Sign in successful // Sign in successful
if ( request.body.email === 'hughes.brian@company.com' && request.body.password === 'admin' ) if (
{ request.body.email === 'hughes.brian@company.com' &&
request.body.password === 'admin'
) {
return [ return [
200, 200,
{ {
@ -78,10 +67,7 @@ export class AuthMockApi
} }
// Invalid credentials // Invalid credentials
return [ return [404, false];
404,
false,
];
}); });
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -89,14 +75,12 @@ export class AuthMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/auth/sign-in-with-token') .onPost('api/auth/sign-in-with-token')
.reply(({request}) => .reply(({ request }) => {
{
// Get the access token // Get the access token
const accessToken = request.body.accessToken; const accessToken = request.body.accessToken;
// Verify the token // Verify the token
if ( this._verifyJWTToken(accessToken) ) if (this._verifyJWTToken(accessToken)) {
{
return [ return [
200, 200,
{ {
@ -119,15 +103,9 @@ export class AuthMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Sign up - POST // @ Sign up - POST
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onPost('api/auth/sign-up', 1500).reply(() =>
.onPost('api/auth/sign-up', 1500)
.reply(() =>
// Simply return true // Simply return true
[ [200, true]
200,
true,
],
); );
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -135,11 +113,12 @@ export class AuthMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/auth/unlock-session', 1500) .onPost('api/auth/unlock-session', 1500)
.reply(({request}) => .reply(({ request }) => {
{
// Sign in successful // Sign in successful
if ( request.body.email === 'hughes.brian@company.com' && request.body.password === 'admin' ) if (
{ request.body.email === 'hughes.brian@company.com' &&
request.body.password === 'admin'
) {
return [ return [
200, 200,
{ {
@ -151,10 +130,7 @@ export class AuthMockApi
} }
// Invalid credentials // Invalid credentials
return [ return [404, false];
404,
false,
];
}); });
} }
@ -168,8 +144,7 @@ export class AuthMockApi
* @param source * @param source
* @private * @private
*/ */
private _base64url(source: any): string private _base64url(source: any): string {
{
// Encode in classical base64 // Encode in classical base64
let encodedSource = Base64.stringify(source); let encodedSource = Base64.stringify(source);
@ -192,8 +167,7 @@ export class AuthMockApi
* *
* @private * @private
*/ */
private _generateJWTToken(): string private _generateJWTToken(): string {
{
// Define token header // Define token header
const header = { const header = {
alg: 'HS256', alg: 'HS256',
@ -203,7 +177,7 @@ export class AuthMockApi
// Calculate the issued at and expiration dates // Calculate the issued at and expiration dates
const date = new Date(); const date = new Date();
const iat = Math.floor(date.getTime() / 1000); const iat = Math.floor(date.getTime() / 1000);
const exp = Math.floor((date.setDate(date.getDate() + 7)) / 1000); const exp = Math.floor(date.setDate(date.getDate() + 7) / 1000);
// Define token payload // Define token payload
const payload = { const payload = {
@ -235,8 +209,7 @@ export class AuthMockApi
* @param token * @param token
* @private * @private
*/ */
private _verifyJWTToken(token: string): boolean private _verifyJWTToken(token: string): boolean {
{
// Split the token into parts // Split the token into parts
const parts = token.split('.'); const parts = token.split('.');
const header = parts[0]; const header = parts[0];
@ -244,9 +217,11 @@ export class AuthMockApi
const signature = parts[2]; const signature = parts[2];
// Re-sign and encode the header and payload using the secret // Re-sign and encode the header and payload using the secret
const signatureCheck = this._base64url(HmacSHA256(header + '.' + payload, this._secret)); const signatureCheck = this._base64url(
HmacSHA256(header + '.' + payload, this._secret)
);
// Verify that the resulting signature is valid // Verify that the resulting signature is valid
return (signature === signatureCheck); return signature === signatureCheck;
} }
} }

View File

@ -4,15 +4,13 @@ import { messages as messagesData } from 'app/mock-api/common/messages/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class MessagesMockApi export class MessagesMockApi {
{
private _messages: any = messagesData; private _messages: any = messagesData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class MessagesMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Messages - GET // @ Messages - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -38,8 +35,7 @@ export class MessagesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/messages') .onPost('api/common/messages')
.reply(({request}) => .reply(({ request }) => {
{
// Get the message // Get the message
const newMessage = cloneDeep(request.body.message); const newMessage = cloneDeep(request.body.message);
@ -58,8 +54,7 @@ export class MessagesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/common/messages') .onPatch('api/common/messages')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and message // Get the id and message
const id = request.body.id; const id = request.body.id;
const message = cloneDeep(request.body.message); const message = cloneDeep(request.body.message);
@ -68,17 +63,21 @@ export class MessagesMockApi
let updatedMessage = null; let updatedMessage = null;
// Find the message and update it // Find the message and update it
this._messages.forEach((item: any, index: number, messages: any[]) => this._messages.forEach(
{ (item: any, index: number, messages: any[]) => {
if ( item.id === id ) if (item.id === id) {
{
// Update the message // Update the message
messages[index] = assign({}, messages[index], message); messages[index] = assign(
{},
messages[index],
message
);
// Store the updated message // Store the updated message
updatedMessage = messages[index]; updatedMessage = messages[index];
} }
}); }
);
// Return the response // Return the response
return [200, updatedMessage]; return [200, updatedMessage];
@ -89,8 +88,7 @@ export class MessagesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/common/messages') .onDelete('api/common/messages')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
@ -98,7 +96,9 @@ export class MessagesMockApi
let deletedMessage = null; let deletedMessage = null;
// Find the message // Find the message
const index = this._messages.findIndex((item: any) => item.id === id); const index = this._messages.findIndex(
(item: any) => item.id === id
);
// Store the deleted message // Store the deleted message
deletedMessage = cloneDeep(this._messages[index]); deletedMessage = cloneDeep(this._messages[index]);
@ -115,15 +115,15 @@ export class MessagesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/common/messages/mark-all-as-read') .onGet('api/common/messages/mark-all-as-read')
.reply(() => .reply(() => {
{
// Go through all messages // Go through all messages
this._messages.forEach((item: any, index: number, messages: any[]) => this._messages.forEach(
{ (item: any, index: number, messages: any[]) => {
// Mark it as read // Mark it as read
messages[index].read = true; messages[index].read = true;
messages[index].seen = true; messages[index].seen = true;
}); }
);
// Return the response // Return the response
return [200, true]; return [200, true];
@ -134,8 +134,7 @@ export class MessagesMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/messages/toggle-read-status') .onPost('api/common/messages/toggle-read-status')
.reply(({request}) => .reply(({ request }) => {
{
// Get the message // Get the message
const message = cloneDeep(request.body.message); const message = cloneDeep(request.body.message);
@ -143,17 +142,17 @@ export class MessagesMockApi
let updatedMessage = null; let updatedMessage = null;
// Find the message and update it // Find the message and update it
this._messages.forEach((item: any, index: number, messages: any[]) => this._messages.forEach(
{ (item: any, index: number, messages: any[]) => {
if ( item.id === message.id ) if (item.id === message.id) {
{
// Update the message // Update the message
messages[index].read = message.read; messages[index].read = message.read;
// Store the updated message // Store the updated message
updatedMessage = messages[index]; updatedMessage = messages[index];
} }
}); }
);
// Return the response // Return the response
return [200, updatedMessage]; return [200, updatedMessage];

View File

@ -17,7 +17,8 @@ export const messages = [
id: '608b4479-a3ac-4e26-8675-3609c52aca58', id: '608b4479-a3ac-4e26-8675-3609c52aca58',
image: 'images/avatars/male-04.jpg', image: 'images/avatars/male-04.jpg',
title: 'Leo Gill (Client #8817)', title: 'Leo Gill (Client #8817)',
description: 'You can download the latest invoices now. Please check and let me know.', description:
'You can download the latest invoices now. Please check and let me know.',
time: now.minus({ minutes: 50 }).toISO(), // 50 minutes ago time: now.minus({ minutes: 50 }).toISO(), // 50 minutes ago
read: false, read: false,
}, },
@ -25,7 +26,7 @@ export const messages = [
id: '22148c0c-d788-4d49-9467-447677d11b76', id: '22148c0c-d788-4d49-9467-447677d11b76',
image: 'images/avatars/female-01.jpg', image: 'images/avatars/female-01.jpg',
title: 'Sarah', title: 'Sarah',
description: 'Don\'t forget to pickup Jeremy after school!', description: "Don't forget to pickup Jeremy after school!",
time: now.minus({ hours: 3 }).toISO(), // 3 hours ago time: now.minus({ hours: 3 }).toISO(), // 3 hours ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',
@ -35,7 +36,7 @@ export const messages = [
id: '492e2917-760c-4921-aa5a-3201a857cd48', id: '492e2917-760c-4921-aa5a-3201a857cd48',
image: 'images/avatars/female-12.jpg', image: 'images/avatars/female-12.jpg',
title: 'Nancy Salazar &bull; Joy Publishing', title: 'Nancy Salazar &bull; Joy Publishing',
description: 'I\'ll proof read your bio on next Monday.', description: "I'll proof read your bio on next Monday.",
time: now.minus({ hours: 5 }).toISO(), // 5 hours ago time: now.minus({ hours: 5 }).toISO(), // 5 hours ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',
@ -45,7 +46,8 @@ export const messages = [
id: '214a46e5-cae7-4b18-9869-eabde7c7ea52', id: '214a46e5-cae7-4b18-9869-eabde7c7ea52',
image: 'images/avatars/male-06.jpg', image: 'images/avatars/male-06.jpg',
title: 'Matthew Wood', title: 'Matthew Wood',
description: 'Dude, I heard that they are going to promote you! Congrats man, tonight the drinks are on me!', description:
'Dude, I heard that they are going to promote you! Congrats man, tonight the drinks are on me!',
time: now.minus({ hours: 7 }).toISO(), // 7 hours ago time: now.minus({ hours: 7 }).toISO(), // 7 hours ago
read: false, read: false,
link: '/dashboards/project', link: '/dashboards/project',
@ -55,7 +57,8 @@ export const messages = [
id: '95930319-61cc-4c7e-9324-f1091865330c', id: '95930319-61cc-4c7e-9324-f1091865330c',
image: 'images/avatars/female-04.jpg', image: 'images/avatars/female-04.jpg',
title: 'Elizabeth (New assistant)', title: 'Elizabeth (New assistant)',
description: 'Boss, I\'ve sent all client invoices but Geoffrey refusing to pay.', description:
"Boss, I've sent all client invoices but Geoffrey refusing to pay.",
time: now.minus({ hours: 9 }).toISO(), // 9 hours ago time: now.minus({ hours: 9 }).toISO(), // 9 hours ago
read: false, read: false,
link: '/dashboards/project', link: '/dashboards/project',
@ -65,7 +68,8 @@ export const messages = [
id: '802935e9-9577-48bc-98d1-308a4872afd7', id: '802935e9-9577-48bc-98d1-308a4872afd7',
image: 'images/avatars/male-06.jpg', image: 'images/avatars/male-06.jpg',
title: 'William Bell', title: 'William Bell',
description: 'Did you see this game? We should hang out and give it a shot sometime.', description:
'Did you see this game? We should hang out and give it a shot sometime.',
time: now.minus({ day: 1 }).toISO(), // 1 day ago time: now.minus({ day: 1 }).toISO(), // 1 day ago
read: true, read: true,
link: 'https://www.google.com', link: 'https://www.google.com',
@ -75,7 +79,8 @@ export const messages = [
id: '059f3738-633b-48ea-ad83-19016ce24c62', id: '059f3738-633b-48ea-ad83-19016ce24c62',
image: 'images/avatars/female-09.jpg', image: 'images/avatars/female-09.jpg',
title: 'Cheryl Obrien - HR', title: 'Cheryl Obrien - HR',
description: 'Why did\'t you still look at the kitten pictures I\'ve sent to you!', description:
"Why did't you still look at the kitten pictures I've sent to you!",
time: now.minus({ day: 3 }).toISO(), // 3 days ago time: now.minus({ day: 3 }).toISO(), // 3 days ago
read: false, read: false,
link: '/dashboards/project', link: '/dashboards/project',
@ -85,7 +90,8 @@ export const messages = [
id: '5c2bb44d-5ca7-42ff-ad7e-46ced9f49a24', id: '5c2bb44d-5ca7-42ff-ad7e-46ced9f49a24',
image: 'images/avatars/female-15.jpg', image: 'images/avatars/female-15.jpg',
title: 'Joan Jones - Tech', title: 'Joan Jones - Tech',
description: 'Dude, Cheryl keeps bugging me with kitten pictures all the time :( What are we gonna do about it?', description:
'Dude, Cheryl keeps bugging me with kitten pictures all the time :( What are we gonna do about it?',
time: now.minus({ day: 4 }).toISO(), // 4 days ago time: now.minus({ day: 4 }).toISO(), // 4 days ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',

View File

@ -1,22 +1,29 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseNavigationItem } from '@fuse/components/navigation'; import { FuseNavigationItem } from '@fuse/components/navigation';
import { FuseMockApiService } from '@fuse/lib/mock-api'; import { FuseMockApiService } from '@fuse/lib/mock-api';
import { compactNavigation, defaultNavigation, futuristicNavigation, horizontalNavigation } from 'app/mock-api/common/navigation/data'; import {
compactNavigation,
defaultNavigation,
futuristicNavigation,
horizontalNavigation,
} from 'app/mock-api/common/navigation/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NavigationMockApi export class NavigationMockApi {
{ private readonly _compactNavigation: FuseNavigationItem[] =
private readonly _compactNavigation: FuseNavigationItem[] = compactNavigation; compactNavigation;
private readonly _defaultNavigation: FuseNavigationItem[] = defaultNavigation; private readonly _defaultNavigation: FuseNavigationItem[] =
private readonly _futuristicNavigation: FuseNavigationItem[] = futuristicNavigation; defaultNavigation;
private readonly _horizontalNavigation: FuseNavigationItem[] = horizontalNavigation; private readonly _futuristicNavigation: FuseNavigationItem[] =
futuristicNavigation;
private readonly _horizontalNavigation: FuseNavigationItem[] =
horizontalNavigation;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -28,47 +35,40 @@ export class NavigationMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Navigation - GET // @ Navigation - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService.onGet('api/common/navigation').reply(() => {
.onGet('api/common/navigation')
.reply(() =>
{
// Fill compact navigation children using the default navigation // Fill compact navigation children using the default navigation
this._compactNavigation.forEach((compactNavItem) => this._compactNavigation.forEach((compactNavItem) => {
{ this._defaultNavigation.forEach((defaultNavItem) => {
this._defaultNavigation.forEach((defaultNavItem) => if (defaultNavItem.id === compactNavItem.id) {
{ compactNavItem.children = cloneDeep(
if ( defaultNavItem.id === compactNavItem.id ) defaultNavItem.children
{ );
compactNavItem.children = cloneDeep(defaultNavItem.children);
} }
}); });
}); });
// Fill futuristic navigation children using the default navigation // Fill futuristic navigation children using the default navigation
this._futuristicNavigation.forEach((futuristicNavItem) => this._futuristicNavigation.forEach((futuristicNavItem) => {
{ this._defaultNavigation.forEach((defaultNavItem) => {
this._defaultNavigation.forEach((defaultNavItem) => if (defaultNavItem.id === futuristicNavItem.id) {
{ futuristicNavItem.children = cloneDeep(
if ( defaultNavItem.id === futuristicNavItem.id ) defaultNavItem.children
{ );
futuristicNavItem.children = cloneDeep(defaultNavItem.children);
} }
}); });
}); });
// Fill horizontal navigation children using the default navigation // Fill horizontal navigation children using the default navigation
this._horizontalNavigation.forEach((horizontalNavItem) => this._horizontalNavigation.forEach((horizontalNavItem) => {
{ this._defaultNavigation.forEach((defaultNavItem) => {
this._defaultNavigation.forEach((defaultNavItem) => if (defaultNavItem.id === horizontalNavItem.id) {
{ horizontalNavItem.children = cloneDeep(
if ( defaultNavItem.id === horizontalNavItem.id ) defaultNavItem.children
{ );
horizontalNavItem.children = cloneDeep(defaultNavItem.children);
} }
}); });
}); });

View File

@ -1103,7 +1103,7 @@ export const defaultNavigation: FuseNavigationItem[] = [
children: [ children: [
{ {
id: 'navigation-features.disabled-collapsable.child', id: 'navigation-features.disabled-collapsable.child',
title: 'You shouldn\'t be able to see this child', title: "You shouldn't be able to see this child",
type: 'basic', type: 'basic',
}, },
], ],

View File

@ -4,15 +4,13 @@ import { notifications as notificationsData } from 'app/mock-api/common/notifica
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NotificationsMockApi export class NotificationsMockApi {
{
private _notifications: any = notificationsData; private _notifications: any = notificationsData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class NotificationsMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Notifications - GET // @ Notifications - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -38,8 +35,7 @@ export class NotificationsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/notifications') .onPost('api/common/notifications')
.reply(({request}) => .reply(({ request }) => {
{
// Get the notification // Get the notification
const newNotification = cloneDeep(request.body.notification); const newNotification = cloneDeep(request.body.notification);
@ -58,8 +54,7 @@ export class NotificationsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/common/notifications') .onPatch('api/common/notifications')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and notification // Get the id and notification
const id = request.body.id; const id = request.body.id;
const notification = cloneDeep(request.body.notification); const notification = cloneDeep(request.body.notification);
@ -68,17 +63,21 @@ export class NotificationsMockApi
let updatedNotification = null; let updatedNotification = null;
// Find the notification and update it // Find the notification and update it
this._notifications.forEach((item: any, index: number, notifications: any[]) => this._notifications.forEach(
{ (item: any, index: number, notifications: any[]) => {
if ( item.id === id ) if (item.id === id) {
{
// Update the notification // Update the notification
notifications[index] = assign({}, notifications[index], notification); notifications[index] = assign(
{},
notifications[index],
notification
);
// Store the updated notification // Store the updated notification
updatedNotification = notifications[index]; updatedNotification = notifications[index];
} }
}); }
);
// Return the response // Return the response
return [200, updatedNotification]; return [200, updatedNotification];
@ -89,8 +88,7 @@ export class NotificationsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/common/notifications') .onDelete('api/common/notifications')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
@ -98,7 +96,9 @@ export class NotificationsMockApi
let deletedNotification = null; let deletedNotification = null;
// Find the notification // Find the notification
const index = this._notifications.findIndex((item: any) => item.id === id); const index = this._notifications.findIndex(
(item: any) => item.id === id
);
// Store the deleted notification // Store the deleted notification
deletedNotification = cloneDeep(this._notifications[index]); deletedNotification = cloneDeep(this._notifications[index]);
@ -115,15 +115,15 @@ export class NotificationsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onGet('api/common/notifications/mark-all-as-read') .onGet('api/common/notifications/mark-all-as-read')
.reply(() => .reply(() => {
{
// Go through all notifications // Go through all notifications
this._notifications.forEach((item: any, index: number, notifications: any[]) => this._notifications.forEach(
{ (item: any, index: number, notifications: any[]) => {
// Mark it as read // Mark it as read
notifications[index].read = true; notifications[index].read = true;
notifications[index].seen = true; notifications[index].seen = true;
}); }
);
// Return the response // Return the response
return [200, true]; return [200, true];
@ -134,8 +134,7 @@ export class NotificationsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/notifications/toggle-read-status') .onPost('api/common/notifications/toggle-read-status')
.reply(({request}) => .reply(({ request }) => {
{
// Get the notification // Get the notification
const notification = cloneDeep(request.body.notification); const notification = cloneDeep(request.body.notification);
@ -143,17 +142,17 @@ export class NotificationsMockApi
let updatedNotification = null; let updatedNotification = null;
// Find the notification and update it // Find the notification and update it
this._notifications.forEach((item: any, index: number, notifications: any[]) => this._notifications.forEach(
{ (item: any, index: number, notifications: any[]) => {
if ( item.id === notification.id ) if (item.id === notification.id) {
{
// Update the notification // Update the notification
notifications[index].read = notification.read; notifications[index].read = notification.read;
// Store the updated notification // Store the updated notification
updatedNotification = notifications[index]; updatedNotification = notifications[index];
} }
}); }
);
// Return the response // Return the response
return [200, updatedNotification]; return [200, updatedNotification];

View File

@ -16,7 +16,8 @@ export const notifications = [
{ {
id: '6e3e97e5-effc-4fb7-b730-52a151f0b641', id: '6e3e97e5-effc-4fb7-b730-52a151f0b641',
image: 'images/avatars/male-04.jpg', image: 'images/avatars/male-04.jpg',
description: '<strong>Leo Gill</strong> added you to <em>Top Secret Project</em> group and assigned you as a <em>Project Manager</em>', description:
'<strong>Leo Gill</strong> added you to <em>Top Secret Project</em> group and assigned you as a <em>Project Manager</em>',
time: now.minus({ minute: 50 }).toISO(), // 50 minutes ago time: now.minus({ minute: 50 }).toISO(), // 50 minutes ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',
@ -45,7 +46,8 @@ export const notifications = [
{ {
id: 'ef7b95a7-8e8b-4616-9619-130d9533add9', id: 'ef7b95a7-8e8b-4616-9619-130d9533add9',
image: 'images/avatars/male-06.jpg', image: 'images/avatars/male-06.jpg',
description: '<strong>Roger Murray</strong> accepted your friend request', description:
'<strong>Roger Murray</strong> accepted your friend request',
time: now.minus({ hour: 7 }).toISO(), // 7 hours ago time: now.minus({ hour: 7 }).toISO(), // 7 hours ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',
@ -74,7 +76,8 @@ export const notifications = [
id: '8f8e1bf9-4661-4939-9e43-390957b60f42', id: '8f8e1bf9-4661-4939-9e43-390957b60f42',
icon: 'heroicons_mini:star', icon: 'heroicons_mini:star',
title: 'Daily challenges', title: 'Daily challenges',
description: 'Your submission has been accepted and you are ready to sign-up for the final assigment which will be ready in 2 days', description:
'Your submission has been accepted and you are ready to sign-up for the final assigment which will be ready in 2 days',
time: now.minus({ day: 3 }).toISO(), // 3 days ago time: now.minus({ day: 3 }).toISO(), // 3 days ago
read: true, read: true,
link: '/dashboards/project', link: '/dashboards/project',

View File

@ -1,5 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FuseNavigationItem, FuseNavigationService } from '@fuse/components/navigation'; import {
FuseNavigationItem,
FuseNavigationService,
} from '@fuse/components/navigation';
import { FuseMockApiService } from '@fuse/lib/mock-api'; import { FuseMockApiService } from '@fuse/lib/mock-api';
import { contacts } from 'app/mock-api/apps/contacts/data'; import { contacts } from 'app/mock-api/apps/contacts/data';
import { tasks } from 'app/mock-api/apps/tasks/data'; import { tasks } from 'app/mock-api/apps/tasks/data';
@ -7,9 +10,9 @@ import { defaultNavigation } from 'app/mock-api/common/navigation/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SearchMockApi export class SearchMockApi {
{ private readonly _defaultNavigation: FuseNavigationItem[] =
private readonly _defaultNavigation: FuseNavigationItem[] = defaultNavigation; defaultNavigation;
private readonly _contacts: any[] = contacts; private readonly _contacts: any[] = contacts;
private readonly _tasks: any[] = tasks; private readonly _tasks: any[] = tasks;
@ -18,9 +21,8 @@ export class SearchMockApi
*/ */
constructor( constructor(
private _fuseMockApiService: FuseMockApiService, private _fuseMockApiService: FuseMockApiService,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
) ) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -32,49 +34,51 @@ export class SearchMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// Get the flat navigation and store it // Get the flat navigation and store it
const flatNavigation = this._fuseNavigationService.getFlatNavigation(this._defaultNavigation); const flatNavigation = this._fuseNavigationService.getFlatNavigation(
this._defaultNavigation
);
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Search results - GET // @ Search results - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/search') .onPost('api/common/search')
.reply(({request}) => .reply(({ request }) => {
{
// Get the search query // Get the search query
const query = cloneDeep(request.body.query.toLowerCase()); const query = cloneDeep(request.body.query.toLowerCase());
// If the search query is an empty string, // If the search query is an empty string,
// return an empty array // return an empty array
if ( query === '' ) if (query === '') {
{
return [200, { results: [] }]; return [200, { results: [] }];
} }
// Filter the contacts // Filter the contacts
const contactsResults = cloneDeep(this._contacts) const contactsResults = cloneDeep(this._contacts).filter(
.filter(contact => contact.name.toLowerCase().includes(query)); (contact) => contact.name.toLowerCase().includes(query)
);
// Filter the navigation // Filter the navigation
const pagesResults = cloneDeep(flatNavigation) const pagesResults = cloneDeep(flatNavigation).filter(
.filter(page => (page.title?.toLowerCase().includes(query) || (page.subtitle && page.subtitle.includes(query)))); (page) =>
page.title?.toLowerCase().includes(query) ||
(page.subtitle && page.subtitle.includes(query))
);
// Filter the tasks // Filter the tasks
const tasksResults = cloneDeep(this._tasks) const tasksResults = cloneDeep(this._tasks).filter((task) =>
.filter(task => task.title.toLowerCase().includes(query)); task.title.toLowerCase().includes(query)
);
// Prepare the results array // Prepare the results array
const results = []; const results = [];
// If there are contacts results... // If there are contacts results...
if ( contactsResults.length > 0 ) if (contactsResults.length > 0) {
{
// Normalize the results // Normalize the results
contactsResults.forEach((result) => contactsResults.forEach((result) => {
{
// Add a link // Add a link
result.link = '/apps/contacts/' + result.id; result.link = '/apps/contacts/' + result.id;
@ -91,11 +95,9 @@ export class SearchMockApi
} }
// If there are page results... // If there are page results...
if ( pagesResults.length > 0 ) if (pagesResults.length > 0) {
{
// Normalize the results // Normalize the results
pagesResults.forEach((result: any) => pagesResults.forEach((result: any) => {
{
// Add the page title as the value // Add the page title as the value
result.value = result.title; result.value = result.title;
}); });
@ -109,11 +111,9 @@ export class SearchMockApi
} }
// If there are tasks results... // If there are tasks results...
if ( tasksResults.length > 0 ) if (tasksResults.length > 0) {
{
// Normalize the results // Normalize the results
tasksResults.forEach((result) => tasksResults.forEach((result) => {
{
// Add a link // Add a link
result.link = '/apps/tasks/' + result.id; result.link = '/apps/tasks/' + result.id;

View File

@ -4,15 +4,13 @@ import { shortcuts as shortcutsData } from 'app/mock-api/common/shortcuts/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ShortcutsMockApi export class ShortcutsMockApi {
{
private _shortcuts: any = shortcutsData; private _shortcuts: any = shortcutsData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class ShortcutsMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Shortcuts - GET // @ Shortcuts - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -38,8 +35,7 @@ export class ShortcutsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPost('api/common/shortcuts') .onPost('api/common/shortcuts')
.reply(({request}) => .reply(({ request }) => {
{
// Get the shortcut // Get the shortcut
const newShortcut = cloneDeep(request.body.shortcut); const newShortcut = cloneDeep(request.body.shortcut);
@ -58,8 +54,7 @@ export class ShortcutsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/common/shortcuts') .onPatch('api/common/shortcuts')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id and shortcut // Get the id and shortcut
const id = request.body.id; const id = request.body.id;
const shortcut = cloneDeep(request.body.shortcut); const shortcut = cloneDeep(request.body.shortcut);
@ -68,17 +63,21 @@ export class ShortcutsMockApi
let updatedShortcut = null; let updatedShortcut = null;
// Find the shortcut and update it // Find the shortcut and update it
this._shortcuts.forEach((item: any, index: number, shortcuts: any[]) => this._shortcuts.forEach(
{ (item: any, index: number, shortcuts: any[]) => {
if ( item.id === id ) if (item.id === id) {
{
// Update the shortcut // Update the shortcut
shortcuts[index] = assign({}, shortcuts[index], shortcut); shortcuts[index] = assign(
{},
shortcuts[index],
shortcut
);
// Store the updated shortcut // Store the updated shortcut
updatedShortcut = shortcuts[index]; updatedShortcut = shortcuts[index];
} }
}); }
);
// Return the response // Return the response
return [200, updatedShortcut]; return [200, updatedShortcut];
@ -89,8 +88,7 @@ export class ShortcutsMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onDelete('api/common/shortcuts') .onDelete('api/common/shortcuts')
.reply(({request}) => .reply(({ request }) => {
{
// Get the id // Get the id
const id = request.params.get('id'); const id = request.params.get('id');
@ -98,7 +96,9 @@ export class ShortcutsMockApi
let deletedShortcut = null; let deletedShortcut = null;
// Find the shortcut // Find the shortcut
const index = this._shortcuts.findIndex((item: any) => item.id === id); const index = this._shortcuts.findIndex(
(item: any) => item.id === id
);
// Store the deleted shortcut // Store the deleted shortcut
deletedShortcut = cloneDeep(this._shortcuts[index]); deletedShortcut = cloneDeep(this._shortcuts[index]);

View File

@ -4,15 +4,13 @@ import { user as userData } from 'app/mock-api/common/user/data';
import { assign, cloneDeep } from 'lodash-es'; import { assign, cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class UserMockApi export class UserMockApi {
{
private _user: any = userData; private _user: any = userData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class UserMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ User - GET // @ User - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@ -38,8 +35,7 @@ export class UserMockApi
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
this._fuseMockApiService this._fuseMockApiService
.onPatch('api/common/user') .onPatch('api/common/user')
.reply(({request}) => .reply(({ request }) => {
{
// Get the user mock-api // Get the user mock-api
const user = cloneDeep(request.body.user); const user = cloneDeep(request.body.user);

View File

@ -4,15 +4,13 @@ import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/da
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AnalyticsMockApi export class AnalyticsMockApi {
{
private _analytics: any = analyticsData; private _analytics: any = analyticsData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class AnalyticsMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Sales - GET // @ Sales - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,13 @@ import { crypto as cryptoData } from 'app/mock-api/dashboards/crypto/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class CryptoMockApi export class CryptoMockApi {
{
private _crypto: any = cryptoData; private _crypto: any = cryptoData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class CryptoMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Crypto - GET // @ Crypto - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -14,7 +14,7 @@ export const crypto = {
marketCap: 148752956966, marketCap: 148752956966,
volume: 22903438381, volume: 22903438381,
supply: 18168448, supply: 18168448,
allTimeHigh: 19891.00, allTimeHigh: 19891.0,
price: { price: {
series: [ series: [
{ {
@ -62,7 +62,7 @@ export const crypto = {
}, },
{ {
x: -135, x: -135,
y: 6540.10, y: 6540.1,
}, },
{ {
x: -134, x: -134,
@ -142,7 +142,7 @@ export const crypto = {
}, },
{ {
x: -115, x: -115,
y: 6558.70, y: 6558.7,
}, },
{ {
x: -114, x: -114,
@ -150,15 +150,15 @@ export const crypto = {
}, },
{ {
x: -113, x: -113,
y: 6568.80, y: 6568.8,
}, },
{ {
x: -112, x: -112,
y: 6568.80, y: 6568.8,
}, },
{ {
x: -111, x: -111,
y: 6568.80, y: 6568.8,
}, },
{ {
x: -110, x: -110,
@ -178,7 +178,7 @@ export const crypto = {
}, },
{ {
x: -106, x: -106,
y: 6560.40, y: 6560.4,
}, },
{ {
x: -105, x: -105,
@ -194,7 +194,7 @@ export const crypto = {
}, },
{ {
x: -102, x: -102,
y: 6553.30, y: 6553.3,
}, },
{ {
x: -101, x: -101,
@ -210,11 +210,11 @@ export const crypto = {
}, },
{ {
x: -98, x: -98,
y: 6560.00, y: 6560.0,
}, },
{ {
x: -97, x: -97,
y: 6560.00, y: 6560.0,
}, },
{ {
x: -96, x: -96,
@ -246,7 +246,7 @@ export const crypto = {
}, },
{ {
x: -89, x: -89,
y: 6565.10, y: 6565.1,
}, },
{ {
x: -88, x: -88,
@ -378,7 +378,7 @@ export const crypto = {
}, },
{ {
x: -56, x: -56,
y: 6562.10, y: 6562.1,
}, },
{ {
x: -55, x: -55,
@ -434,11 +434,11 @@ export const crypto = {
}, },
{ {
x: -42, x: -42,
y: 6569.70, y: 6569.7,
}, },
{ {
x: -41, x: -41,
y: 6570.10, y: 6570.1,
}, },
{ {
x: -40, x: -40,
@ -494,7 +494,7 @@ export const crypto = {
}, },
{ {
x: -27, x: -27,
y: 6577.70, y: 6577.7,
}, },
{ {
x: -26, x: -26,
@ -506,7 +506,7 @@ export const crypto = {
}, },
{ {
x: -24, x: -24,
y: 6581.30, y: 6581.3,
}, },
{ {
x: -23, x: -23,
@ -574,11 +574,11 @@ export const crypto = {
}, },
{ {
x: -7, x: -7,
y: 6562.70, y: 6562.7,
}, },
{ {
x: -6, x: -6,
y: 6562.70, y: 6562.7,
}, },
{ {
x: -5, x: -5,
@ -598,7 +598,7 @@ export const crypto = {
}, },
{ {
x: -1, x: -1,
y: 6571.30, y: 6571.3,
}, },
], ],
}, },
@ -672,7 +672,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 10 }).toFormat('HH:mm'), x: now.minus({ minutes: 10 }).toFormat('HH:mm'),
y: 140.10, y: 140.1,
}, },
{ {
x: now.minus({ minutes: 9 }).toFormat('HH:mm'), x: now.minus({ minutes: 9 }).toFormat('HH:mm'),
@ -780,11 +780,11 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 7 }).toFormat('HH:mm'), x: now.minus({ minutes: 7 }).toFormat('HH:mm'),
y: 362.70, y: 362.7,
}, },
{ {
x: now.minus({ minutes: 6 }).toFormat('HH:mm'), x: now.minus({ minutes: 6 }).toFormat('HH:mm'),
y: 362.70, y: 362.7,
}, },
{ {
x: now.minus({ minutes: 5 }).toFormat('HH:mm'), x: now.minus({ minutes: 5 }).toFormat('HH:mm'),
@ -804,7 +804,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 1 }).toFormat('HH:mm'), x: now.minus({ minutes: 1 }).toFormat('HH:mm'),
y: 371.30, y: 371.3,
}, },
], ],
}, },
@ -872,7 +872,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 8 }).toFormat('HH:mm'), x: now.minus({ minutes: 8 }).toFormat('HH:mm'),
y: 0.250, y: 0.25,
}, },
{ {
x: now.minus({ minutes: 7 }).toFormat('HH:mm'), x: now.minus({ minutes: 7 }).toFormat('HH:mm'),
@ -900,7 +900,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 1 }).toFormat('HH:mm'), x: now.minus({ minutes: 1 }).toFormat('HH:mm'),
y: 0.240, y: 0.24,
}, },
], ],
}, },
@ -968,7 +968,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 8 }).toFormat('HH:mm'), x: now.minus({ minutes: 8 }).toFormat('HH:mm'),
y: 59.50, y: 59.5,
}, },
{ {
x: now.minus({ minutes: 7 }).toFormat('HH:mm'), x: now.minus({ minutes: 7 }).toFormat('HH:mm'),
@ -1032,7 +1032,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 16 }).toFormat('HH:mm'), x: now.minus({ minutes: 16 }).toFormat('HH:mm'),
y: 49.50, y: 49.5,
}, },
{ {
x: now.minus({ minutes: 15 }).toFormat('HH:mm'), x: now.minus({ minutes: 15 }).toFormat('HH:mm'),
@ -1164,11 +1164,11 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 7 }).toFormat('HH:mm'), x: now.minus({ minutes: 7 }).toFormat('HH:mm'),
y: 12.70, y: 12.7,
}, },
{ {
x: now.minus({ minutes: 6 }).toFormat('HH:mm'), x: now.minus({ minutes: 6 }).toFormat('HH:mm'),
y: 12.70, y: 12.7,
}, },
{ {
x: now.minus({ minutes: 5 }).toFormat('HH:mm'), x: now.minus({ minutes: 5 }).toFormat('HH:mm'),
@ -1188,7 +1188,7 @@ export const crypto = {
}, },
{ {
x: now.minus({ minutes: 1 }).toFormat('HH:mm'), x: now.minus({ minutes: 1 }).toFormat('HH:mm'),
y: 11.30, y: 11.3,
}, },
], ],
}, },
@ -1196,4 +1196,3 @@ export const crypto = {
}, },
], ],
}; };

View File

@ -4,15 +4,13 @@ import { finance as financeData } from 'app/mock-api/dashboards/finance/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class FinanceMockApi export class FinanceMockApi {
{
private _finance: any = financeData; private _finance: any = financeData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class FinanceMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Sales - GET // @ Sales - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,13 @@ import { project as projectData } from 'app/mock-api/dashboards/project/data';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ProjectMockApi export class ProjectMockApi {
{
private _project: any = projectData; private _project: any = projectData;
/** /**
* Constructor * Constructor
*/ */
constructor(private _fuseMockApiService: FuseMockApiService) constructor(private _fuseMockApiService: FuseMockApiService) {
{
// Register Mock API handlers // Register Mock API handlers
this.registerHandlers(); this.registerHandlers();
} }
@ -24,8 +22,7 @@ export class ProjectMockApi
/** /**
* Register Mock API handlers * Register Mock API handlers
*/ */
registerHandlers(): void registerHandlers(): void {
{
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Sales - GET // @ Sales - GET
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@ -10,7 +10,7 @@ export const project = {
'this-week': { 'this-week': {
'new-issues': 214, 'new-issues': 214,
'closed-issues': 75, 'closed-issues': 75,
'fixed' : 3, fixed: 3,
'wont-fix': 4, 'wont-fix': 4,
're-opened': 8, 're-opened': 8,
'needs-triage': 6, 'needs-triage': 6,
@ -18,7 +18,7 @@ export const project = {
'last-week': { 'last-week': {
'new-issues': 197, 'new-issues': 197,
'closed-issues': 72, 'closed-issues': 72,
'fixed' : 6, fixed: 6,
'wont-fix': 11, 'wont-fix': 11,
're-opened': 6, 're-opened': 6,
'needs-triage': 5, 'needs-triage': 5,
@ -55,12 +55,12 @@ export const project = {
taskDistribution: { taskDistribution: {
overview: { overview: {
'this-week': { 'this-week': {
'new' : 594, new: 594,
'completed': 287, completed: 287,
}, },
'last-week': { 'last-week': {
'new' : 526, new: 526,
'completed': 260, completed: 260,
}, },
}, },
labels: ['API', 'Backend', 'Frontend', 'Issues'], labels: ['API', 'Backend', 'Frontend', 'Issues'],
@ -94,14 +94,14 @@ export const project = {
location: 'Magnolia', location: 'Magnolia',
}, },
{ {
title : 'Jane\'s Birthday Party', title: "Jane's Birthday Party",
time: '07:30 PM', time: '07:30 PM',
location: 'Home', location: 'Home',
}, },
{ {
title : 'Overseer\'s Retirement Party', title: "Overseer's Retirement Party",
time: '09:30 PM', time: '09:30 PM',
location: 'Overseer\'s room', location: "Overseer's room",
}, },
], ],
tomorrow: [ tomorrow: [
@ -130,12 +130,12 @@ export const project = {
{ {
title: 'Release Party', title: 'Release Party',
time: '07:30 PM', time: '07:30 PM',
location: 'CEO\'s house', location: "CEO's house",
}, },
{ {
title : 'CEO\'s Private Party', title: "CEO's Private Party",
time: '09:30 PM', time: '09:30 PM',
location: 'CEO\'s Penthouse', location: "CEO's Penthouse",
}, },
], ],
}, },
@ -151,12 +151,24 @@ export const project = {
weeklyExpenses: { weeklyExpenses: {
amount: 17663, amount: 17663,
labels: [ labels: [
now.minus({days: 47}).toFormat('dd MMM') + ' - ' + now.minus({days: 40}).toFormat('dd MMM'), now.minus({ days: 47 }).toFormat('dd MMM') +
now.minus({days: 39}).toFormat('dd MMM') + ' - ' + now.minus({days: 32}).toFormat('dd MMM'), ' - ' +
now.minus({days: 31}).toFormat('dd MMM') + ' - ' + now.minus({days: 24}).toFormat('dd MMM'), now.minus({ days: 40 }).toFormat('dd MMM'),
now.minus({days: 23}).toFormat('dd MMM') + ' - ' + now.minus({days: 16}).toFormat('dd MMM'), now.minus({ days: 39 }).toFormat('dd MMM') +
now.minus({days: 15}).toFormat('dd MMM') + ' - ' + now.minus({days: 8}).toFormat('dd MMM'), ' - ' +
now.minus({days: 7}).toFormat('dd MMM') + ' - ' + now.toFormat('dd MMM'), now.minus({ days: 32 }).toFormat('dd MMM'),
now.minus({ days: 31 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 24 }).toFormat('dd MMM'),
now.minus({ days: 23 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 16 }).toFormat('dd MMM'),
now.minus({ days: 15 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 8 }).toFormat('dd MMM'),
now.minus({ days: 7 }).toFormat('dd MMM') +
' - ' +
now.toFormat('dd MMM'),
], ],
series: [ series: [
{ {
@ -168,10 +180,18 @@ export const project = {
monthlyExpenses: { monthlyExpenses: {
amount: 54663, amount: 54663,
labels: [ labels: [
now.minus({days: 31}).toFormat('dd MMM') + ' - ' + now.minus({days: 24}).toFormat('dd MMM'), now.minus({ days: 31 }).toFormat('dd MMM') +
now.minus({days: 23}).toFormat('dd MMM') + ' - ' + now.minus({days: 16}).toFormat('dd MMM'), ' - ' +
now.minus({days: 15}).toFormat('dd MMM') + ' - ' + now.minus({days: 8}).toFormat('dd MMM'), now.minus({ days: 24 }).toFormat('dd MMM'),
now.minus({days: 7}).toFormat('dd MMM') + ' - ' + now.toFormat('dd MMM'), now.minus({ days: 23 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 16 }).toFormat('dd MMM'),
now.minus({ days: 15 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 8 }).toFormat('dd MMM'),
now.minus({ days: 7 }).toFormat('dd MMM') +
' - ' +
now.toFormat('dd MMM'),
], ],
series: [ series: [
{ {
@ -183,26 +203,56 @@ export const project = {
yearlyExpenses: { yearlyExpenses: {
amount: 648813, amount: 648813,
labels: [ labels: [
now.minus({days: 79}).toFormat('dd MMM') + ' - ' + now.minus({days: 72}).toFormat('dd MMM'), now.minus({ days: 79 }).toFormat('dd MMM') +
now.minus({days: 71}).toFormat('dd MMM') + ' - ' + now.minus({days: 64}).toFormat('dd MMM'), ' - ' +
now.minus({days: 63}).toFormat('dd MMM') + ' - ' + now.minus({days: 56}).toFormat('dd MMM'), now.minus({ days: 72 }).toFormat('dd MMM'),
now.minus({days: 55}).toFormat('dd MMM') + ' - ' + now.minus({days: 48}).toFormat('dd MMM'), now.minus({ days: 71 }).toFormat('dd MMM') +
now.minus({days: 47}).toFormat('dd MMM') + ' - ' + now.minus({days: 40}).toFormat('dd MMM'), ' - ' +
now.minus({days: 39}).toFormat('dd MMM') + ' - ' + now.minus({days: 32}).toFormat('dd MMM'), now.minus({ days: 64 }).toFormat('dd MMM'),
now.minus({days: 31}).toFormat('dd MMM') + ' - ' + now.minus({days: 24}).toFormat('dd MMM'), now.minus({ days: 63 }).toFormat('dd MMM') +
now.minus({days: 23}).toFormat('dd MMM') + ' - ' + now.minus({days: 16}).toFormat('dd MMM'), ' - ' +
now.minus({days: 15}).toFormat('dd MMM') + ' - ' + now.minus({days: 8}).toFormat('dd MMM'), now.minus({ days: 56 }).toFormat('dd MMM'),
now.minus({days: 7}).toFormat('dd MMM') + ' - ' + now.toFormat('dd MMM'), now.minus({ days: 55 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 48 }).toFormat('dd MMM'),
now.minus({ days: 47 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 40 }).toFormat('dd MMM'),
now.minus({ days: 39 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 32 }).toFormat('dd MMM'),
now.minus({ days: 31 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 24 }).toFormat('dd MMM'),
now.minus({ days: 23 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 16 }).toFormat('dd MMM'),
now.minus({ days: 15 }).toFormat('dd MMM') +
' - ' +
now.minus({ days: 8 }).toFormat('dd MMM'),
now.minus({ days: 7 }).toFormat('dd MMM') +
' - ' +
now.toFormat('dd MMM'),
], ],
series: [ series: [
{ {
name: 'Expenses', name: 'Expenses',
data: [45891, 45801, 45834, 45843, 45800, 45900, 45814, 45856, 45910, 45849], data: [
45891, 45801, 45834, 45843, 45800, 45900, 45814, 45856,
45910, 45849,
],
}, },
], ],
}, },
budgetDetails: { budgetDetails: {
columns: ['type', 'total', 'expensesAmount', 'expensesPercentage', 'remainingAmount', 'remainingPercentage'], columns: [
'type',
'total',
'expensesAmount',
'expensesPercentage',
'remainingAmount',
'remainingPercentage',
],
rows: [ rows: [
{ {
id: 1, id: 1,

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