diff --git a/package-lock.json b/package-lock.json index 6a710c7..03d47c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7477,6 +7477,11 @@ "deep-freeze-strict": "^1.1.1" } }, + "ngx-color-picker": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-7.5.0.tgz", + "integrity": "sha512-bTW16A3IitWUNGc3kZtE+N1r3FpmUj7uJTG80eym2nQLvsROkMntalKJDo1SQVQhh9Nh4N9C88UezdMg8fjR5Q==" + }, "ngx-cookie-service": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-2.2.0.tgz", diff --git a/package.json b/package.json index 3b71081..bc16012 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "hammerjs": "^2.0.8", "lodash": "^4.17.15", "moment": "^2.24.0", + "ngx-color-picker": "7.5.0", "ngx-cookie-service": "^2.2.0", "perfect-scrollbar": "^1.4.0", "prismjs": "^1.17.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0b76505..641db6d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -29,6 +29,7 @@ import { UserModule } from 'src/modules/user/user.module'; import { GameModule } from 'src/modules/game/game.module'; import { SitesModule } from 'src/modules/sites/sites.module'; import { SpecialModule } from 'src/modules/special/special.module'; +import { InfosModule } from 'src/modules/infos/infos.module'; import { from } from 'rxjs'; @NgModule({ imports: [ @@ -62,7 +63,8 @@ import { from } from 'rxjs'; UserModule.forRoot(), GameModule.forRoot(), SitesModule.forRoot(), - SpecialModule.forRoot() + SpecialModule.forRoot(), + InfosModule.forRoot() ], declarations: [AppComponent], providers: [], diff --git a/src/app/app.theme.scss b/src/app/app.theme.scss index b88693b..a62158d 100644 --- a/src/app/app.theme.scss +++ b/src/app/app.theme.scss @@ -85,6 +85,7 @@ $typography: mat-typography-config( @import 'src/app/pages/accounts/component/authentication/authentication.theme'; @import 'src/app/pages/specials/special/component/alert-config.theme'; +@import 'src/app/pages/statics/calendar.theme'; // Define a mixin for easier access @mixin components-theme($theme) { @@ -95,6 +96,7 @@ $typography: mat-typography-config( // UI @include ui-cards-theme($theme); + @include calendar-theme($theme); } // ----------------------------------------------------------------------------------------------------- diff --git a/src/app/pages/infos/info/component/popup-set-detail.component.html b/src/app/pages/infos/info/component/popup-set-detail.component.html new file mode 100644 index 0000000..e4f07c9 --- /dev/null +++ b/src/app/pages/infos/info/component/popup-set-detail.component.html @@ -0,0 +1,244 @@ + diff --git a/src/app/pages/infos/info/component/popup-set-detail.component.scss b/src/app/pages/infos/info/component/popup-set-detail.component.scss new file mode 100644 index 0000000..c47e481 --- /dev/null +++ b/src/app/pages/infos/info/component/popup-set-detail.component.scss @@ -0,0 +1,397 @@ +@import "src/@fuse/scss/fuse"; + +#popup-set-detail { + + .header { + + .subtitle { + margin: 6px 0 0 0; + } + } + + .content { + + .mat-tab-group, + .mat-tab-body-wrapper, + .tab-content { + flex: 1 1 auto; + max-width: 100%; + } + + .tab-content { + + &.products { + + .product-row { + cursor: pointer; + } + } + + &.invoice { + + #invoice { + + &.compact { + padding: 0; + overflow: auto; + -webkit-overflow-scrolling: touch; + + .invoice-container { + padding: 16px; + + .card { + width: 1020px; + min-width: 1020px; + max-width: 1020px; + padding: 64px 88px; + overflow: hidden; + background: #FFFFFF; + color: rgba(0, 0, 0, 0.87); + @include mat-elevation(7); + + .header { + + .invoice-date { + font-size: 14px; + color: rgba(0, 0, 0, 0.54); + margin-bottom: 32px; + } + + .client { + + .invoice-number { + font-size: 18px; + padding-bottom: 2px; + + .title { + color: rgba(0, 0, 0, 0.54); + } + + .number { + padding-left: 6px; + } + } + + .due-date { + font-size: 18px; + padding-bottom: 16px; + + .title { + color: rgba(0, 0, 0, 0.54); + } + + .date { + padding-left: 6px; + } + } + + .info { + color: rgba(0, 0, 0, 0.54); + line-height: 22px; + } + } + + .issuer { + margin-right: -88px; + padding-right: 66px; + + .logo { + width: 96px; + padding: 0 8px; + border-right: 1px solid rgba(255, 255, 255, 0.7); + } + + .info { + padding: 16px; + } + } + } + + .content { + + .invoice-table { + margin-top: 64px; + font-size: 15px; + + thead { + + tr { + + th { + + &:first-child { + padding-left: 8px; + } + + &:last-child { + padding-right: 8px; + } + } + } + } + + tbody { + + tr { + + td { + border-color: rgba(0, 0, 0, 0.12); + + &:first-child { + padding-left: 8px; + } + + &:last-child { + padding-right: 8px; + } + } + } + } + + .title { + font-size: 16px; + } + + .detail { + margin-top: 8px; + font-size: 12px; + color: rgba(0, 0, 0, 0.54); + max-width: 360px; + } + } + + .invoice-table-footer { + margin: 32px 0 72px 0; + + tr { + + td { + text-align: right; + font-size: 16px; + font-weight: 600; + color: rgba(0, 0, 0, 0.54); + border-bottom: none; + padding: 4px 8px; + + &:first-child { + text-align: left; + } + } + + &.discount { + + td { + padding-bottom: 32px; + } + } + + &.total { + + td { + padding: 24px 8px; + border-top: 1px solid rgba(0, 0, 0, 0.12); + font-size: 35px; + font-weight: 300; + color: rgba(0, 0, 0, 1); + } + } + } + } + } + + .footer { + + .note { + font-size: 15px; + font-weight: 600; + margin-bottom: 24px; + } + + // IE10 fix + .logo, .small-note { + -ms-flex: 0 1 auto; + } + + .logo { + width: 32px; + min-width: 32px; + margin-right: 24px; + } + + .small-note { + font-size: 12px; + font-weight: 600; + color: rgba(0, 0, 0, 0.54); + line-height: 18px; + } + } + } + } + } + } + + /* PRINT STYLES */ + @media print { + + /* Invoice Specific Styles */ + #invoice { + + &.compact { + + .invoice-container { + padding: 0; + + .card { + width: 100%; + min-width: 0; + background: none; + padding: 0; + box-shadow: none; + + .header { + + .invoice-date { + margin-bottom: 16pt; + } + + .issuer { + padding-right: 0; + margin-right: 0; + } + } + + .content { + + .invoice-table { + margin-top: 16pt; + + thead { + + tr { + + th { + font-size: 10pt; + max-width: 60pt; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + } + } + + tbody { + + tr { + + td { + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + } + } + + .title { + font-size: 10pt; + } + + .detail { + margin-top: 4pt; + font-size: 9pt; + max-width: none; + } + } + + .invoice-table-footer { + margin: 16pt 0; + + tr { + + td { + font-size: 13pt; + padding: 4pt 4pt; + + &:first-child { + text-align: left; + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + + &.discount { + + td { + padding-bottom: 16pt; + } + } + + &.total { + + td { + padding: 16pt 4pt 0 4pt; + font-size: 16pt; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + } + } + } + } + + .footer { + + .note { + font-size: 10pt; + margin-bottom: 8pt; + } + + .logo { + margin-right: 8pt; + } + + .small-note { + font-size: 8pt; + line-height: normal; + } + } + } + } + } + } + } + } + } + + .mat-tab-body-content { + display: flex; + } + + .mat-tab-label { + height: 64px; + } + + table { + table-layout: fixed; + } + } + +} diff --git a/src/app/pages/infos/info/component/popup-set-detail.component.ts b/src/app/pages/infos/info/component/popup-set-detail.component.ts new file mode 100644 index 0000000..2649145 --- /dev/null +++ b/src/app/pages/infos/info/component/popup-set-detail.component.ts @@ -0,0 +1,41 @@ +import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { fuseAnimations } from 'src/@fuse/animations'; +@Component({ + selector: 'app-popup-set-detail', + templateUrl: './popup-set-detail.component.html', + styleUrls: ['./popup-set-detail.component.scss'], + encapsulation: ViewEncapsulation.None, + animations: fuseAnimations +}) +export class PopupSetDetailComponent implements OnInit, OnDestroy { + private unsubscribeAll: Subject; + + constructor(private fb: FormBuilder) { + // Set the private defaults + this.unsubscribeAll = new Subject(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + // Subscribe to update order on changes + } + + /** + * On destroy + */ + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this.unsubscribeAll.next(); + this.unsubscribeAll.complete(); + } +} diff --git a/src/app/pages/infos/info/component/popup-set.component.html b/src/app/pages/infos/info/component/popup-set.component.html index 2f10e69..22949f8 100644 --- a/src/app/pages/infos/info/component/popup-set.component.html +++ b/src/app/pages/infos/info/component/popup-set.component.html @@ -1 +1,164 @@ -

