first commit

This commit is contained in:
2024-04-19 12:53:45 +07:00
commit 71a3a661dc
1943 changed files with 246917 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
import { ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core';
import {
getSeconds,
getMinutes,
getHours,
getDate,
getMonth,
getYear,
setSeconds,
setMinutes,
setHours,
setDate,
setMonth,
setYear
} from 'date-fns';
import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const DATE_TIME_PICKER_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateTimePickerComponent),
multi: true
};
@Component({
selector: 'app-mwl-demo-utils-date-time-picker',
templateUrl: 'dateTimeComponent.component.html',
styleUrls: ['dateTimePickerComponent.component.css'],
providers: [DATE_TIME_PICKER_CONTROL_VALUE_ACCESSOR]
})
export class DateTimePickerComponent implements ControlValueAccessor {
@Input() placeholder: string;
date: Date;
dateStruct: NgbDateStruct;
timeStruct: NgbTimeStruct;
datePicker: any;
private onChangeCallback: (date: Date) => void = () => { };
constructor(private cdr: ChangeDetectorRef) { }
writeValue(date: Date): void {
this.date = date;
this.dateStruct = {
day: getDate(date),
month: getMonth(date) + 1,
year: getYear(date)
};
this.timeStruct = {
second: getSeconds(date),
minute: getMinutes(date),
hour: getHours(date)
};
this.cdr.detectChanges();
}
registerOnChange(fn: any): void {
this.onChangeCallback = fn;
}
registerOnTouched(fn: any): void { }
updateDate(): void {
const newDate: Date = setYear(
setMonth(
setDate(this.date, this.dateStruct.day),
this.dateStruct.month - 1
),
this.dateStruct.year
);
this.onChangeCallback(newDate);
}
updateTime(): void {
const newDate: Date = setHours(
setMinutes(
setSeconds(this.date, this.timeStruct.second),
this.timeStruct.minute
),
this.timeStruct.hour
);
this.onChangeCallback(newDate);
}
}

View File

@@ -0,0 +1,57 @@
:host ::ng-deep .card-block .h4 {
font-size: 1.32rem;
font-family: bold;
}
:host ::ng-deep .content-sub-header {
margin-top: 0;
margin-bottom: 3rem;
}
:host ::ng-deep .mb-3,
.my-3 {
margin-bottom: 2rem !important;
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
:host ::ng-deep .ngb-datepicker-month-view {
display: block;
background-color: red !important;
}
@media screen and (max-width: 767px) {
.bottom_space {
padding-bottom: 1rem !important;
text-align: unset !important;
padding-left: 0px !important;
}
}
@media screen and (max-width: 767px) {
.pull-right {
float: left !important;
}
}
.close {
outline: none;
}
::ng-deep ngb-modal-backdrop {
z-index: 1050 !important;
}
:host ::ng-deep .ngb-dp-header {
background-color: var(--light) !important;
}
:host ::ng-deep .ngb-dp-weekdays {
background-color: var(--light);
}
:host ::ng-deep .ngb-dp-month-name {
background-color: var(--light);
}

View File

@@ -0,0 +1,144 @@
<div class="app-content content">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="calendar">
<div class="row">
<div class="col-12" *blockUI="'default'; message: 'Loading'">
<m-card [options]="options" (reloadFunction)="reloadDefault($event)">
<ng-container mCardHeaderTitle>
Add Event
</ng-container>
<ng-container mCardBody>
<p class="content-sub-header">This is the most advanced example having various features. This
example lists all the events on the calendar with Add new event functionality.</p>
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">{{ modalData?.action }}</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-body">
<div class="form-group">
<label>Event Title:</label>
<input type="text" name="event-title" class="form-control" [(ngModel)]="modalData?.event.title"
(keyup)="refresh.next()">
</div>
<div class="row">
<div class="col-md-6 col-12">
<div class="form-group">
<label>Primary Color:</label>
<input type="color" name="primary-color" [(ngModel)]="modalData?.event.color.primary"
(change)="refresh.next()">
</div>
</div>
<div class="col-md-6 col-12">
<div class="form-group">
<label>Secondary Color:</label>
<input type="color" name="secondary-color" [(ngModel)]="modalData?.event.color.secondary"
(change)="refresh.next()">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-12">
<div class="form-group">
<label>Starts At:</label>
<!-- <input type="date" name="starts-at" [(ngModel)]="modalData?.event.start" (ngModelChange)="refresh.next()" placeholder="Not set"> -->
<!-- <input type="date" class="form-control" id="date" > -->
<app-mwl-demo-utils-date-time-picker name="starts-at" [(ngModel)]="modalData?.event.start" (ngModelChange)="refresh.next()" placeholder="Not set">
</app-mwl-demo-utils-date-time-picker>
</div>
</div>
<div class="col-md-6 col-12">
<div class="form-group">
<label>Ends At:</label>
<app-mwl-demo-utils-date-time-picker name="ends-at" [(ngModel)]="modalData?.event.end" (ngModelChange)="refresh.next()" placeholder="Not set">
</app-mwl-demo-utils-date-time-picker>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">OK</button>
</div>
</ng-template>
<div class="card">
<div class="card-block">
<div class=" row d-flex flex-wrap justify-content-md-between mb-3 no-gutters">
<div class="col-md-4 bottom_space">
<div class="btn-group">
<div class="btn btn-primary" mwlCalendarPreviousView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="activeDayIsOpen = false">
Previous
</div>
<div class="btn btn-outline-secondary" mwlCalendarToday [(viewDate)]="viewDate">
Today
</div>
<div class="btn btn-primary" mwlCalendarNextView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="activeDayIsOpen = false">
Next
</div>
</div>
</div>
<div class="col-md-4 text-center align-self-center bottom_space">
<h3 class="text-uppercase mb-0">{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}</h3>
</div>
<div class="col-md-4 text-right bottom_space">
<div class="btn-group">
<div class="btn btn-primary" (click)="view = CalendarView.Month"
[class.active]="view === CalendarView.Month">
Month
</div>
<div class="btn btn-primary" (click)="view = CalendarView.Week"
[class.active]="view === CalendarView.Week">
Week
</div>
<div class="btn btn-primary" (click)="view = CalendarView.Day"
[class.active]="view === CalendarView.Day">
Day
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-raised btn-primary pull-right" (click)="addEvent()">
Add Event
</button>
</div>
</div>
<div [ngSwitch]="view">
<mwl-calendar-month-view *ngSwitchCase="'month'" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" [activeDayIsOpen]="activeDayIsOpen" (dayClicked)="dayClicked($event.day)"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="'week'" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="'day'" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
</div>
</div>
</ng-container>
</m-card>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AddeventComponent } from './addevent.component';
describe('AddeventComponent', () => {
let component: AddeventComponent;
let fixture: ComponentFixture<AddeventComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ AddeventComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddeventComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,233 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ViewChild, TemplateRef } from '@angular/core';
import { Subject } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { startOfDay, endOfDay, subDays, addDays, endOfMonth, startOfMonth, isSameDay, isSameMonth, addHours } from 'date-fns';
import { CalendarEventTimesChangedEvent, CalendarView, CalendarEvent, CalendarEventAction } from 'angular-calendar';
import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap';
import { BlockUI, NgBlockUI } from 'ng-block-ui';
const colors: any = {
red: {
primary: '#ad2121',
secondary: '#FAE3E3'
},
blue: {
primary: '#1e90ff',
secondary: '#D1E8FF'
},
yellow: {
primary: '#e3bc08',
secondary: '#FDF1BA'
}
};
@Component({
selector: 'app-adddeleteevent',
templateUrl: './addevent.component.html',
styleUrls: ['./addevent.component.css']
})
export class AddeventComponent implements OnInit {
@BlockUI('default') blockUIDefault: NgBlockUI;
date: Date;
options = {
close: true,
expand: true,
minimize: true,
reload: true
};
@ViewChild('modalContent') modalContent: TemplateRef<any>;
view: CalendarView = CalendarView.Month;
CalendarView = CalendarView;
newEvent: CalendarEvent;
viewDate: Date = new Date();
activeDayIsOpen = true;
modalData: {action: string;
event: CalendarEvent;
};
actions: CalendarEventAction[] = [
{
label: '<i class="fa fa-fw fa-pencil"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.handleEvent('Edited', event);
}
},
{
label: '<i class="fa fa-fw fa-times"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.events = this.events.filter(iEvent => iEvent !== event);
this.handleEvent('Deleted', event);
}
}
];
refresh: Subject<any> = new Subject();
events: CalendarEvent[] = [
{
start: subDays(startOfDay(new Date()), 0),
end: addDays(new Date(), 1),
title: 'Business Lunch',
color: colors.red,
actions: this.actions,
allDay: true,
resizable: {
beforeStart: true,
afterEnd: true
},
draggable: true
},
{
start: subDays(endOfMonth(new Date()), 2),
end: addDays(endOfMonth(new Date()), 1),
title: 'A long event that spans 2 months',
color: colors.blue,
allDay: true
},
{
start: subDays(startOfMonth(new Date()), 1),
end: addDays(startOfMonth(new Date()), 0),
title: 'Meeting',
color: colors.yellow,
actions: this.actions,
resizable: {
beforeStart: true,
afterEnd: true
},
draggable: true
}
];
dateStruct: NgbDateStruct;
timeStruct: NgbTimeStruct;
public breadcrumb: any;
datePicker: any;
private onChangeCallback: (date: Date) => void = () => { };
/**
*
* @param NgbModal modal
*/
constructor(private modal: NgbModal) { }
/**
* onInit
*/
ngOnInit() {
this.breadcrumb = {
'mainlabel': 'Calendar AddEvent',
'links': [
{
'name': 'Home',
'isLink': true,
'link': '/dashboard/sales'
},
{
'name': 'Apps',
'isLink': true,
'link': '#'
},
{
'name': 'Calendars',
'isLink': true,
'link': '#'
},
{
'name': 'AddEvent',
'isLink': false,
'link': '#'
}
]
};
}
/**
* selacted date
*
* @param date Clicked date in datepicker
* @param events Events of selected date
*/
dayClicked({ date, events }: { date: Date; events: CalendarEvent[] }): void {
if (isSameMonth(date, this.viewDate)) {
this.viewDate = date;
if (
(isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) ||
events.length === 0
) {
this.activeDayIsOpen = false;
} else {
this.activeDayIsOpen = true;
}
}
}
/**
*
* @param event Event of the time change
* @param newStart Event start date
* @param newEnd Event end date
*/
eventTimesChanged({
event,
newStart,
newEnd
}: CalendarEventTimesChangedEvent): void {
event.start = newStart;
event.end = newEnd;
this.handleEvent('Dropped or resized', event);
this.refresh.next({});
}
/**
*
* @param action Event action
* @param event calendar event
*/
handleEvent(action: string, event: CalendarEvent): void {
this.modalData = { event, action };
this.modal.open(this.modalContent, { size: 'lg' });
}
/**
* Add new event in modal
*/
addEvent(): void {
this.newEvent = {
title: 'New event',
start: startOfDay(new Date()),
end: endOfDay(new Date()),
color: colors.red,
draggable: true,
resizable: {
beforeStart: true,
afterEnd: true
},
actions: this.actions,
};
this.events.push(this.newEvent);
// this.refresh.next();
this.handleEvent('Add new event', this.newEvent);
this.refresh.next({});
}
/**
* Reload card
*/
reloadDefault () {
this.blockUIDefault.start('Loading..');
setTimeout(() => {
this.blockUIDefault.stop();
}, 2500);
}
}

View File

@@ -0,0 +1,25 @@
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input
readonly
class="form-control"
[placeholder]="placeholder"
name="date"
[(ngModel)]="dateStruct"
(ngModelChange)="updateDate()"
ngbDatepicker
#datePicker="ngbDatepicker">
<div class="input-group-append">
<div class="input-group-text" (click)="datePicker.toggle()" >
<i class="fa fa-calendar"></i>
</div>
</div>
</div>
</div>
</form>
<ngb-timepicker
[(ngModel)]="timeStruct"
(ngModelChange)="updateTime()"
[meridian]="true">
</ngb-timepicker>

View File