popup-set works!

+ diff --git a/src/app/pages/infos/info/component/popup-set.component.scss b/src/app/pages/infos/info/component/popup-set.component.scss index e69de29..62ddb45 100644 --- a/src/app/pages/infos/info/component/popup-set.component.scss +++ b/src/app/pages/infos/info/component/popup-set.component.scss @@ -0,0 +1,117 @@ +@import 'src/@fuse/scss/fuse'; + +app-popup-set { + #popup-set { + .top-bg { + @include media-breakpoint('xs') { + height: 224px; + } + } + + > .center { + > .header { + .search-wrapper { + width: 100%; + max-width: 480px; + border-radius: 28px; + overflow: hidden; + @include mat-elevation(1); + + @include media-breakpoint('xs') { + width: 100%; + } + + .search { + width: 100%; + height: 48px; + line-height: 48px; + padding: 0 18px; + + input { + width: 100%; + height: 48px; + min-height: 48px; + max-height: 48px; + padding: 0 16px; + border: none; + outline: none; + } + } + } + + @include media-breakpoint('xs') { + padding: 8px 0; + height: 160px !important; + min-height: 160px !important; + max-height: 160px !important; + } + } + } + } + + .mat-tab-group, + .mat-tab-body-wrapper, + .tab-content { + flex: 1 1 auto; + max-width: 100%; + } + + .popup-set-table { + flex: 1 1 auto; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); + overflow: auto; + -webkit-overflow-scrolling: touch; + + .mat-header-row { + min-height: 64px; + } + + .popup-set { + position: relative; + cursor: pointer; + height: 84px; + } + + .mat-cell { + min-width: 0; + display: flex; + align-items: center; + } + + .mat-column-id { + flex: 0 1 84px; + } + + .mat-column-image { + flex: 0 1 84px; + + .product-image { + width: 52px; + height: 52px; + border: 1px solid rgba(0, 0, 0, 0.12); + } + } + + .mat-column-buttons { + flex: 0 1 80px; + } + + .quantity-indicator { + display: inline-block; + vertical-align: middle; + width: 8px; + height: 8px; + border-radius: 4px; + margin-right: 8px; + + & + span { + display: inline-block; + vertical-align: middle; + } + } + + .active-icon { + border-radius: 50%; + } + } +} diff --git a/src/app/pages/infos/info/component/popup-set.component.ts b/src/app/pages/infos/info/component/popup-set.component.ts index bd13317..0e9c278 100644 --- a/src/app/pages/infos/info/component/popup-set.component.ts +++ b/src/app/pages/infos/info/component/popup-set.component.ts @@ -1,12 +1,73 @@ -import { Component, OnInit } from '@angular/core'; +import { + Component, + OnDestroy, + OnInit, + ViewEncapsulation, + ViewChild, + ElementRef +} from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { DataSource } from '@angular/cdk/collections'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { fuseAnimations } from 'src/@fuse/animations'; @Component({ selector: 'app-popup-set', templateUrl: './popup-set.component.html', - styleUrls: ['./popup-set.component.scss'] + styleUrls: ['./popup-set.component.scss'], + encapsulation: ViewEncapsulation.None, + animations: fuseAnimations }) -export class PopupSetComponent implements OnInit { - constructor() {} +export class PopupSetComponent implements OnInit, OnDestroy { + dataSource: null; + displayedColumns = [ + 'id', + 'reference', + 'customer', + 'total', + 'payment', + 'status', + 'date' + ]; - ngOnInit() {} + @ViewChild(MatPaginator, { static: true }) + paginator: MatPaginator; + + @ViewChild('filter', { static: true }) + filter: ElementRef; + + @ViewChild(MatSort, { static: true }) + sort: MatSort; + + // Private + private unsubscribeAll: Subject; + + constructor(private fb: FormBuilder) { + // Set the private defaults + this.unsubscribeAll = new Subject(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + // Subscribe to update order on changes + } + + /** + * On destroy + */ + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this.unsubscribeAll.next(); + this.unsubscribeAll.complete(); + } } diff --git a/src/app/pages/pages-routing.module.ts b/src/app/pages/pages-routing.module.ts index 0f32e61..bd74e10 100644 --- a/src/app/pages/pages-routing.module.ts +++ b/src/app/pages/pages-routing.module.ts @@ -25,6 +25,10 @@ const routes: Routes = [ { path: 'specials', loadChildren: './specials/specials.module#SpecialsModule' + }, + { + path: 'statics', + loadChildren: './statics/statics.module#StaticsModule' } ]; diff --git a/src/app/pages/statics/calendar.component.html b/src/app/pages/statics/calendar.component.html new file mode 100644 index 0000000..5a920c4 --- /dev/null +++ b/src/app/pages/statics/calendar.component.html @@ -0,0 +1,173 @@ +
+ +
+
+
+ + + +
+ + + + + + + + + +
+
+ + + +
+ + +
+ {{ viewDate | calendarDate: view + 'ViewTitle':'en' }} +
+ + +
+ +
+ + + + +
+ + + +
+
+ + + + + + +
+
+ +
diff --git a/src/app/pages/statics/calendar.component.scss b/src/app/pages/statics/calendar.component.scss new file mode 100644 index 0000000..2864918 --- /dev/null +++ b/src/app/pages/statics/calendar.component.scss @@ -0,0 +1,310 @@ +@import "src/@fuse/scss/fuse"; +@import "node_modules/angular-calendar/scss/angular-calendar"; + +.cal-month-view { + + .cal-header { + + .cal-cell { + font-weight: 600; + } + } + + .cal-day-cell { + + @include media-breakpoint(lg) { + min-height: 150px; + } + + @include media-breakpoint(gt-lg) { + min-height: 200px; + } + + &.cal-selected { + border: none; + @include mat-elevation(9); + } + } + + .cal-open-day-events { + box-shadow: inset 0 0 2px 0 rgba(0, 0, 0, 0.54); + padding: 0; + display: flex; + flex-direction: column; + + > div { + padding: 0 16px; + margin: 8px 16px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + @include mat-elevation(1); + transition: box-shadow 300ms ease; + + &:first-of-type { + margin-top: 16px; + } + + &:last-of-type { + margin-bottom: 16px; + } + + &:hover { + @include mat-elevation(3); + } + + .cal-event { + top: 0; + margin: 0; + } + + mwl-calendar-event-title { + flex: 1; + + .cal-event-title { + display: block; + padding: 21px 24px; + line-height: 1; + text-decoration: none; + } + } + + mwl-calendar-event-actions { + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 8px; + } + } + } + + } + } +} + +.cal-week-view { + + .cal-header > b { + font-weight: 600; + } + + .cal-event { + display: flex; + flex-direction: row; + align-items: center; + + mwl-calendar-event-title { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + order: 0; + + .cal-event-title { + display: block; + line-height: 1; + text-decoration: none; + } + } + + mwl-calendar-event-actions { + order: 1; + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 8px; + } + } + } + } +} + +.cal-day-view { + + .cal-time { + font-weight: 600; + } + + .cal-event { + display: flex; + flex-direction: row; + + mwl-calendar-event-title { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + order: 0; + + .cal-event-title { + display: block; + height: 26px; + line-height: 26px; + text-decoration: none; + } + } + + mwl-calendar-event-actions { + order: 1; + + .cal-event-actions { + display: flex; + flex-direction: row; + align-items: center; + + .cal-event-action { + display: block; + line-height: 1; + padding: 4px; + } + } + } + } + + .cal-hour-segment { + + &:after, + &::after { + content: '' !important; + } + } +} + +#calendar { + + .header { + height: 200px; + min-height: 200px; + max-height: 200px; + position: relative; + background-size: 100% auto; + background-position: 0 50%; + background-repeat: no-repeat; + background-color: #FAFAFA; + color: #FFFFFF; + + @include media-breakpoint(xs) { + height: 164px; + min-height: 164px; + max-height: 164px; + } + + &:before { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + background: rgba(0, 0, 0, 0.45); + } + + &.Jan { + background-image: url('/assets/images/calendar/winter.jpg'); + background-position: 0 85%; + } + &.Feb { + background-image: url('/assets/images/calendar/winter.jpg'); + background-position: 0 85%; + } + &.Mar { + background-image: url('/assets/images/calendar/spring.jpg'); + background-position: 0 40%; + } + &.Apr { + background-image: url('/assets/images/calendar/spring.jpg'); + background-position: 0 40%; + } + &.May { + background-image: url('/assets/images/calendar/spring.jpg'); + background-position: 0 40%; + } + &.Jun { + background-image: url('/assets/images/calendar/summer.jpg'); + background-position: 0 80%; + } + &.Jul { + background-image: url('/assets/images/calendar/summer.jpg'); + background-position: 0 80%; + } + &.Aug { + background-image: url('/assets/images/calendar/summer.jpg'); + background-position: 0 80%; + } + &.Sep { + background-image: url('/assets/images/calendar/autumn.jpg'); + background-position: 0 40%; + } + &.Oct { + background-image: url('/assets/images/calendar/autumn.jpg'); + background-position: 0 40%; + } + &.Nov { + background-image: url('/assets/images/calendar/autumn.jpg'); + background-position: 0 40%; + } + &.Dec { + background-image: url('/assets/images/calendar/winter.jpg'); + background-position: 0 85%; + } + + .header-content { + height: 100%; + + .header-top { + position: relative; + z-index: 2; + + .logo { + + .logo-icon { + margin-right: 16px; + } + + .logo-text { + font-size: 24px; + } + } + } + + .header-bottom { + position: relative; + z-index: 2; + + .title { + font-size: 20px; + min-width: 160px; + text-align: center; + font-weight: 600; + } + } + } + + .add-event-button { + position: absolute; + right: 18px; + bottom: -26px; + z-index: 10; + } + } + + .content { + padding: 24px; + } +} diff --git a/src/app/pages/statics/calendar.component.ts b/src/app/pages/statics/calendar.component.ts new file mode 100644 index 0000000..10d28ee --- /dev/null +++ b/src/app/pages/statics/calendar.component.ts @@ -0,0 +1,262 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { Subject } from 'rxjs'; +import { startOfDay, isSameDay, isSameMonth } from 'date-fns'; +import { + CalendarEvent, + CalendarEventAction, + CalendarEventTimesChangedEvent, + CalendarMonthViewDay +} from 'angular-calendar'; + +import { FuseConfirmDialogComponent } from 'src/@fuse/components/confirm-dialog/confirm-dialog.component'; +import { fuseAnimations } from 'src/@fuse/animations'; + +import { CalendarService } from './calendar.service'; +import { CalendarEventModel } from './event.model'; +import { CalendarEventFormDialogComponent } from './event-form/event-form.component'; + +@Component({ + selector: 'calendar', + templateUrl: './calendar.component.html', + styleUrls: ['./calendar.component.scss'], + encapsulation: ViewEncapsulation.None, + animations: fuseAnimations +}) +export class CalendarComponent implements OnInit { + actions: CalendarEventAction[]; + activeDayIsOpen: boolean; + confirmDialogRef: MatDialogRef; + dialogRef: any; + events: CalendarEvent[]; + refresh: Subject = new Subject(); + selectedDay: any; + view: string; + viewDate: Date; + + constructor( + private _matDialog: MatDialog, + private _calendarService: CalendarService + ) { + // Set the defaults + this.view = 'month'; + this.viewDate = new Date(); + this.activeDayIsOpen = true; + this.selectedDay = { date: startOfDay(new Date()) }; + + this.actions = [ + { + label: 'edit', + onClick: ({ event }: { event: CalendarEvent }): void => { + this.editEvent('edit', event); + } + }, + { + label: 'delete', + onClick: ({ event }: { event: CalendarEvent }): void => { + this.deleteEvent(event); + } + } + ]; + + /** + * Get events from service/server + */ + this.setEvents(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + /** + * Watch re-render-refresh for updating db + */ + this.refresh.subscribe(updateDB => { + if (updateDB) { + this._calendarService.updateEvents(this.events); + } + }); + + this._calendarService.onEventsUpdated.subscribe(events => { + this.setEvents(); + this.refresh.next(); + }); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Set events + */ + setEvents(): void { + // this.events = this._calendarService.events.map(item => { + // item.actions = this.actions; + // return new CalendarEventModel(item); + // }); + } + + /** + * Before View Renderer + * + * @param {any} header + * @param {any} body + */ + beforeMonthViewRender({ header, body }): void { + /** + * Get the selected day + */ + const _selectedDay = body.find(_day => { + return _day.date.getTime() === this.selectedDay.date.getTime(); + }); + + if (_selectedDay) { + /** + * Set selected day style + * @type {string} + */ + _selectedDay.cssClass = 'cal-selected'; + } + } + + /** + * Day clicked + * + * @param {MonthViewDay} day + */ + dayClicked(day: CalendarMonthViewDay): void { + const date: Date = day.date; + const events: CalendarEvent[] = day.events; + + if (isSameMonth(date, this.viewDate)) { + if ( + (isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) || + events.length === 0 + ) { + this.activeDayIsOpen = false; + } else { + this.activeDayIsOpen = true; + this.viewDate = date; + } + } + this.selectedDay = day; + this.refresh.next(); + } + + /** + * Event times changed + * Event dropped or resized + * + * @param {CalendarEvent} event + * @param {Date} newStart + * @param {Date} newEnd + */ + eventTimesChanged({ + event, + newStart, + newEnd + }: CalendarEventTimesChangedEvent): void { + event.start = newStart; + event.end = newEnd; + // console.warn('Dropped or resized', event); + this.refresh.next(true); + } + + /** + * Delete Event + * + * @param event + */ + deleteEvent(event): void { + this.confirmDialogRef = this._matDialog.open(FuseConfirmDialogComponent, { + disableClose: false + }); + + this.confirmDialogRef.componentInstance.confirmMessage = + 'Are you sure you want to delete?'; + + this.confirmDialogRef.afterClosed().subscribe(result => { + if (result) { + const eventIndex = this.events.indexOf(event); + this.events.splice(eventIndex, 1); + this.refresh.next(true); + } + this.confirmDialogRef = null; + }); + } + + /** + * Edit Event + * + * @param {string} action + * @param {CalendarEvent} event + */ + editEvent(action: string, event: CalendarEvent): void { + const eventIndex = this.events.indexOf(event); + + this.dialogRef = this._matDialog.open(CalendarEventFormDialogComponent, { + panelClass: 'event-form-dialog', + data: { + event: event, + action: action + } + }); + + this.dialogRef.afterClosed().subscribe(response => { + if (!response) { + return; + } + const actionType: string = response[0]; + const formData: FormGroup = response[1]; + switch (actionType) { + /** + * Save + */ + case 'save': + this.events[eventIndex] = Object.assign( + this.events[eventIndex], + formData.getRawValue() + ); + this.refresh.next(true); + + break; + /** + * Delete + */ + case 'delete': + this.deleteEvent(event); + + break; + } + }); + } + + /** + * Add Event + */ + addEvent(): void { + this.dialogRef = this._matDialog.open(CalendarEventFormDialogComponent, { + panelClass: 'event-form-dialog', + data: { + action: 'new', + date: this.selectedDay.date + } + }); + this.dialogRef.afterClosed().subscribe((response: FormGroup) => { + if (!response) { + return; + } + const newEvent = response.getRawValue(); + newEvent.actions = this.actions; + this.events.push(newEvent); + this.refresh.next(true); + }); + } +} diff --git a/src/app/pages/statics/calendar.service.ts b/src/app/pages/statics/calendar.service.ts new file mode 100644 index 0000000..bfa1590 --- /dev/null +++ b/src/app/pages/statics/calendar.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { + ActivatedRouteSnapshot, + Resolve, + RouterStateSnapshot +} from '@angular/router'; +import { Observable, Subject } from 'rxjs'; + +@Injectable() +export class CalendarService implements Resolve { + events: any; + onEventsUpdated: Subject; + + /** + * Constructor + * + * @param {HttpClient} _httpClient + */ + constructor(private _httpClient: HttpClient) { + // Set the defaults + this.onEventsUpdated = new Subject(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param {ActivatedRouteSnapshot} route + * @param {RouterStateSnapshot} state + * @returns {Observable | Promise | any} + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | any { + return new Promise((resolve, reject) => { + Promise.all([this.getEvents()]).then(([events]: [any]) => { + resolve(); + }, reject); + }); + } + + /** + * Get events + * + * @returns {Promise} + */ + getEvents(): Promise { + // return new Promise((resolve, reject) => { + + // this._httpClient.get('api/calendar/events') + // .subscribe((response: any) => { + // this.events = response.data; + // this.onEventsUpdated.next(this.events); + // resolve(this.events); + // }, reject); + // }); + return null; + } + + /** + * Update events + * + * @param events + * @returns {Promise} + */ + updateEvents(events): Promise { + return new Promise((resolve, reject) => { + this._httpClient + .post('api/calendar/events', { + id: 'events', + data: [...events] + }) + .subscribe((response: any) => { + this.getEvents(); + }, reject); + }); + } +} diff --git a/src/app/pages/statics/calendar.theme.scss b/src/app/pages/statics/calendar.theme.scss new file mode 100644 index 0000000..6302266 --- /dev/null +++ b/src/app/pages/statics/calendar.theme.scss @@ -0,0 +1,192 @@ +@mixin calendar-theme($theme) { + + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $accent: map-get($theme, accent); + $is-dark: map-get($theme, is-dark); + + // Month view + .cal-month-view { + + .cal-day-badge { + background: map-get($accent, default); + color: map-get($accent, default-contrast); + } + + .cal-cell-row { + + &.cal-header { + background: none !important; + + .cal-cell { + background: none !important; + } + } + } + + .cal-days { + border-color: map-get($foreground, divider); + + .cal-cell-row { + border-color: map-get($foreground, divider); + + &:hover { + background: map-get($background, hover); + } + + .cal-day-cell { + + &:not(:last-child) { + border-color: map-get($foreground, divider); + } + + &.cal-has-events { + + &.cal-open { + background: map-get($background, hover); + } + } + + &.cal-weekend { + + .cal-day-number { + color: map-get($accent, default); + } + } + + &:hover { + background: map-get($background, hover); + } + } + } + } + + .cal-open-day-events { + @if ($is-dark) { + background: map-get($background, app-bar); + } @else { + background: map-get($accent, default); + } + + > div { + background: map-get($background, card); + + .cal-event-title { + color: map-get($foreground, text); + } + } + } + } + + // Week view + .cal-week-view { + + .cal-event { + + mwl-calendar-event-actions { + + .cal-event-actions { + + .cal-event-action { + + i { + color: rgba(0, 0, 0, 0.54); + } + } + } + } + } + + .cal-day-headers { + border-color: map-get($foreground, divider); + + .cal-drag-over { + background: map-get($background, hover); + } + + .cal-header { + + &.cal-weekend { + + span { + color: map-get($accent, default); + } + } + + &.cal-today { + background: map-get($background, hover); + } + + &:hover { + background: map-get($background, hover); + } + + &:not(:last-child) { + border-color: map-get($foreground, divider); + } + } + } + } + + // Day view + .cal-day-view { + + .cal-hour-rows { + border-color: map-get($foreground, divider); + } + + .cal-drag-over { + + .cal-hour-segment { + background: map-get($background, hover); + } + } + + .cal-event { + + mwl-calendar-event-actions { + + .cal-event-actions { + + .cal-event-action { + + i { + color: rgba(0, 0, 0, 0.54); + } + } + } + } + } + + .cal-hour { + + .cal-hour-segment { + + &:hover { + background: map-get($background, hover); + } + } + + &:nth-child(odd) { + background: map-get($background, hover); + } + + &:not(:last-child) { + + .cal-hour-segment { + border-color: map-get($foreground, divider); + } + } + + &:last-child { + + :not(:last-child) { + + .cal-hour-segment { + border-color: map-get($foreground, divider); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/statics/event-form/event-form.component.html b/src/app/pages/statics/event-form/event-form.component.html new file mode 100644 index 0000000..5909bb9 --- /dev/null +++ b/src/app/pages/statics/event-form/event-form.component.html @@ -0,0 +1,143 @@ +
+ + + {{dialogTitle}} + + + + +
+ +
+ + + Title + + + +
+ + + Primary color + + + + + Secondary color + + + +
+ +
+ + + All Day + + +
+ +
+ + + Start date + + + + + + + Start time + + + +
+ +
+ + + End date + + + + + + + End time + + + +
+ + + Location + + + + + Notes + + + +
+ +
+ +
+ + + + + + + +
+
diff --git a/src/app/pages/statics/event-form/event-form.component.scss b/src/app/pages/statics/event-form/event-form.component.scss new file mode 100644 index 0000000..57bfbe2 --- /dev/null +++ b/src/app/pages/statics/event-form/event-form.component.scss @@ -0,0 +1,27 @@ +@import "src/@fuse/scss/fuse"; + +.event-form-dialog { + + @include media-breakpoint('xs') { + width: 100%; + } + + @include media-breakpoint('gt-xs') { + width: 480px; + } + + .mat-dialog-container { + padding: 0; + } + + .dialog-content-wrapper { + max-height: 85vh; + display: flex; + flex-direction: column; + } + + .primary-color-input, + .secondary-color-input { + padding: 6px 8px; + } +} diff --git a/src/app/pages/statics/event-form/event-form.component.ts b/src/app/pages/statics/event-form/event-form.component.ts new file mode 100644 index 0000000..33a6f56 --- /dev/null +++ b/src/app/pages/statics/event-form/event-form.component.ts @@ -0,0 +1,76 @@ +import { Component, Inject, ViewEncapsulation } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { CalendarEvent } from 'angular-calendar'; + +import { MatColors } from 'src/@fuse/mat-colors'; + +import { CalendarEventModel } from '../event.model'; + +@Component({ + selector: 'calendar-event-form-dialog', + templateUrl: './event-form.component.html', + styleUrls: ['./event-form.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class CalendarEventFormDialogComponent { + action: string; + event: CalendarEvent; + eventForm: FormGroup; + dialogTitle: string; + presetColors = MatColors.presets; + + /** + * Constructor + * + * @param {MatDialogRef} matDialogRef + * @param _data + * @param {FormBuilder} _formBuilder + */ + constructor( + public matDialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private _data: any, + private _formBuilder: FormBuilder + ) { + this.event = _data.event; + this.action = _data.action; + + if (this.action === 'edit') { + this.dialogTitle = this.event.title; + } else { + this.dialogTitle = 'New Event'; + this.event = new CalendarEventModel({ + start: _data.date, + end: _data.date + }); + } + + this.eventForm = this.createEventForm(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create the event form + * + * @returns {FormGroup} + */ + createEventForm(): FormGroup { + return new FormGroup({ + title: new FormControl(this.event.title), + start: new FormControl(this.event.start), + end: new FormControl(this.event.end), + allDay: new FormControl(this.event.allDay), + color: this._formBuilder.group({ + primary: new FormControl(this.event.color.primary), + secondary: new FormControl(this.event.color.secondary) + }), + meta: this._formBuilder.group({ + location: new FormControl(this.event.meta.location), + notes: new FormControl(this.event.meta.notes) + }) + }); + } +} diff --git a/src/app/pages/statics/event.model.ts b/src/app/pages/statics/event.model.ts new file mode 100644 index 0000000..2083059 --- /dev/null +++ b/src/app/pages/statics/event.model.ts @@ -0,0 +1,54 @@ +import { CalendarEventAction } from 'angular-calendar'; +import { startOfDay, endOfDay } from 'date-fns'; + +export class CalendarEventModel +{ + start: Date; + end?: Date; + title: string; + color: { + primary: string; + secondary: string; + }; + actions?: CalendarEventAction[]; + allDay?: boolean; + cssClass?: string; + resizable?: { + beforeStart?: boolean; + afterEnd?: boolean; + }; + draggable?: boolean; + meta?: { + location: string, + notes: string + }; + + /** + * Constructor + * + * @param data + */ + constructor(data?) + { + data = data || {}; + this.start = new Date(data.start) || startOfDay(new Date()); + this.end = new Date(data.end) || endOfDay(new Date()); + this.title = data.title || ''; + this.color = { + primary : data.color && data.color.primary || '#1e90ff', + secondary: data.color && data.color.secondary || '#D1E8FF' + }; + this.draggable = data.draggable; + this.resizable = { + beforeStart: data.resizable && data.resizable.beforeStart || true, + afterEnd : data.resizable && data.resizable.afterEnd || true + }; + this.actions = data.actions || []; + this.allDay = data.allDay || false; + this.cssClass = data.cssClass || ''; + this.meta = { + location: data.meta && data.meta.location || '', + notes : data.meta && data.meta.notes || '' + }; + } +} diff --git a/src/app/pages/statics/statics-routing.module.ts b/src/app/pages/statics/statics-routing.module.ts new file mode 100644 index 0000000..6d5a5db --- /dev/null +++ b/src/app/pages/statics/statics-routing.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { CalendarComponent } from './calendar.component'; +import { CalendarService } from './calendar.service'; + +const routes: Routes = [ + { + path: '', + component: CalendarComponent, + children: [], + resolve: { + chat: CalendarService + } + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class StaticsRoutingModule {} diff --git a/src/app/pages/statics/statics.module.ts b/src/app/pages/statics/statics.module.ts new file mode 100644 index 0000000..39745e5 --- /dev/null +++ b/src/app/pages/statics/statics.module.ts @@ -0,0 +1,54 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ColorPickerModule } from 'ngx-color-picker'; +import { + CalendarModule as AngularCalendarModule, + DateAdapter +} from 'angular-calendar'; +import { adapterFactory } from 'angular-calendar/date-adapters/date-fns'; + +import { FuseSharedModule } from 'src/@fuse/shared.module'; +import { FuseConfirmDialogModule } from 'src/@fuse/components'; + +import { CalendarComponent } from './calendar.component'; + +import { CalendarEventFormDialogComponent } from './event-form/event-form.component'; + +import { StaticsRoutingModule } from './statics-routing.module'; +import { CalendarService } from './calendar.service'; +@NgModule({ + imports: [ + StaticsRoutingModule, + + MatButtonModule, + MatDatepickerModule, + MatDialogModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatSlideToggleModule, + MatToolbarModule, + MatTooltipModule, + + AngularCalendarModule.forRoot({ + provide: DateAdapter, + useFactory: adapterFactory + }), + ColorPickerModule, + + FuseSharedModule, + FuseConfirmDialogModule + ], + providers: [CalendarService], + declarations: [CalendarComponent, CalendarEventFormDialogComponent] +}) +export class StaticsModule {} diff --git a/src/modules/infos/infos.module.ts b/src/modules/infos/infos.module.ts new file mode 100644 index 0000000..18fb69b --- /dev/null +++ b/src/modules/infos/infos.module.ts @@ -0,0 +1,23 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { POPUP_SET_SERVICES } from './popup-set/service'; + +@NgModule({ + imports: [], + exports: [] +}) +export class InfosRootModule {} + +@NgModule({ + declarations: [], + imports: [CommonModule], + exports: [] +}) +export class InfosModule { + public static forRoot(): ModuleWithProviders { + return { + ngModule: InfosRootModule, + providers: [POPUP_SET_SERVICES] + }; + } +} diff --git a/src/modules/infos/popup-set/model/popup-set.model.ts b/src/modules/infos/popup-set/model/popup-set.model.ts new file mode 100644 index 0000000..c51b2be --- /dev/null +++ b/src/modules/infos/popup-set/model/popup-set.model.ts @@ -0,0 +1,9 @@ +import { DateAudit } from 'src/modules/common/data/model/audit'; + +export interface PopupSet extends DateAudit { + id?: number; + name?: string; + number?: string; + holder?: string; + description?: string; +} diff --git a/src/modules/infos/popup-set/service/index.ts b/src/modules/infos/popup-set/service/index.ts new file mode 100644 index 0000000..31b81e9 --- /dev/null +++ b/src/modules/infos/popup-set/service/index.ts @@ -0,0 +1,3 @@ +import { PopupSetService } from './popup-set.service'; + +export const POPUP_SET_SERVICES = [PopupSetService]; diff --git a/src/modules/infos/popup-set/service/popup-set.service.ts b/src/modules/infos/popup-set/service/popup-set.service.ts new file mode 100644 index 0000000..f1407c2 --- /dev/null +++ b/src/modules/infos/popup-set/service/popup-set.service.ts @@ -0,0 +1,26 @@ +import { Injectable, Inject } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; + +import { Observable } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; +import { PopupSet } from '../model/popup-set.model'; +import { API_BASE_URL } from 'src/modules/common/type/injection-token.type'; +import { Page } from 'src/modules/common/data/model/page'; +import { FormArray } from '@angular/forms'; + +@Injectable({ + providedIn: 'root' +}) +export class PopupSetService { + constructor( + @Inject(API_BASE_URL) private apiBaseUrl: string, + private httpClient: HttpClient + ) {} + + // public getBankInfos(): Observable> { + // return this.httpClient.get>( + // `${this.apiBaseUrl}/bank_info`, + // {} + // ); + // } +}