@@ -0,0 +1,143 @@
.form-group {
width: 100%;
}
:host ::ng-deep .btn-light:not(:disabled):not(.disabled):active {
color: unset !important;
background-color: unset !important;
border-color: #d3d9df !important;
}
:host ::ng-deep .btn-light:hover:not(.disabled):active {
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
:host ::ng-deep .btn-light {
color: unset !important;
background-color: unset !important;
border-color: unset !important;
}
:host ::ng-deep .bg-primary {
background-color: #007bff !important;
}
:host ::ng-deep .text-white {
color: #fff !important;
}
:host ::ng-deep .custom-day {
text-align: center;
padding: .185rem .25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
:host ::ng-deep .custom-day:active {
color: white !important;
}
div.bg-light {
background-color: #f8f9fa !important;
}
:host ::ng-deep .hidden {
display: block !important;
}
.ngb-dp-weekday {
color: #17a2b8;
}
.ngb-dp-week-number,
.ngb-dp-weekday {
line-height: 2rem;
text-align: center;
font-style: italic;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.small {
font-size: 80%;
font-weight: 400;
}
.ngb-dp-day {
cursor: pointer !important;
}
.ngb-dp-month {
pointer-events: none;
}
.btn-light:hover {
color: #212529 !important;
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.ngb-dp-header {
border-bottom: 0;
border-radius: .25rem .25rem 0 0;
padding-top: .25rem;
}
.ngb-dp-day,
.ngb-dp-week-number,
.ngb-dp-weekday {
width: 2rem;
height: 2rem;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
.custom-day.focused {
background-color: #e6e6e6;
}
.custom-day.range,
.custom-day:hover {
background-color: rgb(2, 117, 216);
color: white;
}
.custom-day.faded {
background-color: rgba(2, 117, 216, 0.5);
}
:host ::ng-deep.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
:host ::ng-deep .ngb-dp-header {
background-color: var(--light) !important;
}
:host ::ng-deep .ngb-dp-weekdays {
background-color: var(--light);
}
:host ::ng-deep .ngb-dp-month-name {
background-color: var(--light);
}

View File

@@ -0,0 +1,55 @@
:host ::ng-deep .h3 {
margin: 0 0 10px;
}
:host ::ng-deep .pre {
background-color: #f5f5f5;
padding: 15px;
}
:host ::ng-deep.card-header .heading-elements, .card-header .heading-elements-toggle {
background-color: inherit;
position: absolute;
top: 60px;
right: 77px;
}
:host ::ng-deep .cal-month-view .cal-open-day-events {
padding: 0px !important;
color: white;
background-color: #555;
-webkit-box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.5);
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.5);
}
:host ::ng-deep .card-header .heading-elements {
background-color: inherit;
position: absolute;
top: 19px;
right: 19px;
}
.fc-toolbar .fc-left {
float: left;
}
.fc-toolbar .fc-right {
float: right;
}
.fc-toolbar.fc-header-toolbar {
margin-bottom: 3em;
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
@media screen and (max-width: 767px) {
.bottom_space {
padding-bottom: 1rem !important;
text-align: unset !important;
padding-left: 0px !important;
}
}
::ng-deep ngb-modal-backdrop {
z-index: 1050 !important;
}

View File

@@ -0,0 +1,183 @@
<div class="app-content content">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="basic-examples">
<div class="row">
<div class="col-12" *blockUI="'basicViews'; message: 'Loading'">
<m-card [options]="options" (reloadFunction)="reloadBasicViews($event)">
<ng-container mCardHeaderTitle>
Basic Views
</ng-container>
<ng-container mCardBody>
<p>This is the most basic example having navigation buttons as well as month, week and day views. In
this example you have the option to change your view to a basicWeek or basicDay view. In the Basic
Week or Basic Day View events are listed all together.</p>
<br>
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
Action:
<pre>{{ modalData?.action }}</pre>
</div>
<div>
Event:
<pre>{{ modalData?.event | json }}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">
OK
</button>
</div>
</ng-template>
<div class="row">
<div class="col-md-6 bottom_space">
<h3>{{ BviewDate | calendarDate:(Bview + 'ViewTitle'):'en' }}</h3>
</div>
<div class="col-md-6 text-right bottom_space">
<div class="btn-group">
<div class="btn btn-primary" mwlCalendarPreviousView [view]="Bview" [(viewDate)]="BviewDate"
(viewDateChange)="activeDayIsOpen = false">
Previous
</div>
<div class="btn btn-outline-secondary" mwlCalendarToday [(viewDate)]="BviewDate">
Today
</div>
<div class="btn btn-primary" mwlCalendarNextView [view]="Bview" [(viewDate)]="BviewDate"
(viewDateChange)="activeDayIsOpen = false">
Next
</div>
</div>
</div>
</div>
<br />
<div [ngSwitch]="Bview">
<mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="BviewDate" [refresh]="refresh"
[activeDayIsOpen]="activeDayIsOpen" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="BviewDate" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="BviewDate" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
<br /><br /><br />
</ng-container>
</m-card>
</div>
</div>
</section>
</div>
<div class="content-body">
<section id="basic-examples">
<div class="row">
<div class="col-12" *blockUI="'basicViews'; message: 'Loading'">
<m-card [options]="options" (reloadFunction)="reloadBasicViews($event)">
<ng-container mCardHeaderTitle>
Basic Views
</ng-container>
<ng-container mCardBody>
<p>This is the most basic example having navigation buttons as well as month, week and day views. In
this example you have the option to change your view to a basicWeek or basicDay view. In the Basic
Week or Basic Day View events are listed all together.</p>
<br>
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
Action:
<pre>{{ modalData?.action }}</pre>
</div>
<div>
Event:
<pre>{{ modalData?.event | json }}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">
OK
</button>
</div>
</ng-template>
<div class="row">
<div class="col-md-4 bottom_space">
<div class="btn-group">
<div class="btn btn-primary" mwlCalendarPreviousView [view]="Bview" [(viewDate)]="BviewDate"
(viewDateChange)="activeDayIsOpen = false">
Previous
</div>
<div class="btn btn-outline-secondary" mwlCalendarToday [(viewDate)]="BviewDate">
Today
</div>
<div class="btn btn-primary" mwlCalendarNextView [view]="Bview" [(viewDate)]="BviewDate"
(viewDateChange)="activeDayIsOpen = false">
Next
</div>
</div>
</div>
<div class="col-md-4 text-center bottom_space">
<h3>{{ BviewDate | calendarDate:(Bview + 'ViewTitle'):'en' }}</h3>
</div>
<div class="col-md-4 text-right bottom_space">
<div class="btn-group">
<div class="btn btn-primary" (click)="Bview = CalendarView.Month"
[class.active]="Bview === CalendarView.Month">
Month
</div>
<div class="btn btn-primary" (click)="Bview = CalendarView.Week"
[class.active]="Bview === CalendarView.Week">
Week
</div>
<div class="btn btn-primary" (click)="Bview = CalendarView.Day"
[class.active]="Bview === CalendarView.Day">
Day
</div>
</div>
</div>
</div>
<br />
<div [ngSwitch]="Bview">
<mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="BviewDate" [refresh]="refresh"
[activeDayIsOpen]="activeDayIsOpen" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="BviewDate" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="BviewDate" [refresh]="refresh"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
<br /><br /><br />
</ng-container>
</m-card>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { BasicComponent } from './basic.component';
describe('BasicComponent', () => {
let component: BasicComponent;
let fixture: ComponentFixture<BasicComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ BasicComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BasicComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,89 @@
import { Component, OnInit } from '@angular/core';
import { ViewChild, TemplateRef } from '@angular/core';
import { Subject } from 'rxjs';
import { CalendarView } from 'angular-calendar';
import { BlockUI, NgBlockUI } from 'ng-block-ui';
@Component({
selector: 'app-basic',
templateUrl: './basic.component.html',
styleUrls: ['./basic.component.css']
})
export class BasicComponent implements OnInit {
@BlockUI('default') blockUIDefault: NgBlockUI;
@BlockUI('basicViews') blockUIBasicViews: NgBlockUI;
public breadcrumb: any;
options = {
close: true,
expand: true,
minimize: true,
reload: true
};
@ViewChild('modalContent', { static: true }) modalContent: TemplateRef<any>;
view: CalendarView = CalendarView.Month;
Bview: CalendarView = CalendarView.Month;
CalendarView = CalendarView;
viewDate: Date = new Date();
BviewDate: Date = new Date();
modalData: {action: string};
refresh: Subject<any> = new Subject();
activeDayIsOpen: boolean;
/**
* onInit
*/
ngOnInit() {
this.breadcrumb = {
mainlabel: 'Calendar Basic',
links: [
{
name: 'Home',
isLink: true,
link: '/dashboard/sales'
},
{
name: 'Apps',
isLink: true,
link: '#'
},
{
name: 'Calendars',
isLink: true,
link: '#'
},
{
name: 'Basic',
isLink: false,
link: '#'
}
]
};
}
/**
* realod card
*/
reloadDefault() {
this.blockUIDefault.start('Loading..');
setTimeout(() => {
this.blockUIDefault.stop();
}, 2500);
}
/**
* Realod card
*/
reloadBasicViews() {
this.blockUIBasicViews.start('Loading..');
setTimeout(() => {
this.blockUIBasicViews.stop();
}, 2500);
}
}

View File

@@ -0,0 +1,13 @@
import { CalenderModule } from './calender.module';
describe('CalenderModule', () => {
let calenderModule: CalenderModule;
beforeEach(() => {
calenderModule = new CalenderModule();
});
it('should create an instance', () => {
expect(calenderModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,61 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { CalendarModule, DateAdapter } from 'angular-calendar';
import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
import { RouterModule } from '@angular/router';
import { FlatpickrModule } from 'angularx-flatpickr';
import { BasicComponent } from './basic/basic.component';
import { EventsComponent } from './events/events.component';
import { AddeventComponent } from './addevent/addevent.component';
import { CardModule } from '../../partials/general/card/card.module';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { BlockUIModule } from 'ng-block-ui';
import { BlockTemplateComponent } from 'src/app/_layout/blockui/block-template.component';
import { DateTimePickerComponent } from './addevent/DateTimePickerComponent';
import {
NgbModalModule,
NgbDatepickerModule,
NgbTimepickerModule
} from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [
CommonModule,
CardModule,
BreadcrumbModule,
FormsModule,
FlatpickrModule.forRoot(),
BlockUIModule.forRoot({
template: BlockTemplateComponent
}),
NgbModalModule,
NgbDatepickerModule,
NgbTimepickerModule,
CalendarModule.forRoot({
provide: DateAdapter,
useFactory: adapterFactory
}),
RouterModule.forChild([
{
path: 'basic',
component: BasicComponent
},
{
path: 'events',
component: EventsComponent
},
{
path: 'addevent',
component: AddeventComponent
}
])
],
declarations: [
BasicComponent,
EventsComponent,
AddeventComponent,
DateTimePickerComponent
]
})
export class CalenderModule {}

View File

@@ -0,0 +1,27 @@
:host ::ng-deep .cal-month-view .cal-open-day-events {
padding: 0px !important;
color: white;
background-color: #555;
-webkit-box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.5);
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.5);
}
:host ::ng-deep .title {
width: 140px;
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
@media screen and (max-width: 767px) {
.bottom_space {
padding-bottom: 1rem !important;
text-align: unset !important;
padding-left: 0px !important;
}
}
::ng-deep ngb-modal-backdrop {
z-index: 1050 !important;
}

View File

@@ -0,0 +1,181 @@
<div class="app-content content">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="basic-examples">
<div class="row">
<div class="col-12">
<m-card [options]="options" (reloadFunction)="reloadEvents()">
<ng-container mCardHeaderTitle>
Events
</ng-container>
<ng-container mCardBody>
<p>This is the most basic example having navigation button to navigate next and previous months and
today button. This example lists all the events on the calendar with Add new event functionality. </p>
<br>
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
Action:
<pre>{{ modalData?.action }}</pre>
</div>
<div>
Event:
<pre>{{ modalData?.event | json }}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">
OK
</button>
</div>
</ng-template>
<div class="row">
<div class="col-md-4 bottom_space">
<div class="btn-group">
<div class="btn btn-primary" mwlCalendarPreviousView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="activeDayIsOpen = false">
Previous
</div>
<div class="btn btn-outline-secondary" mwlCalendarToday [(viewDate)]="viewDate">
Today
</div>
<div class="btn btn-primary" mwlCalendarNextView [view]="view" [(viewDate)]="viewDate"
(viewDateChange)="activeDayIsOpen = false">
Next
</div>
</div>
</div>
<div class="col-md-4 text-center bottom_space">
<h3>{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}</h3>
</div>
<div class="col-md-4 text-right bottom_space">
<div class="btn-group">
<div class="btn btn-primary" (click)="view = CalendarView.Month"
[class.active]="view === CalendarView.Month">
Month
</div>
<div class="btn btn-primary" (click)="view = CalendarView.Week"
[class.active]="view === CalendarView.Week">
Week
</div>
<div class="btn btn-primary" (click)="view = CalendarView.Day"
[class.active]="view === CalendarView.Day">
Day
</div>
</div>
</div>
</div>
<br />
<div [ngSwitch]="view">
<mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" [activeDayIsOpen]="activeDayIsOpen" (dayClicked)="dayClicked($event.day)"
(eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)">
</mwl-calendar-day-view>
</div>
<br /><br /><br />
<h3>
Edit events
<button class="btn btn-primary pull-right" (click)="addEvent()">
Add new
</button>
<div class="clearfix"></div>
</h3>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Primary color</th>
<th>Secondary color</th>
<th>Starts at</th>
<th>Ends at</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let event of events; let index = index">
<td>
<input type="text" name="event-title" class="form-control title" [(ngModel)]="event.title"
>
</td>
<td>
<input type="color" [(ngModel)]="event.color.primary" (change)="refresh.next()" />
</td>
<td>
<input type="color" [(ngModel)]="event.color.secondary" (change)="refresh.next()" />
</td>
<td>
<input class="form-control" type="text" mwlFlatpickr [(ngModel)]="event.start"
(ngModelChange)="refresh.next()" [altInput]="true" [convertModelValue]="true"
[enableTime]="true" dateFormat="Y-m-dTH:i" altFormat="F j, Y H:i" placeholder="Not set" />
</td>
<td>
<input class="form-control" type="text" mwlFlatpickr [(ngModel)]="event.end"
(ngModelChange)="refresh.next()" [convertModelValue]="true"
[enableTime]="true" dateFormat="Y-m-dTH:i" altFormat="F j, Y H:i" placeholder="Not set" />
</td>
<td>
<button class="btn btn-danger" (click)="events.splice(index, 1); refresh.next()">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
<br /><br /><br />
<ng-template #modalContent let-close="close">
<div class="modal-header">
<h5 class="modal-title">Event action occurred</h5>
<button type="button" class="close" (click)="close()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
Action:
<pre>{{ modalData?.action }}</pre>
</div>
<div>
Event:
<pre>{{ modalData?.event | json }}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="close()">
OK
</button>
</div>
</ng-template>
</ng-container>
</m-card>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { EventsComponent } from './events.component';
describe('EventsComponent', () => {
let component: EventsComponent;
let fixture: ComponentFixture<EventsComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ EventsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EventsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,214 @@
import { Component, OnInit } from '@angular/core';
import { ViewChild, TemplateRef } from '@angular/core';
import { Subject } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import {
startOfDay,
endOfDay,
subDays,
addDays,
endOfMonth,
isSameDay,
isSameMonth,
addHours
} from 'date-fns';
import {
CalendarEventTimesChangedEvent,
CalendarView,
CalendarEvent,
CalendarEventAction
} from 'angular-calendar';
import { BlockUI, NgBlockUI } from 'ng-block-ui';
const colors: any = {
red: {
primary: '#ad2121',
secondary: '#FAE3E3'
},
blue: {
primary: '#1e90ff',
secondary: '#D1E8FF'
},
yellow: {
primary: '#e3bc08',
secondary: '#FDF1BA'
}
};
@Component({
selector: 'app-events',
templateUrl: './events.component.html',
styleUrls: ['./events.component.css']
})
export class EventsComponent implements OnInit {
public breadcrumb: any;
options = {
close: true,
expand: true,
minimize: true,
reload: true
};
@BlockUI('events') blockUIEvents: NgBlockUI;
@ViewChild('modalContent', { static: true }) modalContent: TemplateRef<any>;
view: CalendarView = CalendarView.Month;
CalendarView = CalendarView;
viewDate: Date = new Date();
activeDayIsOpen = true;
modalData: {
action: string;
event: CalendarEvent;
};
actions: CalendarEventAction[] = [
{
label: '<i class="fa fa-fw fa-pencil"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.handleEvent('Edited', event);
}
},
{
label: '<i class="fa fa-fw fa-times"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.events = this.events.filter(iEvent => iEvent !== event);
this.handleEvent('Deleted', event);
}
}
];
refresh: Subject<any> = new Subject();
events: CalendarEvent[] = [
{
start: subDays(startOfDay(new Date()), 1),
end: addDays(new Date(), 1),
title: 'A 3 day event',
color: colors.red,
actions: this.actions,
allDay: true,
resizable: {
beforeStart: true,
afterEnd: true
},
draggable: true
},
{
start: startOfDay(new Date()),
title: 'An event with no end date',
color: colors.yellow,
actions: this.actions
},
{
start: subDays(endOfMonth(new Date()), 3),
end: addDays(endOfMonth(new Date()), 3),
title: 'A long event that spans 2 months',
color: colors.blue,
allDay: true
},
{
start: addHours(startOfDay(new Date()), 2),
end: new Date(),
title: 'A draggable and resizable event',
color: colors.yellow,
actions: this.actions,
resizable: {
beforeStart: true,
afterEnd: true
},
draggable: true
}
];
ngOnInit() {
this.breadcrumb = {
mainlabel: 'Full Calendar Events',
links: [
{
name: 'Home',
isLink: true,
link: '/dashboard/sales'
},
{
name: 'Apps',
isLink: true,
link: '#'
},
{
name: 'Calendars',
isLink: true,
link: '#'
},
{
name: 'Events',
isLink: false,
link: '#'
}
]
};
}
constructor(private modal: NgbModal) {}
dayClicked({ date, events }: { date: Date; events: CalendarEvent[] }): void {
if (isSameMonth(date, this.viewDate)) {
this.viewDate = date;
if (
(isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) ||
events.length === 0
) {
this.activeDayIsOpen = false;
} else {
this.activeDayIsOpen = true;
}
}
}
eventTimesChanged({
event,
newStart,
newEnd
}: CalendarEventTimesChangedEvent): void {
event.start = newStart;
event.end = newEnd;
this.handleEvent('Dropped or resized', event);
this.refresh.next({});
}
handleEvent(action: string, event: CalendarEvent): void {
this.modalData = { event, action };
this.modal.open(this.modalContent, { size: 'lg' });
}
addEvent(): void {
this.events.push({
title: 'New event',
start: startOfDay(new Date()),
end: endOfDay(new Date()),
color: colors.red,
draggable: true,
resizable: {
beforeStart: true,
afterEnd: true
}
});
this.refresh.next({});
}
deleteEvent(eventToDelete: CalendarEvent) {
this.events = this.events.filter(event => event !== eventToDelete);
}
setView(view: CalendarView) {
this.view = view;
}
closeOpenMonthViewDay() {
this.activeDayIsOpen = false;
}
reloadEvents() {
this.blockUIEvents.start('Loading..');
setTimeout(() => {
this.blockUIEvents.stop();
}, 2500);
}
}

View File

@@ -0,0 +1,56 @@
:host ::ng-deep .content-right {
width: calc(100% - 300px) !important;
}
:host ::ng-deep .mx-75 {
margin-left: .75rem!important;
margin-right: .75rem!important;
}
:host ::ng-deep .py-75 {
padding-bottom: .75rem!important;
padding-top: .75rem!important;
}
:host ::ng-deep .px-1 {
padding-left: 1rem!important;
padding-right: 1rem!important;
}
:host ::ng-deep .mr-50, .mx-50 {
margin-right: .5rem!important;
margin-left: .5rem!important;
}
:host ::ng-deep .mb-50, .my-50 {
margin-bottom: .5rem!important;
}
:host ::ng-deep .dropdown-menu
{
transform: translate3d(-131px, 18px, 0px) !important;
}
:host ::ng-deep .chat-sidebar .chat-sidebar-list-wrapper {
overflow-y: scroll;
}
:host ::ng-deep .sidebar-fixed {
overflow: hidden !important;
width: 300px;
}
:host ::ng-deep .app-content {
overflow-y: hidden !important;
}
:host ::ng-deep .pb-25, .py-25 {
padding-bottom: .25rem!important;
}
:host ::ng-deep .ml-25, .mx-25 {
margin-left: .25rem!important;
}
:host ::ng-deep .ps--active-y > .ps__rail-y {
display: none;
}
@media(max-width:767px) {
:host ::ng-deep .content-right {
width: calc(100% - 0px) !important;
}
}
:host ::ng-deep .d-inline-block {
display: inline !important;
}

View File

@@ -0,0 +1,254 @@
<div class="app-content content" >
<div class="sidebar-left" id="sidebar-left">
<div class="sidebar"><!-- app chat user profile left sidebar start -->
<div class="chat-user-profile" id="user-profile" >
<header class="chat-user-profile-header text-center border-bottom">
<span class="chat-profile-close" (click)="showProfile($event)" >
<i class="ficon feather ft-x" ></i>
</span>
<div class="my-2">
<img *ngIf="loggedInUser.photoURL" src="{{loggedInUser.photoURL}}" class="round mb-1" alt="user_avatar" height="100" width="100" >
<img *ngIf="!loggedInUser.photoURL" src="../../../../assets/images/portrait/small/avatar-s-19.png" class="round mb-1" alt="user_avatar"
alt="avatar" height="100" width="100" >
<!-- <img src="../../../assets/images/portrait/small/avatar-s-16.png" class="round mb-1" alt="user_avatar"
height="100" width="100" > -->
<h5 class="mb-0">{{loggedInUser.displayName}}</h5>
<span>Designer</span>
</div>
</header>
<div class="chat-user-profile-content">
<div class="chat-user-profile-scroll" fxFlex="auto" [perfectScrollbar]="config">
<h6 class="text-uppercase mb-1">ABOUT</h6>
<p class="mb-2">It is a long established fact that a reader will be distracted by the readable content .</p>
<h6>PERSONAL INFORAMTION</h6>
<ul class="list-unstyled mb-2">
<li class="mb-25">email@gmail.com</li>
<li>+1(789) 950 -7654</li>
</ul>
<h6 class="text-uppercase mb-1">CHANNELS</h6>
<ul class="list-unstyled mb-2">
<li><a href=""># Devlopers</a></li>
<li><a href=""># Designers</a></li>
</ul>
<h6 class="text-uppercase mb-1">SETTINGS</h6>
<ul class="list-unstyled">
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i class="ficon feather ft-tag mr-50"></i>
Add
Tag</a></li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i class="ficon feather ft-star mr-50"></i>
Important Contact</a>
</li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i
class="ficon feather ft-image mr-50"></i>
Shared
Documents</a></li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i
class="ficon feather ft-trash-2 mr-50"></i>
Deleted
Documents</a></li>
<li><a href="" class="d-flex align-items-center"><i class="ficon feather ft-x-circle mr-50"></i> Blocked
Contact</a></li>
</ul>
</div>
</div>
</div>
<!-- app chat user profile left sidebar ends -->
<!-- app chat sidebar start -->
<div class="chat-sidebar card" id="sidebar-card">
<span class="chat-sidebar-close" (click) = "Sidebar($event)">
<i class="ficon feather ft-x" ></i>
</span>
<div class="chat-sidebar-search">
<div class="d-flex align-items-center">
<div class="chat-sidebar-profile-toggle" (click)="showProfile($event)">
<div class="avatar">
<img *ngIf="loggedInUser.photoURL" src="{{loggedInUser.photoURL}}" class="cursor-pointer" alt="user_avatar" height="36" width="36">
<img *ngIf="!loggedInUser.photoURL" src="../../../../assets/images/portrait/small/avatar-s-19.png"
alt="avatar" height="36" width="36">
<!-- <img src="../../../assets/images/portrait/small/avatar-s-16.png" class="cursor-pointer" alt="user_avatar"
height="36" width="36"> -->
</div>
</div>
<fieldset class="form-group position-relative has-icon-left mx-75 mb-0">
<input type="text" class="form-control round" id="chat-search" (keyup)="searchContact($event)" placeholder="Search">
<div class="form-control-position">
<i class="ficon feather ft-search text-dark"></i>
</div>
</fieldset>
</div>
</div>
<div class="chat-sidebar-list-wrapper pt-2 " fxFlex="auto" [perfectScrollbar]="config" >
<h6 class="px-2 pb-25 mb-0">CHANNELS<i class="ficon feather ft-plus float-right cursor-pointer"></i></h6>
<ul class="chat-sidebar-list">
<li>
<h6 class="mb-0"># Devlopers</h6>
</li>
<li>
<h6 class="mb-0"># Designers</h6>
</li>
</ul>
<h6 class="px-2 pt-2 pb-25 mb-0">CHATS</h6>
<ul class="chat-sidebar-list" >
<li class = "chatSidebar" *ngFor="let chat of chatList" id="users-list" (click)="loadMyChatRoom(chat)" (click)="contentOverlay($event)"
id="_media" [ngClass]="(chat.uid === clickedUser) ? 'active': ''">
<div class="d-flex align-items-center" >
<div class="avatar m-0 mr-50" ><img [src]="chat.image" height="36"
width="36" >
<span class="avatar-status-busy"></span>
</div>
<div class="chat-sidebar-name">
<span class="mb-0">{{chat.name}}</span>
<span class="badge badge-pill float-right badge-danger mr-2">{{chat.unreadMessageCount ? chat.unreadMessageCount : ''}}</span>
<span class="text-muted"></span>
</div>
</div>
</li>
</ul>
<h6 class="px-2 pt-2 pb-25 mb-0">CONTACTS<i class="ficon feather ft-plus float-right cursor-pointer"></i></h6>
<ul class="chat-sidebar-list" >
<li class = "chatSidebar" *ngFor="let contact of contactList" (click)="showChat(contact)" (click)="contentOverlay($event)"
id="_media" [ngClass]="(contact.uid === clickedUser) ? 'active': ''">
<div class="d-flex align-items-center">
<div class="avatar m-0 mr-50"><img [src]="contact.image" height="36"
width="36" >
<span class="avatar-status-away"></span>
</div>
<div class="chat-sidebar-name">
<h6 class="mb-0">{{contact.name}}</h6><span class="text-muted"> {{contact.showMessage}}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<!-- app chat sidebar ends -->
</div>
</div>
<div class="content-right ">
<div class="content-overlay" ></div>
<div class="content-wrapper">
<div class="content-header row">
</div>
<div class="content-body"><!-- app chat overlay -->
<div class="chat-overlay " id="overlayChat" (click) = "Sidebar($event)"></div>
<!-- app chat window start -->
<section class="chat-window-wrapper">
<div class="chat-start" id='chat-overlay1'>
<span class="ficon feather ft-message-square chat-sidebar-toggle chat-start-icon font-large-3 p-3 mb-1" (click)="showSidebar($event)" ></span>
<h4 class="d-none d-lg-block py-50 text-bold-500">Select a contact to start a chat!</h4>
<button class="btn btn-light-primary chat-start-text chat-sidebar-toggle d-block d-lg-none py-50 px-1" (click)="showSidebar($event)" >Start
Conversation!</button>
</div>
<div class="chat-area d-none" id = "chat-area d-none">
<div class="chat-header">
<header class="d-flex justify-content-between align-items-center px-1 py-75">
<div class="d-flex align-items-center" >
<div class="chat-sidebar-toggle d-block d-lg-none mr-1" id="chat-sidebar"><i
class="ficon feather ft-align-justify font-large-1 cursor-pointer" (click)="showSidebar($event)"></i>
</div>
<div class="avatar chat-profile-toggle m-0 mr-1" (click)="ShowChatProfile($event)">
<span >
<img [src]="headerImage" class="cursor-pointer" alt="avatar"/>
</span>
<span class="avatar-status-busy"></span>
</div>
<h6 class="mb-0">{{headerName}}</h6>
</div>
<div class="chat-header-icons">
<span class="chat-icon-favorite" id='chat-icon'(click)="chatFavorite($event)" >
<i class="ficon feather ft-star font-medium-5 cursor-pointer"></i>
</span>
<span class="dropdown">
<span ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<i class="ficon feather ft-more-vertical font-medium-4 ml-25 cursor-pointer dropdown-toggle nav-hide-arrow cursor-pointer"
id="dropdownMenuButton" dropdown-menu dropdown-menu-left show data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" role="menu" ngbDropdownToggle>
</i>
<div ngbDropdownMenu ="dropdownMenuButton">
<a class="dropdown-item">Pin to top</a>
<a class="dropdown-item">Delete chat</a>
<a class="dropdown-item ">Block</a>
</div>
</span>
</span>
</div>
</header>
</div>
<!-- chat card start -->
<div class="card chat-wrapper shadow-none mb-0">
<div class="card-content">
<div class="card-body chat-container" id="componentRef">
<perfect-scrollbar [config]="config" #chatPS>
<div class="chat-content" #content>
<div id="chatWindow" class="chat" [ngClass]="(currentUserUid === messages.uid)?'chat-right':'chat-left'"
*ngFor="let messages of displayChat">
<div class="chat-avatar">
<a [routerLink]="" class="avatar m-0">
<img [src]="messages.userImage" *ngIf="messages.userImage"/>
</a>
</div>
<div class="chat-body">
<div class="chat-message">
<p>{{messages.message}}</p>
<span class="chat-time" title="{{messages.date | date}}">{{messages.date| date: 'HH:mm a'}}</span>
</div>
</div>
</div>
</div>
</perfect-scrollbar>
</div>
</div>
<div class="card-footer chat-footer px-2 py-1 pb-0">
<form class="d-flex align-items-center">
<i class="ficon feather ft-user cursor-pointer"></i>
<i class="ficon feather ft-paperclip ml-1 cursor-pointer"></i>
<input type="text" class="form-control chat-message-send mx-1" [(ngModel)]="newMessage" name="newmessage" id="iconLeft4"
placeholder="Type your message here...." #box (keyup.enter)="onEnter(box.value)">
<button type="submit" class="btn btn-primary glow send d-lg-flex" (click)="sendMessage()"><i class="ficon feather ft-play"></i>
<span class="d-none d-lg-block mx-50">Send</span></button>
</form>
</div>
</div>
<!-- chat card ends -->
</div>
</section>
<section class="chat-profile " id="chat-profile">
<header class="chat-profile-header text-center border-bottom">
<span class="chat-profile-close" (click)="ChatProfile($event)">
<i class="ficon feather ft-x"></i>
</span>
<div class="my-2">
<img [src]="headerImage" class="round mb-1" alt="chat avatar"
height="100" width="100">
<h5 class="app-chat-user-name mb-0">{{headerName}}</h5>
<span>Devloper</span>
</div>
</header>
<div class="chat-profile-content p-2">
<h6 class="mt-1">ABOUT</h6>
<p>It is a long established fact that a reader will be distracted by the readable content.</p>
<h6 class="mt-2">PERSONAL INFORMATION</h6>
<ul class="list-unstyled">
<li class="mb-25">email@gmail.com</li>
<li>+1(789) 950-7654</li>
</ul>
</div>
</section>
<!-- app chat window ends -->
</div>
</div>
</div>
</div>
<!-- END: Content-->
<!-- app chat profile right sidebar ends -->

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ChatsComponent } from './chats.component';
describe('ChatComponent', () => {
let component: ChatsComponent;
let fixture: ComponentFixture<ChatsComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ ChatsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ChatsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,543 @@
import { Component, OnInit, AfterViewInit, ViewChild, Renderer2, ViewChildren, QueryList, ElementRef } from '@angular/core';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { UserService } from '../../../../../src/app/_api/user/user.service';
import { ChatService } from '../../../../../src/app/_api/chat/chat.service';
import { Chats } from './chats';
import { AppConstants } from '../../../_helpers/app.constants';
import * as _ from 'lodash';
@Component({
selector: 'app-chat',
templateUrl: './chats.component.html',
styleUrls: ['./chats.component.css']
})
export class ChatsComponent implements OnInit, AfterViewInit {
showicon: boolean;
name: any;
isicon: boolean;
isactive: boolean;
isSelected: boolean;
chatArray: any;
status = true;
newMessage = '';
contactId: string;
messageInfo: any;
userInfo: any;
public displayName: boolean;
public config: PerfectScrollbarConfigInterface = { wheelPropagation: false };
@ViewChild('chatPS') public chatPS?: PerfectScrollbarComponent;
@ViewChild(PerfectScrollbarDirective, { static: true })
directiveRef?: PerfectScrollbarDirective;
@ViewChildren('messages') messages: QueryList<any>;
@ViewChild('content', { static: true }) content: ElementRef;
contactList = [];
contactChats = [];
headerName: string;
headerImage: any;
showChatInProgress = false;
createInProgress = false;
contactMaster = this.contactList;
chatListMaster = [];
temp = [];
loggedInUser = JSON.parse(localStorage.getItem('currentUser'));
currentUserUid = this.loggedInUser.uid;
currentUser = ''; // UID of user 1
clickedUser = ''; // UID of user 2
chatList = [];
displayChat = [];
chatId = '';
senderImage = '';
currentUserImage = '';
loadContacts = false;
prevMsg = '';
/**
* Constructor
*
* @param ApplicationApiService chatApiService;
* @param Renderer2 renderer
*/
constructor(
private renderer: Renderer2,
private userService: UserService,
private chatService: ChatService) {
}
/**
* Scroll chat to bottom
*/
ngAfterViewInit() {
this.messages.forEach(this.scrollToBottom);
this.messages.changes.subscribe(this.scrollToBottom);
}
getLoggedInUserChats () {
if (this.contactChats) {
const chatsWithHistory = this.contactChats.filter((item: Chats) => {
return item.chatHistory.length > 0;
});
const loggedInUserChats = chatsWithHistory.filter(chatHistory => {
return chatHistory.name.indexOf(this.loggedInUser.uid) !== -1;
});
return loggedInUserChats;
}
return [];
}
/**
* OnInit
*/
ngOnInit() {
this.userService.getUsers().subscribe(users => {
let contactList = users.map(item => {
return {
...item.payload.doc.data() as {},
id: item.payload.doc['id']
};
});
contactList = contactList.filter(element => {
return this.loggedInUser.uid !== element['uid'];
});
this.contactMaster = contactList;
this.chatService.getChats().subscribe(chats => {
this.chatList = [];
this.contactList = [];
const chatRooms = chats.map(item => {
return {
...item.payload.doc.data() as Chats,
id: item.payload.doc['id']
};
});
this.contactChats = chatRooms;
const loggedInUserChats = this.getLoggedInUserChats();
const contactListMap = [];
const chatListMap = [];
this.contactMaster.forEach(element => {
const userChat = this.isChatExistsWithUser(loggedInUserChats, element.uid);
if (userChat && !chatListMap[element.uid]) {
element['chatRoomId'] = userChat['id'];
if (userChat['chatHistory'] && userChat['chatHistory'].length > 0) {
const unreadMessages = userChat['chatHistory'].filter( history => {
return history.uid !== this.loggedInUser.uid && history.status === 'send';
});
if (unreadMessages.length > 0 && this.clickedUser !== element.uid) {
element['unreadMessageCount'] = unreadMessages.length;
} else {
element['unreadMessageCount'] = 0;
}
element['chatHistory'] = userChat['chatHistory'];
}
chatListMap[element.uid] = true;
this.chatList.push(element);
this.chatListMaster.push(element);
} else if (!contactListMap[element.uid]) {
contactListMap[element.uid] = true;
this.contactList.push(element);
}
});
this.chatList = _.orderBy(this.chatList, [user => user.name.toLowerCase()], ['asc']);
this.contactList = _.orderBy(this.contactList, [user => user.name && user.name.toLowerCase()], ['asc']);
this.currentUserImage = this.loggedInUser.photoURL;
this.senderImage = this.contactList[0].image;
this.createInProgress = false;
});
});
}
isChatExistsWithUser (userChats, userId) {
for (let index = 0; index < userChats.length; index++) {
if (userChats[index].name.indexOf(userId) !== -1) {
return userChats[index];
}
}
return false;
}
/**
* Chat scroll to bottom
*/
scrollToBottom = () => {
try {
this.content.nativeElement.scrollTop = this.content.nativeElement.scrollHeight;
this.chatPS.directiveRef.scrollToBottom(0, 500);
} catch (err) { }
}
/**
* Filter Chat
*
* @param event search event
*/
searchContact (event) {
const value = event.target.value.toLowerCase();
// filter our data
let temp = this.contactMaster.filter(function (d) {
return d.name && d.name.toLowerCase().indexOf(value) !== -1 || !value;
});
const loggedInUserChats = this.getLoggedInUserChats();
const _self = this;
temp = temp.filter(function (user) {
return !_self.isChatExistsWithUser(loggedInUserChats, user.uid);
});
// update the rows
this.contactList = [];
const contactListMap = [];
temp.forEach(element => {
if (!contactListMap[element.uid]) {
contactListMap[element.uid] = true;
this.contactList.push(element);
}
});
// filter our data
const tempChat = this.chatListMaster.filter(function (d) {
return d.name && d.name.toLowerCase().indexOf(value) !== -1 || !value;
});
// update the rows
this.chatList = [];
const chatListMap = [];
tempChat.forEach(element => {
if (!chatListMap[element.uid]) {
chatListMap[element.uid] = true;
this.chatList.push(element);
}
});
}
/**
* Send message
*/
sendMessage() {
if (this.newMessage !== null && this.newMessage !== '') {
this.messageInfo = {
message: this.newMessage,
date: Date.now(),
uid: this.loggedInUser.uid,
status: 'send'
};
if (this.chatId !== '') {
this.chatService.sendMessage(this.chatId, this.messageInfo);
} else {
this.currentUser = this.loggedInUser.uid;
const roomName = (this.currentUser < this.clickedUser ? this.currentUser + '_' +
this.clickedUser : this.clickedUser + '_' + this.currentUser);
this.userInfo = {
uid: this.loggedInUser.uid,
name: roomName,
image: this.loggedInUser.photoURL,
time: Date.now(),
showMessage: '',
badge: '',
showicon: true,
isicon: false,
isactive: 'online',
isSelected: false,
chatHistory: []
};
// Add message to box before sending
this.displayChat.push(this.messageInfo);
this.createInProgress = true;
this.chatService.createChatRoom(this.userInfo).then(chatRoom => {
this.chatId = chatRoom.id;
this.loadChatRoom(chatRoom.id);
this.chatService.sendMessage(this.chatId, this.messageInfo);
this.createInProgress = false;
});
}
this.newMessage = null;
}
}
/**
* Message send on Enter
*
* @param value New message
*/
onEnter(value: string) {
this.newMessage = value;
this.sendMessage();
}
/**
* Display chat when click on contact
*
* @param friendId Friend id
*/
showChat(data) {
if (this.showChatInProgress || this.createInProgress) {
return;
}
this.showChatInProgress = true;
this.currentUser = this.loggedInUser.uid;
this.currentUserImage = this.loggedInUser.photoURL;
this.clickedUser = data.uid;
this.contactId = data.id;
this.senderImage = data.image;
this.showHeaderData();
const roomName = (this.currentUser < this.clickedUser ? this.currentUser + '_' +
this.clickedUser : this.clickedUser + '_' + this.currentUser);
this.userInfo = {
uid: this.loggedInUser.uid,
name: roomName,
image: this.loggedInUser.photoURL,
time: Date.now(),
showMessage: '',
badge: '',
showicon: true,
isicon: false,
isactive: 'online',
isSelected: false,
chatHistory: []
};
if (this.contactChats.length === 0) {
this.createAndLoadChatRoom();
} else if (this.contactChats.length !== 0) {
let chatExists = false;
for (let i = 0; i < this.contactChats.length; i++) {
const room = this.contactChats[i].name;
if (room === roomName) {
chatExists = true;
this.chatId = this.contactChats[i].id;
break;
}
}
if (!chatExists) {
this.createAndLoadChatRoom();
} else {
// subscribe
this.loadChatRoom(this.chatId);
this.showChatInProgress = false;
this.userInfo.isSelected = true;
}
}
}
createAndLoadChatRoom() {
this.createInProgress = true;
this.chatService.createChatRoom(this.userInfo).then(chatRoom => {
this.chatId = chatRoom.id;
this.loadChatRoom(chatRoom.id);
});
}
setUserImage(chatHistory) {
for (let i = 0; i < chatHistory.length; i++) {
if (chatHistory.length > 0) {
if (i > 0) {
this.prevMsg = chatHistory[i - 1];
}
if (i > 0 && chatHistory[i].uid === this.prevMsg['uid']) {
continue;
}
if (chatHistory[i].uid === this.loggedInUser.uid) {
chatHistory[i]['userImage'] = this.currentUserImage;
} else {
chatHistory[i]['userImage'] = this.senderImage;
}
}
}
}
loadMyChatRoom(selectedUser) {
this.chatId = selectedUser.chatRoomId;
this.loadChatRoom(selectedUser.chatRoomId);
this.clickedUser = selectedUser.uid;
this.contactId = selectedUser.id;
this.senderImage = selectedUser.image;
this.headerName = selectedUser.name;
this.headerImage = selectedUser.image;
this.setHistoryAsRead(selectedUser, selectedUser.chatRoomId, selectedUser.chatHistory);
}
setHistoryAsRead (selectedUser, chatRoomId, history) {
let unreadMsgExists = false;
history.forEach(element => {
if (element.uid !== this.loggedInUser.uid && element.status !== 'read') {
element.status = 'read';
unreadMsgExists = true;
}
});
if (unreadMsgExists) {
this.chatService.updateChatStatus(chatRoomId, history).then(result => {
delete selectedUser.unreadMessageCount;
});
}
}
loadChatRoom(chatRoomId) {
this.chatService.showChat(chatRoomId).subscribe(res => {
if (chatRoomId === this.chatId) {
this.setUserImage(res.chatHistory);
this.displayChat = res['chatHistory'];
this.chatId = res['chatId'];
}
this.showChatInProgress = false;
this.scrollToBottom();
});
}
showDefaultChat() {
this.currentUser = this.loggedInUser.uid;
const roomName = (this.currentUser < this.clickedUser ? this.currentUser + '_' +
this.clickedUser : this.clickedUser + '_' + this.currentUser);
for (let j = 0; j < this.contactChats.length; j++) {
if (this.contactChats[j].name === roomName) {
if (this.contactChats[j].chatHistory && this.contactChats[j].chatHistory.length > 0) {
this.displayChat = this.contactChats[j].chatHistory;
this.setUserImage(this.contactChats[j].chatHistory);
}
this.chatId = this.contactChats[j].id;
}
}
}
showLastMessage() {
for (let i = 0; i < this.contactList.length; i++) {
if (this.contactList[i]) {
for (let j = 0; j < this.contactChats.length; j++) {
const room = this.contactChats[j].name;
const index = room.indexOf(this.contactList[i].uid);
const currentUser = this.loggedInUser.uid;
const userUid = this.contactList[i].uid;
const roomName = (currentUser < userUid ? currentUser + '_' +
userUid : userUid + '_' + currentUser);
if (index >= 0) {
if (this.contactChats[j].chatHistory.length > 0 && roomName === room) {
this.contactList[i]['lastmsg'] = this.contactChats[j].chatHistory[this.contactChats[j].chatHistory.length - 1].message;
this.contactList[i]['isActive'] = this.contactChats[j].isactive;
this.contactList[i]['icon'] = this.contactChats[j].isicon;
this.contactList[i]['showicon'] = this.contactChats[j].showicon;
this.contactList[i]['lastmsgTime'] = this.contactChats[j].chatHistory[this.contactChats[j].chatHistory.length - 1].date;
}
}
}
}
}
}
/**
* Overlay add/remove fuction in responsive
*
* @param event Overlay click event
*/
contentOverlay(event) {
const toggleIcon = document.getElementById('chat-overlay1');
const toggle = document.getElementById('chat-area d-none');
if (event.currentTarget.className === 'chatSidebar ng-star-inserted') {
this.renderer.addClass(toggleIcon, 'd-none');
this.renderer.removeClass(toggle, 'd-none');
}
}
/**
* Show add/remove class at open profile
*
* @param event Overlay click event
*/
showProfile(event) {
const toggleIcon = document.getElementById('user-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-sidebar-profile-toggle') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.addClass(toggle, 'show');
} else if (event.currentTarget.className === 'chat-profile-close') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
/**
* Show add/remove function in responsive
*
* @param event Overlay click event
*/
Sidebar(event) {
const toggleChat = document.getElementById('sidebar-card');
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-sidebar-close' || 'chat-overlay') {
this.renderer.removeClass(toggle, 'show');
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggleChat, 'show');
}
}
/**
* Show add/remove function in responsive
*
* @param event Overlay click event
*/
showSidebar(event) {
if (window.innerWidth < AppConstants.MOBILE_RESPONSIVE_WIDTH) {
const toggleIcon = document.getElementById('sidebar-card');
const toggle = document.getElementById('overlayChat');
const toggleChat = document.getElementById('sidebar-left');
if (event.currentTarget.className === 'ficon feather ft-message-square chat-sidebar-toggle chat-start-icon font-large-3 p-3 mb-1' ||
'ficon feather ft-align-justify font-large-1 cursor-pointer') {
this.renderer.addClass(toggle, 'show');
this.renderer.addClass(toggleIcon, 'show');
}
if (window.innerWidth < AppConstants.MOBILE_RESPONSIVE_WIDTH) {
this.renderer.removeClass(toggleChat, 'sidebar-fixed');
}
}
}
// FixChat() {
// const toggleIcon = document.getElementById('sidebar-left');
// if (window.innerWidth < AppConstants.MOBILE_RESPONSIVE_WIDTH) {
// this.renderer.removeClass(toggleIcon, 'sidebar-fixed');
// }
// }
chatFavorite(event) {
const chatIcon = document.getElementById('chat-icon');
if (event.currentTarget.className === 'chat-icon-favorite') {
this.renderer.addClass(chatIcon, 'warning');
} else if (event.currentTarget.className === 'chat-icon-favorite warning') {
this.renderer.removeClass(chatIcon, 'warning');
}
}
showHeaderData() {
this.contactList.forEach(element => {
if (this.clickedUser === element['uid']) {
this.headerName = element.name;
this.headerImage = element.image;
}
});
}
/**
* Show add/remove function
*
* @param event Overlay click event
*/
ShowChatProfile(event) {
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'avatar chat-profile-toggle m-0 mr-1') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.addClass(toggle, 'show');
}
}
/**
* Show add/remove function
*
* @param event Overlay click event
*/
ChatProfile(event) {
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-profile-close') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
}

View File

@@ -0,0 +1,13 @@
import { ChatsModule } from './chats.module';
describe('ChatModule', () => {
let chatsModule: ChatsModule;
beforeEach(() => {
chatsModule = new ChatsModule();
});
it('should create an instance', () => {
expect(chatsModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,33 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ChatsComponent } from './chats.component';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { ChatService } from 'src/app/_api/chat/chat.service';
import { StaticChatComponent } from './static-chat/static-chat.component';
import { ApplicationApiService } from 'src/app/_services/application-api.service';
import { ToastrModule } from 'ngx-toastr';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [
CommonModule,
FormsModule,
NgbModule,
ToastrModule.forRoot(),
PerfectScrollbarModule,
RouterModule.forChild([
{
path: '',
component: ChatsComponent
},
{
path: 'static-chat',
component: StaticChatComponent
}
])
],
declarations: [ChatsComponent, StaticChatComponent],
providers: [ChatService, ApplicationApiService]
})
export class ChatsModule { }

View File

@@ -0,0 +1,20 @@
export class Chats {
uid: string;
senderUid: string;
name: string;
image: string;
time: Date;
showMessage: String;
badge: String;
showicon: boolean;
isicon: boolean;
isactive: boolean;
isSelected: boolean;
chatHistory: Array<ChatHistory>;
}
export class ChatHistory {
date: Date;
message: string;
uid: string;
}

View File

@@ -0,0 +1,86 @@
<div class="content-right">
<!-- <div class="content-wrapper"> -->
<div class="content-header row"></div>
<div class="content-overlay"></div>
<div class="chat-header">
<header class="d-flex justify-content-between align-items-center px-1 py-75">
<div class="d-flex align-items-center">
<div class="chat-sidebar-toggle d-block d-lg-none mr-1"><i
class="ft-align-justify font-large-1 cursor-pointer"></i>
</div>
<div class="avatar chat-profile-toggle m-0 mr-1">
<img src="../../../assets/images/portrait/small/avatar-s-26.png" class="cursor-pointer" alt="avatar"
height="36" width="36" />
<span class="avatar-status-busy"></span>
</div>
<h6 class="mb-0">Elizabeth Elliott</h6>
</div>
<div class="chat-header-icons">
<span class="chat-icon-favorite">
<i class="ficon feather ft-star font-medium-5 cursor-pointer"></i>
</span>
<span class="dropdown">
<i class="ficon feather ft-more-vertical font-medium-4 ml-25 cursor-pointer dropdown-toggle nav-hide-arrow cursor-pointer"
id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" role="menu">
</i>
<span class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="JavaScript:void(0);"><i class="ficon feather ft-tag mr-25"></i> Pin to top</a>
<a class="dropdown-item" href="JavaScript:void(0);"><i class="ficon feather ft-trash-2 mr-25"></i> Delete chat</a>
<a class="dropdown-item" href="JavaScript:void(0);"><i class="ficon feather ft-x-circle mr-25"></i> Block</a>
</span>
</span>
</div>
</header>
</div>
<div class="card chat-wrapper shadow-none mb-0">
<div class="card-content">
<div class="content-body chat-container">
<div class="chat-content">
<div class="chat">
<div class="chat-avatar">
<div class="content-overlay" id='content-overlay' (click)="contentOverlay($event)"></div>
<section class="chat-app-window" #content>
<div class="sidebar-toggle d-block d-lg-none" (click)="sidebar($event)"><i class="feather ft-menu font-large-1"></i></div>
<div class="badge badge-secondary mb-1">Chat History</div>
<div class="chats" #messages>
<div class="chat" [ngClass]="{'chat-right': messages.sender == 0 , 'chat-left': messages.sender == 1 }"
*ngFor="let messages of contactChat">
<div class="chat-avatar">
<a [routerLink]="" class="avatar">
<span class="avatar-online"><img [src]="messages.senderImage" /></span>
</a>
</div>
<div class="chat-body">
<div class="chat-content">
<p>{{messages.message}}</p>
</div>
</div>
</div>
</div>
</section>
<section class="chat-app-form">
<form class="chat-app-input d-flex">
<fieldset class="form-group position-relative has-icon-left col-10 m-0">
<div class="form-control-position">
<i class="icon-emoticon-smile"></i>
</div>
<input type="text" class="form-control" [(ngModel)]="newMessage" name="newmessage" id="iconLeft4"
placeholder="Type your message" #box (keyup.enter)="onEnter(box.value)">
<div class="form-control-position control-position-right">
<i class="feather ft-image"></i>
</div>
</fieldset>
<fieldset class="form-group position-relative has-icon-left col-2 m-0">
<button type="button" class="btn btn-primary" (click)="sendMessage()" keyboardShouldPersistTaps={true}><i
class="la la-paper-plane-o d-lg-none"></i><span class="d-none d-lg-block">Send</span></button>
</fieldset>
</form>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- </div> -->
</div>

View File

@@ -0,0 +1,66 @@
:host ::ng-deep .content-right {
width: calc(100% - 300px) !important;
}
:host ::ng-deep .mx-75 {
margin-left: .75rem!important;
margin-right: .75rem!important;
}
:host ::ng-deep .py-75 {
padding-bottom: .75rem!important;
padding-top: .75rem!important;
}
:host ::ng-deep .px-1 {
padding-left: 1rem!important;
padding-right: 1rem!important;
}
:host ::ng-deep .mr-50, .mx-50 {
margin-right: .5rem!important;
margin-left: .5rem!important;
}
:host ::ng-deep .mb-50, .my-50 {
margin-bottom: .5rem!important;
}
:host ::ng-deep .dropdown-menu
{
transform: translate3d(-131px, 18px, 0px) !important;
}
:host ::ng-deep .chat-sidebar .chat-sidebar-list-wrapper {
overflow-y: scroll;
}
:host ::ng-deep .sidebar-fixed {
overflow: hidden !important;
width: 300px;
}
:host ::ng-deep .app-content {
overflow-y: hidden !important;
}
:host ::ng-deep .pb-25, .py-25 {
padding-bottom: .25rem!important;
}
:host ::ng-deep .ml-25, .mx-25 {
margin-left: .25rem!important;
}
@media(max-width:767px) {
:host ::ng-deep .chat-footer
{
position: relative !important;
bottom: 17px !important;
}
}
@media(max-width:767px) {
:host ::ng-deep .chat-wrapper
{
background-color: unset !important;
}
}
@media(max-width:767px) {
:host ::ng-deep .content-right {
width: calc(100% - 0px) !important;
}
}
:host ::ng-deep .d-inline-block {
display: inline !important;
}

View File

@@ -0,0 +1,239 @@
<div class="app-content content" >
<div class="sidebar-left " id="sidebar-left">
<div class="sidebar"><!-- app chat user profile left sidebar start -->
<div class="chat-user-profile" id="user-profile" >
<header class="chat-user-profile-header text-center border-bottom">
<span class="chat-profile-close" (click)="showProfile($event)" >
<i class="ficon feather ft-x" ></i>
</span>
<div class="my-2">
<img src="../../../assets/images/portrait/small/avatar-s-19.png" class="round mb-1" alt="user_avatar"
height="100" width="100" >
<h5 class="mb-0">John Doe</h5>
<span>Designer</span>
</div>
</header>
<div class="chat-user-profile-content">
<div class="chat-user-profile-scroll" fxFlex="auto" [perfectScrollbar]="config">
<h6 class="text-uppercase mb-1">ABOUT</h6>
<p class="mb-2">It is a long established fact that a reader will be distracted by the readable content .</p>
<h6>PERSONAL INFORAMTION</h6>
<ul class="list-unstyled mb-2">
<li class="mb-25">email@gmail.com</li>
<li>+1(789) 950 -7654</li>
</ul>
<h6 class="text-uppercase mb-1">CHANNELS</h6>
<ul class="list-unstyled mb-2">
<li><a href=""># Devlopers</a></li>
<li><a href=""># Designers</a></li>
</ul>
<h6 class="text-uppercase mb-1">SETTINGS</h6>
<ul class="list-unstyled">
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i class="ficon feather ft-tag mr-50"></i>
Add
Tag</a></li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i class="ficon feather ft-star mr-50"></i>
Important Contact</a>
</li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i
class="ficon feather ft-image mr-50"></i>
Shared
Documents</a></li>
<li class="mb-50 "><a href="" class="d-flex align-items-center"><i
class="ficon feather ft-trash-2 mr-50"></i>
Deleted
Documents</a></li>
<li><a href="" class="d-flex align-items-center"><i class="ficon feather ft-x-circle mr-50"></i> Blocked
Contact</a></li>
</ul>
</div>
</div>
</div>
<!-- app chat user profile left sidebar ends -->
<!-- app chat sidebar start -->
<div class="chat-sidebar card " id="sidebar-card">
<span class="chat-sidebar-close" (click) = "Sidebar($event)">
<i class="ficon feather ft-x" ></i>
</span>
<div class="chat-sidebar-search">
<div class="d-flex align-items-center">
<div class="chat-sidebar-profile-toggle" (click)="showProfile($event)">
<div class="avatar">
<img src="../../../assets/images/portrait/small/avatar-s-19.png" class="cursor-pointer" alt="user_avatar"
height="36" width="36">
</div>
</div>
<fieldset class="form-group position-relative has-icon-left mx-75 mb-0">
<input type="text" class="form-control round" id="chat-search" (keyup)="updateFilter($event)" (keyup)="updateFilter1($event)" placeholder="Search">
<div class="form-control-position">
<i class="ficon feather ft-search text-dark"></i>
</div>
</fieldset>
</div>
</div>
<div class="chat-sidebar-list-wrapper pt-2 " [perfectScrollbar]="config" style="top: 0px; height: 450px;">
<h6 class="px-2 pb-25 mb-0">CHANNELS<i class="ficon feather ft-plus float-right cursor-pointer"></i></h6>
<ul class="chat-sidebar-list">
<li>
<h6 class="mb-0"># Devlopers</h6>
</li>
<li>
<h6 class="mb-0"># Designers</h6>
</li>
</ul>
<h6 class="px-2 pt-2 pb-25 mb-0">CHATS</h6>
<ul class="chat-sidebar-list" >
<li class = "chatSidebar" *ngFor="let chats of chatsList" id="users-list" (click)="contentOverlay($event)"
(click)="showChat(chats.friendId,1)" [ngClass]="{'active':chats.isSelected === true, '':chats.isSelected === false}">
<div class="d-flex align-items-center" >
<div class="avatar m-0 mr-50" ><img [src]="chats.image" height="36"
width="36" alt="sidebar user image">
<span class="avatar-status-busy"></span>
</div>
<div class="chat-sidebar-name">
<h6 class="mb-0">{{chats.name}}</h6><span class="text-muted">{{chats.showMessage}}</span>
</div>
</div>
</li>
</ul>
<h6 class="px-2 pt-2 pb-25 mb-0">CONTACTS<i class="ficon feather ft-plus float-right cursor-pointer"></i></h6>
<ul class="chat-sidebar-list" >
<li class = "chatSidebar" *ngFor="let contact of contactList" (click)="contentOverlay($event)"
(click)="showChat(contact.friendId,2)" id="_media" [ngClass]="{'active':contact.isSelected === true, '':contact.isSelected === false}">
<div class="d-flex align-items-center">
<div class="avatar m-0 mr-50"><img [src]="contact.image">
<span class="avatar-status-away"></span>
</div>
<div class="chat-sidebar-name">
<h6 class="mb-0">{{contact.name}}</h6><span class="text-muted"> {{contact.showMessage}}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<!-- app chat sidebar ends -->
</div>
</div>
<div class="content-right ">
<div class="content-overlay" ></div>
<div class="content-wrapper">
<div class="content-header row">
</div>
<div class="content-body"><!-- app chat overlay -->
<div class="chat-overlay " (click) = "Sidebar($event)" id="overlayChat" ></div>
<!-- app chat window start -->
<section class="chat-window-wrapper">
<div class="chat-start" id='chat-overlay1'>
<span class="ficon feather ft-message-square chat-sidebar-toggle chat-start-icon font-large-3 p-3 mb-1" (click)="showSidebar($event)" ></span>
<h4 class="d-none d-lg-block py-50 text-bold-500">Select a contact to start a chat!</h4>
<button class="btn btn-light-primary chat-start-text chat-sidebar-toggle d-block d-lg-none py-50 px-1" (click)="showSidebar($event)">Start
Conversation!</button>
</div>
<div class="chat-area d-none" id = "chat-area d-none">
<div class="chat-header">
<header class="d-flex justify-content-between align-items-center px-1 py-75">
<div class="d-flex align-items-center" *ngFor="let chats of headerChat">
<div class="chat-sidebar-toggle d-block d-lg-none mr-1" id="chat-sidebar"><i
class="ficon feather ft-align-justify font-large-1 cursor-pointer" (click)="showSidebar($event)"></i>
</div>
<div class="avatar chat-profile-toggle m-0 mr-1" (click)="ShowChatProfile($event)">
<span >
<img [src]="chats.headerImage" class="cursor-pointer" alt="avatar"/>
</span>
<span class="avatar-status-busy"></span>
</div>
<h6 class="mb-0">{{chats.senderName}}</h6>
</div>
<div class="chat-header-icons">
<span class="chat-icon-favorite" id='chat-icon' (click)="chatFavorite($event)">
<i class="ficon feather ft-star font-medium-5 cursor-pointer"></i>
</span>
<span ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block dropdown">
<i class="ficon feather ft-more-vertical font-medium-4 ml-25 cursor-pointer dropdown-toggle nav-hide-arrow cursor-pointer"
id="dropdownMenuButton" dropdown-menu dropdown-menu-left show data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" role="menu" ngbDropdownToggle>
</i>
<div ngbDropdownMenu ="dropdownMenuButton">
<a class="dropdown-item">Pin to top</a>
<a class="dropdown-item">Delete chat</a>
<a class="dropdown-item ">Block</a>
</div>
</span>
</div>
</header>
</div>
<!-- chat card start -->
<div class="card chat-wrapper shadow-none mb-0">
<div class="card-content">
<div class="card-body chat-container" id="test" fxFlex="auto" [perfectScrollbar]="config">
<div class="chat-content">
<div class="chat" #messages [ngClass]="{'chat-right': messages.sender === 0 , 'chat-left': messages.sender === 1 }"
*ngFor="let messages of contactChat">
<div class="chat-avatar">
<a [routerLink]="" class="avatar m-0">
<img [src]="messages.senderImage" />
</a>
</div>
<div class="chat-body">
<div class="chat-message">
<p>{{messages.message}}</p>
<span class="chat-time">{{messages.time}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer chat-footer px-2 py-1 pb-0">
<form class="d-flex align-items-center">
<i class="ficon feather ft-user cursor-pointer"></i>
<i class="ficon feather ft-paperclip ml-1 cursor-pointer"></i>
<input type="text" class="form-control chat-message-send mx-1" [(ngModel)]="newMessage" name="newmessage" id="iconLeft4"
placeholder="Type your message here...." #box (keyup.enter)="onEnter(box.value)">
<button type="submit" class="btn btn-primary glow send d-lg-flex" (click)="sendMessage()" ><i class="ficon feather ft-play"></i>
<span class="d-none d-lg-block mx-50">Send</span></button>
</form>
</div>
</div>
<!-- chat card ends -->
</div>
</section>
<section class="chat-profile " id="chat-profile">
<header class="chat-profile-header text-center border-bottom">
<span class="chat-profile-close" (click)="ChatProfile($event)">
<i class="ficon feather ft-x"></i>
</span>
<div class="my-2" *ngFor="let chats of headerChat">
<img [src]="chats.headerImage"class="round mb-1" alt="chat avatar"
height="100" width="100">
<h5 class="app-chat-user-name mb-0">{{chats.senderName}}</h5>
<span>Devloper</span>
</div>
</header>
<div class="chat-profile-content p-2">
<h6 class="mt-1">ABOUT</h6>
<p>It is a long established fact that a reader will be distracted by the readable content.</p>
<h6 class="mt-2">PERSONAL INFORMATION</h6>
<ul class="list-unstyled">
<li class="mb-25">email@gmail.com</li>
<li>+1(789) 950-7654</li>
</ul>
</div>
</section>
<!-- app chat window ends -->
</div>
</div>
</div>
</div>
<!-- END: Content-->
<!-- app chat profile right sidebar ends -->

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { StaticChatComponent } from './static-chat.component';
describe('StaticChatComponent', () => {
let component: StaticChatComponent;
let fixture: ComponentFixture<StaticChatComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ StaticChatComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StaticChatComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,431 @@
import {
Component, OnInit, AfterViewInit, ViewChild, Renderer2,
ViewChildren, QueryList, ElementRef
} from '@angular/core';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { ApplicationApiService } from '../../../../../../src/app/_services/application-api.service';
import { ToastrService } from 'ngx-toastr';
import { AppConstants } from '../../../../_helpers/app.constants';
/**
* ChatHistory class
*/
class ChatHistory {
constructor(
public message: string[],
public sender: number,
public senderImage: string,
public senderName: string,
public time: string
) { }
}
class ChatHeader {
constructor(
public senderImage: string,
public senderName: string
) { }
}
class ContactHeader {
constructor(
public senderImage: string,
public senderName: string
) { }
}
/**
* Chats class
*/
class Chats {
constructor(
public friendId: number,
public name: string,
public image: string,
public showMessage: string,
public _ChatHistory: Array<ChatHistory>,
public chatHeader: Array<ChatHeader>,
public isSelected: false
) { }
}
/**
* Contact class
*/
class Contact {
constructor(
public friendId: number,
public name: string,
public image: string,
public showMessage: string,
public isSelected: false,
public contactHeader: Array<ContactHeader>,
) {}
}
@Component({
selector: 'app-static-chat',
templateUrl: './static-chat.component.html',
styleUrls: ['./static-chat.component.css']
})
export class StaticChatComponent implements OnInit, AfterViewInit {
showicon: boolean;
isicon: boolean;
isactive: boolean;
isSelected: boolean;
chatArray: any;
headerArray: any;
status = true;
public contactChat = [];
public headerChat = [];
public Contact = [];
newMessage = '';
newMessageArray = [];
public config: PerfectScrollbarConfigInterface = { wheelPropagation: false };
@ViewChild(PerfectScrollbarComponent) componentRef?: PerfectScrollbarComponent;
@ViewChild(PerfectScrollbarDirective, { static: true }) directiveRef?: PerfectScrollbarDirective;
@ViewChildren('messages') messages: QueryList<any>;
@ViewChild('content', { static: true }) content: ElementRef;
chatsList: any[] = [];
contactList: any[] = [];
temp = [];
temp2 = this.chatsList;
temp3 = this.contactList;
ContactHeader: any[] = [];
/**
* Constructor
*
* @param ApplicationApiService chatApiService;
* @param Renderer2 renderer
*/
constructor(
private chatApiService: ApplicationApiService,
private renderer: Renderer2,
private toastr: ToastrService) {
this.headerArray = this.chatApiService.getChatContactData().subscribe(Response => {
this.headerArray = Response;
});
}
/**
* Scroll chat to bottom
*/
ngAfterViewInit() {
this.messages.forEach(this.scrollToBottom);
this.messages.changes.subscribe(this.scrollToBottom);
}
/**
* OnInit
*/
ngOnInit() {
this.toastr.clear();
this.toastr.warning('', 'Please login through a personal account to experience the Live chat. We have it disabled for demo account.',
{ timeOut: 5000, disableTimeOut: true, closeButton: true });
this.chatArray = this.chatApiService.getChatsData().subscribe(Response => {
this.chatArray = Response;
this.contactChat = this.chatArray[2].ChatHistory;
this.chatsList.push(
new Chats(
2,
'Kristopher Candy',
'../../../assets/images/portrait/small/avatar-s-7.png',
' Thank you',
this.chatArray[1].ChatHistory,
this.chatArray[1].chatHeader,
false
)
);
this.chatsList.push(
new Chats(
3,
'Sarah Woods',
'../../../assets/images/portrait/small/avatar-s-8.png',
' Hello krish!',
this.chatArray[2].ChatHistory,
this.chatArray[2].chatHeader,
false
)
);
this.contactList.push(
new Contact(
1,
'Kristopher Candy',
'../../../assets/images/portrait/small/avatar-s-9.png',
'lemon drops',
false,
this.headerArray[0].contactHeader,
)
);
this.contactList.push(
new Contact(
2,
'Jenny Perich',
'../../../assets/images/portrait/small/avatar-s-10.png',
'candy canes',
false,
this.headerArray[1].contactHeader,
)
);
this.contactList.push(
new Contact(
3,
'Rock Montgomery',
'../../../assets/images/portrait/small/avatar-s-11.png',
'powder gum',
false,
this.headerArray[2].contactHeader,
)
);
this.contactList.push(
new Contact(
4,
'Heather Howell',
'../../../assets/images/portrait/small/avatar-s-12.png',
'cheesecake toffee',
false,
this.headerArray[3].contactHeader,
)
);
});
}
/**
* Chat scroll to bottom
*/
scrollToBottom = () => {
try {
this.content.nativeElement.scrollTop = this.content.nativeElement.scrollHeight;
} catch (err) { }
}
/**
* Filter Chat
*
* @param event search event
*/
updateFilter(event) {
const value = event.target.value.toLowerCase();
this.chatsList = [...this.temp2]; // and here you have to initialize it with your data
this.temp = [...this.chatsList];
// filter our data
const temp = this.chatsList.filter(function (d) {
return d.name.toLowerCase().indexOf(value) !== -1 || !value;
});
// update the rows
this.chatsList = temp;
// Whenever the filter changes, always go back to the first page
}
updateFilter1(event) {
const value = event.target.value.toLowerCase();
this.contactList = [...this.temp3]; // and here you have to initialize it with your data
this.temp = [...this.contactList];
// filter our data
const temp = this.contactList.filter(function (d) {
return d.name.toLowerCase().indexOf(value) !== -1 || !value;
});
// update the rows
this.contactList = temp;
// Whenever the filter changes, always go back to the first page
}
/**
* Send message
*/
sendMessage() {
if (this.newMessage !== null && this.newMessage !== '') {
this.contactChat.push({
message: [this.newMessage],
sender: 0,
senderImage: '',
time: '3:35AM',
});
this.newMessage = null;
}
}
/**
* Message send on Enter
*
* @param value New message
*/
onEnter(value: string) {
this.newMessage = value;
if (this.newMessage !== null && this.newMessage !== '') {
this.contactChat.push({
message: [this.newMessage],
sender: 0,
senderImage: '',
time: '3:35AM'
});
this.newMessage = null;
}
}
/**
* Display chat when click on contact
*
* @param friendId Friend id
*/
showChat(friendId, number) {
this.contactChat = [];
if (number === 1) {
for (let i = 0; i < this.chatsList.length; i++) {
if (friendId !== this.chatsList[i].friendId) {
this.chatsList[i].isSelected = false;
}
if (friendId === this.chatsList[i].friendId) {
this.chatsList[i].isSelected = true;
}
}
for (let i = 0; i < this.contactList.length; i++) {
this.contactList[i].isSelected = false;
}
for (const friend of this.chatsList) {
if (friend.friendId === friendId) {
this.contactChat = this.chatArray[friendId - 1].ChatHistory;
this.headerChat = this.chatArray[friendId - 1].chatHeader;
break;
}
}
} else if (number === 2) {
for (let i = 0; i < this.contactList.length; i++) {
if (friendId !== this.contactList[i].friendId) {
this.contactList[i].isSelected = false;
}
if (friendId === this.contactList[i].friendId) {
this.contactList[i].isSelected = true;
}
}
for (let i = 0; i < this.chatsList.length; i++) {
this.chatsList[i].isSelected = false;
}
this.contactChat = [];
this.headerChat = this.headerArray[friendId - 1].contactHeader;
}
}
/**
* Overlay add/remove fuction in responsive
*
* @param event Overlay click event
*/
contentOverlay(event) {
const toggleIcon = document.getElementById('chat-overlay1');
const toggle = document.getElementById('chat-area d-none');
if (event.currentTarget.className === 'chatSidebar ng-star-inserted') {
this.renderer.addClass(toggleIcon, 'd-none');
this.renderer.removeClass(toggle, 'd-none');
}
}
/**
* Warning add/remove class
*
* @param event click event
*/
chatFavorite(event) {
const chatIcon = document.getElementById('chat-icon');
if (event.currentTarget.className === 'chat-icon-favorite') {
this.renderer.addClass(chatIcon, 'warning');
} else if (event.currentTarget.className === 'chat-icon-favorite warning') {
this.renderer.removeClass(chatIcon, 'warning');
}
}
showContact(friendId) {
this.contactChat = [];
for (let i = 0; i < this.contactList.length; i++) {
if (friendId !== this.contactList[i].friendId) {
this.contactList[i].isSelected = false;
}
if (friendId === this.contactList[i].friendId) {
this.contactList[i].isSelected = true;
}
}
}
/**
* Show add/remove class at open profile
*
* @param event Overlay click event
*/
showProfile(event) {
const toggleIcon = document.getElementById('user-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-sidebar-profile-toggle') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.addClass(toggle, 'show');
} else if (event.currentTarget.className === 'chat-profile-close') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
/**
* Show add/remove function in responsive
*
* @param event Overlay click event
*/
showSidebar(event) {
if (window.innerWidth < AppConstants.MOBILE_RESPONSIVE_WIDTH) {
const toggleIcon = document.getElementById('sidebar-card');
const toggle = document.getElementById('overlayChat');
const toggleChat = document.getElementById('sidebar-left');
if (event.currentTarget.className === 'ficon feather ft-message-square chat-sidebar-toggle chat-start-icon font-large-3 p-3 mb-1' ||
'ficon feather ft-align-justify font-large-1 cursor-pointer') {
this.renderer.addClass(toggle, 'show');
this.renderer.addClass(toggleIcon, 'show');
}
if (window.innerWidth < AppConstants.MOBILE_RESPONSIVE_WIDTH) {
this.renderer.removeClass(toggleChat, 'sidebar-fixed');
}
}
}
/**
* Show add/remove function in responsive
*
* @param event Overlay click event
*/
Sidebar(event) {
const toggleChat = document.getElementById('sidebar-card');
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-sidebar-close' || 'chat-overlay show') {
this.renderer.removeClass(toggle, 'show');
this.renderer.removeClass(toggleChat, 'show');
this.renderer.removeClass(toggleIcon, 'show');
}
}
/**
* Show add/remove function
*
* @param event Overlay click event
*/
ShowChatProfile(event) {
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'avatar chat-profile-toggle m-0 mr-1') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.addClass(toggle, 'show');
}
}
/**
* Show add/remove function
*
* @param event Overlay click event
*/
ChatProfile(event) {
const toggleIcon = document.getElementById('chat-profile');
const toggle = document.getElementById('overlayChat');
if (event.currentTarget.className === 'chat-profile-close') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
}

View File

@@ -0,0 +1,89 @@
<div class="app-content content">
<div class="sidebar-left sidebar-fixed" fxFlex="auto" [perfectScrollbar]="config" id="sidebar-left">
<div class="sidebar">
<div class="sidebar-content card">
<div class="card-body chat-fixed-search">
<fieldset class="form-group position-relative has-icon-left m-0">
<input type="text" class="form-control" id="iconLeft" placeholder="Search user"
(keyup)='updateFilter($event)'>
<div class="form-control-position">
<i class="feather ft-search"></i>
</div>
</fieldset>
</div>
<div id="users-list" class="list-group position-relative">
<div class="users-list-padding media-list">
<a [routerLink]="" class="media _media" *ngFor="let chats of contactList"
(click)="showChat(chats.friendId)" id="_media" [ngClass]="{'active':chats.isSelected === true, '':chats.isSelected === false}">
<div class="media-left pr-1">
<span class="avatar avatar-md avatar-{{chats.isactive}}"><img class="media-object rounded-circle"
[src]="chats.image" alt="avatar"><i></i></span></div>
<i></i>
<div class="imagename media-body w-100">
<h6 class="list-group-item-heading">{{chats.name}}<span
class="font-small-3 float-right info">{{chats.time}}</span>
</h6>
<p class="list-group-item-text text-muted mb-0">
<i class="feather ft-check primary font-small-2"></i>{{chats.showMessage}}
<span class="float-right primary">
<i class="font-medium-1 icon-pin blue-grey lighten-3" *ngIf="chats.showicon"></i>
</span>
<span class="float-right primary">
<i class="font-medium-1 icon-volume-off blue-grey lighten-3 mr-1" *ngIf="chats.isicon "></i>
<span class="badge badge-pill badge-danger">{{chats.badge}}</span>
</span>
</p>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="content-right">
<div class="content-wrapper">
<div class="content-header"></div>
<div class="content-body">
<div class="content-overlay" id='content-overlay' (click)="contentOverlay($event)"></div>
<section class="chat-app-window" #content>
<div class="sidebar-toggle d-block d-lg-none" (click)="sidebar($event)"><i class="feather ft-menu font-large-1"></i></div>
<div class="badge badge-secondary mb-1">Chat History</div>
<div class="chats" #messages>
<div class="chat" [ngClass]="{'chat-right': messages.sender == 0 , 'chat-left': messages.sender == 1 }"
*ngFor="let messages of contactChat">
<div class="chat-avatar">
<a [routerLink]="" class="avatar">
<span class="avatar-online"><img [src]="messages.senderImage" /></span>
</a>
</div>
<div class="chat-body">
<div class="chat-content">
<p>{{messages.message}}</p>
</div>
</div>
</div>
</div>
</section>
<section class="chat-app-form">
<form class="chat-app-input d-flex">
<fieldset class="form-group position-relative has-icon-left col-10 m-0">
<div class="form-control-position">
<i class="icon-emoticon-smile"></i>
</div>
<input type="text" class="form-control" [(ngModel)]="newMessage" name="newmessage" id="iconLeft4"
placeholder="Type your message" #box (keyup.enter)="onEnter(box.value)">
<div class="form-control-position control-position-right">
<i class="feather ft-image"></i>
</div>
</fieldset>
<fieldset class="form-group position-relative has-icon-left col-2 m-0">
<button type="button" class="btn btn-primary" (click)="sendMessage()" keyboardShouldPersistTaps={true}><i
class="la la-paper-plane-o d-lg-none"></i><span class="d-none d-lg-block">Send</span></button>
</fieldset>
</form>
</section>
</div>
</div>
</div>
</div>
<!-- ////////////////////////////////////////////////////////////////////////////-->

View File

@@ -0,0 +1,374 @@
.custom-file {
width: 440px;
margin-left: 15px;
}
.dropdown {
position: relative !important;
margin-left: 0px !important;
}
.dropdown-toggle::after {
content: none !important;
}
.favoriteChange:hover {
background: url("../../../../assets/images/raty/star-on.png");
}
:host ::ng-deep .datatable-icon-right:before {
font-family: 'icofont';
font-style: normal;
}
:host ::ng-deep .datatable-icon-skip:before {
font-family: 'icofont';
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
font-family: 'icofont';
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
content: "\2039";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-prev:before {
content: "\00AB";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-right:before {
content: "\203A";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-skip:before {
content: "\00BB";
font-size: x-large;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-left,
.ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-right,
.ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-right,
.ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-right {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-skip {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host ::ng-deep .datatable-footer .datatable-pager ul li:not(.disabled).active a,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] ul[_ngcontent-c11] li[_ngcontent-c11]:not(.disabled):hover a[_ngcontent-c11] {
background-color: #d4d2e7;
font-weight: bold;
font-size: larger;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager a {
height: 32px;
min-width: 34px;
line-height: 22px;
padding: 0;
border-radius: 3px;
margin: 0 3px;
text-align: center;
vertical-align: top;
padding-top: 3px;
text-decoration: none;
vertical-align: bottom;
color: #7c8091;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-prev[_ngcontent-c11] {
font-size: 14px;
line-height: 9px;
padding: 0px 08px;
background-color: #ffffff;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-right,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-skip,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager .datatable-icon-prev,
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11] .datatable-footer[_ngcontent-c11] .datatable-pager[_ngcontent-c11] .datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager ul li:not(.disabled):hover a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager ul li:not(.disabled).active a,
.ngx-datatable.bootstrap .datatable-footer .datatable-pager ul li:not(.disabled):hover a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer {
background: #727e8e;
color: #ededed;
margin-top: -1px;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header .datatable-header-cell .datatable-header-cell-label {
font-weight: bold;
line-height: 24px;
font-size: medium;
color: #6b6f82;
}
:host ::ng-deep .cotnblck img {
vertical-align: middle
}
@media only screen and (max-width: 767px) {
:horizontal.app-contacts .sidebar-left.show {
margin-left: 0rem !important;
}
}
:host ::ng-deep .heading-elements .gap_contact {
margin-right: 0.2rem;
}
:host ::ng-deep .image-name-space {
margin-right: 8px;
}
:host ::ng-deep .datatable-row-center,
:host ::ng-deep .datatable-header-inner,
:host ::ng-deep .datatable-row-wrapper,
:host ::ng-deep .datatable-body-row.datatable-row-even,
:host ::ng-deep .datatable-scroll,
:host ::ng-deep .datatable-body-row.datatable-row-odd,
:host ::ng-deep .datatable-footer,
:host ::ng-deep .datatable-footer-inner {
width: 100% !important;
}
:host ::ng-deep .datatable-body-cell-label,
:host ::ng-deep .datatable-header-cell {
padding: 0.6rem;
}
:host ::ng-deep .datatable-body-row {
padding: 0 !important
}
:host ::ng-deep .mrless {
margin-right: 0.4rem !important
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header {
font-weight: bold;
height: unset !important;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap {
font-size: 14px;
}
:host ::ng-deep .datatable-header-cell {
font-size: 13px
}
@media(max-width:767px) {
:host ::ng-deep .page-count,
:host ::ng-deep .datatable-pager {
flex: 1 1 100% !important
}
}
:host ::ng-deep .my-custom-cell {
padding: 0.1rem 1.4rem !important;
}
:host ::ng-deep .ngx-datatable .datatable-body-cell,
.ngx-datatable .datatable-header-cell {
line-height: 3.625;
}
:host ::ng-deep .avatar i {
position: absolute;
right: -2px;
bottom: 5px;
width: 10px;
height: 10px;
border: 2px solid white;
border-radius: 100%;
}
:host ::ng-deep .icon {
right: 0px;
left: 26px;
bottom: -1px !important;
width: 10px;
height: 10px;
border: 0px !important;
border: 2px solid white !important;
}
@media only screen and (max-width: 767px) {
.custom-file {
margin-left: 15px !important;
margin-right: 15px !important;
width: 282px !important;
}
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body .datatable-body-row.datatable-row-even {
background-color: rgba(0, 0, 0, 0);
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body .datatable-body-row {
border-top: none !important;
border-bottom: 1px solid #e3ebf3;
}
.avatar {
margin-right: 15px;
}
.dropdown-toggle::after {
font-family: 'LineAwesome';
font-size: .8rem;
position: relative;
top: 0;
right: 0;
margin: 0 .3em 0 0;
padding: 0 2px 0 6px;
content: '\f110' !important;
vertical-align: 0;
border: none !important;
}
.pr-1,
.px-1 {
padding-right: 0rem !important;
}
:host ::ng-deep .ngx-datatable .datatable-body .datatable-body-row>div {
color: #6b6f82 !important;
}
:host ::ng-deep .close:not(:disabled):not(.disabled):hover,
.close:not(:disabled):not(.disabled):focus {
outline: none !important;
}
.datatable-header-cell-template-wrap {
padding-top: 1rem !important;
}
._center {
padding-top: 1rem !important;
}
:host ::ng-deep .ngx-datatable {
display: -webkit-box;
}
:host ::ng-deep .empty-row {
padding-left: 1rem !important;
}
:host ::ng-deep .dropdown .dropdown-menu .space,
:host ::ng-deep .dropup .dropdown-menu .space {
padding: 10px 20px !important;
height: 38px;
line-height: 1.625;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body .datatable-body-row .datatable-body-cell {
padding: 0rem;
overflow-y: visible !important;
overflow-x: visible !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager {
padding: 0px 25px;
}
:host ::ng-deep .contacts-table{
padding-top: 30px;
padding-bottom: 35px;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager {
text-align: left !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body .datatable-body-row.active {
background-color: #FFF !important;
}
:host ::ng-deep .content-right {
width: calc(100% - -30px) !important;
}
::ng-deep ngb-modal-backdrop {
z-index: 1050 !important;
}

View File

@@ -0,0 +1,309 @@
<div class="app-content content">
<div class="content-wrapper">
<div class="content-header row mb-1"></div>
<div class="content-detached content-right">
<div class="content-body">
<div class="content-overlay" id='content-overlay' (click)="contentOverlay($event)"></div>
<section class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="bug-list-search">
<div class="bug-list-search-content">
<div class="sidebar-toggle d-block d-lg-none" (click)="sidebar($event)"><i
class="feather ft-menu font-large-1"></i></div>
<form action="">
<div class="position-relative">
<input type="text" id="search-contacts" class="form-control" placeholder="Search contacts..."
(keyup)='updateFilter($event)'>
<div class="form-control-position">
<i class="la la-search text-size-base text-muted la-rotate-270"></i>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="row">
<div class="col-12">
<div class="card">
<div class="card-head">
<div class="card-header">
<h4 class="card-title">All Contacts</h4>
<div class="heading-elements mt-0">
<button class="btn btn-primary btn-sm gap_contact"
(click)="addTableDataModal(addTableDataModalContent)">
<i class="d-md-none d-block feather ft-plus white"></i>
<span class="d-md-block d-none">Add Contacts</span></button>
<span ngbDropdown class="d-inline-block dropdown gap_contact" [placement]="placement">
<button id="btnSearchDrop1" type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="true" class="btn btn-warning dropdown-toggle dropdown-menu-right btn-sm"
ngbDropdownToggle><i class="feather ft-download-cloud white"></i></button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="mt-1">
<button class="dropdown-item"><i class="feather ft-upload"></i> Import</button>
<button class="dropdown-item"><i class="feather ft-download"></i> Export</button>
<button class="dropdown-item"><i class="feather ft-shuffle"></i> Find Duplicate</button>
</div>
</span>
<button class="btn btn-default btn-sm"><i class="feather ft-settings white"></i></button>
</div>
</div>
</div>
<div class="card-content">
<div class="card-body">
<!-- Task List table -->
<div class="row">
<div class="col-sm-12">
<button type="button" class="btn btn-danger" (click)="deleteCheckedRow()">Delete
All</button>
<span class="pull-right">
Search :
<input type='search'
style='padding:8px;margin:15px auto;width:200px;height: calc(1.875rem + 2px);border-radius: 0.21rem;border: 1px solid #babfc7;'
(keyup)='updateFilter($event)' /></span>
</div>
</div>
<ngx-datatable #table class="bootstrap row contacts-table" [rows]="rows" [columnMode]="'force'" [headerHeight]="50"
[footerHeight]="50" [rowHeight]="70" [limit]="5" [selected]="selected" [selectionType]="'checkbox'"
(select)='onSelectContact($event)' fxFlex="auto" [perfectScrollbar]="config">
<ngx-datatable-column [flexGrow]="1" [minWidth]="30" [maxWidth]="50">
<ng-template ngx-datatable-header-template let-value="value" let-allRowsSelected="allRowsSelected"
let-selectFn="selectFn">
<span class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="selectAll" [checked]="allRowsSelected"
(change)="selectFn(!allRowsSelected)" />
<label class="custom-control-label" for="selectAll"></label>
</span>
</ng-template>
<ng-template ngx-datatable-cell-template let-value="value" let-row="row" let-rowIndex="rowIndex"
let-onCheckboxChangeFn="onCheckboxChangeFn" let-isSelected="isSelected">
<div class="custom-control custom-checkbox _center">
<input type="checkbox" class="custom-control-input" id="select{{rowIndex}}"
[checked]="isSelected" (change)="onCheckboxChangeFn($event)" />
<label class="custom-control-label" for="select{{rowIndex}}"></label>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="Name" [flexGrow]="1" [minWidth]="140">
<ng-template ngx-datatable-cell-template let-row="row">
<span class="avatar avatar-sm avatar-{{row.isActive}} rounded-circle">
<img [src]="row.image"><i></i></span>{{row.name}}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="Email" [flexGrow]="1" [minWidth]="140">
<ng-template ngx-datatable-cell-template let-row="row">
<a [routerLink]="">{{row.email}}</a>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="Phone" [flexGrow]="1" [minWidth]="140"></ngx-datatable-column>
<ngx-datatable-column name="Favorite" [flexGrow]="1" [minWidth]="90" [cellClass]="'my-custom-cell'">
<ng-template ngx-datatable-cell-template let-row="row">
<img src="../../../assets/images/raty/star-off.png" class="favoriteChange"
*ngIf="!row.isFavorite" (click)="favoriteChange(row)">
<img src="../../../assets/images/raty/star-on.png" class="favoriteChange" *ngIf="row.isFavorite"
(click)="favoriteChange(row)">
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="Actions" [flexGrow]="1" [minWidth]="100">
<ng-template ngx-datatable-cell-template let-row="row">
<a [routerLink]="" class="primary edit mr-1 mrless"
(click)="editTableDataModal(editTableDataModalContent, row)"><i class="la la-pencil"></i>
</a>
<a class="danger delete mr-1 mrless"><i class="la la-trash-o" (click)="deleteRow(row)"></i></a>
<span class="dropdown" ngbDropdown placement="left">
<a [routerLink]="" id="btnSearchDrop27" ngbDropdownToggle id="dropdownBasic1"
class="mrless"><i class="la la-ellipsis-v"></i></a>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<a [routerLink]="" class="dropdown-item edit space"
(click)="editTableDataModal(editTableDataModalContent, row)"><i
class="feather ft-edit-2"></i>Edit</a>
<a [routerLink]="" class="dropdown-item delete space" (click)="deleteRow(row)"><i
class="feather ft-trash-2"></i> Delete</a>
<a [routerLink]="" class="dropdown-item space"><i class="feather ft-plus-circle primary"></i>
Projects</a>
<a [routerLink]="" class="dropdown-item space"><i class="feather ft-plus-circle info"></i>
Team</a>
<a [routerLink]="" class="dropdown-item space"><i class="feather ft-plus-circle warning"></i>
Clients</a>
<a [routerLink]="" class="dropdown-item space"><i class="feather ft-plus-circle success"></i>
Friends</a>
</div>
</span>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="sidebar-detached sidebar-left" id="sidebar-left" fxFlex="auto" [perfectScrollbar]="config">
<div class="sidebar">
<div class="bug-list-sidebar-content">
<!-- Predefined Views -->
<div class="card">
<div class="card-head">
<div class="media p-1">
<div class="media-left pr-1"><span class="avatar avatar-sm avatar-online rounded-circle"><img
src="../../../assets/images/portrait/small/avatar-s-1.png" alt="avatar"><i
class="icon"></i></span></div>
<div class="media-body media-middle">
<h5 class="media-heading">Margaret Govan</h5>
</div>
</div>
</div>
<!-- contacts view -->
<div class="card-body border-top-blue-grey border-top-lighten-5">
<div class="list-group">
<a [routerLink]="" class="list-group-item active active">All Contacts</a>
<a [routerLink]="" class="list-group-item list-group-item-action">Recently contacted</a>
<a [routerLink]="" class="list-group-item list-group-item-action">Favorite contacts</a>
</div>
</div>
<!-- Groups-->
<div class="card-body">
<p class="lead">Groups</p>
<ul class="list-group">
<li class="list-group-item">
<span class="badge badge-primary badge-pill float-right">14</span> <a [routerLink]="" class="info">
Project</a>
</li>
<li class="list-group-item">
<span class="badge badge-info badge-pill float-right">22</span> <a [routerLink]="" class="info">
Team</a>
</li>
<li class="list-group-item">
<span class="badge badge-warning badge-pill float-right">10</span> <a [routerLink]="" class="info">
Clients</a>
</li>
<li class="list-group-item">
<span class="badge badge-success badge-pill float-right">5</span> <a [routerLink]="" class="info">
Friends</a>
</li>
</ul>
</div>
<!--/ Groups-->
<!--More-->
<div class="card-body ">
<p class="lead">More</p>
<ul class="list-group">
<a [routerLink]="" class="list-group-item info">Import</a>
<a [routerLink]="" class="list-group-item info">Export</a>
<a [routerLink]="" class="list-group-item info">Print</a>
<a [routerLink]="" class="list-group-item info">Restore contacts</a>
<a [routerLink]="" class="list-group-item info">Find duplicate</a>
</ul>
</div>
<!--/More-->
</div>
<!--/ Predefined Views -->
</div>
</div>
</div>
</div>
</div>
<!-- ////////////////////////////////////////////////////////////////////////////-->
<ng-template #editTableDataModalContent let-c="close" let-d="dismiss">
<form (ngSubmit)="onUpdate(editForm,selectedContact.id)" #editForm="ngForm">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Contact</h5>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group col-12">
<input type="text" [(ngModel)]="selectedContact.name" #editname="ngModel" name="name" id="name"
class="name form-control" placeholder="Name"
[ngClass]="{ 'is-invalid': editForm.submitted && editname.invalid }" required>
<div *ngIf="editForm.submitted && editname.invalid" class="invalid-feedback">
<div *ngIf="editname.errors.required">Name is required</div>
</div>
</div>
<div class="form-group col-12">
<input type="text" [(ngModel)]="selectedContact.email" #editemail="ngModel" name="email" id="email"
class="email form-control" placeholder="Email"
[ngClass]="{ 'is-invalid':editForm.submitted && editemail.invalid }" required email>
<div *ngIf="editForm.submitted && editemail.invalid" class="invalid-feedback">
<div *ngIf="editemail.errors.required">Email is required</div>
<div *ngIf="editemail.errors.email">Email must be a valid email address</div>
</div>
</div>
<div class="form-group col-12">
<input type="text" [(ngModel)]="selectedContact.phone" #editphone="ngModel" name="phone" id="phone"
class="phone form-control" placeholder="Phone Number" (keyup)="onFormat()" maxlength="14">
</div>
</div>
<div class="modal-footer">
<div class="form-group position-relative has-icon-left mb-0">
<button type="submit" id="edit-contact-item" class="btn btn-info edit-contact-item" data-dismiss="modal"><i
class="la la-paper-plane-o d-lg-none"></i> <span class="d-none d-lg-block">Edit</span></button>
</div>
</div>
</form>
</ng-template>
<ng-template #addTableDataModalContent let-c="close" let-d="dismiss">
<form (ngSubmit)="addNewContact(addForm)" #addForm="ngForm">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel1">Add New Contact</h5>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group col-12">
<input type="text" [(ngModel)]="contactName" name="name" id="contact-name" class="contact-name form-control"
placeholder="Name" #contactname="ngModel"
[ngClass]="{ 'is-invalid': addForm.submitted && contactname.invalid }" required>
<div *ngIf="addForm.submitted && contactname.invalid" class="invalid-feedback">
<div *ngIf="contactname.errors.required">Name is required</div>
</div>
</div>
<div class="form-group col-12">
<input type="text" [(ngModel)]="contactEmail" name="email" id="contact-email" class="contact-email form-control"
placeholder="Email" #contactemail="ngModel"
[ngClass]="{ 'is-invalid':addForm.submitted && contactemail.invalid }" required email>
<div *ngIf="addForm.submitted && contactemail.invalid" class="invalid-feedback">
<div *ngIf="contactemail.errors.required">Email is required</div>
<div *ngIf="contactemail.errors.email">Email must be a valid email address</div>
</div>
</div>
<div class="form-group col-12">
<input type="text" [(ngModel)]="contactPhone" name="contactPhone" #contactphone="ngModel" id="contact-phone"
class="contact-phone form-control" placeholder="Phone Number" (keyup)="onFormat()" maxlength="14">
</div>
<div class="form-group col-12">
<span class="custom-control custom-checkbox">
<input type="checkbox" [(ngModel)]="contactFavorite" name="favorite" (change)="addFavoriteImage($event)"
id="favorite" class="contact-fav input-chk custom-control-input">
<label class="custom-control-label" for="favorite">Favorite</label>
</span>
</div>
<div class="custom-file form-group col-12">
<input type="file" class="custom-file-input" accept='contactImage/*' (change)="preview($event)" id="customFile">
<label class="custom-file-label">Choose Image</label>
</div>
</div>
<div class="modal-footer">
<div class="form-group position-relative has-icon-left mb-0">
<button type="submit" id="add-contact-item" class="btn btn-info add-contact-item"><i
class="la la-paper-plane-o d-lg-none"></i> <span class="d-none d-lg-block">Add
New</span></button>
</div>
</div>
</form>
</ng-template>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ContactsComponent } from './contacts.component';
describe('ContactsComponent', () => {
let component: ContactsComponent;
let fixture: ComponentFixture<ContactsComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ ContactsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContactsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,350 @@
import { Component, OnInit, ViewChild, EventEmitter, Output, Renderer2 } from '@angular/core';
import { NgForm } from '@angular/forms';
import { DatatableComponent } from '@swimlane/ngx-datatable';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
class Contact {
constructor(
public id: number,
public name: string,
public email: string,
public phone: string,
public image: any,
public isFavorite: boolean,
public isActive: string
) { }
}
@Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css']
})
export class ContactsComponent implements OnInit {
columns: any = [];
contactName: any;
contactEmail: any;
contactPhone: any;
contactImage: any;
contactFavorite: boolean;
contactactive: string;
rows: any[] = [];
name = 'Angular';
public imagePath;
imgURL: any;
selectedContact: any;
contactFlag: boolean;
addContact: any;
placement = 'bottom-right';
imagepathdefault: any;
addModal = null;
editModal = null;
value: any;
loadingIndicator: true;
selected = [];
temp = [];
temp2 = this.rows;
public config: PerfectScrollbarConfigInterface = { };
@ViewChild(PerfectScrollbarComponent) componentRef?: PerfectScrollbarComponent;
@ViewChild(PerfectScrollbarDirective) directiveRef?: PerfectScrollbarDirective;
@Output() closeModalEvent = new EventEmitter<boolean>();
@ViewChild(DatatableComponent, { static: true }) table: DatatableComponent;
/**
* Constructor
*
* @param NgbModal modal;
* @param Renderer2 _renderer
*/
constructor(
private modal: NgbModal,
private _renderer: Renderer2
) { }
/**
* OnInit
*/
ngOnInit() {
this.rows.push(new Contact(1, 'Scott Marsh', 'scott@gmail.com', '(954)-654-5641',
'../../../assets/images/portrait/small/avatar-s-5.png', false, 'online'));
this.rows.push(new Contact(2, 'Russell Bry', 'russell@gmail.com', '(235)-654-5642',
'../../../assets/images/portrait/small/avatar-s-3.png', false, 'busy'));
this.rows.push(new Contact(3, 'james john', 'john@gmail.com', '(125)-654-5643',
'../../../assets/images/portrait/small/avatar-s-1.png', true, 'away'));
this.rows.push(new Contact(4, 'Cynth Tuck', 'tuck@gmail.com', '(974)-654-5644',
'../../../assets/images/portrait/small/avatar-s-4.png', false, 'busy'));
this.rows.push(new Contact(5, 'Margi Govan', 'govan@gmail.com', '(954)-654-5645',
'../../../assets/images/portrait/small/avatar-s-6.png', true, 'online'));
this.rows.push(new Contact(6, 'Eugene Wood', 'wood@gmail.com', '(987)-654-5646',
'../../../assets/images/portrait/small/avatar-s-9.png', false, 'busy'));
this.rows.push(new Contact(7, 'Eric Marshall', 'eric@gmail.com', '(545)-654-5647',
'../../../assets/images/portrait/small/avatar-s-7.png', false, 'online'));
}
/**
* Add new contact
*
* @param addTableDataModalContent Id of the add contact modal;
*/
addTableDataModal(addTableDataModalContent) {
this.addModal = this.modal.open(addTableDataModalContent, {
windowClass: 'animated fadeInDown'
});
this.contactFlag = true;
}
/**
* Edit selected contact row.
*
* @param editTableDataModalContent Id of the edit contact model.
* @param row The row which needs to be edited.
*/
editTableDataModal(editTableDataModalContent, row) {
this.selectedContact = Object.assign({}, row);
this.editModal = this.modal.open(editTableDataModalContent, {
windowClass: 'animated fadeInDown'
});
this.contactFlag = false;
}
/**
* Selected contact
*
* @param selected Selected contact;
*/
onSelectContact({ selected }) {
this.selected.splice(0, this.selected.length);
this.selected.push(...selected);
}
/**
* Search contact from contact table
*
* @param event Convert value uppercase to lowercase;
*/
updateFilter(event) {
const val = event.target.value.toLowerCase();
this.rows = [...this.temp2];
this.temp = [...this.rows];
const temp = this.rows.filter(function (d) {
return d.name.toLowerCase().indexOf(val) !== -1 || !val;
});
this.rows = temp;
this.table.offset = 0;
}
/**
* Choose contact image
*
* @param event Select contact image;
*/
preview(event) {
const reader = new FileReader();
reader.onload = (e: any) => {
this.contactImage = e.target.result;
};
reader.readAsDataURL(event.target.files[0]);
}
/**
* Delete contact row
* @param row Selected row for delete contact
*/
deleteRow(row) {
let index = 0;
const temp = [...this.rows];
for (const tempRow of temp) {
if (tempRow.id === row.id) {
temp.splice(index, 1);
break;
}
index++;
}
this.rows = temp;
}
/**
* Update contact details
*
* @param editForm Edit form for values check
* @param id Id match to the selected row Id
*/
onUpdate(editForm: NgForm, id) {
for (const row of this.rows) {
if (row.id === id && editForm.valid === true) {
row.name = this.selectedContact['name'];
row.email = this.selectedContact['email'];
row.phone = this.selectedContact['phone'];
this.editModal.close(editForm.resetForm);
break;
}
}
}
/**
* Contact changed to favorite or non-favorite
*
* @param row Row of the favorite contact
*/
favoriteChange(row) {
if (row.isFavorite) {
row.isFavorite = row.isFavorite ? false : true;
} else {
row.isFavorite = true;
}
}
/**
* Delete selected contact
*/
deleteCheckedRow() {
let index = 0;
const removedIndex = [];
const temp = [...this.rows];
for (const row of temp) {
for (const selectedRow of this.selected) {
if (row.id === selectedRow.id) {
removedIndex.push(index);
}
}
index++;
}
for (let i = removedIndex.length - 1; i >= 0; i--) {
temp.splice(removedIndex[i], 1);
}
this.rows = temp;
this.selected = [];
}
/**
* favorite set when add contact
*
* @param event favorite set on click event
*/
addFavoriteImage(event) {
if (event.target.checked === true) {
this.contactFavorite = true;
} else {
this.contactFavorite = false;
}
}
/**
* New contact add to the table
*
* @param addForm Add contact form
*/
addNewContact(addForm: NgForm) {
if (this.contactImage == null) {
this.contactImage = '../../../assets/images/portrait/small/default.png';
} else {
this.contactImage = this.contactImage;
}
if (this.contactactive === undefined) {
this.contactactive = 'away';
} else {
this.contactactive = this.contactactive;
}
/**
* Add contact if valid addform value
*/
if (addForm.valid === true) {
this.rows.push(
new Contact(
this.rows.length + 1,
this.contactName,
this.contactEmail,
this.contactPhone,
this.contactImage,
this.contactFavorite,
this.contactactive
)
);
this.rows = [...this.rows];
addForm.reset();
this.addModal.close(addForm.resetForm);
}
}
/**
* Set the phone number format
*/
onFormat() {
if (this.contactFlag === true) {
this.value = this.contactPhone;
} else if (this.contactFlag === false) {
this.value = this.selectedContact['phone'];
}
let country, city, number;
switch (this.value.length) {
case 6:
country = 1;
city = this.value.slice(0, 3);
number = this.value.slice(3);
break;
case 7:
country = this.value[0];
city = this.value.slice(1, 4);
number = this.value.slice(4);
break;
case 8:
country = this.value.slice(0, 3);
city = this.value.slice(3, 5);
number = this.value.slice(5);
break;
default:
return this.value;
}
if (country === 1) {
country = '';
}
number = number.slice(0, 3) + '-' + number.slice(3);
const no = '(' + city + ')' + '-' + number;
if (this.contactFlag === true) {
this.contactPhone = no;
} else if (this.contactFlag === false) {
this.selectedContact['phone'] = no;
}
}
/**
* Sidebar open/close in responsive
*
* @param event Sidebar open/close
*/
sidebar(event) {
const toggleIcon = document.getElementById('sidebar-left');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'sidebar-toggle d-block d-lg-none') {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
}
}
/**
* Overlay add/remove fuction in responsive
*
* @param event Overlay click event
*/
contentOverlay(event) {
const toggleIcon = document.getElementById('sidebar-left');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'content-overlay show') {
this._renderer.removeClass(toggleIcon, 'show');
this._renderer.removeClass(toggle, 'show');
}
}
}

View File

@@ -0,0 +1,13 @@
import { ContactsModule } from './contacts.module';
describe('ContactsModule', () => {
let contactsModule: ContactsModule;
beforeEach(() => {
contactsModule = new ContactsModule();
});
it('should create an instance', () => {
expect(contactsModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,29 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ContactsComponent } from './contacts.component';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
@NgModule({
imports: [
CommonModule,
NgxDatatableModule,
BreadcrumbModule,
FormsModule,
NgbModule,
PerfectScrollbarModule,
RouterModule.forChild([
{
path: '',
component: ContactsComponent
}
])
],
declarations: [ContactsComponent],
exports: [RouterModule]
})
export class ContactsModule { }

View File

@@ -0,0 +1,169 @@
:host ::ng-deep .mr-50, .mx-50 {
margin-right: .5rem!important;
}
:host ::ng-deep .mr-25, .mx-25 {
margin-right: .25rem!important;
}
:host ::ng-deep .pr-50, .px-50 {
padding-right: .5rem!important;
}
:host ::ng-deep .content-right .email-app-list-wrapper .email-app-list .email-user-list .users-list-wrapper li {
border-top: 1px solid #E4E5EC !important;
}
:host ::ng-deep .app-content .content-right {
width: calc(100% - 300px) !important;
background-color: #fff;
}
@media (max-width: 768px){
.app-content .content-right {
width: calc(100% - 0px) !important;
background-color: #fff;
}
}
:host ::ng-deep .ps__thumb-y {
background-color: #aaa;
border-radius: 6px;
position: absolute;
}
:host ::ng-deep .content-right .email-app-details .email-scroll-area {
overflow-x: hidden !important;
}
:host ::ng-deep .d-block {
text-align: left;
}
:host ::ng-deep .d-flex {
display: flex!important;
}
:host ::ng-deep .btn-link {
font-weight: 400;
color:#6b6f82 !important;
text-decoration: none;
}
:host ::ng-deep .email-detail-head .collapse-header .card-header {
background-color: transparent !important;
}
:host ::ng-deep .py-1 {
background-color: #f4f5fa;
}
:host ::ng-deep .ps {
overflow: hidden!important;
}
:host ::ng-deep .content.app-content {
overflow-y: hidden !important;
}
:host ::ng-deep .tagDropdown {
transform: translate3d(-118px, -16px, 0px) !important;
top: 8px;
}
@media (max-width: 768px){
.tagDropdown {
transform: translate3d(-98px, -64px, 0px) !important;
top: 8px;
}
}
:host ::ng-deep .listDropdown {
transform: translate3d(-123px, 30px, 0px) !important;
}
@media (max-width: 768px){
.listDropdown {
transform: translate3d(-88px, -65px, 0px) !important;
top: 8px;
}
}
:host ::ng-deep .taglistDropdown {
transform: translate3d(-123px, -8px, 0px) !important;
}
@media (max-width: 768px){
.taglistDropdown {
transform: translate3d(-124px, -65px, 0px) !important;
top: 8px;
}
}
:host ::ng-deep .dropdown .dropdown-menu .dropdown-item {
padding: 10px 17px !important;
}
:host ::ng-deep .bg-1{
background-color: #666ee8;
}
:host ::ng-deep .badge {
display: inline-block;
padding: .35em .4em;
font-size: 80%;
text-align: center;
vertical-align: baseline;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
:host ::ng-deep .badge-pill {
padding-right: .6em;
padding-left: .6em;
border-radius: 10rem;
}
:host ::ng-deep .show>.inboxDropdown {
transform: translate3d(-118px, 30px, 0px) !important;
}
@media (max-width: 768px){
.show>.inboxDropdown {
transform: translate3d(-106px, -65px, 0px) !important;
}
}
:host ::ng-deep .app-content .sidebar .email-app-sidebar {
width: 300px !important;
}
:host ::ng-deep .emailtoolbar {
float: right;
margin-bottom: 12px;
display: initial;
}
:host ::ng-deep .app-content .quill-wrapper .snow-container .send-btn {
height: 38px;
}
:host ::ng-deep .ql-editor {
overflow-y: initial !important;
padding: 0px 0px !important;
}
:host ::ng-deep .gradient-mint {
background-image: linear-gradient(45deg,#28d094,#28d094)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-primary{
background-image: linear-gradient(45deg,#666ee8,#666ee8)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-warning {
background-image: linear-gradient(45deg,#ff9149,#ff9149)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-danger {
background-image: linear-gradient(45deg,#ff4961,#ff4961)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-info {
background-image: linear-gradient(45deg,#1e9ff2,#1e9ff2)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .list-group .list-group-messages :hover {
color: #0c84d1 !important;
}
:host ::ng-deep .mr-75, .mx-75 {
margin-right: .75rem!important;
}
.list-inline-item:not(:last-child) {
margin-right: 0.7rem !important;
}
@media (max-width: 768px){
.list-inline-item:not(:last-child) {
margin-right: 0.25rem !important;
}
}

View File

@@ -0,0 +1,527 @@
<div class="app-content content">
<div class="sidebar-left" id="sidebar-left">
<div class="sidebar">
<div class="sidebar-content email-app-sidebar d-flex">
<!-- sidebar close icon -->
<span class="sidebar-close-icon" (click)="showSidebar($event) ">
<i class="ficon feather ft-x"></i>
</span>
<!-- sidebar close icon -->
<div class="email-app-menu">
<div class="form-group form-group-compose">
<!-- compose button -->
<button type="button" class="btn btn-danger btn-glow btn-block my-2 compose-btn" id="compose-btn"
(click)="showComposeSidebar($event)">
<i class="ficon feather ficon feather ft-plus"></i>
Compose
</button>
</div>
<div class="sidebar-menu-list" fxFlex="auto" [perfectScrollbar]="config">
<!-- sidebar menu -->
<div class="list-group list-group-messages" *ngFor="let email of emailMenuList">
<a class="list-group-item" id="inbox-menu" (click)="showEmailMenu(email.Id, emailMenuList)"
[ngClass]="{'active':email.isSelected === true, '':email.isSelected === false}">
<div class="d-inline mr-25">
<i class="{{email.icon}}"></i>
</div>
{{email.name}}
<span class="{{email.budgeClass}}">{{email.budge}}</span>
</a>
</div>
<!-- sidebar menu end-->
<!-- sidebar label start -->
<label class="sidebar-label">Labels</label>
<div class="list-group list-group-labels " *ngFor="let email of emailLable">
<a [routerLink]="" class="list-group-item d-flex justify-content-between align-items-center"
(click)="showEmailMenu(email.Id, emailLable)"
[ngClass]="{'active':email.isSelected === true, '':email.isSelected === false}">
{{email.name}}
<span class="{{email.bulletClass}} d-inline-block rounded-circle "></span>
</a>
</div>
<!-- sidebar label end -->
</div>
</div>
</div>
<!-- User new mail right area -->
<div class="compose-new-mail-sidebar" id="compose-sidebar" fxFlex="auto" [perfectScrollbar]="config">
<div class="card mb-0 shadow-none quill-wrapper p-0">
<div class="card-header">
<h3 class="card-title" id="emailCompose">New Message</h3>
<button type="button" class="close close-icon" id="showCompose" (click)="showCompose($event)">
<i class="ficon feather ft-x"></i>
</button>
</div>
<!-- form start -->
<form action="" id="compose-form">
<div class="card-content">
<div class="card-body pt-0">
<div class="form-group pb-50">
<label for="emailfrom">from</label>
<input type="text" id="emailfrom" class="form-control" placeholder="user@example.com" disabled>
</div>
<div class="form-label-group mb-1">
<input type="email" id="emailTo" class="form-control" placeholder="To" required>
</div>
<div class="form-label-group mb-1">
<input type="text" id="emailSubject" class="form-control" placeholder="Subject">
</div>
<div class="form-label-group mb-1">
<input type="text" id="emailCC" class="form-control" placeholder="CC">
</div>
<div class="form-label-group mb-1">
<input type="text" id="emailBCC" class="form-control" placeholder="BCC">
</div>
<!-- Compose mail Quill editor -->
<div class="snow-container border rounded p-50 ">
<section class="default-editor">
<div class="row">
<div class="col-12">
<div class="snow-container border rounded p-50">
<quill-editor [styles]="{height: '80px'}" [modules]="quillConfig" (onFocus)="focus()"
(onBlur)="blur()" ></quill-editor>
<!-- <div class="toolbar1">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
<button class="ql-underline">Underline</button>
<button class="ql-strike">Strike</button>
</div> -->
</div>
</div>
</div>
</section>
</div>
<div class="form-group mt-2">
<div class="custom-file">
<input type="file" class="custom-file-input" id="emailAttach">
<label class="custom-file-label" for="emailAttach">Attach file</label>
</div>
</div>
</div>
</div>
<div class="card-footer border-0 d-flex justify-content-end pt-0">
<button type="reset" class="btn btn-secondary cancel-btn mr-1" (click)="showCompose($event)">
<i class='ficon feather ft-x mr-25'></i>
<span class="d-sm-inline d-none">Cancel</span>
</button>
<button type="submit" class="btn-send btn btn-danger btn-glow">
<i class='ficon feather ft-play mr-25'></i> <span class="d-sm-inline d-none">Send</span>
</button>
</div>
</form>
<!-- form start end-->
</div>
</div>
<!--/ User Chat profile right area -->
</div>
</div>
<div class="content-right">
<div class="content-header row">
</div>
<div class="content-overlay"></div>
<div class="content-wrapper">
<div class="content-body">
<!-- email app overlay -->
<div class="app-content-overlay" id="app-content-overlay" (click)="showCompose($event)"
(click)="showSidebar($event)"></div>
<div class="email-app-area">
<!-- Email list Area -->
<div class="email-app-list-wrapper">
<div class="email-app-list">
<div class="email-action">
<!-- action left start here -->
<div class="action-left d-flex align-items-center">
<div class="custom-control custom-checkbox selectAll mr-50">
<input type="checkbox" class="custom-control-input" id="selectAll" [(ngModel)]="selectAll"
(click)="selectAllEmails();">
<label class="custom-control-label" for="selectAll"></label>
</div>
<!-- delete unread dropdown -->
<ul class="list-inline m-0 d-flex">
<li class="list-inline-item mail-delete">
<button type="button" class="btn btn-icon action-icon" (click)="deleteCheckedRow()">
<i class="ficon feather ft-trash-2"></i>
</button>
</li>
<li class="list-inline-item mail-unread">
<button type="button" class="btn btn-icon action-icon">
<i class="ficon feather ft-mail"></i>
</button>
</li>
<li class="list-inline-item">
<div class="dropdown">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<button type="button" class="dropdown-toggle btn btn-icon action-icon" id="folder"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdownMenuButton"
dropdown-menu dropdown-menu-right show data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" role="menu" ngbDropdownToggle>
<i class="ficon feather ft-folder mr-0"></i>
</button>
<div ngbDropdownMenu="dropdownMenuButton" class="dropdown-menu dropdown-menu-right inboxDropdown" >
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-edit"></i>Draft</a>
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-info"></i>Spam</a>
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-trash-2"></i>Trash</a>
</div>
</div>
</div>
</li>
<li class="list-inline-item">
<div class="dropdown">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<button type="button" class="btn btn-icon dropdown-toggle action-icon" id="tag"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdownMenuButton"
ropdown-menu dropdown-menu-right show data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" role="menu" ngbDropdownToggle>
<i class="ficon feather ft-tag mr-0"></i>
</button>
<div aria-labelledby="tag">
<div ngbDropdownMenu="dropdownMenuButton" class="tagDropdown" >
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-mint d-inline-block rounded-circle "></span>
<span>Product</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-primary d-inline-block rounded-circle "></span>
<span>Work</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-warning d-inline-block rounded-circle "></span>
<span>Misc</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-danger d-inline-block rounded-circle "></span>
<span>Family</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-info d-inline-block rounded-circle "></span>
<span> Design</span>
</a>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
<!-- action left end here -->
<!-- action right start here -->
<div class="action-right d-flex flex-grow-1 align-items-center justify-content-around">
<!-- search bar -->
<div class="email-fixed-search flex-grow-1">
<div class="sidebar-toggle d-block d-lg-none" (click)="showSidebar($event)">
<i class="ficon feather ft-align-justify"></i>
</div>
<fieldset class="form-group position-relative has-icon-left m-0">
<input type="text" class="form-control" id="email-search" (keyup)="search($event)"
placeholder="Search email">
<div class="form-control-position">
<i class="ficon feather ft-search"></i>
</div>
</fieldset>
</div>
<!-- pagination and page count -->
<span class="d-none d-sm-block">1-10 of 653</span>
<button class="btn btn-icon email-pagination-prev d-none d-sm-block">
<i class="ficon feather ft-chevron-left"></i>
</button>
<button class="btn btn-icon email-pagination-next d-none d-sm-block">
<i class="ficon feather ft-chevron-right"></i>
</button>
</div>
</div>
<!-- / action right -->
<!-- email user list start -->
<div class="email-user-list list-group" [perfectScrollbar]="config">
<ul class="users-list-wrapper media-list">
<li class="{{contact.mediaClass}}" *ngFor="let contact of emailList; let i = index">
<div class="user-action">
<div class="checkbox-con mr-25">
<div class="custom-control custom-checkbox">
<input type="checkbox" [(ngModel)]="contact.isSelected" class="custom-control-input"
id="checkboxsmall{{i}}">
<label class="custom-control-label" for="checkboxsmall{{i}}"></label>
</div>
</div>
<span class="{{contact.starClass}}" [attr.id]="'emailstar-icon' + contact.emailId" (click)="emailFavorite($event,contact.emailId)">
<i class="{{contact.starIcon}}"></i>
</span>
</div>
<div class="pr-50">
<div class="avatar">
<img [src]="contact.image" alt="avtar img holder">
</div>
</div>
<div class="media-body" (click)="showEmail($event)">
<div class="user-details">
<div class="mail-items">
<span class="list-group-item-text text-truncate">{{contact.title}}</span>
</div>
<div class="mail-meta-item">
<span class="float-right">
<span class="mail-date">{{contact.time}}</span>
</span>
</div>
</div>
<div class="mail-message">
<p class="list-group-item-text truncate mb-0">
{{contact.message}}
</p>
<div class="mail-meta-item">
<span class="float-right">
<i class="ficon feather ft-paperclip mr-50" *ngIf="contact.showicon"></i>
<span class="bullet bullet-sm {{contact.bullet}} d-inline-block rounded-circle"></span>
</span>
</div>
</div>
</div>
</li>
</ul>
<!-- email user list end -->
<!-- no result when nothing to show on list -->
<div class="no-results">
<i class="ficon feather ft-info font-large-2"></i>
<h5>No Items Found</h5>
</div>
</div>
</div>
</div>
<!--/ Email list Area -->
<!-- Detailed Email View -->
<div class="email-app-details" id="app-details">
<!-- email detail view header -->
<div class="email-detail-header">
<div class="email-header-left d-flex align-items-center mb-1">
<span class="go-back mr-50">
<i class="ficon feather ft-chevron-left font-medium-4 align-middle" (click)="showEmail($event)"></i>
</span>
<h5 class="email-detail-title font-weight-normal mb-0">
Advertising Internet Online
<span class="badge badge-light-danger badge-pill ml-1 bg-1">PRODUCT</span>
</h5>
</div>
<div class="email-header-right mb-1 ml-2 pl-1">
<ul class="list-inline m-0">
<li class="list-inline-item">
<button class="btn btn-icon action-icon">
<i class="ficon feather ft-trash-2"></i>
</button>
</li>
<li class="list-inline-item">
<button class="btn btn-icon action-icon">
<i class="ficon feather ft-mail"></i>
</button>
</li>
<li class="list-inline-item">
<div class="dropdown">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<button type="button" class="dropdown-toggle btn btn-icon action-icon" id="folder"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdownMenuButton"
dropdown-menu dropdown-menu-right show data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" role="menu" ngbDropdownToggle>
<i class="ficon feather ft-folder mr-0"></i>
</button>
<div ngbDropdownMenu="dropdownMenuButton" class="dropdown-menu dropdown-menu-right listDropdown" >
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-edit"></i>Draft</a>
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-info"></i>Spam</a>
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-trash-2"></i>Trash</a>
</div>
</div>
</div>
</li>
<li class="list-inline-item">
<div class="dropdown">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<button type="button" class="btn btn-icon dropdown-toggle action-icon" id="tag"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdownMenuButton"
ropdown-menu dropdown-menu-right show data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" role="menu" ngbDropdownToggle>
<i class="ficon feather ft-tag mr-0"></i>
</button>
<div aria-labelledby="tag">
<div ngbDropdownMenu="dropdownMenuButton" class="taglistDropdown">
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-mint d-inline-block rounded-circle "></span>
<span>Product</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-primary d-inline-block rounded-circle "></span>
<span>Work</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-warning d-inline-block rounded-circle "></span>
<span>Misc</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-danger d-inline-block rounded-circle "></span>
<span>Family</span>
</a>
<a [routerLink]="" class="dropdown-item align-items-center">
<span class="bullet bullet-sm gradient-info d-inline-block rounded-circle "></span>
<span> Design</span>
</a>
</div>
</div>
</div>
</div>
</li>
<li class="list-inline-item">
<span class="no-of-list d-none d-sm-block ml-1">1-10 of 653</span>
</li>
<li class="list-inline-item">
<button class="btn btn-icon email-pagination-prev action-icon">
<i class='ficon feather ft-chevron-left'></i>
</button>
</li>
<li class="list-inline-item">
<button class="btn btn-icon email-pagination-next action-icon">
<i class='ficon feather ft-chevron-right'></i>
</button>
</li>
</ul>
</div>
</div>
<!-- email detail view header end-->
<div class="email-scroll-area" fxFlex="auto" [perfectScrollbar]="config">
<!-- email details -->
<div class="row">
<div class="col-12">
<div class="collapsible email-detail-head">
<div class="card collapse-header" role="tablist" *ngFor="let email of emailDisplayList"
[attr.id]="'emailThread'+ email.emailId" (click)="showMassage($event, email.emailId)">
<div [attr.id]="'headingCollapse5'+email.emailId"
class="card-header d-flex justify-content-between align-items-center" data-toggle="collapse"
role="tab" [attr.data-target]="'collapse5'+email.emailId"
[attr.aria-expanded]="email.isCollapsed" aria-controls="collapse5">
<div class="collapse-title media">
<div class="pr-1">
<div class="avatar mr-75">
<img [src]="email.image" alt="avtar img holder" width="30" height="30">
</div>
</div>
<div class="media-body mt-25">
<span class="text-primary">{{email.username}}</span>
<span class="d-sm-inline d-none"> &lt;{{email.email}};</span>
<small class="text-muted d-block">{{email.title}}</small>
</div>
</div>
<div class="information d-sm-flex d-none align-items-center">
<i class="ficon feather ft-paperclip mr-50"></i>
<small class="text-muted mr-50">{{email.date}}</small>
<span class="favorite" [attr.id]="'email-icon' + email.emailId" (click)="emailFavorite($event,email.emailId)" (click)="$event.stopPropagation();">
<i class="ficon feather ft-star mr-25"></i>
</span>
<div class="dropdown">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<a class="dropdown-toggle " id="folder" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" id="dropdownMenuButton" dropdown-menu dropdown-menu-right show
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" role="menu"
ngbDropdownToggle (click)="$event.stopPropagation();">
<i class='ficon feather ft-more-vertical mr-0'></i>
</a>
<div ngbDropdownMenu="dropdownMenuButton" class="dropdown-menu dropdown-menu-right ">
<a class="dropdown-item" [routerLink]="">Add to another project</a>
<a class="dropdown-item" [routerLink]="">Create follow up task</a>
<a class="dropdown-item" [routerLink]="">Print</a>
</div>
</div>
</div>
<!-- <div class="dropdown">
<a [routerLink]="" class="dropdown-toggle" id="fisrt-open-submenu" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class='ficon feather ft-more-vertical mr-0'></i>
</a>
</div> -->
</div>
</div>
<div [attr.id]="'collapse5'+email.emailId" role="tabpanel"
[attr.aria-labelledby]="'headingCollapse5'+ email.emailId" class="collapse">
<div class="card-content">
<div class="card-body py-1">
<p class="text-bold-500">{{email.message}}</p>
<p>
{{email.descrition}}
</p>
<p>
{{email.descrition_detail}}
</p>
<p class="mb-0"> {{email.sender}}</p>
<p class="text-bold-500"> {{email.sender_name}}</p>
</div>
<div class="card-footer pt-0 border-top">
<label class="sidebar-label">Attached Files</label>
<ul class="list-unstyled mb-0">
<li class="cursor-pointer pb-25">
<img [src]="email.image_icon1" height="30" alt="psd.png">
<small class="text-muted ml-1 attchement-text"> {{email.file_name1}}</small>
</li>
<li class="cursor-pointer">
<img [src]="email.image_icon2" height="30" alt="sketch.png">
<small class="text-muted ml-1 attchement-text"> {{email.file_name2}}</small>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- email details end-->
<div class="row px-2 mb-4">
<div class="col-12 px-0">
<div class="card shadow-none border rounded">
<div class="card-content">
<div class="card-body quill-wrapper emailquillConfig">
<div class="snow-container">
<quill-editor [styles]="{height: '80px'}" [modules]="quillConfig" (onFocus)="focus()"
(onBlur)="blur()" >
<span>Reply to Lois Jimenez</span></quill-editor>
<!-- <quill-editor [styles]="{height: '100px'}" placeholder="Enter Text"
[modules]="emailquillConfig" (onFocus)="focus()" (onBlur)="blur()"></quill-editor> -->
</div>
<div class="emailtoolbar">
<button class="btn btn-primary send-btn">
<i class='ficon feather ft-play mr-25'></i>
<span class="d-none d-sm-inline"> Send</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!--/ Detailed Email View -->
</div>
</div>
</div>
</div>
</div>
<!-- END: Content-->

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { EmailComponent } from './email.component';
describe('EmailComponent', () => {
let component: EmailComponent;
let fixture: ComponentFixture<EmailComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ EmailComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EmailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,562 @@
import { Component, OnInit, ViewChild, Renderer2 } from '@angular/core';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { ApplicationApiService } from '../../../_services/application-api.service';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { QuillInitializeServiceService } from '../../../_services/quill-initialize-service.service';
class Email {
constructor(
public emailId: number,
public mediaClass: string,
public starClass: string,
public starIcon: string,
public image: any,
public time: string,
public title: string,
public message: string,
public showicon: boolean,
public bullet: string,
) { }
}
class EmailHistory {
constructor(
public emailId: number,
public username: string,
public email: string,
public image: any,
public date: any,
public title: string,
public message: string,
public descrition: string,
public descrition_detail: string,
public sender: string,
public sender_name: string,
public iconClass: string,
public image_icon1: string,
public file_name1: string,
public image_icon2: string,
public file_name2: string
) { }
}
class EmailMenu {
constructor(
public Id: string,
public name: string,
public icon: string,
public budge: string,
public budgeClass: string,
public budgeIcon: boolean,
public isSelected: false
) { }
}
class EmailLable {
constructor(
public Id: string,
public name: string,
public isSelected: boolean,
public bulletClass: string,
) { }
}
@Component({
selector: 'app-email',
templateUrl: './email.component.html',
styleUrls: ['./email.component.css'],
})
export class EmailComponent implements OnInit {
public config: PerfectScrollbarConfigInterface = { wheelPropagation: true };
@ViewChild(PerfectScrollbarComponent)
componentRef?: PerfectScrollbarComponent;
directiveRef?: PerfectScrollbarDirective;
@ViewChild(PerfectScrollbarDirective, { static: true })
isHidden = false;
isShown = true;
emailList: any[] = [];
emailDisplayList: any[] = [];
emailMenuList: any[] = [];
email: EmailHistory[];
emailLable: EmailLable[];
emailArray: any;
temp = [];
temp2 = this.emailList;
isSelected: boolean;
isCollapsed = false;
selectAll = false;
selected: [];
blured = false;
focused = false;
hide = false;
htmlText = 'Type Something';
form: FormGroup;
atValues = [
{ id: 1, value: 'Fredrik Sundqvist', link: 'https://google.com' },
{ id: 2, value: 'Patrik Sjölin' }
];
hashValues = [
{ id: 3, value: 'Fredrik Sundqvist 2' },
{ id: 4, value: 'Patrik Sjölin 2' }
];
quillConfig = {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'],
]
},
autoLink: true,
keyboard: {
bindings: {
enter: {
key: 13,
handler: (range, context) => {
console.log('enter');
return true;
}
}
}
}
};
emailquillConfig = {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'],
]
},
autoLink: true
};
/**
* Constructor
*
* @param ApplicationApiService emailApiService
* @param Renderer2 renderer
*/
constructor(
private emailApiService: ApplicationApiService,
private renderer: Renderer2,
private QuillInitializeServiceServicec: QuillInitializeServiceService,
fb: FormBuilder
) {
this.form = fb.group({
editor: ['']
});
}
/**
* OnInit
*/
ngOnInit() {
this.emailApiService.getEmailData().subscribe(Response => {
this.emailArray = Response;
this.emailDisplayList = Response.EmailHistory;
this.emailMenuList = Response.EmailMenu;
this.emailLable = Response.EmailLable;
this.email = this.emailArray.EmailHistory[1];
this.emailList.push(
new Email(
1,
'media mail-read',
'favorite warning',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-1.png',
'4.14 AM',
'Open source project public release 👍',
'Hey John, bah kivu decrete epanorthotic unnotched Argyroneta nonius veratrine preimaginary ',
false,
'gradient-mint',
)
);
this.emailList.push(
new Email(
2,
'media mail-read',
'favorite',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-2.png',
'2.15 AM',
'Ecommerce website Paypal integration 😃',
' We will start the new application development soon once this will be completed. ',
false,
'gradient-danger',
)
);
this.emailList.push(
new Email(
3,
'media',
'favorite warning',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-3.png',
'11.18AM',
'How To Set Intentions That Energize You',
' I will provide you more details after this Saturday. Hope that will be fine for you.. ',
true,
'gradient-mint',
)
);
this.emailList.push(
new Email(
4,
'media',
'favorite',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-4.png',
'Yesterday',
'Harness The Power Of Words In Your Life',
'When the equation, first to ability the forwards, the a but travelling, outlines sentinels bad expand to goodness....',
true,
'gradient-warning',
)
);
this.emailList.push(
new Email(
5,
'media mail-read',
'favorite',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-5.png',
'24 Feb',
'Helen Keller A Teller And A Seller',
'Thanks for your feedback ! Here`s a new layout for a new Modern Admin theme.',
true,
'gradient-warning',
)
);
this.emailList.push(
new Email(
6,
'media mail-read',
'favorite warning',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-6.png',
'15 March',
'Use Your Reset Button To Change Your Vibration 🎉',
'Hey John, bah kivu decrete epanorthotic unnotched Argyroneta nonius veratrine preimaginary',
false,
'gradient-info',
)
);
this.emailList.push(
new Email(
7,
'media',
'favorite',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-7.png',
'12-07-2019',
'Will connect you',
'Hi Kelly!',
false,
'gradient-mint',
)
);
this.emailList.push(
new Email(
8,
'media mail-read',
'favorite',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-8.png',
'03-29-2019',
'Harness The Power Of Words In Your Life',
'Hope your like it, or feel free to comment, feedback or rebound !',
false,
'gradient-danger'
)
);
this.emailList.push(
new Email(
9,
'media ',
'favorite warning',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-9.png',
'19 Jun',
'Hypnosis 12 Steps To Acquire Mind Power',
'Monstrous with geared from far and these, morals, phase rome; Class. Called get amidst of geared from next...',
false,
'gradient-info'
)
);
this.emailList.push(
new Email(
10,
'media ',
'favorite warning',
'ficon feather ft-star',
'../../../assets/images/portrait/small/avatar-s-10.png',
'21 Mar',
' Know Yourself Your Inner Power ',
' Hope your like it, or feel free to comment, feedback or rebound.',
false,
'gradient-warning'
)
);
});
this.form
.controls
.editor
.valueChanges.pipe(
debounceTime(400),
distinctUntilChanged()
)
.subscribe((data) => {
});
}
/**
* Search email
*
* @param event Convert value uppercase to lowercase;
*/
updateFilter(event) {
const value = event.target.value.toLowerCase();
this.emailList = [...this.temp2]; // and here you have to initialize it with your data
this.temp = [...this.emailList];
// filter our data
const temp = this.emailList.filter(function (d) {
return d.name.toLowerCase().indexOf(value) !== -1 || !value;
});
// update the rows
this.emailList = temp;
// Whenever the filter changes, always go back to the first page
}
/**
* Overlay add/remove fuction in responsive
*
* @param event Overlay click event
*/
contentOverlay(event) {
const toggleIcon = document.getElementById('email-app-menu');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'content-overlay show') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
/**
* Add overlay when open sidebar
*
* @param event Content overlay
*/
contentRightSidebar(event) {
const toggle = document.getElementById('content-right');
if (event.currentTarget.className === 'media _media border-0 ng-star-inserted active') {
this.renderer.addClass(toggle, 'show');
}
}
/**
* Remove overlay when close sidebar
*
* @param event Content overlay
*/
contentRight(event) {
const toggle = document.getElementById('content-right');
if (event.currentTarget.className === 'btn btn-primary go-back') {
this.renderer.removeClass(toggle, 'show');
}
}
/**
* Open Media-body
*
* @param event Mail Read
*/
showEmail(event) {
const toggleIcon = document.getElementById('app-details');
if (event.currentTarget.className === 'media-body') {
this.renderer.addClass(toggleIcon, 'show');
} else if (event.currentTarget.className === 'ficon feather ft-chevron-left font-medium-4 align-middle') {
this.renderer.removeClass(toggleIcon, 'show');
}
}
/**
*
* @'param' event
* @'param' emailId
*/
showMassage(event, emailId) {
for (let i = 1; i <= this.emailDisplayList.length; i++) {
if (emailId === i) {
const toggleIcon = document.getElementById('headingCollapse5' + emailId);
const toggle = document.getElementById('collapse5' + emailId);
const toggleHeader = document.getElementById('emailThread' + emailId);
if (event.currentTarget.className === 'card collapse-header ng-star-inserted') {
this.renderer.addClass(toggle, 'show');
this.renderer.addClass(toggleHeader, 'open');
this.renderer.removeClass(toggleIcon, 'collapsed');
this.emailDisplayList[i - 1].isCollapsed = true;
} else if (event.currentTarget.className === 'card collapse-header ng-star-inserted open') {
this.renderer.removeClass(toggle, 'show');
this.renderer.removeClass(toggleHeader, 'open');
this.renderer.addClass(toggleIcon, 'collapsed');
this.emailDisplayList[i - 1].isCollapsed = false;
}
}
}
}
/**
* Add overlay when open sidebar
*
* @param event Content overlay
*/
showComposeSidebar(event) {
const toggleIcon = document.getElementById('compose-sidebar');
const toggleSidebar = document.getElementById('sidebar-left');
const toggleOverlay = document.getElementById('app-content-overlay');
if (event.currentTarget.className === 'btn btn-danger btn-glow btn-block my-2 compose-btn') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.removeClass(toggleSidebar, 'show');
this.renderer.addClass(toggleOverlay, 'show');
} else if (event.currentTarget.className === 'btn btn-danger btn-glow btn-block my-2 compose-btn show') {
this.renderer.removeClass(toggleIcon, 'show');
}
}
/**
* Remove overlay when open sidebar
*
* @param event Content overlay
*/
showCompose(event) {
const toggleIcon = document.getElementById('compose-sidebar');
const toggleOverlay = document.getElementById('app-content-overlay');
if (event.currentTarget.className === 'close close-icon' || 'app-content-overlay') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggleOverlay, 'show');
}
}
/**
* Add overlay when open sidebar
*
* @param event Content overlay
*/
showSidebar(event) {
const toggleIcon = document.getElementById('sidebar-left');
const toggle = document.getElementById('app-content-overlay');
if (event.currentTarget.className === 'sidebar-toggle d-block d-lg-none') {
this.renderer.addClass(toggleIcon, 'show');
this.renderer.addClass(toggle, 'show');
} else if (event.currentTarget.className === 'sidebar-close-icon' || 'app-content-overlay') {
this.renderer.removeClass(toggleIcon, 'show');
this.renderer.removeClass(toggle, 'show');
}
}
/**
* Filter Email
*
* @param term search term
*/
search(term) {
const searchTerm = term.currentTarget.value;
if (searchTerm !== '') {
this.emailList = this.emailList.filter(result => {
if (result && searchTerm) {
if (result.title.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 ||
result.message.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1) {
return true;
}
return false;
}
});
} else {
this.emailList = this.temp2;
}
}
selectAllEmails() {
for (let i = 0; i < this.emailList.length; i++) {
if (this.selectAll) {
this.emailList[i].isSelected = false;
} else {
this.emailList[i].isSelected = true;
}
}
}
deleteCheckedRow() {
let index = 0;
const removedIndex = [];
const temp = [...this.emailList];
for (const row of temp) {
if (row.isSelected === true) {
removedIndex.push(index);
}
index++;
}
for (let i = removedIndex.length - 1; i >= 0; i--) {
temp.splice(removedIndex[i], 1);
}
this.emailList = temp;
this.selectAll = false;
}
showEmailMenu(Id, emailMenu) {
for (let j = 0; j < emailMenu.length; j++) {
for (let i = 0; i < this.emailMenuList.length; i++) {
for (let k = 0; k < this.emailLable.length; k++) {
if (emailMenu[j].name === this.emailMenuList[i].name) {
if (Id !== this.emailMenuList[i].Id) {
this.emailMenuList[i].isSelected = false;
}
if (Id === this.emailMenuList[i].Id) {
this.emailMenuList[i].isSelected = true;
this.emailLable[k].isSelected = false;
}
} else if (emailMenu[j].name === this.emailLable[k].name) {
if (Id !== this.emailLable[k].Id) {
this.emailLable[k].isSelected = false;
}
if (Id === this.emailLable[k].Id) {
this.emailLable[k].isSelected = true;
this.emailMenuList[i].isSelected = false;
}
}
}
}
}
for (const friend of this.emailMenuList) {
if (friend.Id === Id) {
break;
}
}
}
focus() {
this.focused = true;
this.blured = false;
}
blur() {
this.focused = false;
this.blured = true;
}
setControl() {
this.form.setControl('editor', new FormControl('test - new Control'));
}
/**
* Filter Email
*
* @ param event warning Class
* @ param emailId
*/
emailFavorite(event, emailId) {
for (let i = 1; i <= this.emailDisplayList.length; i++) {
if (emailId === i) {
const emailIcon = document.getElementById('email-icon' + emailId);
const emailstarIcon = document.getElementById('emailstar-icon' + emailId);
if (event.currentTarget.className === 'favorite') {
this.renderer.addClass(emailIcon, 'warning');
this.renderer.addClass(emailstarIcon, 'warning');
} else if (event.currentTarget.className === 'favorite warning') {
this.renderer.removeClass(emailIcon, 'warning');
this.renderer.removeClass(emailstarIcon, 'warning');
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
import { EmailModule } from './email.module';
describe('EmailModule', () => {
let emailModule: EmailModule;
beforeEach(() => {
emailModule = new EmailModule();
});
it('should create an instance', () => {
expect(emailModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,34 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EmailComponent } from './email.component';
import { RouterModule } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { QuillModule } from 'ngx-quill';
import { CustomFormsModule } from 'ngx-custom-validators';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { ArchwizardModule } from 'angular-archwizard';
import { BlockUIModule } from 'ng-block-ui';
@NgModule({
imports: [
CommonModule,
NgbModule,
FormsModule,
QuillModule.forRoot(),
CustomFormsModule,
PerfectScrollbarModule,
ArchwizardModule,
BreadcrumbModule,
RouterModule.forChild([
{
path: '',
component: EmailComponent
}
])
],
declarations: [EmailComponent]
})
export class EmailModule { }

View File

@@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { KanbanComponent } from './kanban/kanban.component';
import { RouterModule } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { QuillModule } from 'ngx-quill';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { DndListModule } from 'ngx-drag-and-drop-lists';
@NgModule({
declarations: [KanbanComponent],
imports: [
CommonModule,
NgbModule,
FormsModule,
NgSelectModule,
QuillModule.forRoot(),
DndListModule,
ReactiveFormsModule,
PerfectScrollbarModule,
RouterModule.forChild([
{
path: '',
component: KanbanComponent
}
])
]
})
export class KanbanModule { }

View File

@@ -0,0 +1,496 @@
.mr-25, .mx-25 {
margin-right: .25rem!important;
}
.mr-50, .mx-50 {
margin-right: .5rem!important;
}
.dropdown .dropdown-menu
{
transform: translate3d(-130px, 24px, 0px) !important;
}
:host ::ng-deep .ng-select .ng-select-container {
color: #fff;
background-color: #666ee8 !important;
}
:host ::ng-deep .ng-arrow {
top: -2px;
border-color: transparent transparent #fff !important;
}
:host ::ng-deep .ng-arrow-wrapper {
top: -2px;
border-color: transparent transparent #fff !important;
border-width: 0 5px 5px !important;
}
:host ::ng-deep .ng-select .ng-select-container {
height: calc(1.25em + 1.5rem + 2px)!important;
padding: .75rem 1rem;
font-size: 1rem;
line-height: 1.25;
}
:host ::ng-deep .ng-select {
position: relative;
display: block;
box-sizing: border-box;
}
:host ::ng-deep .ml-25, .mx-25 {
margin-left: .25rem!important;
}
:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option {
box-sizing: border-box;
cursor: pointer;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-height: 1.2em;
padding: 0px 5px 1px;
}
:host ::ng-deep .text {
/* display: block;
width: 100%; */
height: calc(1.25em + 1.5rem + 2px);
padding: .75rem 1rem;
font-size: 1rem;
line-height: 1.25;
/* color: #4e5154; */
background-color: #fff;
background-clip: padding-box;
border: 1px solid #babfc7;
border-radius: .25rem;
/* transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; */
}
:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option .bg-primary {
background-color: #666ee8!important;
}
ng-reflect-ng-item-label{
background-color: #ff4961!important;
}
:host ::ng-deep .bg-danger {
background-color: #ff4961!important;
}
:host ::ng-deep .justify-scontent-between {
justify-content: space-between!important;
}
/* datepicker css */
:host ::ng-deep .btn-light:not(:disabled):not(.disabled):active {
color: unset !important;
background-color: unset !important;
border-color: #d3d9df !important;
}
:host ::ng-deep .btn-light:hover:not(.disabled):active {
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
:host ::ng-deep .btn-light {
color: unset !important;
background-color: unset !important;
border-color: unset !important;
}
:host ::ng-deep .bg-primary {
background-color: #007bff !important;
}
:host ::ng-deep .text-white {
color: #fff !important;
}
:host ::ng-deep .custom-day {
text-align: center;
padding: .185rem .25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
:host ::ng-deep .custom-day:active {
color: #6d7183 !important;
background-color: #fff !important;
border-block-color: rgb(2, 117, 216) !important;
}
.bg-light {
background-color: #f8f9fa !important;
}
:host ::ng-deep .hidden {
display: block !important;
}
.ngb-dp-weekday {
color: #17a2b8;
}
.ngb-dp-week-number,
.ngb-dp-weekday {
line-height: 2rem;
text-align: center;
font-style: italic;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.small {
font-size: 80%;
font-weight: 400;
}
.ngb-dp-day {
cursor: pointer !important;
}
.ngb-dp-month {
pointer-events: none;
}
.btn-light:hover {
color: #212529 !important;
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.ngb-dp-header {
border-bottom: 0;
border-radius: .25rem .25rem 0 0;
padding-top: .25rem;
}
.ngb-dp-day,
.ngb-dp-week-number,
.ngb-dp-weekday {
width: 2rem;
height: 2rem;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
.custom-day.focused {
background-color: #e6e6e6;
}
.custom-day.range,
.custom-day:hover {
background-color: rgb(2, 117, 216);
color: white;
}
.custom-day.faded {
background-color: rgba(2, 117, 216, 0.5);
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
border-radius: 0.25rem;
display: inline-block;
width: 2rem;
}
.custom-day:hover, .custom-day.focused {
background-color: #e6e6e6;
}
.weekend {
background-color: #f0ad4e;
border-radius: 1rem;
color: white;
}
.hidden {
display: none;
}
:host ::ng-deep .ft-calendar{
font-family: feather!important;
speak: none;
font-style: normal;
font-weight: 400;
font-size: large;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:host ::ng-deep .input-group-text {
display: flex;
align-items: center;
padding: unset !important;
margin-bottom: unset !important;
font-size: unset !important;
font-weight: unset !important;
line-height: unset !important;
color:unset !important;
text-align: unset !important;
white-space: unset !important;
background-color: unset !important;
border: unset !important;
border-radius: unset !important;
}
.ml-75, .mx-75 {
margin-left: .75rem!important;
}
.mx-50 {
margin-left: .5rem!important;
}
.sidebar {
width : 270px !important;
}
.align-middle {
margin-left: 3px;
}
.mr-50, .mx-50 {
margin-right: .5rem!important;
}
:host ::ng-deep .ft-plus{
margin-right: 3px !important;
}
control:disabled, .form-control[readonly] {
background-color: #ffffff;
margin-left: -12px;
}
/* Kanban Board Application css */
/*------------------------------*/
.kanban-container {
width : 100% !important;
}
.kanban-container .kanban-board {
border-radius : 0.25rem;
padding : 1rem 0rem;
margin : 0 1.8rem 1rem 0 !important;
width : 18.67rem !important;
background-color : #E7EDF3;
}
.kanban-container .kanban-board .kanban-board-header {
/* kanban-header */
font-size : 1.2rem;
font-family : 'Quicksand', Georgia, 'Times New Roman', Times, serif;
color : #6B6F82;
padding : 0 0.93rem;
display : -webkit-box;
display : -webkit-flex;
display : -ms-flexbox;
display : flex;
-webkit-box-pack : justify;
-webkit-justify-content : space-between;
-ms-flex-pack : justify;
justify-content : space-between;
}
.kanban-container .kanban-board .kanban-board-header .dropdown {
float : right;
}
.kanban-container .kanban-board .kanban-board-header .dropdown .dropdown-toggle:after {
display : none;
}
.kanban-container .kanban-board .kanban-board-header .kanban-title-board {
/* kanban title */
font-weight : normal;
cursor : text;
padding : 0 0.5rem;
width : 200px;
}
.kanban-container .kanban-board .kanban-board-header .kanban-title-board:hover, .kanban-container .kanban-board .kanban-board-header .kanban-title-board:focus {
background-color : #6B6F82;
color : #FFFFFF;
border-radius : 0.25rem;
outline : none;
text-overflow : clip;
}
.kanban-container .kanban-board .kanban-board-header .kanban-title-button {
/* kanban title button */
position : absolute;
bottom : 0;
padding : 0.467rem 0rem;
display : block;
color : #6B6F82;
font-weight : 700;
font-size : 0.8rem;
}
.kanban-container .kanban-board .kanban-drag {
padding : 13px;
min-height : auto;
}
.kanban-container .kanban-board .kanban-item {
/* kanban item */
padding : 0.53rem 0.8rem;
border-radius : 0.25rem;
margin-bottom : 1rem;
box-shadow : -4px 4px 6px 0 rgba(55, 70, 95, 0.12);
position : relative;
overflow-wrap: break-word;
}
.kanban-container .kanban-board .kanban-item:before {
content : '';
width : 3px;
height : 100%;
position : absolute;
left : 0;
top : 0;
border-radius : 0.5rem;
}
.kanban-container .kanban-board .kanban-item:hover {
cursor : default;
}
.kanban-container .kanban-board .kanban-item:last-child {
margin-bottom : 0.5rem;
}
.kanban-container .kanban-board .kanban-item .kanban-image img {
border-radius : 0.25rem;
}
.kanban-overlay {
/* kanban overlay */
top : 0;
left : 0;
right : 0;
bottom : 0;
position : absolute;
z-index : 999;
visibility : hidden;
opacity : 0;
}
.kanban-overlay.show {
visibility : visible;
-webkit-transition : all 0.3s ease;
transition : all 0.3s ease;
opacity : 1;
background-color : rgba(0, 0, 0, 0.2);
}
.badge-circle {
display : -webkit-box;
display : -webkit-flex;
display : -ms-flexbox;
display : flex;
-webkit-box-align : center;
-webkit-align-items : center;
-ms-flex-align : center;
align-items : center;
-webkit-box-pack : center;
-webkit-justify-content : center;
-ms-flex-pack : center;
justify-content : center;
background-color : #E6EAEE;
color : #475F7B;
border-radius : 50%;
height : 30px;
width : 30px;
}
.avatar img {
border : 2px solid #FFFFFF;
}
.kanban-sidebar {
/* kanban sidebar */
box-shadow : -8px 0 18px 0 rgba(25, 42, 70, 0.13);
height : 100vh;
width : 23.8rem;
background-color : #FFFFFF;
position : fixed;
-webkit-transform : translateX(110%);
-ms-transform : translateX(110%);
transform : translateX(110%);
-webkit-transition : all 0.3s ease;
transition : all 0.3s ease;
z-index : 1050;
right : 2rem;
left : auto;
bottom : 0;
top : -1px;
opacity : 0;
overflow : hidden;
}
.kanban-sidebar .card-header .close-icon {
color : #6B6F82;
opacity : 1 !important;
}
.kanban-sidebar .card-header .close-icon:focus {
outline : none;
}
.kanban-sidebar.show {
opacity : 1;
-webkit-transform : translateX(9%) translateY(1px);
-ms-transform : translateX(9%) translateY(1px);
transform : translateX(9%) translateY(1px);
}
.kanban-sidebar .edit-kanban-item {
height : 100vh;
}
.kanban-sidebar .edit-kanban-item .card-content {
height : calc(100% - 9rem);
}
.kanban-sidebar .edit-kanban-item .card-content .form-group > label {
color : #BAC0C7;
margin-bottom : 0.67rem;
}
.kanban-sidebar .edit-kanban-item .card-content .form-group select {
border-radius : 0.25rem;
display : block;
}
.kanban-sidebar .edit-kanban-item .card-content .custom-file .custom-file-label:after {
background-color : transparent;
}
.kanban-sidebar .edit-kanban-item .card-footer .btn i {
top : 0;
}
.kanban-sidebar .edit-kanban-item .picker {
position : relative;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-snow, .kanban-sidebar .quill-wrapper .snow-container .ql-toolbar {
border : none;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-toolbar .btn {
width : auto;
line-height : 0.9;
padding : 0.467rem 1.2rem;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-toolbar .btn:hover {
color : #FFFFFF;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-tooltip {
left : 0 !important;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-tooltip input[type=text] {
width : 100px;
}
.kanban-sidebar .quill-wrapper .ql-editor.ql-blank::before {
left : 0.3rem;
}
.kanban-sidebar .quill-wrapper .ql-editor {
min-height : 7.93rem;
padding : 0;
}
.kanban-title-button {
background-color : transparent;
box-shadow : none;
}
@media (max-width: 420px) {
.kanban-sidebar {
width : 19rem;
right : 1.6rem;
}
.kanban-sidebar .quill-wrapper .snow-container .ql-tooltip input[type=text] {
width : 70px;
}
}

View File

@@ -0,0 +1,204 @@
<div class="app-content content">
<div class="content-overlay"></div>
<div class="content-wrapper">
<div class="content-header row">
</div>
<div class="content-body">
<!-- Basic Kanban App -->
<div class="kanban-overlay" id="content-overlay" (click)="showSidebar($event)"></div>
<section id="kanban-wrapper">
<div class="row">
<div class="col-12">
<button type="button" class="btn btn-primary mb-1" id="add-kanban" (click)="addKanbanBoard()">
<i class='ficon feather ft-plus-square mr-50'></i> Add New Board
</button>
<div id="kanban-app"></div>
</div>
</div>
<div class="kanban-sidebar" id="kanban_sidebar">
<div class="card shadow-none quill-wrapper">
<div class="card-header d-flex justify-scontent-between align-items-center border-bottom px-2 py-1">
<h3 class="card-title">UI Design</h3>
<button type="button" class="close close-icon">
<i class="ficon feather ft-x" (click)="showSidebar($event)"></i>
</button>
</div>
<!-- form start -->
<form [formGroup]="kanban" class="edit-kanban-item">
<div class="card-content position-relative" [perfectScrollbar]="config">
<div class="card-body">
<div class="form-group">
<label>Card Title</label>
<input type="text" formControlName="title" class="form-control edit-kanban-item-title"
placeholder="kanban Title">
</div>
<div class="form-group">
<label>Due Date</label>
<!-- <input type="text" formControlName="date" class="form-control edit-kanban-item-date"
placeholder="21 August, 2019"> -->
<div class="input-group">
<div class="input-group-text mr-50">
</div>
<input class="form-control" placeholder="yyyy-mm-dd" formControlName="date" name="date"
ngbDatepicker #d2="ngbDatepicker" (click)="d2.toggle()">
</div>
</div>
<div class="row form-group">
<div class="col-6">
<label>Label </label>
<select class="form-control text-white " bindLabel="name" formControlName="selectedLabel"
(change)="getSelectedKanbanText($event)" id="selectLable">
<option class="bg-primary">Primary</option>
<option class="bg-danger">Danger</option>
<option class="bg-success">Success</option>
<option class="bg-info">Info</option>
<option class="bg-warning">Warning</option>
<option class="bg-secondary">Secondary</option>
</select>
</div>
<div class="col-6">
<div class="form-group">
<label>Member</label>
<div class="d-flex align-items-center">
<div class="avatar m-0 mr-1">
<img src="../../../assets/images/portrait/small/avatar-s-20.png" height="36" width="36"
alt="avtar img holder">
</div>
<div class="badge-circle">
<i class="ficon feather ft-plus"></i>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Attachment</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="emailAttach">
<label class="custom-file-label" for="emailAttach">Attach file</label>
</div>
</div>
<!-- Compose mail Quill editor -->
<div class="form-group">
<label>Comment</label>
<div class="snow-container border rounded p-1">
<div class="editor">
<quill-editor calss="kanbanQuill" formControlName="description"
[styles]="{height: '110px', border: '0px solid #ccc' }" [modules]="kanbanquillConfig"
(onFocus)="focus()" (onBlur)="blur()" placeholder="Write a Comment... "></quill-editor>
</div>
<div class="d-flex justify-content-end">
<div class="compose-quill-toolbar ql-toolbar ql-snow kanbantoolbar">
<span class="ql-formats mr-0">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
<button class="ql-underline">Underline</button>
<button class="ql-link"></button>
<button class="ql-image"></button>
<button class="btn btn-sm btn-primary btn-comment ml-25">Comment</button>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<button type="reset" class="btn btn-danger delete-kanban-item mr-1" (click)="deleteKanban($event)">
<i class='ficon feather ft-trash-2 mr-50'></i>
<span>Delete</span>
</button>
<button type="submit" class="btn btn-primary glow update-kanban-item"
(click)="updateKanbanItem($event)">
<i class='ficon feather ft-play mr-50'></i>
<span>Save</span>
</button>
</div>
</form>
<!-- form start end-->
</div>
</div>
<!--/ User Chat profile right area -->
<div class="kanban-container">
<div data-id="kanban-board-1" data-order="1" class="kanban-board"
style="width: 250px; margin-left: 15px; margin-right: 15px;" *ngFor="let kanbanList of kanbanList">
<header class="kanban-board-header">
<div type="text" class="kanban-title-board truncate" contenteditable="true" [innerHTML]="kanbanList.name"
(input)="contentNew=$event.target.textContent" (click)="showSidebar($event)"
(focusout)="validateProfile(contentNew,kanbanList)"></div>
<button class="kanban-title-button btn btn-default btn-xs" (click)="addKanbanItem(kanbanList)">+ Add
New Item</button>
<div ngbDropdown>
<i class="ficon feather ft-more-vertical" id="dropdownBasic1" ngbDropdownToggle></i>
<div ngbDropdownMenu>
<a class="dropdown-item" [routerLink]=""><i class="ficon feather ft-link mr-50"></i>Copy Link</a>
<a class="dropdown-item kanban-delete" [routerLink]="" (click)="deleteKanbanList(kanbanList)"><i
class="ficon feather ft-trash-2 mr-50"></i>Delete</a>
</div>
</div>
</header>
<main class="kanban-drag">
<div>
<!-- <div *ngFor="let kanban of kanbanList.tickets" class="kanban"> -->
<div>
<div [dndList] [dndModel]="kanbanList.tickets">
<div class="kanban-item" (click)="showSidebar($event)" (click)="editItem(kanban,kanbanList.tickets)"
*ngFor="let kanban of kanbanList.tickets"
[dndType]="'kanban'"
(dndMoved)="removeItem(kanban, kanbanList)"
[dndDraggable] [dndObject]="kanban">
{{kanban.name}}
<div class="kanban-image mb-1" [hidden]="!kanban.showImage"><img class="img-fluid"
[src]="kanban.bg_image" alt="kanban-image"></div>
<div class="kanban-footer d-flex justify-content-between mt-1">
<div class="kanban-footer-left d-flex">
<div class="kanban-due-date mr-50"><i
[ngClass]="{'ficon feather ft-clock font-size-small mr-25': kanban.date}"></i><span
class="font-size-small">{{kanban.date| date: 'MMM d' }}</span></div>
<div class="kanban-comment mr-50"><i
[ngClass]="{'ficon feather ft-message-square font-size-small mr-25': kanban.comment}"></i><span
class="font-size-small">{{kanban.comment}}</span></div>
<div class="kanban-attachment"><i
[ngClass]="{'ficon feather ft-link font-size-small mr-25':kanban.attachment}"></i><span
class="font-size-small">{{kanban.attachment}}</span></div>
</div>
<div class="kanban-footer-right">
<div class="kanban-users">
<ul class="list-unstyled users-list cursor-pointer m-0 d-flex align-items-center">
<li class="avatar pull-up my-0" *ngFor="let imageUrl of kanban.image"><img
class="media-object" [src]="imageUrl" alt="Avatar" height="18" width="18"></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<footer></footer>
<!-- </div> -->
</div>
<form class="itemform not-draggable" [hidden]="!kanbanList.showNewItem">
<div class="form-group">
<textarea class="form-control add-new-item" [(ngModel)]="addTitle" rows="2" autofocus="" required=""
[ngModelOptions]="{standalone: true}"></textarea>
</div>
<div class="form-group"><button type="submit" class="btn btn-primary btn-sm mr-50"
(click)="submitItem(kanbanList)">Submit</button><button type="button" id="CancelBtn"
class="btn btn-sm btn-danger" (click)="cancleKanban(kanbanList)">Cancel</button></div>
</form>
</main>
<div>
</div>
</div>
</div>
<!--/ User Chat profile right area -->
</section>
<!--/ Sample Project kanban -->
</div>
</div>
</div>
<!-- END: Content-->

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { KanbanComponent } from './kanban.component';
describe('KanbanComponent', () => {
let component: KanbanComponent;
let fixture: ComponentFixture<KanbanComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ KanbanComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(KanbanComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,365 @@
import { Component, Renderer2, OnInit, AfterViewInit } from "@angular/core";
import { PerfectScrollbarConfigInterface } from "ngx-perfect-scrollbar";
import { FormGroup, FormControl, FormBuilder } from "@angular/forms";
import { QuillInitializeServiceService } from "../../../../_services/quill-initialize-service.service";
import "quill-mention";
declare var require: any;
const data = require("../../../../../assets/data/application/kanban.json");
@Component({
selector: "app-kanban",
templateUrl: "./kanban.component.html",
styleUrls: ["./kanban.component.css"],
})
export class KanbanComponent implements OnInit, AfterViewInit {
public config: PerfectScrollbarConfigInterface = { wheelPropagation: true };
selectKanbanClass: any;
kanbanList: any[] = [];
kanbanValue: any;
selectKanbanArray: any[] = [];
kanban: FormGroup;
showImage: boolean;
showNewItem: boolean;
selectedLabel: any;
addTitle: string;
header: string;
blured = false;
focused = false;
atValues = [
{ id: 1, value: "Fredrik Sundqvist", link: "https://google.com" },
{ id: 2, value: "Patrik Sjölin" },
];
hashValues = [
{ id: 3, value: "Fredrik Sundqvist 2" },
{ id: 4, value: "Patrik Sjölin 2" },
];
list = [
{ id: 1, name: 'item1', children: [] },
{ id: 2, name: 'item2', children: [] },
{ id: 3, name: 'item3', children: [] },
];
kanbanquillConfig = {
toolbar: ".kanbantoolbar",
autoLink: true,
keyboard: {
bindings: {
enter: {
key: 13,
handler: (range, context) => {
console.log("enter");
return true;
},
},
},
},
};
constructor(
private _renderer: Renderer2,
private formBuilder: FormBuilder,
private QuillInitializeServiceServicec: QuillInitializeServiceService
) { }
ngOnInit(): void {
this.showNewItem = false;
this.selectKanbanClass = "bg-primary";
this.kanban = this.formBuilder.group({
title: new FormControl(['']),
date: new FormControl(['']),
selectedLabel: new FormControl(['']),
description: new FormControl([''])
});
this.kanbanList = JSON.parse(localStorage.getItem("kanban"));
if (this.kanbanList == null || this.kanbanList.length == 0) {
this.kanbanList = data;
} else { }
localStorage.setItem("kanban", JSON.stringify(this.kanbanList));
}
ngAfterViewInit(): void {
const items = localStorage.getItem("kanban");
if (items) {
this.kanbanList = JSON.parse(items);
}
}
draggable = {
// note that data is handled with JSON.stringify/JSON.parse
// only set simple data or POJO's as methods will be lost
data: "myDragData",
effectAllowed: "all",
disable: false,
handle: false
};
onDragStart(event: DragEvent) {
console.log("drag started", JSON.stringify(event, null, 2));
}
onDragEnd(event: DragEvent) {
console.log("drag ended", JSON.stringify(event, null, 2));
}
onDraggableCopied(event: DragEvent) {
console.log("draggable copied", JSON.stringify(event, null, 2));
}
onDraggableLinked(event: DragEvent) {
console.log("draggable linked", JSON.stringify(event, null, 2));
}
onDraggableMoved(event: DragEvent) {
console.log("draggable moved", JSON.stringify(event, null, 2));
}
onDragCanceled(event: DragEvent) {
console.log("drag cancelled", JSON.stringify(event, null, 2));
}
onDragover(event: DragEvent) {
console.log("dragover", JSON.stringify(event, null, 2));
}
showSidebar(event) {
const toggleIcon = document.getElementById("kanban_sidebar");
const toggle = document.getElementById("content-overlay");
if (event.currentTarget.className === "kanban-item") {
this._renderer.addClass(toggleIcon, "show");
this._renderer.addClass(toggle, "show");
} else if (
event.currentTarget.className === "ficon feather ft-x" ||
"kanban-overlay"
) {
this._renderer.removeClass(toggleIcon, "show");
this._renderer.removeClass(toggle, "show");
}
}
editItem(value, kanban) {
const date1 = value.date;
const split = date1.split('/');
let dateObj = {
"year": Number(split[2]),
"month": Number([split[0]]),
"day": Number(split[1])
}
this.kanban.setValue({
title: value.name,
date: dateObj,
selectedLabel: value.selectedLabel,
description: value.description,
})
this.kanbanValue = value
this.selectKanbanArray = kanban
if (value.selectedLabel == 'Primary') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-sucess');
this._renderer.removeClass(toggleIcon, 'bg-danger');
this._renderer.removeClass(toggleIcon, 'bg-info');
this._renderer.removeClass(toggleIcon, 'bg-warning');
this._renderer.removeClass(toggleIcon, 'bg-secondary');
this._renderer.addClass(toggleIcon, 'bg-primary');
} else if (value.selectedLabel == 'Danger') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-primary');
this._renderer.removeClass(toggleIcon, 'bg-sucess');
this._renderer.removeClass(toggleIcon, 'bg-info');
this._renderer.removeClass(toggleIcon, 'bg-warning');
this._renderer.removeClass(toggleIcon, 'bg-secondary');
this._renderer.addClass(toggleIcon, 'bg-danger');
}
else if (value.selectedLabel == 'Success') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-primary');
this._renderer.removeClass(toggleIcon, 'bg-danger');
this._renderer.removeClass(toggleIcon, 'bg-info');
this._renderer.removeClass(toggleIcon, 'bg-warning');
this._renderer.removeClass(toggleIcon, 'bg-secondary');
this._renderer.addClass(toggleIcon, 'bg-success');
}
else if (value.selectedLabel == 'Info') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-primary');
this._renderer.removeClass(toggleIcon, 'bg-danger');
this._renderer.removeClass(toggleIcon, 'bg-success');
this._renderer.removeClass(toggleIcon, 'bg-warning');
this._renderer.removeClass(toggleIcon, 'bg-secondary');
this._renderer.addClass(toggleIcon, 'bg-info');
}
else if (value.selectedLabel == 'Warning') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-primary');
this._renderer.removeClass(toggleIcon, 'bg-danger');
this._renderer.removeClass(toggleIcon, 'bg-success');
this._renderer.removeClass(toggleIcon, 'bg-info');
this._renderer.removeClass(toggleIcon, 'bg-secondary');
this._renderer.addClass(toggleIcon, 'bg-warning');
}
else if (value.selectedLabel == 'Secondary') {
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, 'bg-primary');
this._renderer.removeClass(toggleIcon, 'bg-danger');
this._renderer.removeClass(toggleIcon, 'bg-success');
this._renderer.removeClass(toggleIcon, 'bg-warning');
this._renderer.removeClass(toggleIcon, 'bg-info');
this._renderer.addClass(toggleIcon, 'bg-secondary');
}
}
deleteKanban(event) {
this.kanbanValue;
for (let i = 0; i < this.kanbanList.length; i++) {
if (this.selectKanbanArray.length == this.kanbanList[i].tickets.length) {
for (var j = 0; j < this.selectKanbanArray.length; j++)
if (JSON.stringify(this.selectKanbanArray) === JSON.stringify(this.kanbanList[i].tickets)) {
for (var k = 0; k < this.kanbanList[i].tickets.length; k++) {
if (this.kanbanValue.name == this.kanbanList[i].tickets[k].name) {
this.kanbanList[i].tickets.splice(k, 1);
}
}
}
}
}
const toggleIcon = document.getElementById("kanban_sidebar");
const toggle = document.getElementById("content-overlay");
if (
event.currentTarget.className === "btn btn-danger delete-kanban-item mr-1"
) {
this._renderer.removeClass(toggleIcon, "show");
this._renderer.removeClass(toggle, "show");
}
}
addKanbanBoard() {
let defaultobject = {
name: "Default Title",
tickets: [],
type: "",
kanbanId: this.kanbanList.length + 1,
showNewItem: false
};
this.kanbanList.push(defaultobject);
}
public kanbanArray: any;
submitItem(kanbanArray) {
let dateObj = new Date();
var date = (dateObj.getMonth() + 1) + '/' + dateObj.getDate() + '/' + dateObj.getFullYear();
let object = {
'name': this.addTitle,
'date': date,
'selectedLabel': "Primary",
'description': this.addTitle
}
for (let i = 0; i < this.kanbanList.length; i++) {
if (kanbanArray.kanbanId == this.kanbanList[i].kanbanId && kanbanArray.tickets.length == this.kanbanList[i].tickets.length && JSON.stringify(kanbanArray.tickets) === JSON.stringify(this.kanbanList[i].tickets)) {
this.kanbanList[i].tickets.push(object),
this.kanbanList[i].showNewItem = false;
}
}
this.addTitle = null;
}
addKanbanItem(kanabanArray) {
this.addTitle = null;
for (let i = 0; i < this.kanbanList.length; i++) {
if (kanabanArray.kanbanId == this.kanbanList[i].kanbanId) {
this.kanbanList[i].showNewItem = true;
}
}
}
updateKanbanItem(event) {
let dateObj = this.kanban.value.date;
var date = dateObj.month + '/' + dateObj.day + '/' + dateObj.year
this.kanbanValue;
for (let i = 0; i < this.kanbanList.length; i++) {
if (this.selectKanbanArray.length == this.kanbanList[i].tickets.length) {
for (
var j = 0;
j < this.selectKanbanArray.length;
j++ // assert each element equal
)
JSON.stringify(this.selectKanbanArray) === JSON.stringify(this.kanbanList[i].kanabanArray);
for (var k = 0; k < this.kanbanList[i].tickets.length; k++) {
if (this.kanbanValue.name == this.kanbanList[i].tickets[k].name) {
(this.kanbanList[i].tickets[k].name = this.kanban.value.title),
(this.kanbanList[i].tickets[k].date = date),
(this.kanbanList[i].tickets[k].selectedLabel = this.kanban.value.selectedLabel),
(this.kanbanList[i].tickets[k].description = this.kanban.value.description);
}
}
this.kanbanList[i].tickets;
}
}
const toggleIcon = document.getElementById("kanban_sidebar");
const toggle = document.getElementById("content-overlay");
if (
event.currentTarget.className ===
"btn btn-primary glow update-kanban-item"
) {
this._renderer.removeClass(toggleIcon, "show");
this._renderer.removeClass(toggle, "show");
}
}
public removeItem(kanban: any, kanbanList): void {
for (let i = 0; i < this.kanbanList.length; i++) {
if (kanbanList.kanbanId == this.kanbanList[i].kanbanId) {
for (var k = 0; k < this.kanbanList[i].tickets.length; k++) {
if (kanban.name == this.kanbanList[i].tickets[k].name) {
kanbanList.tickets.splice(kanbanList.tickets.indexOf(kanban), 1);
this.kanbanList[i].tickets == kanbanList.tickets;
break;
}
}
}
}
}
cancleKanban(kanbanList) {
kanbanList.showNewItem = false;
}
deleteKanbanList(kanbanList) {
for (let i = 0; i < this.kanbanList.length; i++) {
if (JSON.stringify(kanbanList) === JSON.stringify(this.kanbanList[i])) {
this.kanbanList.splice(i, 1);
}
}
}
updateHeader(kanbanList) {
for (let i = 0; i < this.kanbanList.length; i++) {
if (JSON.stringify(kanbanList) === JSON.stringify(this.kanbanList[i])) {
this.kanbanList[i].name = this.header
}
}
}
focus() {
this.focused = true;
this.blured = false;
}
blur() {
this.focused = false;
this.blured = true;
}
validateProfile(content, kanbanList) {
this.header = content;
console.log(content);
setTimeout(() => {
this.updateHeader(kanbanList);
}, 2500);
}
getSelectedKanbanText(event: Event) {
let temp = event.target['options'][event.target['options'].selectedIndex].className;
const toggleIcon = document.getElementById('selectLable');
this._renderer.removeClass(toggleIcon, this.selectKanbanClass);
this._renderer.addClass(toggleIcon, temp);
this.selectKanbanClass = temp;
temp = null;
}
}

View File

@@ -0,0 +1,106 @@
import Delta from 'quill-delta';
import Autolinker from 'autolinker';
const defaults = {
globalRegularExpression: /https?:\/\/[\S]+/g,
urlRegularExpression: /(https?:\/\/[\S]+)/
}
export default class QuillAutoLink {
autolinker = new Autolinker();
quill;
options;
constructor (quill, options) {
this.quill = quill;
options = options || {};
this.options = {...defaults, ...options};
this.registerTypeListener();
this.registerPasteListener();
}
registerPasteListener () {
this.quill.clipboard.addMatcher(Node.TEXT_NODE, (node, delta) => {
if (typeof node.data !== 'string') {
return;
}
const matches = node.data.match(this.options.globalRegularExpression)
if (matches && matches.length > 0) {
const newDelta = new Delta();
let str = node.data;
matches.forEach(match => {
const split = str.split(match);
const beforeLink = split.shift();
newDelta.insert(beforeLink);
newDelta.insert(match, {link: match});
str = split.join(match);
})
newDelta.insert(str);
delta.ops = newDelta.ops;
}
return delta;
})
}
registerTypeListener () {
this.quill.on('text-change', (delta) => {
let ops = delta.ops
// Only return true, if last operation includes whitespace inserts
// Equivalent to listening for enter, tab or space
if (!ops || ops.length < 1 || ops.length > 2) {
return
}
let lastOp = ops[ops.length - 1]
if (!lastOp.insert || typeof lastOp.insert !== 'string' || !lastOp.insert.match(/\s/)) {
return
}
this.checkTextForUrl()
})
}
checkTextForUrl () {
let sel = this.quill.getSelection();
if (!sel) {
return;
}
let [leaf] = this.quill.getLeaf(sel.index);
if (!leaf.text) {
return;
}
if(leaf.parent != undefined && leaf.parent.domNode != undefined && leaf.parent.domNode.tagName.toLowerCase() == "a"){
return;
}
var temp = leaf.text.substring(0,leaf.text.length - 1);
var to_check_str = temp.substring(temp.lastIndexOf(' '));
this.checkIfHasLink(sel.index, to_check_str);
}
textToUrl (index,text, url) {
const ops = new Delta()
.retain(index)
.delete(text.length)
.insert(text,{link: url});
this.quill.updateContents(ops);
}
checkIfHasLink = (currentIndex: number, input: string) =>{
var hasLink = false;
var linkedText = Autolinker.link( input, {
replaceFn : ( match ) => {
switch(match.getType()){
// case 'url' :
// //console.log( "url: ", match.getUrl() );
// var index = (currentIndex - match.matchedText.length) - 1;
// this.textToUrl(index,match.matchedText,match.getUrl());
// return true; // let Autolinker perform its normal anchor tag replacement
// case 'email' :
// var email = match.getEmail();
// console.log( "email: ", email );
// return true;
// case 'phone' :
// console.log( "Phone Number: ", match.getNumber() );
}
}
} );
return hasLink;
}
}

View File

@@ -0,0 +1,42 @@
import { NgModule } from '@angular/core';
import { CommonModule, DatePipe } from '@angular/common';
import { TodoappComponent } from './todoapp.component';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PerfectScrollbarModule, PerfectScrollbarConfigInterface } from 'ngx-perfect-scrollbar';
import { AngularFireModule } from '@angular/fire/compat';
import { environment } from 'src/environments/environment.prod';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { TodoService } from 'src/app/_api/todo/todo.service';
import { ToastrModule } from 'ngx-toastr';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
import { QuillModule } from 'ngx-quill';
import { ImagePreloadDirective } from '../../../_directives/image.preload.directive';
@NgModule({
declarations: [TodoappComponent, ImagePreloadDirective],
providers: [TodoService],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
PerfectScrollbarModule,
FormsModule,
NgbModule,
NgSelectModule,
QuillModule.forRoot(),
ToastrModule.forRoot(),
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule.enablePersistence(),
RouterModule.forChild([
{
path: '',
component: TodoappComponent
}
])
],
exports: [ImagePreloadDirective]
})
export class TodoAppModule { }

View File

@@ -0,0 +1,304 @@
:host ::ng-deep .todo-app-menu {
width: 100% !important;
padding: .5rem 0;
}
:host ::ng-deep .ml-25 {
margin-left: .25rem!important;
}
:host ::ng-deep .todo-sidebar .todo-app-menu .sidebar-menu-list .filter-label {
font-family: Quicksand,Georgia,"Times New Roman",Times,serif;
letter-spacing: 1px;
color: #bac0c7;
}
:host ::ng-deep .border {
border: 1px solid #dde1e6 !important;
}
:host ::ng-deep .ql-container.ql-snow {
border: 0px solid #ccc;
}
:host ::ng-deep .ql-toolbar.ql-snow {
border: 0px solid #ccc;
}
:host ::ng-deep .btn-primary {
height: fit-content;
margin-right: 7px;
}
:host ::ng-deep .new-taskDropdown::after {
content: "" !important;
}
:host ::ng-deep .show > .dropdownnew-task {
left: -166px !important;
}
:host ::ng-deep .show > .dropdown-sort {
left: -48px !important;
}
@media (max-width: 991.98px){
.app-content .content-right {
width: 100%!important;
}
}
:host ::ng-deep .py-75 {
padding-top: .75rem!important;
}
:host ::ng-deep .py-75 {
padding-bottom: .75rem!important;
}
:host ::ng-deep .ImageUpload {
width: 10%;
height: 10%;
}
:host ::ng-deep .add-todo{
margin-left: 10px;
}
:host ::ng-deep .todo-new-task-sidebar .avatar i {
right : 18px;
bottom : 18px;
}
:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items {
width: fit-content !important;
}
:host ::ng-deep .ng-select {
width: 130px;
}
@media (max-width: 349.98px) {
.sidebar .todo-sidebar {
width : 230px;
}
}
@media (max-width: 575.98px) {
.todo-new-task-sidebar {
width : 88%;
}
}
:host ::ng-deep .gradient-mint {
background-image: linear-gradient(45deg,#28d094,#28d094)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-primary{
background-image: linear-gradient(45deg,#666ee8,#666ee8)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-warning {
background-image: linear-gradient(45deg,#ff9149,#ff9149)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-danger {
background-image: linear-gradient(45deg,#ff4961,#ff4961)!important;
width: 10px !important;
height: 10px !important;
}
:host ::ng-deep .gradient-info {
background-image: linear-gradient(45deg,#1e9ff2,#1e9ff2)!important;
width: 10px !important;
height: 10px !important;
}
/* date picker css */
:host ::ng-deep .btn-light:not(:disabled):not(.disabled):active {
color: unset !important;
background-color: unset !important;
border-color: #d3d9df !important;
}
:host ::ng-deep .btn-light:hover:not(.disabled):active {
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
:host ::ng-deep .btn-light {
color: unset !important;
background-color: unset !important;
border-color: unset !important;
}
:host ::ng-deep .bg-primary {
background-color: #007bff !important;
}
:host ::ng-deep .text-white {
color: #fff !important;
}
:host ::ng-deep .custom-day {
text-align: center;
padding: .185rem .25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
:host ::ng-deep .custom-day:active {
color: #6d7183 !important;
background-color: #fff !important;
border-block-color: rgb(2, 117, 216) !important;
}
.bg-light {
background-color: #f8f9fa !important;
}
:host ::ng-deep .hidden {
display: block !important;
}
.ngb-dp-weekday {
color: #17a2b8;
}
.ngb-dp-week-number,
.ngb-dp-weekday {
line-height: 2rem;
text-align: center;
font-style: italic;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.small {
font-size: 80%;
font-weight: 400;
}
.ngb-dp-day {
cursor: pointer !important;
}
.ngb-dp-month {
pointer-events: none;
}
.btn-light:hover {
color: #212529 !important;
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.ngb-dp-header {
border-bottom: 0;
border-radius: .25rem .25rem 0 0;
padding-top: .25rem;
}
.ngb-dp-day,
.ngb-dp-week-number,
.ngb-dp-weekday {
width: 2rem;
height: 2rem;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
.custom-day.focused {
background-color: #e6e6e6;
}
.custom-day.range,
.custom-day:hover {
background-color: rgb(2, 117, 216);
color: white;
}
.custom-day.faded {
background-color: rgba(2, 117, 216, 0.5);
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
border-radius: 0.25rem;
display: inline-block;
width: 2rem;
}
.custom-day:hover, .custom-day.focused {
background-color: #e6e6e6;
}
.weekend {
background-color: #f0ad4e;
border-radius: 1rem;
color: white;
}
.hidden {
display: none;
}
:host ::ng-deep .ft-calendar{
font-family: feather!important;
speak: none;
font-style: normal;
font-weight: 400;
font-size: large;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:host ::ng-deep .input-group-text {
display: flex;
align-items: center;
padding: unset !important;
margin-bottom: unset !important;
font-size: unset !important;
font-weight: unset !important;
line-height: unset !important;
color:unset !important;
text-align: unset !important;
white-space: unset !important;
background-color: unset !important;
border: unset !important;
border-radius: unset !important;
}
.ml-75, .mx-75 {
margin-left: .75rem!important;
}
.mx-50 {
margin-left: .5rem!important;
}
.sidebar {
width : 270px !important;
}
.align-middle {
margin-left: 3px;
}
.mr-50, .mx-50 {
margin-right: .5rem!important;
}
:host ::ng-deep .ft-plus{
margin-right: 3px !important;
}
control:disabled, .form-control[readonly] {
background-color: #ffffff;
margin-left: -12px;
}
:host ::ng-deep .ngb-dp-header {
background-color: var(--light) !important;
}
:host ::ng-deep .ngb-dp-weekdays {
background-color: var(--light);
}
:host ::ng-deep .ngb-dp-month-name {
background-color: var(--light);
}

View File

@@ -0,0 +1,318 @@
<div class="app-content content">
<div class="sidebar-left" id="sidebar-left">
<div class="sidebar">
<div class="todo-sidebar d-flex">
<span class="sidebar-close-icon" (click)="sidebar($event)">
<i class="ficon feather ft-x"></i>
</span>
<!-- todo app menu -->
<div class="todo-app-menu">
<div class="form-group text-center add-task">
<!-- new task button -->
<button type="button" class="btn btn-danger btn-glow add-task-btn btn-block my-1"
(click)="sidebartask($event)" (click)="open()">
<i class="ficon feather ft-plus"></i>
<span>New Task</span>
</button>
</div>
<!-- sidebar list start -->
<div class="sidebar-menu-list" fxFlex="auto" [perfectScrollbar]="config">
<div class="list-group">
<a href="#" class="list-group-item border-0 ">
<span class="fonticon-wrap mr-50">
<i class="ficon feather ft-align-justify"></i>
</span>
<span> All</span>
</a>
</div>
<label class="filter-label mt-2 mb-1 pt-25">Filters</label>
<div class="list-group" *ngFor="let todo of todoFilterList">
<a [routerLink]="" class="list-group-item border-0" (click)="showTodoMenu(todo.Id, todoFilterList)"
[ngClass]="{'active':todo.isSelected === true, '':todo.isSelected === false}">
<span class="fonticon-wrap mr-50">
<i class="{{todo.lableIcon}}"></i>
</span>
<span>{{todo.lableName}}</span>
</a>
</div>
<label class="filter-label mt-2 mb-1 pt-25">Labels</label>
<div class="list-group" *ngFor="let todo of todoLableList">
<a [routerLink]="" class="list-group-item border-0 d-flex align-items-center justify-content-between"
(click)="showTodoMenu(todo.Id, todoLableList)"
[ngClass]="{'active':todo.isSelected === true, '':todo.isSelected === false}">
<span>{{todo.lableName}}</span>
<span class="{{todo.bulletClass}} d-inline-block rounded-circle "></span>
</a>
</div>
</div>
<!-- sidebar list end -->
</div>
</div>
<!-- todo new task sidebar -->
<div class="todo-new-task-sidebar" id="todo-new-task" fxFlex="auto" [perfectScrollbar]="config">
<div class="card shadow-none p-0 m-0">
<div class="card-header border-bottom py-75">
<div class="task-header d-flex justify-content-between align-items-center">
<h5 class="new-task-title mb-0" *ngIf="todoId === null">New Task</h5>
<button class="mark-complete-btn btn btn-primary btn-sm" *ngIf="todoId !== null">
<i class="ficon feather ft-check align-middle"></i>
<span class="mark-complete align-middle">Mark Complete</span>
</button>
<span class="dropdown mr-1">
<i class='ficon feather ft-paperclip cursor-pointer mr-50'></i>
<span ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<a class="dropdown-toggle new-taskDropdown" id="folder" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" id="dropdownMenuButton" dropdown-menu dropdown-menu-right show
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" role="menu" ngbDropdownToggle>
<i class='ficon feather ft-more-vertical'></i>
</a>
<div ngbDropdownMenu="dropdownMenuButton" class="dropdown-menu dropdown-menu-right dropdownnew-task">
<a class="dropdown-item" [routerLink]="">Add to another project</a>
<a class="dropdown-item" [routerLink]="">Create follow up task</a>
<a class="dropdown-item" [routerLink]="">Print</a>
</div>
</span>
</span>
</div>
<button type="button" class="close close-icon" (click)="sidebartask($event)">
<i class="ficon feather ft-x"></i>
</button>
</div>
<!-- form start -->
<form [formGroup]="todo" id="compose-form" class="mt-1" (ngSubmit)="onSubmit($event)">
<div class="card-content">
<div class="card-body py-0 border-bottom">
<div class="form-group">
<!-- text area for task title -->
<textarea formControlName="title" name="title" class="form-control task-title" cols="1" rows="2"
placeholder="Write a Task Name" required>
</textarea>
</div>
<!-- Modal -->
<div class="assigned d-flex justify-content-between">
<div class="form-group d-flex align-items-center mr-1">
<div class="avatar avatar-image">
<img *ngIf="todoId === null"
[src]="selectedUserImage?selectedUserImage:'../../../../assets/images/portrait/small/default.png'"
class="avatar-user-image " id="avatar-user" alt="#" width="38" height="38">
<img *ngIf="todoId !== null"
[src]="selectedAssignee?selectedAssignee.image:'../../../../assets/images/portrait/small/default.png'"
class="avatar-user-image " id="avatar-user" alt="#" width="38" height="38">
<div class="avatar-content d-none" id="avatar-content">
<i class='ficon feather ft-user font-medium-4'></i>
</div>
</div>
<!-- select2 for user name -->
<div class="select-box mr-1">
<ng-select [items]="contact" bindLabel="name" formControlName="assignedTo"
[(ngModel)]="selectedAssignee" (change)="getSelectedTODOTypeImage($event)">
</ng-select>
</div>
</div>
<div class="form-group d-flex align-items-center position-relative">
<!-- date picker -->
<div class="input-group">
<div class="input-group-text mr-50" (click)="d2.toggle()">
<i class="ficon feather ft-calendar" style="cursor: pointer;"></i>
</div>
<input class="form-control" placeholder="yyyy-mm-dd" formControlName="assignDate"
name="assignDate" ngbDatepicker #d2="ngbDatepicker">
</div>
</div>
</div>
</div>
<div class="card-body border-bottom task-description">
<!-- Quill editor for task description -->
<div class="snow-container border rounded p-50">
<div class="compose-editor mx-75 ql-container ql-snow"></div>
<quill-editor [styles]="{height: '90px'}" [modules]="newTodoquillConfig" (onFocus)="focus()"
(onBlur)="blur()" formControlName="description"></quill-editor>
</div>
<div class="tag d-flex justify-content-between align-items-center pt-1">
<div class="flex-grow-1 d-flex align-items-center">
<i class="ficon feather ft-tag align-middle mr-25"></i>
<select class="custom-select form-control" id="todo-select" placeholder="Select Tag"
formControlName="type" name="type" (change)="getSelectedTODOTypeText($event)">
<option value="" selected>Select Tag</option>
<option value="warning">Project</option>
<option value="secondary">Product</option>
<option value="primary">Bug</option>
<option value="success">API</option>
<option value="danger">UI</option>
</select>
<!-- <ng-select [items]="multipleSelectArray" [multiple]="true" bindLabel="item_text"
formControlName="type" name="type" (change)="getSelectedTODOTypeText($event)">
</ng-select> -->
</div>
<div class="ml-25">
<i class="ficon feather ft-plus-circle cursor-pointer add-tags"></i>
</div>
</div>
</div>
<div class="card-body pb-1">
<div class="d-flex align-items-center mb-1">
<div class="avatar mr-75">
<img *ngIf="!selectedItem && loggedInUser.photoURL" src="{{currentUserImage}}" width="38"
height="38">
<img *ngIf="selectedItem && selectedItem.creator && selectedItem.creator.image"
src="{{selectedItem.creator.image}}" width="38" height="38">
<img *ngIf="(!selectedItem && !loggedInUser.photoURL) || (selectedItem && !selectedItem.creator)"
src="../../../../assets/images/portrait/small/avatar-s-19.png" width="38" height="38">
</div>
<div *ngIf="!selectedItem && currentUserName" class="avatar-content">
{{currentUserName}} creating this task
</div>
<div *ngIf="selectedItem && selectedItem.creator" class="avatar-content">
{{selectedItem.creator.name}} created this task
</div>
<div *ngIf="!currentUserName && (!selectedItem || !selectedItem.creator)" class="avatar-content">
John Doe creating this task
</div>
</div>
<h4 style="font-family:bold;">Comments:</h4>
<div class="d-flex align-items-center mb-1" *ngFor="let todo of todoComments">
<div class="avatar avatar-image">
<img [src]="todo.userid?todo.userid:'../../../../assets/images/portrait/small/default.png'"
class="avatar-user-image " id="avatar-user" alt="#" width="38" height="38">
</div>
<div class="avatar-content">
{{todo.comment}}
</div>
<small class="ml-75 text-muted">{{todo.created_at_date | date: 'dd/MMM HH:mm'}}</small>
</div>
<!-- quill editor for comment -->
<div class="snow-container border rounded p-50 ">
<div class="comment-editor mx-75 ql-container ql-snow"></div>
<div class="editor">
<quill-editor [styles]="{height: '90px'}" #quill="ngModel" [modules]="TodoquillConfig"
(onFocus)="focus()" (onBlur)="blur()" [(ngModel)]="todoCommentsField"
[ngModelOptions]="{standalone: true}"></quill-editor>
</div>
<div class="d-flex justify-content-end">
<button type="button" *ngIf="todoId !== null" class="btn btn-sm btn-primary comment-btn"
(click)="submitComment(selectedItem.id, todoCommentsField)">
<span>Comment</span>
</button>
</div>
</div>
<div class="mt-1 d-flex justify-content-end">
<button type="submit" class="btn btn-danger add-todo" *ngIf="todoId === null">Add Task</button>
<button type="button" class="btn btn-danger update-todo"
(click)="onUpdate(selectedItem.id, todo.value, $event)" *ngIf="todoId !== null">Save
Changes</button>
</div>
</div>
</div>
</form>
<!-- form start end-->
</div>
</div>
</div>
</div>
<div class="content-right">
<div class="content-overlay"></div>
<div class="content-wrapper">
<div class="content-header row">
</div>
<div class="content-body">
<div class="app-content-overlay" id="content-overlay" (click)="sidebartask($event)"></div>
<div class="todo-app-area">
<div class="todo-app-list-wrapper">
<div class="todo-app-list">
<div class="todo-fixed-search d-flex justify-content-between align-items-center">
<div class="sidebar-toggle d-block d-lg-none" (click)="sidebar($event)">
<i class="ficon feather ft-align-justify"></i>
</div>
<fieldset class="form-group position-relative has-icon-left m-0 flex-grow-1 pl-2">
<input type="text" class="form-control todo-search" id="todo-search" (keyup)="search($event)"
placeholder="Search Task">
<div class="form-control-position">
<i class="ficon feather ft-search"></i>
</div>
</fieldset>
<div class="todo-sort dropdown mr-1">
<div ngbDropdown [open]="false" [autoClose]="true" class="d-inline-block">
<button type="button" class="btn dropdown-toggle sorting" id="folder" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" id="dropdownMenuButton" dropdown-menu
dropdown-menu-right show data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
role="menu" ngbDropdownToggle>
<i class="ficon feather ft-filter"></i>
<span>Sort</span>
</button>
<div ngbDropdownMenu="dropdownMenuButton" class="dropdown-menu dropdown-menu-right dropdown-sort">
<a class="dropdown-item" [routerLink]="">Ascending</a>
<a class="dropdown-item" [routerLink]="">Descending</a>
</div>
</div>
</div>
</div>
<div class="todo-task-list list-group" [perfectScrollbar]="config">
<!-- task list start -->
<ul class="todo-task-list-wrapper list-unstyled" id="todo-task-list-drag"
*ngFor="let item of todos let i = index">
<li class="todo-item " [checked]="item.completed" [ngClass]="{'completed': item.completed}"
(click)="sidebartask($event)" (click)="editModal(item,$event)">
<div
class="todo-title-wrapper d-flex justify-content-sm-between justify-content-end align-items-center">
<div class="todo-title-area d-flex">
<i class='ficon feather ft-more-vertical handle'></i>
<div class="custom-control custom-checkbox" (click)="$event.stopPropagation();">
<input type="checkbox" [checked]="item.completed" (change)="completeTODO(item, $event)"
class="custom-control-input" id="checkbox{{i}}">
<label class="custom-control-label" for="checkbox{{i}}"></label>
</div>
<p class="todo-title mx-50 m-0 truncate">{{item.title}}</p>
</div>
<div class="todo-item-action d-flex align-items-center">
<div class="todo-badge-wrapper d-flex">
<span class="badge badge-{{item.type}} badge-pill">{{item.badge}}</span>
</div>
<div class="avatar ml-1" *ngIf="item.assignedTo">
<img src="{{item.assignedTo.image}}"
default="../../../../assets/images/portrait/small/default.png" alt="avatar" height="30"
width="30">
</div>
<a class='todo-item-favorite ml-75' [attr.id]="'todo-icon' + item.id"
(click)="todoFavorite($event,item.id)" (click)="$event.stopPropagation();"><i
class="ficon feather ft-star"></i></a>
<a class='todo-item-delete ml-75' (click)="onDelete(item.id)"
(click)="$event.stopPropagation();"><i class="ficon feather ft-trash-2"></i></a>
</div>
</div>
</li>
</ul>
<!-- task list end -->
<div class="no-results">
<h5>No Items Found</h5>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- END: Content-->

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { TodoappComponent } from './todoapp.component';
describe('TodoappComponent', () => {
let component: TodoappComponent;
let fixture: ComponentFixture<TodoappComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ TodoappComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TodoappComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,765 @@
import { Component, OnInit, Renderer2, ViewChild } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { NgbDateStruct, NgbTimeStruct, NgbDate } from '@ng-bootstrap/ng-bootstrap';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { TodoService } from '../../../_api/todo/todo.service';
import { AngularFirestore } from '@angular/fire/compat/firestore';
import { NgForm, FormGroup, FormControl, FormBuilder, Validators, FormArray } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { forkJoin } from 'rxjs';
import { QuillEditorComponent } from 'ngx-quill';
import { UserService } from '../../../_api/user/user.service';
import { QuillInitializeServiceService } from '../../../_services/quill-initialize-service.service';
import 'quill-mention';
declare let require: any;
class TodoFilter {
constructor(
public Id: string,
public lableName: string,
public isSelected: boolean
) { }
}
class Comment {
public userid: string;
public comment: string;
public created_at_date: number;
}
@Component({
selector: 'app-todoapp',
templateUrl: './todoapp.component.html',
styleUrls: ['./todoapp.component.css']
})
export class TodoappComponent implements OnInit {
initialData = require('../../../../assets/data/application/todo.json');
LableData = require('../../../../assets/data/application/todo_lable.json');
private demoUserEmail = 'john@pixinvent.com';
todo: FormGroup;
submitted = false;
commentList: Comment[] = [];
date: any;
todoDisplayList: any[] = [];
todoLableList: any[] = [];
todoFilterList: TodoFilter[] = [];
todoId: any;
todoType: 'secondary';
assignedTo: any;
selectedUserId = '';
selectedUserImage: any;
selectedAssignee: any;
assignDate: any;
todoComments: any[] = [];
todoCommentsField: string;
selectedTodoTypeText = '';
selectedTodoTypeValue = '';
isShown = true;
selectedItem: any;
completedTodo = false;
TodoId: any;
id: any;
temp: any;
todosAssigned: any;
loader = true;
blured = false;
focused = false;
hide = false;
hasFocus = false;
d2: any;
model: NgbDateStruct;
createdDate: any;
contact: any;
commentArray: any[] = [];
public isShowCropper = false;
loggedInUser = JSON.parse(localStorage.getItem('currentUser'));
demoUserImage = '../../../assets/images/portrait/small/avatar-s-19.png';
currentUserImage = this.loggedInUser.photoURL;
currentUserName = this.loggedInUser.name;
public config: PerfectScrollbarConfigInterface = {};
@ViewChild(PerfectScrollbarComponent) componentRef?: PerfectScrollbarComponent;
@ViewChild(PerfectScrollbarDirective, { static: true }) directiveRef?: PerfectScrollbarDirective;
@ViewChild('editor', { static: true }) editor: QuillEditorComponent;
todos: any;
atValues = [
{ id: 1, value: 'Fredrik Sundqvist', link: 'https://google.com' },
{ id: 2, value: 'Patrik Sjölin' }
];
hashValues = [
{ id: 3, value: 'Fredrik Sundqvist 2' },
{ id: 4, value: 'Patrik Sjölin 2' }
];
newTodoquillConfig = {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike', 'image'],
]
},
autoLink: true,
mention: {
allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
mentionDenotationChars: ['@', '#'],
source: (searchTerm, renderList, mentionChar) => {
let values;
if (mentionChar === '@') {
values = this.atValues;
} else {
values = this.hashValues;
}
if (searchTerm.length === 0) {
renderList(values, searchTerm);
} else {
const matches = [];
for (let i = 0; i < values.length; i++) {
if (values[i].value.toLowerCase().indexOf(searchTerm.toLowerCase())) {
matches.push(values[i]);
renderList(matches, searchTerm);
}
}
}
},
},
keyboard: {
bindings: {
enter: {
key: 13,
handler: (range, context) => {
console.log('enter');
return true;
}
}
}
}
};
TodoquillConfig = {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'],
]
},
autoLink: true,
// mention: {
// allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
// mentionDenotationChars: ['@', '#'],
// source: (searchTerm: string, renderList: (arg0: any[], arg1: any) => void, mentionChar: string) => {
// let values;
// if (mentionChar === '@') {
// values = this.atValues;
// } else {
// values = this.hashValues;
// }
// if (searchTerm.length === 0) {
// renderList(values, searchTerm);
// } else {
// const matches = [];
// for (let i = 0; i < values.length; i++) {
// if (values[i].value.toLowerCase().indexOf(searchTerm.toLowerCase())) {
// matches.push(values[i]);
// renderList(matches, searchTerm);
// }
// }
// }
// },
// },
keyboard: {
bindings: {
enter: {
key: 13,
handler: (range, context) => {
console.log('enter');
return true;
}
}
}
}
};
get TodoFormGroup() {
return this.todo.get('todoComments') as FormArray;
}
/**
* Constructor
*
* @param NgbModal modal
* @param FormBuilder formBuilder
* @param Renderer2 _renderer
* @param TodoService firebaseService
* @param AngularFirestore firestore
* @param ToastrService toastr
*/
constructor(private modal: NgbModal,
private formBuilder: FormBuilder,
private _renderer: Renderer2,
private firebaseService: TodoService,
private firestore: AngularFirestore,
private toastr: ToastrService,
private modalService: NgbModal,
private userService: UserService,
private QuillInitializeServiceServicec: QuillInitializeServiceService) {
}
/**
* OnInit
*/
ngOnInit() {
this.loader = true;
this.resetForm();
this.todos = [];
this.currentUserImage = this.loggedInUser.photoURL;
this.currentUserName = this.loggedInUser.displayName;
this.todoLableList = this.LableData.todoLableList;
this.todoFilterList = this.LableData.todoFilterList;
this.userService.getUsers().subscribe(users => {
let contactList = users.map(item => {
return {
...item.payload.doc.data() as {},
id: item.payload.doc['id']
};
});
contactList = contactList.filter(element => {
return this.loggedInUser.uid !== element['uid'] || this.loggedInUser.uid === element['uid'];
});
this.contact = contactList;
});
if (this.loggedInUser.email === this.demoUserEmail) {
// To load default todo for demo account
this.getDemoAccTodos().then(todos => {
if (todos.length === 0) {
this.addDemoAccountTodos().then(result => {
if (result) {
this.loadTodos();
}
});
} else {
this.loadTodos();
}
});
} else {
this.loadTodos();
}
this.todo = this.formBuilder.group({
title: new FormControl(['', Validators.required]),
type: new FormControl(''),
description: new FormControl(''),
createdDate: new FormControl(''),
assignedTo: new FormControl(''),
assignDate: new FormControl(['', Validators.nullValidator]),
todoComments: this.formBuilder.array([
this.formBuilder.group({
comment: '',
created_at_date: '',
userid: ''
})]),
});
}
// Load TODO of user
loadTodos() {
this.firebaseService.getTODOS(this.loggedInUser.uid).subscribe(todos => {
let todo = [];
todo = todos.map(item => {
return {
...item.payload.doc.data() as {},
id: item.payload.doc['id']
};
});
this.firebaseService.getAssignedTODOS(this.loggedInUser.uid).subscribe(assignedTodos => {
let todoAssigned = [];
todoAssigned = assignedTodos.map(item => {
const toDoObj = item.payload.doc.data() as {};
if (toDoObj && toDoObj['uid'] !== this.loggedInUser.uid) {
return {
...item.payload.doc.data() as {},
id: item.payload.doc['id']
};
}
});
todoAssigned = todoAssigned.filter(item => {
return item && item['uid'];
});
this.todos = todo.concat(todoAssigned);
this.temp = this.todos;
this.loader = false;
});
});
}
/**
* Get the add todo Form value
*/
get add() {
return this.todo.controls;
}
/**
* Get the update todo Form value
*/
get update() {
return this.todo.controls;
}
/**
* Reset form value
*
* @param form Form fields with previous value
*/
resetForm(form?: NgForm) {
if (form != null) {
form.resetForm();
}
}
addDemoAccountTodos() {
const dataPromise = new Promise((resolve, reject) => {
const promises = [];
// Add default TODO for demo account
for (let i = 0; i < this.initialData.length; i++) {
const promise = this.firestore.collection('todo').add({
title: this.initialData[i].title,
description: this.initialData[i].description,
badge: this.initialData[i].badge,
type: this.initialData[i].type,
completed: this.initialData[i].completed,
isDeleted: this.initialData[i].isDeleted,
createdDate: new Date(),
uid: this.loggedInUser.uid,
assignedTo: this.initialData[i].assignedTo,
assignDate: this.initialData[i].assignDate,
todoComments: this.initialData[i].todoComments
});
promises.push(promise);
}
forkJoin(promises).subscribe(results => {
resolve(true);
});
});
return dataPromise;
}
/**
* Initial todo display
*/
getDemoAccTodos(): Promise<any> {
const dataPromise = new Promise((resolve, reject) => {
this.firebaseService.getTODOS(this.loggedInUser.uid).subscribe(todos => {
resolve(todos);
});
});
return dataPromise;
}
/**
* Add new todo
*
* Update todo
*
* @param value Form field values
* @param id todo id
*/
onSubmit(event) {
this.submitted = true;
let temp: any[] = [];
this.todo.controls.assignDate.clearValidators();
this.todo.get('assignDate').clearValidators();
this.todo.get('assignDate').updateValueAndValidity();
if (this.assignedTo) {
this.selectedAssignee = this.assignedTo;
}
if (this.todoCommentsField == null) {
temp = null;
} else if (this.todoCommentsField !== null) {
this.todoCommentsField = this.todoCommentsField.replace(/(<p>|<\/p>)/g, '');
temp = [{
comment: this.todoCommentsField,
created_at_date: Date.now(),
userid: this.currentUserImage
}];
}
if (this.todo.invalid) {
return;
} else if (this.todo.valid) {
this.todo.setValue({
uid: this.loggedInUser.uid,
title: this.todo.value.title,
description: this.todo.value.description,
type: this.selectedTodoTypeValue,
completed: false,
isDeleted: false,
createdDate: new Date(),
badge: this.selectedTodoTypeText,
assignedTo: this.todo.value.assignedTo,
assignDate: this.todo.value.assignDate,
todoComments: temp,
});
this.firebaseService.createTodo(this.todo.value).subscribe(res => {
this.toastr.success('Added', 'Todo Created Successfully.', { timeOut: 500, closeButton: true });
}, (err) => {
this.toastr.error('Error', 'Add Todo Error.', { timeOut: 500, closeButton: true });
});
const toggleIcon = document.getElementById('todo-new-task');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'mt-1 ng-dirty ng-valid ng-touched' || 'mt-1 ng-dirty ng-touched ng-valid') {
this._renderer.removeClass(toggleIcon, 'show');
this._renderer.removeClass(toggle, 'show');
}
}
}
/**
* Submit Comment
*/
submitComment(id, comments) {
if (comments != null) {
comments = comments.replace(/(<p>|<\/p>)/g, '');
}
if (!comments) {
comments = null;
}
if (this.loggedInUser.email === this.demoUserEmail) {
this.currentUserImage = this.demoUserImage;
}
if (!this.todoComments || this.todoComments.length === 0) {
this.todoComments = [];
}
// TODO Add todo comment from parameters to todoComments first
if (comments != null) {
this.todoComments.push({
comment: comments,
created_at_date: Date.now(),
userid: this.currentUserImage
});
}
if (this.todoId !== null) {
this.todo.setValue({
uid: this.todo.value.uid,
title: this.todo.value.title,
description: this.todo.value.description,
type: this.todo.value.type,
completed: false,
isDeleted: false,
createdDate: this.todo.value.createdDate,
badge: this.todo.value.badge,
assignedTo: this.todo.value.assignedTo,
assignDate: this.todo.value.assignDate,
todoComments: this.todoComments,
});
this.todoCommentsField = null;
comments = null;
this.firebaseService.updateTODO(id, this.todo.value)
.subscribe(res => {
this.toastr.success('Update', 'Todo Comment Updated Successfully.', { timeOut: 500, closeButton: true });
}, (err) => {
this.toastr.error('Error', 'Todo Comment Updated Error', { timeOut: 500, closeButton: true });
});
console.log(this.todo);
}
}
/**
* Update todo
*
* @param value Form field values
* @param id todo id
*/
onUpdate(id, value, event) {
this.submitted = true;
this.todo.controls.assignDate.clearValidators();
this.todo.get('assignDate').clearValidators();
this.todo.get('assignDate').updateValueAndValidity();
if (this.todo.invalid) {
return;
} else if (this.todo.valid) {
this.todo.setValue({
uid: this.loggedInUser.uid,
title: value.title,
description: value.description,
type: this.selectedTodoTypeValue,
completed: value.completed,
isDeleted: false,
createdDate: this.selectedItem.createdDate,
badge: this.selectedTodoTypeText,
assignedTo: value.assignedTo,
assignDate: value.assignDate,
todoComments: this.todoComments
});
this.firebaseService.updateTODO(id, this.todo.value)
.subscribe(res => {
this.toastr.success('Update', 'Todo Updated Successfully.', { timeOut: 500, closeButton: true });
}, (err) => {
this.toastr.error('Error', 'Todo Update Error!', { timeOut: 500, closeButton: true });
});
const toggleIcon = document.getElementById('todo-new-task');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'btn btn-danger update-todo ng-star-inserted' || 'btn btn-danger update-todo') {
this._renderer.removeClass(toggleIcon, 'show');
this._renderer.removeClass(toggle, 'show');
}
}
}
/**
* Delete todo
*
* @param id todo id
*/
onDelete(id: string) {
this.firebaseService.deleteTodo(id)
.then(res => {
this.toastr.success('Deleted', 'Todo Deleted Successfully!', { timeOut: 500, closeButton: true });
}, (err) => {
this.toastr.error('Error', 'Todo Delete Error!', { timeOut: 500, closeButton: true });
}
);
}
/**
* filter task
*/
search(term) {
const searchTerm = term.currentTarget.value;
if (searchTerm !== '') {
this.todos = this.todos.filter(result => {
if (result && searchTerm) {
if (result.title.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 ||
result.description.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1) {
return true;
}
return false;
}
});
} else {
this.todos = this.temp;
}
}
/**
* Open add todo modal
*
* @param content Add todo modal id
*/
open() {
this.selectedItem = null;
this.selectedTodoTypeValue = '';
this.selectedTodoTypeText = '';
this.todoComments = [];
this.todoCommentsField = null;
this.resetForm();
this.selectedUserImage = '';
this.todoId = null;
this.todo = this.formBuilder.group({
uid: this.loggedInUser.uid,
title: '',
description: '',
type: '',
completed: false,
isDeleted: false,
createdDate: new Date(),
badge: '',
assignedTo: '',
assignDate: '',
todoComments: '',
});
}
/**
* Open edit todo modal
*
* @param editContent edit todo modal id
* @param item edit todo modal values
*/
editModal(value, event) {
this.selectedItem = value;
if (this.contact.length > 0) {
for (let index = 0; index < this.contact.length; index++) {
const element = this.contact[index];
if (element.uid === this.selectedItem.uid) {
this.selectedItem['creator'] = element;
break;
}
}
}
this.todoId = this.loggedInUser.uid;
this.TodoId = this.loggedInUser.uid;
this.todo = this.formBuilder.group({
uid: this.loggedInUser.uid,
title: this.selectedItem.title,
description: this.selectedItem.description,
type: this.selectedItem.type,
completed: value.completed,
isDeleted: false,
badge: this.selectedItem.badge,
createdDate: value.createdDate,
assignDate: value.assignDate,
assignedTo: value.assignedTo,
todoComments: value.todoComments,
});
this.todoCommentsField = value.todoComments;
if (value.assignedTo) {
this.selectedAssignee = value.assignedTo;
}
// this.firebaseService.getTODOS(this.loggedInUser.uid).subscribe(todos => {
// this.todos = todos.map(item => {
// return {
// ...item.payload.doc.data() as {},
// id: item.payload.doc.id
// };
// });
// });
for (let i = 0; i < this.todos.length; i++) {
if (value.id === this.todos[i].id) {
this.todoComments = this.todos[i].todoComments;
}
}
const toggleIcon = document.getElementById('todo-new-task');
const toggle = document.getElementById('content-overlay');
const userImage = document.getElementById('avatar-user');
const contentImage = document.getElementById('avatar-content');
if (event.currentTarget.className === 'todo-item ng-star-inserted') {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
} else if (this.todoId !== null) {
this._renderer.addClass(contentImage, 'd-none');
this._renderer.removeClass(userImage, 'd-none');
}
}
/**
* Get text and value
*
* @param event Dropdown event
*/
getSelectedTODOTypeText(event: Event) {
this.selectedTodoTypeText = event.target['options'][event.target['options'].selectedIndex].text;
this.selectedTodoTypeValue = event.target['options'][event.target['options'].selectedIndex].value;
}
getSelectedTODOTypeImage(event) {
this.selectedUserId = event.id;
this.selectImage();
}
selectImage() {
this.contact.forEach(element => {
if (this.selectedUserId === element.id) {
this.selectedUserImage = element.image;
}
});
}
completeTODO(data) {
if (data.completed) {
data.completed = false;
} else {
data.completed = true;
}
this.todo = this.formBuilder.group({
uid: this.loggedInUser.uid,
title: data.title,
description: data.description,
type: data.type,
completed: data.completed,
isDeleted: data.isDeleted,
createdDate: data.createdDate,
badge: data.badge,
assignDate: data.assignDate,
assignedTo: data.assignedTo,
// todoImage: data.todoImage,
todoComments: data.todoComments,
});
this.firebaseService.updateTODO(data.id, this.todo.value)
.subscribe(res => {
if (this.todo.value.completed === true) {
this.toastr.success('Success', 'Todo Completed.', { timeOut: 500, closeButton: true });
} else {
this.toastr.success('Success', 'Todo Updated.', { timeOut: 500, closeButton: true });
}
}, (err) => {
this.toastr.error('Error', 'Something went wrong!', { timeOut: 500, closeButton: true });
});
}
/**
* Overlay add/remove fuction in responsive
*
* @param event Overlay click event
*/
sidebartask(event) {
const toggleIcon = document.getElementById('todo-new-task');
const toggle = document.getElementById('content-overlay');
const toggleSidebarIcon = document.getElementById('sidebar-left');
const userImage = document.getElementById('avatar-user');
const contentImage = document.getElementById('avatar-content');
if (event.currentTarget.className === 'btn btn-danger btn-glow add-task-btn btn-block my-1') {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
this._renderer.removeClass(toggleSidebarIcon, 'show');
} else if (event.currentTarget.className.indexOf('todo-item') !== -1) {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
} else if (event.currentTarget.className === 'close close-icon' || 'app-content-overlay show') {
this._renderer.removeClass(toggleIcon, 'show');
this._renderer.removeClass(toggle, 'show');
this._renderer.removeClass(toggleSidebarIcon, 'show');
}
}
sidebartaskedit(event) {
const toggleIcon = document.getElementById('todo-new-task');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'btn btn-danger btn-glow add-task-btn btn-block my-1') {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
}
}
showTodoMenu(Id, todo) {
for (let j = 0; j < todo.length; j++) {
for (let i = 0; i < this.todoLableList.length; i++) {
for (let k = 0; k < this.todoFilterList.length; k++) {
if (todo[j].lableName === this.todoLableList[i].lableName) {
if (Id !== this.todoLableList[i].Id) {
this.todoLableList[i].isSelected = false;
}
if (Id === this.todoLableList[i].Id) {
this.todoLableList[i].isSelected = true;
this.todoFilterList[k].isSelected = false;
}
} else if (todo[j].lableName === this.todoFilterList[k].lableName) {
if (Id !== this.todoFilterList[k].Id) {
this.todoFilterList[k].isSelected = false;
}
if (Id === this.todoFilterList[k].Id) {
this.todoFilterList[k].isSelected = true;
this.todoLableList[i].isSelected = false;
}
}
}
}
}
}
sidebar(event) {
const toggleIcon = document.getElementById('sidebar-left');
const toggle = document.getElementById('content-overlay');
if (event.currentTarget.className === 'sidebar-toggle d-block d-lg-none') {
this._renderer.addClass(toggleIcon, 'show');
this._renderer.addClass(toggle, 'show');
} else if (event.currentTarget.className === 'sidebar-close-icon') {
this._renderer.removeClass(toggleIcon, 'show');
this._renderer.removeClass(toggle, 'show');
}
}
focus() {
this.focused = true;
this.blured = false;
}
blur() {
this.focused = false;
this.blured = true;
}
todoFavorite(event, todoId) {
const todoIcon = document.getElementById('todo-icon' + todoId);
if (event.currentTarget.className === 'todo-item-favorite ml-75') {
this._renderer.addClass(todoIcon, 'warning');
} else if (event.currentTarget.className === 'todo-item-favorite ml-75 warning') {
this._renderer.removeClass(todoIcon, 'warning');
}
}
}