From 4631acb5a080dac60a18bab96aaf7a8523213fc5 Mon Sep 17 00:00:00 2001 From: JUNG YI DAM Date: Wed, 13 Jul 2022 08:02:41 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=EC=BF=A0=ED=8F=B0=EB=B0=9C=ED=96=89?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20page=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.routing.ts | 7 + .../mock-api/apps/member/coupon-log/api.ts | 217 +++++++++++ .../mock-api/apps/member/coupon-log/data.ts | 33 ++ src/app/mock-api/common/navigation/data.ts | 7 + src/app/mock-api/index.ts | 2 + .../member/coupon-log/components/index.ts | 3 + .../coupon-log/components/list.component.html | 357 ++++++++++++++++++ .../coupon-log/components/list.component.ts | 198 ++++++++++ .../member/coupon-log/coupon-log.module.ts | 50 +++ .../member/coupon-log/coupon-log.routing.ts | 24 ++ .../models/coupon-log-pagination.ts | 8 + .../member/coupon-log/models/coupon-log.ts | 29 ++ .../resolvers/coupon-log.resolver.ts | 89 +++++ .../coupon-log/services/coupon-log.service.ts | 156 ++++++++ src/assets/i18n/en.json | 1 + src/assets/i18n/ko.json | 1 + 16 files changed, 1182 insertions(+) create mode 100644 src/app/mock-api/apps/member/coupon-log/api.ts create mode 100644 src/app/mock-api/apps/member/coupon-log/data.ts create mode 100644 src/app/modules/admin/member/coupon-log/components/index.ts create mode 100644 src/app/modules/admin/member/coupon-log/components/list.component.html create mode 100644 src/app/modules/admin/member/coupon-log/components/list.component.ts create mode 100644 src/app/modules/admin/member/coupon-log/coupon-log.module.ts create mode 100644 src/app/modules/admin/member/coupon-log/coupon-log.routing.ts create mode 100644 src/app/modules/admin/member/coupon-log/models/coupon-log-pagination.ts create mode 100644 src/app/modules/admin/member/coupon-log/models/coupon-log.ts create mode 100644 src/app/modules/admin/member/coupon-log/resolvers/coupon-log.resolver.ts create mode 100644 src/app/modules/admin/member/coupon-log/services/coupon-log.service.ts diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index 9e91b4e..5b0d0a4 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -234,6 +234,13 @@ export const appRoutes: Route[] = [ 'app/modules/admin/member/coupon-moneylog/coupon-moneylog.module' ).then((m: any) => m.CouponMoneylogModule), }, + { + path: 'coupon-log', + loadChildren: () => + import( + 'app/modules/admin/member/coupon-log/coupon-log.module' + ).then((m: any) => m.CouponLogModule), + }, ], }, { diff --git a/src/app/mock-api/apps/member/coupon-log/api.ts b/src/app/mock-api/apps/member/coupon-log/api.ts new file mode 100644 index 0000000..f8007af --- /dev/null +++ b/src/app/mock-api/apps/member/coupon-log/api.ts @@ -0,0 +1,217 @@ +import { Injectable } from '@angular/core'; +import { assign, cloneDeep } from 'lodash-es'; +import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; +import { couponLogs as couponLogsData } from './data'; + +@Injectable({ + providedIn: 'root', +}) +export class MemberCouponLogMockApi { + private _couponLogs: any[] = couponLogsData; + + /** + * Constructor + */ + constructor(private _fuseMockApiService: FuseMockApiService) { + // Register Mock API handlers + this.registerHandlers(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Register Mock API handlers + */ + registerHandlers(): void { + // ----------------------------------------------------------------------------------------------------- + // @ CouponLogs - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/member/coupon-log/coupon-logs', 300) + .reply(({ request }) => { + // Get available queries + const search = request.params.get('search'); + const sort = request.params.get('sort') || 'name'; + const order = request.params.get('order') || 'asc'; + const page = parseInt(request.params.get('page') ?? '1', 10); + const size = parseInt(request.params.get('size') ?? '10', 10); + + // Clone the couponLogs + let couponLogs: any[] | null = cloneDeep(this._couponLogs); + + // Sort the couponLogs + if (sort === 'sku' || sort === 'name' || sort === 'active') { + couponLogs.sort((a, b) => { + const fieldA = a[sort].toString().toUpperCase(); + const fieldB = b[sort].toString().toUpperCase(); + return order === 'asc' + ? fieldA.localeCompare(fieldB) + : fieldB.localeCompare(fieldA); + }); + } else { + couponLogs.sort((a, b) => + order === 'asc' ? a[sort] - b[sort] : b[sort] - a[sort] + ); + } + + // If search exists... + if (search) { + // Filter the couponLogs + couponLogs = couponLogs.filter( + (contact: any) => + contact.name && + contact.name.toLowerCase().includes(search.toLowerCase()) + ); + } + + // Paginate - Start + const couponLogsLength = couponLogs.length; + + // Calculate pagination details + const begin = page * size; + const end = Math.min(size * (page + 1), couponLogsLength); + const lastPage = Math.max(Math.ceil(couponLogsLength / size), 1); + + // Prepare the pagination object + let pagination = {}; + + // If the requested page number is bigger than + // the last possible page number, return null for + // couponLogs but also send the last possible page so + // the app can navigate to there + if (page > lastPage) { + couponLogs = null; + pagination = { + lastPage, + }; + } else { + // Paginate the results by size + couponLogs = couponLogs.slice(begin, end); + + // Prepare the pagination mock-api + pagination = { + length: couponLogsLength, + size: size, + page: page, + lastPage: lastPage, + startIndex: begin, + endIndex: end - 1, + }; + } + + // Return the response + return [ + 200, + { + couponLogs, + pagination, + }, + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ CouponLog - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/member/coupon-log/coupon-log') + .reply(({ request }) => { + // Get the id from the params + const id = request.params.get('id'); + + // Clone the couponLogs + const couponLogs = cloneDeep(this._couponLogs); + + // Find the couponLog + const couponLog = couponLogs.find((item: any) => item.id === id); + + // Return the response + return [200, couponLog]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ CouponLog - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/member/coupon-log/coupon-log') + .reply(() => { + // Generate a new couponLog + const newCouponLog = { + id: FuseMockApiUtils.guid(), + category: '', + name: 'A New User', + description: '', + tags: [], + sku: '', + barcode: '', + brand: '', + vendor: '', + stock: '', + reserved: '', + cost: '', + basePrice: '', + taxPercent: '', + price: '', + weight: '', + thumbnail: '', + images: [], + active: false, + }; + + // Unshift the new couponLog + this._couponLogs.unshift(newCouponLog); + + // Return the response + return [200, newCouponLog]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ CouponLog - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/member/coupon-log/coupon-log') + .reply(({ request }) => { + // Get the id and couponLog + const id = request.body.id; + const couponLog = cloneDeep(request.body.couponLog); + + // Prepare the updated couponLog + let updatedCouponLog = null; + + // Find the couponLog and update it + this._couponLogs.forEach((item, index, couponLogs) => { + if (item.id === id) { + // Update the couponLog + couponLogs[index] = assign({}, couponLogs[index], couponLog); + + // Store the updated couponLog + updatedCouponLog = couponLogs[index]; + } + }); + + // Return the response + return [200, updatedCouponLog]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ CouponLog - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/member/coupon-log/coupon-log') + .reply(({ request }) => { + // Get the id + const id = request.params.get('id'); + + // Find the couponLog and delete it + this._couponLogs.forEach((item, index) => { + if (item.id === id) { + this._couponLogs.splice(index, 1); + } + }); + + // Return the response + return [200, true]; + }); + } +} diff --git a/src/app/mock-api/apps/member/coupon-log/data.ts b/src/app/mock-api/apps/member/coupon-log/data.ts new file mode 100644 index 0000000..9adbb02 --- /dev/null +++ b/src/app/mock-api/apps/member/coupon-log/data.ts @@ -0,0 +1,33 @@ +/* eslint-disable */ + +export const couponLogs = [ + { + id: 'on00', + totalPartnerCount: '5', + totalHoldingMoney: 303675, + totalComp: 108933, + total: 412608, + branchCount: 1, + divisionCount: 1, + officeCount: 1, + storeCount: 1, + memberCount: 1, + nickname: 'on00', + accountHolder: '11', + phoneNumber: '010-1111-1111', + calculateType: '롤링', + ownCash: 50000, + ownComp: 1711, + ownCoupon: 50000, + gameMoney: 0, + todayComp: 0, + totalDeposit: 0, + totalWithdraw: 0, + balance: 0, + registDate: '2022-06-12 15:38', + finalSigninDate: '', + ip: '', + state: '정상', + note: '', + }, +]; diff --git a/src/app/mock-api/common/navigation/data.ts b/src/app/mock-api/common/navigation/data.ts index 703ea4c..771eccb 100644 --- a/src/app/mock-api/common/navigation/data.ts +++ b/src/app/mock-api/common/navigation/data.ts @@ -130,6 +130,13 @@ export const defaultNavigation: FuseNavigationItem[] = [ icon: 'heroicons_outline:academic-cap', link: '/member/coupon-moneylog', }, + { + id: 'member.coupon-log', + title: 'Coupon Log', + type: 'basic', + icon: 'heroicons_outline:academic-cap', + link: '/member/coupon-log', + }, ], }, { diff --git a/src/app/mock-api/index.ts b/src/app/mock-api/index.ts index 972794b..c4d8e57 100644 --- a/src/app/mock-api/index.ts +++ b/src/app/mock-api/index.ts @@ -23,6 +23,7 @@ import { MemberPartnerStoreMockApi } from './apps/member/partner-store/api'; import { MemberPartnerRecommendationMockApi } from './apps/member/partner-recommendation/api'; import { MemberCouponMockApi } from './apps/member/coupon/api'; import { MemberCouponMoneylogMockApi } from './apps/member/coupon-moneylog/api'; +import { MemberCouponLogMockApi } from './apps/member/coupon-log/api'; import { MessagesMockApi } from 'app/mock-api/common/messages/api'; import { NavigationMockApi } from 'app/mock-api/common/navigation/api'; import { NotesMockApi } from 'app/mock-api/apps/notes/api'; @@ -66,6 +67,7 @@ export const mockApiServices = [ MemberPartnerRecommendationMockApi, MemberCouponMockApi, MemberCouponMoneylogMockApi, + MemberCouponLogMockApi, MessagesMockApi, NavigationMockApi, NotesMockApi, diff --git a/src/app/modules/admin/member/coupon-log/components/index.ts b/src/app/modules/admin/member/coupon-log/components/index.ts new file mode 100644 index 0000000..04759eb --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/components/index.ts @@ -0,0 +1,3 @@ +import { ListComponent } from './list.component'; + +export const COMPONENTS = [ListComponent]; diff --git a/src/app/modules/admin/member/coupon-log/components/list.component.html b/src/app/modules/admin/member/coupon-log/components/list.component.html new file mode 100644 index 0000000..0a5803a --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/components/list.component.html @@ -0,0 +1,357 @@ +
+ +
+ +
+ +
+ +
쿠폰발행 로그
+ +
+ + + + + + + 40 + 60 + 80 + 100 + + + + + LV.1 + LV.2 + LV.3 + LV.4 + + + + + 정상 + 대기 + 탈퇴 + 휴면 + 블랙 + 정지 + + + + + 카지노제한 + 슬롯제한 + + + + + 계좌입금 + + + + + 카지노콤프 + 슬롯콤프 + 배팅콤프 + 첫충콤프 + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+ + +
+ There are no coupon logs! +
+
+
+
+
diff --git a/src/app/modules/admin/member/coupon-log/components/list.component.ts b/src/app/modules/admin/member/coupon-log/components/list.component.ts new file mode 100644 index 0000000..04e9364 --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/components/list.component.ts @@ -0,0 +1,198 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { + debounceTime, + map, + merge, + Observable, + Subject, + switchMap, + takeUntil, +} from 'rxjs'; +import { fuseAnimations } from '@fuse/animations'; +import { FuseConfirmationService } from '@fuse/services/confirmation'; + +import { User } from '../../user/models/user'; +import { CouponLog } from '../models/coupon-log'; +import { CouponLogPagination } from '../models/coupon-log-pagination'; +import { CouponLogService } from '../services/coupon-log.service'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'coupon-log-list', + templateUrl: './list.component.html', + styles: [ + /* language=SCSS */ + ` + .inventory-grid { + grid-template-columns: 60px auto 40px; + + @screen sm { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px; + } + + @screen md { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px 60px; + } + + @screen lg { + grid-template-columns: 60px 70px 70px 70px 70px 100px 60px 60px auto 60px 60px 60px 60px; + } + } + `, + ], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + animations: fuseAnimations, +}) +export class ListComponent implements OnInit, AfterViewInit, OnDestroy { + @ViewChild(MatPaginator) private _paginator!: MatPaginator; + @ViewChild(MatSort) private _sort!: MatSort; + + couponLogs$!: Observable; + users$!: Observable; + + isLoading = false; + searchInputControl = new FormControl(); + selectedCouponLog?: CouponLog; + pagination?: CouponLogPagination; + + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _changeDetectorRef: ChangeDetectorRef, + private _fuseConfirmationService: FuseConfirmationService, + private _formBuilder: FormBuilder, + private _couponLogService: CouponLogService, + private router: Router + ) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + // Get the pagination + this._couponLogService.pagination$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((pagination: CouponLogPagination | undefined) => { + // Update the pagination + this.pagination = pagination; + + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + + // Get the products + this.couponLogs$ = this._couponLogService.couponLogs$; + } + + /** + * After view init + */ + ngAfterViewInit(): void { + if (this._sort && this._paginator) { + // Set the initial sort + this._sort.sort({ + id: 'name', + start: 'asc', + disableClear: true, + }); + + // Mark for check + this._changeDetectorRef.markForCheck(); + + // If the couponLog changes the sort order... + this._sort.sortChange + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(() => { + // Reset back to the first page + this._paginator.pageIndex = 0; + }); + + // Get products if sort or page changes + merge(this._sort.sortChange, this._paginator.page) + .pipe( + switchMap(() => { + this.isLoading = true; + return this._couponLogService.getCouponLogs( + this._paginator.pageIndex, + this._paginator.pageSize, + this._sort.active, + this._sort.direction + ); + }), + map(() => { + this.isLoading = false; + }) + ) + .subscribe(); + } + } + + /** + * On destroy + */ + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + viewUserDetail(id: string): void { + let url: string = 'member/user/' + id; + this.router.navigateByUrl(url); + } + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create product + */ + __createProduct(): void {} + + /** + * Toggle product details + * + * @param productId + */ + __toggleDetails(productId: string): void {} + + /** + * Track by function for ngFor loops + * + * @param index + * @param item + */ + __trackByFn(index: number, item: any): any { + return item.id || index; + } +} diff --git a/src/app/modules/admin/member/coupon-log/coupon-log.module.ts b/src/app/modules/admin/member/coupon-log/coupon-log.module.ts new file mode 100644 index 0000000..b324df8 --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/coupon-log.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatRippleModule } from '@angular/material/core'; +import { MatSortModule } from '@angular/material/sort'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { TranslocoModule } from '@ngneat/transloco'; + +import { SharedModule } from 'app/shared/shared.module'; + +import { COMPONENTS } from './components'; + +import { CouponLogRoutes } from './coupon-log.routing'; + +@NgModule({ + declarations: [COMPONENTS], + imports: [ + TranslocoModule, + SharedModule, + RouterModule.forChild(CouponLogRoutes), + + MatButtonModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatPaginatorModule, + MatProgressBarModule, + MatRippleModule, + MatSortModule, + MatSelectModule, + MatTooltipModule, + MatGridListModule, + MatSlideToggleModule, + MatRadioModule, + MatCheckboxModule, + ], +}) +export class CouponLogModule {} diff --git a/src/app/modules/admin/member/coupon-log/coupon-log.routing.ts b/src/app/modules/admin/member/coupon-log/coupon-log.routing.ts new file mode 100644 index 0000000..64edb8b --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/coupon-log.routing.ts @@ -0,0 +1,24 @@ +import { Route } from '@angular/router'; + +import { ListComponent } from './components/list.component'; +import { ViewComponent } from '../user/components/view.component'; + +import { CouponLogsResolver } from './resolvers/coupon-log.resolver'; +import { UserResolver } from '../user/resolvers/user.resolver'; + +export const CouponLogRoutes: Route[] = [ + { + path: '', + component: ListComponent, + resolve: { + CouponLogs: CouponLogsResolver, + }, + }, + { + path: ':id', + component: ViewComponent, + resolve: { + users: UserResolver, + }, + }, +]; diff --git a/src/app/modules/admin/member/coupon-log/models/coupon-log-pagination.ts b/src/app/modules/admin/member/coupon-log/models/coupon-log-pagination.ts new file mode 100644 index 0000000..a4baa22 --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/models/coupon-log-pagination.ts @@ -0,0 +1,8 @@ +export interface CouponLogPagination { + length: number; + size: number; + page: number; + lastPage: number; + startIndex: number; + endIndex: number; +} diff --git a/src/app/modules/admin/member/coupon-log/models/coupon-log.ts b/src/app/modules/admin/member/coupon-log/models/coupon-log.ts new file mode 100644 index 0000000..cc36309 --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/models/coupon-log.ts @@ -0,0 +1,29 @@ +export interface CouponLog { + id?: string; + totalPartnerCount?: number; + totalHoldingMoney?: number; + totalComp?: number; + total?: number; + branchCount?: number; + divisionCount?: number; + officeCount?: number; + storeCount?: number; + memberCount?: number; + nickname?: string; + accountHolder?: string; + phoneNumber?: string; + calculateType?: string; + ownCash?: number; + ownComp?: number; + ownCoupon?: number; + gameMoney?: number; + todayComp?: number; + totalDeposit?: number; + totalWithdraw?: number; + balance?: number; + registDate?: string; + finalSigninDate?: string; + ip?: string; + state?: string; + note?: string; +} diff --git a/src/app/modules/admin/member/coupon-log/resolvers/coupon-log.resolver.ts b/src/app/modules/admin/member/coupon-log/resolvers/coupon-log.resolver.ts new file mode 100644 index 0000000..6af30cd --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/resolvers/coupon-log.resolver.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core'; +import { + ActivatedRouteSnapshot, + Resolve, + Router, + RouterStateSnapshot, +} from '@angular/router'; +import { catchError, Observable, throwError } from 'rxjs'; + +import { CouponLog } from '../models/coupon-log'; +import { CouponLogPagination } from '../models/coupon-log-pagination'; +import { CouponLogService } from '../services/coupon-log.service'; + +@Injectable({ + providedIn: 'root', +}) +export class CouponLogResolver implements Resolve { + /** + * Constructor + */ + constructor( + private _couponLogService: CouponLogService, + private _router: Router + ) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable { + return this._couponLogService + .getCouponLogById(route.paramMap.get('id')) + .pipe( + // Error here means the requested product is not available + catchError((error) => { + // Log the error + console.error(error); + + // Get the parent url + const parentUrl = state.url.split('/').slice(0, -1).join('/'); + + // Navigate to there + this._router.navigateByUrl(parentUrl); + + // Throw an error + return throwError(error); + }) + ); + } +} + +@Injectable({ + providedIn: 'root', +}) +export class CouponLogsResolver implements Resolve { + /** + * Constructor + */ + constructor(private _couponLogService: CouponLogService) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable<{ + pagination: CouponLogPagination; + couponLogs: CouponLog[]; + }> { + return this._couponLogService.getCouponLogs(); + } +} diff --git a/src/app/modules/admin/member/coupon-log/services/coupon-log.service.ts b/src/app/modules/admin/member/coupon-log/services/coupon-log.service.ts new file mode 100644 index 0000000..ac2be33 --- /dev/null +++ b/src/app/modules/admin/member/coupon-log/services/coupon-log.service.ts @@ -0,0 +1,156 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { + BehaviorSubject, + filter, + map, + Observable, + of, + switchMap, + take, + tap, + throwError, +} from 'rxjs'; + +import { CouponLog } from '../models/coupon-log'; +import { CouponLogPagination } from '../models/coupon-log-pagination'; + +@Injectable({ + providedIn: 'root', +}) +export class CouponLogService { + // Private + private __pagination = new BehaviorSubject( + undefined + ); + private __couponLog = new BehaviorSubject(undefined); + private __couponLogs = new BehaviorSubject( + undefined + ); + + /** + * Constructor + */ + constructor(private _httpClient: HttpClient) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Accessors + // ----------------------------------------------------------------------------------------------------- + + /** + * Getter for pagination + */ + get pagination$(): Observable { + return this.__pagination.asObservable(); + } + + /** + * Getter for couponLog + */ + get couponLog$(): Observable { + return this.__couponLog.asObservable(); + } + + /** + * Getter for couponLogs + */ + get couponLogs$(): Observable { + return this.__couponLogs.asObservable(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Get couponLogs + * + * + * @param page + * @param size + * @param sort + * @param order + * @param search + */ + getCouponLogs( + page: number = 0, + size: number = 10, + sort: string = 'name', + order: 'asc' | 'desc' | '' = 'asc', + search: string = '' + ): Observable<{ + pagination: CouponLogPagination; + couponLogs: CouponLog[]; + }> { + return this._httpClient + .get<{ + pagination: CouponLogPagination; + couponLogs: CouponLog[]; + }>('api/apps/member/coupon-log/coupon-logs', { + params: { + page: '' + page, + size: '' + size, + sort, + order, + search, + }, + }) + .pipe( + tap((response) => { + this.__pagination.next(response.pagination); + this.__couponLogs.next(response.couponLogs); + }) + ); + } + + /** + * Get product by id + */ + getCouponLogById(id: string | null): Observable { + return this.__couponLogs.pipe( + take(1), + map((couponLogs) => { + // Find the product + const couponLog = + couponLogs?.find((item) => item.id === id) || undefined; + + // Update the product + this.__couponLog.next(couponLog); + + // Return the product + return couponLog; + }), + switchMap((product) => { + if (!product) { + return throwError('Could not found product with id of ' + id + '!'); + } + + return of(product); + }) + ); + } + + /** + * Create product + */ + createCouponLog(): Observable { + return this.couponLogs$.pipe( + take(1), + switchMap((couponLogs) => + this._httpClient + .post('api/apps/member/coupon-log/product', {}) + .pipe( + map((newCouponLog) => { + // Update the couponLogs with the new product + if (!!couponLogs) { + this.__couponLogs.next([newCouponLog, ...couponLogs]); + } + + // Return the new product + return newCouponLog; + }) + ) + ) + ); + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 5d79623..3e06456 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -15,6 +15,7 @@ "Partner Recommendation": "Partner Recommendation", "Coupon": "Coupon", "Coupon Moneylog": "Coupon Moneylog", + "Coupon Log": "Coupon Log", "Analytics": "Analytics", "Deposit": "Deposit", "Withdraw": "Withdraw", diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index e9e09e7..8ecef03 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -15,6 +15,7 @@ "Partner Recommendation": "추천코드등록", "Coupon": "쿠폰발행리스트", "Coupon Moneylog": "쿠폰발행머니로그", + "Coupon Log": "쿠폰발행 로그", "Analytics": "Analytics", "Deposit": "입금관리", "Withdraw": "출금관리", From 731ea28bd326c1854c4cf0e8caa1b026a51d4b67 Mon Sep 17 00:00:00 2001 From: JUNG YI DAM Date: Wed, 13 Jul 2022 08:45:52 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=EC=9D=BC=EC=9D=BC=ED=98=84=ED=99=A9=20page?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.routing.ts | 12 + src/app/mock-api/apps/report/daily/api.ts | 217 +++++++++++ src/app/mock-api/apps/report/daily/data.ts | 33 ++ src/app/mock-api/common/navigation/data.ts | 16 + src/app/mock-api/index.ts | 2 + .../admin/report/daily/components/index.ts | 3 + .../daily/components/list.component.html | 353 ++++++++++++++++++ .../report/daily/components/list.component.ts | 198 ++++++++++ .../admin/report/daily/daily.module.ts | 50 +++ .../admin/report/daily/daily.routing.ts | 24 ++ .../report/daily/models/daily-pagination.ts | 8 + .../admin/report/daily/models/daily.ts | 29 ++ .../report/daily/resolvers/daily.resolver.ts | 84 +++++ .../report/daily/services/daily.service.ts | 153 ++++++++ src/assets/i18n/en.json | 3 +- src/assets/i18n/ko.json | 3 +- 16 files changed, 1186 insertions(+), 2 deletions(-) create mode 100644 src/app/mock-api/apps/report/daily/api.ts create mode 100644 src/app/mock-api/apps/report/daily/data.ts create mode 100644 src/app/modules/admin/report/daily/components/index.ts create mode 100644 src/app/modules/admin/report/daily/components/list.component.html create mode 100644 src/app/modules/admin/report/daily/components/list.component.ts create mode 100644 src/app/modules/admin/report/daily/daily.module.ts create mode 100644 src/app/modules/admin/report/daily/daily.routing.ts create mode 100644 src/app/modules/admin/report/daily/models/daily-pagination.ts create mode 100644 src/app/modules/admin/report/daily/models/daily.ts create mode 100644 src/app/modules/admin/report/daily/resolvers/daily.resolver.ts create mode 100644 src/app/modules/admin/report/daily/services/daily.service.ts diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index 5b0d0a4..ecae6ef 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -295,6 +295,18 @@ export const appRoutes: Route[] = [ }, ], }, + { + path: 'report', + children: [ + { + path: 'daily', + loadChildren: () => + import('app/modules/admin/report/daily/daily.module').then( + (m: any) => m.DailyModule + ), + }, + ], + }, ], }, ]; diff --git a/src/app/mock-api/apps/report/daily/api.ts b/src/app/mock-api/apps/report/daily/api.ts new file mode 100644 index 0000000..2f7e710 --- /dev/null +++ b/src/app/mock-api/apps/report/daily/api.ts @@ -0,0 +1,217 @@ +import { Injectable } from '@angular/core'; +import { assign, cloneDeep } from 'lodash-es'; +import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; +import { dailys as dailysData } from './data'; + +@Injectable({ + providedIn: 'root', +}) +export class ReportDailyMockApi { + private _dailys: any[] = dailysData; + + /** + * Constructor + */ + constructor(private _fuseMockApiService: FuseMockApiService) { + // Register Mock API handlers + this.registerHandlers(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Register Mock API handlers + */ + registerHandlers(): void { + // ----------------------------------------------------------------------------------------------------- + // @ Dailys - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/member/partner-branch/partner-branchs', 300) + .reply(({ request }) => { + // Get available queries + const search = request.params.get('search'); + const sort = request.params.get('sort') || 'name'; + const order = request.params.get('order') || 'asc'; + const page = parseInt(request.params.get('page') ?? '1', 10); + const size = parseInt(request.params.get('size') ?? '10', 10); + + // Clone the dailys + let dailys: any[] | null = cloneDeep(this._dailys); + + // Sort the dailys + if (sort === 'sku' || sort === 'name' || sort === 'active') { + dailys.sort((a, b) => { + const fieldA = a[sort].toString().toUpperCase(); + const fieldB = b[sort].toString().toUpperCase(); + return order === 'asc' + ? fieldA.localeCompare(fieldB) + : fieldB.localeCompare(fieldA); + }); + } else { + dailys.sort((a, b) => + order === 'asc' ? a[sort] - b[sort] : b[sort] - a[sort] + ); + } + + // If search exists... + if (search) { + // Filter the dailys + dailys = dailys.filter( + (contact: any) => + contact.name && + contact.name.toLowerCase().includes(search.toLowerCase()) + ); + } + + // Paginate - Start + const dailysLength = dailys.length; + + // Calculate pagination details + const begin = page * size; + const end = Math.min(size * (page + 1), dailysLength); + const lastPage = Math.max(Math.ceil(dailysLength / size), 1); + + // Prepare the pagination object + let pagination = {}; + + // If the requested page number is bigger than + // the last possible page number, return null for + // dailys but also send the last possible page so + // the app can navigate to there + if (page > lastPage) { + dailys = null; + pagination = { + lastPage, + }; + } else { + // Paginate the results by size + dailys = dailys.slice(begin, end); + + // Prepare the pagination mock-api + pagination = { + length: dailysLength, + size: size, + page: page, + lastPage: lastPage, + startIndex: begin, + endIndex: end - 1, + }; + } + + // Return the response + return [ + 200, + { + dailys, + pagination, + }, + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Daily - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/member/partne-branch/partner-branch') + .reply(({ request }) => { + // Get the id from the params + const id = request.params.get('id'); + + // Clone the dailys + const dailys = cloneDeep(this._dailys); + + // Find the daily + const daily = dailys.find((item: any) => item.id === id); + + // Return the response + return [200, daily]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Daily - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/member/partner-branch/partner-branch') + .reply(() => { + // Generate a new daily + const newDaily = { + id: FuseMockApiUtils.guid(), + category: '', + name: 'A New User', + description: '', + tags: [], + sku: '', + barcode: '', + brand: '', + vendor: '', + stock: '', + reserved: '', + cost: '', + basePrice: '', + taxPercent: '', + price: '', + weight: '', + thumbnail: '', + images: [], + active: false, + }; + + // Unshift the new daily + this._dailys.unshift(newDaily); + + // Return the response + return [200, newDaily]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Daily - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/member/partner-branch/partner-branch') + .reply(({ request }) => { + // Get the id and daily + const id = request.body.id; + const daily = cloneDeep(request.body.daily); + + // Prepare the updated daily + let updatedDaily = null; + + // Find the daily and update it + this._dailys.forEach((item, index, dailys) => { + if (item.id === id) { + // Update the daily + dailys[index] = assign({}, dailys[index], daily); + + // Store the updated daily + updatedDaily = dailys[index]; + } + }); + + // Return the response + return [200, updatedDaily]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Daily - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/member/partner-branch/partner-branch') + .reply(({ request }) => { + // Get the id + const id = request.params.get('id'); + + // Find the daily and delete it + this._dailys.forEach((item, index) => { + if (item.id === id) { + this._dailys.splice(index, 1); + } + }); + + // Return the response + return [200, true]; + }); + } +} diff --git a/src/app/mock-api/apps/report/daily/data.ts b/src/app/mock-api/apps/report/daily/data.ts new file mode 100644 index 0000000..7a16563 --- /dev/null +++ b/src/app/mock-api/apps/report/daily/data.ts @@ -0,0 +1,33 @@ +/* eslint-disable */ + +export const dailys = [ + { + id: 'on00', + totalPartnerCount: '5', + totalHoldingMoney: 303675, + totalComp: 108933, + total: 412608, + branchCount: 1, + divisionCount: 1, + officeCount: 1, + storeCount: 1, + memberCount: 1, + nickname: 'on00', + accountHolder: '11', + phoneNumber: '010-1111-1111', + calculateType: '롤링', + ownCash: 50000, + ownComp: 1711, + ownCoupon: 50000, + gameMoney: 0, + todayComp: 0, + totalDeposit: 0, + totalWithdraw: 0, + balance: 0, + registDate: '2022-06-12 15:38', + finalSigninDate: '', + ip: '', + state: '정상', + note: '', + }, +]; diff --git a/src/app/mock-api/common/navigation/data.ts b/src/app/mock-api/common/navigation/data.ts index 771eccb..469365c 100644 --- a/src/app/mock-api/common/navigation/data.ts +++ b/src/app/mock-api/common/navigation/data.ts @@ -199,6 +199,22 @@ export const defaultNavigation: FuseNavigationItem[] = [ }, ], }, + { + id: 'report', + title: 'Report', + subtitle: 'report managements', + type: 'group', + icon: 'heroicons_outline:home', + children: [ + { + id: 'report.daily', + title: 'Daily', + type: 'basic', + icon: 'heroicons_outline:academic-cap', + link: '/report/daily', + }, + ], + }, ]; export const compactNavigation: FuseNavigationItem[] = [ { diff --git a/src/app/mock-api/index.ts b/src/app/mock-api/index.ts index c4d8e57..ae2cbc5 100644 --- a/src/app/mock-api/index.ts +++ b/src/app/mock-api/index.ts @@ -40,6 +40,7 @@ import { GamePowerballMockApi } from './apps/game/powerball/api'; import { GameCasinoMockApi } from './apps/game/casino/api'; import { GameEvolutionMockApi } from './apps/game/evolution/api'; import { GameSlotMockApi } from './apps/game/slot/api'; +import { ReportDailyMockApi } from './apps/report/daily/api'; export const mockApiServices = [ AcademyMockApi, @@ -84,4 +85,5 @@ export const mockApiServices = [ GameCasinoMockApi, GameEvolutionMockApi, GameSlotMockApi, + ReportDailyMockApi, ]; diff --git a/src/app/modules/admin/report/daily/components/index.ts b/src/app/modules/admin/report/daily/components/index.ts new file mode 100644 index 0000000..04759eb --- /dev/null +++ b/src/app/modules/admin/report/daily/components/index.ts @@ -0,0 +1,3 @@ +import { ListComponent } from './list.component'; + +export const COMPONENTS = [ListComponent]; diff --git a/src/app/modules/admin/report/daily/components/list.component.html b/src/app/modules/admin/report/daily/components/list.component.html new file mode 100644 index 0000000..567a713 --- /dev/null +++ b/src/app/modules/admin/report/daily/components/list.component.html @@ -0,0 +1,353 @@ +
+ +
+ +
+ +
+ +
일일현황
+ +
+ + + + + + + 40 + 60 + 80 + 100 + + + + + LV.1 + LV.2 + LV.3 + LV.4 + + + + + 정상 + 대기 + 탈퇴 + 휴면 + 블랙 + 정지 + + + + + 카지노제한 + 슬롯제한 + + + + + 계좌입금 + + + + + 카지노콤프 + 슬롯콤프 + 배팅콤프 + 첫충콤프 + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+ + +
+ There are no dailys! +
+
+
+
+
diff --git a/src/app/modules/admin/report/daily/components/list.component.ts b/src/app/modules/admin/report/daily/components/list.component.ts new file mode 100644 index 0000000..4881464 --- /dev/null +++ b/src/app/modules/admin/report/daily/components/list.component.ts @@ -0,0 +1,198 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { + debounceTime, + map, + merge, + Observable, + Subject, + switchMap, + takeUntil, +} from 'rxjs'; +import { fuseAnimations } from '@fuse/animations'; +import { FuseConfirmationService } from '@fuse/services/confirmation'; + +import { User } from '../../../member/user/models/user'; +import { Daily } from '../models/daily'; +import { DailyPagination } from '../models/daily-pagination'; +import { DailyService } from '../services/daily.service'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'partner-branch-list', + templateUrl: './list.component.html', + styles: [ + /* language=SCSS */ + ` + .inventory-grid { + grid-template-columns: 60px auto 40px; + + @screen sm { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px; + } + + @screen md { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px 60px; + } + + @screen lg { + grid-template-columns: 60px 70px 70px 70px 70px 100px 60px 60px auto 60px 60px 60px 60px; + } + } + `, + ], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + animations: fuseAnimations, +}) +export class ListComponent implements OnInit, AfterViewInit, OnDestroy { + @ViewChild(MatPaginator) private _paginator!: MatPaginator; + @ViewChild(MatSort) private _sort!: MatSort; + + dailys$!: Observable; + users$!: Observable; + + isLoading = false; + searchInputControl = new FormControl(); + selectedDaily?: Daily; + pagination?: DailyPagination; + + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _changeDetectorRef: ChangeDetectorRef, + private _fuseConfirmationService: FuseConfirmationService, + private _formBuilder: FormBuilder, + private _dailyService: DailyService, + private router: Router + ) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + // Get the pagination + this._dailyService.pagination$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((pagination: DailyPagination | undefined) => { + // Update the pagination + this.pagination = pagination; + + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + + // Get the products + this.dailys$ = this._dailyService.dailys$; + } + + /** + * After view init + */ + ngAfterViewInit(): void { + if (this._sort && this._paginator) { + // Set the initial sort + this._sort.sort({ + id: 'name', + start: 'asc', + disableClear: true, + }); + + // Mark for check + this._changeDetectorRef.markForCheck(); + + // If the daily changes the sort order... + this._sort.sortChange + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(() => { + // Reset back to the first page + this._paginator.pageIndex = 0; + }); + + // Get products if sort or page changes + merge(this._sort.sortChange, this._paginator.page) + .pipe( + switchMap(() => { + this.isLoading = true; + return this._dailyService.getDailys( + this._paginator.pageIndex, + this._paginator.pageSize, + this._sort.active, + this._sort.direction + ); + }), + map(() => { + this.isLoading = false; + }) + ) + .subscribe(); + } + } + + /** + * On destroy + */ + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + viewUserDetail(id: string): void { + let url: string = 'member/user/' + id; + this.router.navigateByUrl(url); + } + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create product + */ + __createProduct(): void {} + + /** + * Toggle product details + * + * @param productId + */ + __toggleDetails(productId: string): void {} + + /** + * Track by function for ngFor loops + * + * @param index + * @param item + */ + __trackByFn(index: number, item: any): any { + return item.id || index; + } +} diff --git a/src/app/modules/admin/report/daily/daily.module.ts b/src/app/modules/admin/report/daily/daily.module.ts new file mode 100644 index 0000000..2f20366 --- /dev/null +++ b/src/app/modules/admin/report/daily/daily.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatRippleModule } from '@angular/material/core'; +import { MatSortModule } from '@angular/material/sort'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { TranslocoModule } from '@ngneat/transloco'; + +import { SharedModule } from 'app/shared/shared.module'; + +import { COMPONENTS } from './components'; + +import { dailyRoutes } from './daily.routing'; + +@NgModule({ + declarations: [COMPONENTS], + imports: [ + TranslocoModule, + SharedModule, + RouterModule.forChild(dailyRoutes), + + MatButtonModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatPaginatorModule, + MatProgressBarModule, + MatRippleModule, + MatSortModule, + MatSelectModule, + MatTooltipModule, + MatGridListModule, + MatSlideToggleModule, + MatRadioModule, + MatCheckboxModule, + ], +}) +export class DailyModule {} diff --git a/src/app/modules/admin/report/daily/daily.routing.ts b/src/app/modules/admin/report/daily/daily.routing.ts new file mode 100644 index 0000000..c1d6831 --- /dev/null +++ b/src/app/modules/admin/report/daily/daily.routing.ts @@ -0,0 +1,24 @@ +import { Route } from '@angular/router'; + +import { ListComponent } from './components/list.component'; +import { ViewComponent } from '../../member/user/components/view.component'; + +import { DailysResolver } from './resolvers/daily.resolver'; +import { UserResolver } from '../../member/user/resolvers/user.resolver'; + +export const dailyRoutes: Route[] = [ + { + path: '', + component: ListComponent, + resolve: { + dailys: DailysResolver, + }, + }, + { + path: ':id', + component: ViewComponent, + resolve: { + users: UserResolver, + }, + }, +]; diff --git a/src/app/modules/admin/report/daily/models/daily-pagination.ts b/src/app/modules/admin/report/daily/models/daily-pagination.ts new file mode 100644 index 0000000..4ca3b2f --- /dev/null +++ b/src/app/modules/admin/report/daily/models/daily-pagination.ts @@ -0,0 +1,8 @@ +export interface DailyPagination { + length: number; + size: number; + page: number; + lastPage: number; + startIndex: number; + endIndex: number; +} diff --git a/src/app/modules/admin/report/daily/models/daily.ts b/src/app/modules/admin/report/daily/models/daily.ts new file mode 100644 index 0000000..8cc0bc2 --- /dev/null +++ b/src/app/modules/admin/report/daily/models/daily.ts @@ -0,0 +1,29 @@ +export interface Daily { + id?: string; + totalPartnerCount?: number; + totalHoldingMoney?: number; + totalComp?: number; + total?: number; + branchCount?: number; + divisionCount?: number; + officeCount?: number; + storeCount?: number; + memberCount?: number; + nickname?: string; + accountHolder?: string; + phoneNumber?: string; + calculateType?: string; + ownCash?: number; + ownComp?: number; + ownCoupon?: number; + gameMoney?: number; + todayComp?: number; + totalDeposit?: number; + totalWithdraw?: number; + balance?: number; + registDate?: string; + finalSigninDate?: string; + ip?: string; + state?: string; + note?: string; +} diff --git a/src/app/modules/admin/report/daily/resolvers/daily.resolver.ts b/src/app/modules/admin/report/daily/resolvers/daily.resolver.ts new file mode 100644 index 0000000..f7aae2a --- /dev/null +++ b/src/app/modules/admin/report/daily/resolvers/daily.resolver.ts @@ -0,0 +1,84 @@ +import { Injectable } from '@angular/core'; +import { + ActivatedRouteSnapshot, + Resolve, + Router, + RouterStateSnapshot, +} from '@angular/router'; +import { catchError, Observable, throwError } from 'rxjs'; + +import { Daily } from '../models/daily'; +import { DailyPagination } from '../models/daily-pagination'; +import { DailyService } from '../services/daily.service'; + +@Injectable({ + providedIn: 'root', +}) +export class DailyResolver implements Resolve { + /** + * Constructor + */ + constructor(private _dailyService: DailyService, private _router: Router) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable { + return this._dailyService.getDailyById(route.paramMap.get('id')).pipe( + // Error here means the requested product is not available + catchError((error) => { + // Log the error + console.error(error); + + // Get the parent url + const parentUrl = state.url.split('/').slice(0, -1).join('/'); + + // Navigate to there + this._router.navigateByUrl(parentUrl); + + // Throw an error + return throwError(error); + }) + ); + } +} + +@Injectable({ + providedIn: 'root', +}) +export class DailysResolver implements Resolve { + /** + * Constructor + */ + constructor(private _dailyService: DailyService) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable<{ + pagination: DailyPagination; + dailys: Daily[]; + }> { + return this._dailyService.getDailys(); + } +} diff --git a/src/app/modules/admin/report/daily/services/daily.service.ts b/src/app/modules/admin/report/daily/services/daily.service.ts new file mode 100644 index 0000000..d2b308f --- /dev/null +++ b/src/app/modules/admin/report/daily/services/daily.service.ts @@ -0,0 +1,153 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { + BehaviorSubject, + filter, + map, + Observable, + of, + switchMap, + take, + tap, + throwError, +} from 'rxjs'; + +import { Daily } from '../models/daily'; +import { DailyPagination } from '../models/daily-pagination'; + +@Injectable({ + providedIn: 'root', +}) +export class DailyService { + // Private + private __pagination = new BehaviorSubject( + undefined + ); + private __daily = new BehaviorSubject(undefined); + private __dailys = new BehaviorSubject(undefined); + + /** + * Constructor + */ + constructor(private _httpClient: HttpClient) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Accessors + // ----------------------------------------------------------------------------------------------------- + + /** + * Getter for pagination + */ + get pagination$(): Observable { + return this.__pagination.asObservable(); + } + + /** + * Getter for daily + */ + get daily$(): Observable { + return this.__daily.asObservable(); + } + + /** + * Getter for dailys + */ + get dailys$(): Observable { + return this.__dailys.asObservable(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Get Dailys + * + * + * @param page + * @param size + * @param sort + * @param order + * @param search + */ + getDailys( + page: number = 0, + size: number = 10, + sort: string = 'name', + order: 'asc' | 'desc' | '' = 'asc', + search: string = '' + ): Observable<{ + pagination: DailyPagination; + dailys: Daily[]; + }> { + return this._httpClient + .get<{ + pagination: DailyPagination; + dailys: Daily[]; + }>('api/apps/member/partner-branch/partner-branchs', { + params: { + page: '' + page, + size: '' + size, + sort, + order, + search, + }, + }) + .pipe( + tap((response) => { + this.__pagination.next(response.pagination); + this.__dailys.next(response.dailys); + }) + ); + } + + /** + * Get product by id + */ + getDailyById(id: string | null): Observable { + return this.__dailys.pipe( + take(1), + map((dailys) => { + // Find the product + const daily = dailys?.find((item) => item.id === id) || undefined; + + // Update the product + this.__daily.next(daily); + + // Return the product + return daily; + }), + switchMap((product) => { + if (!product) { + return throwError('Could not found product with id of ' + id + '!'); + } + + return of(product); + }) + ); + } + + /** + * Create product + */ + createDaily(): Observable { + return this.dailys$.pipe( + take(1), + switchMap((dailys) => + this._httpClient + .post('api/apps/member/partner-branch/product', {}) + .pipe( + map((newDaily) => { + // Update the dailys with the new product + if (!!dailys) { + this.__dailys.next([newDaily, ...dailys]); + } + + // Return the new product + return newDaily; + }) + ) + ) + ); + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 3e06456..5b163c3 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -23,5 +23,6 @@ "Casino": "Casino", "Evolution": "Evolution", "Slot": "Slot", - "Current User": "Current User" + "Current User": "Current User", + "Daily": "Daily" } diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index 8ecef03..1100756 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -23,5 +23,6 @@ "Casino": "카지노배팅리스트", "Evolution": "에볼루션배팅리스트", "Slot": "슬롯배팅리스트", - "Current User": "현재접속자 & 쪽지전송" + "Current User": "현재접속자 & 쪽지전송", + "Daily": "일일현황" } From 0440325e6702c6bbfecbe278505766dc3698926f Mon Sep 17 00:00:00 2001 From: JUNG YI DAM Date: Wed, 13 Jul 2022 09:16:31 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=EC=9B=94=ED=98=84=ED=99=A9=20page=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.routing.ts | 7 + src/app/mock-api/apps/report/daily/api.ts | 66 ++-- src/app/mock-api/apps/report/monthly/api.ts | 217 +++++++++++ src/app/mock-api/apps/report/monthly/data.ts | 33 ++ src/app/mock-api/common/navigation/data.ts | 7 + src/app/mock-api/index.ts | 2 + .../report/daily/services/daily.service.ts | 24 +- .../admin/report/monthly/components/index.ts | 3 + .../monthly/components/list.component.html | 355 ++++++++++++++++++ .../monthly/components/list.component.ts | 198 ++++++++++ .../monthly/models/monthly-pagination.ts | 8 + .../admin/report/monthly/models/monthly.ts | 29 ++ .../admin/report/monthly/monthly.module.ts | 50 +++ .../admin/report/monthly/monthly.routing.ts | 24 ++ .../monthly/resolvers/monthly.resolver.ts | 87 +++++ .../monthly/services/monthly.service.ts | 153 ++++++++ 16 files changed, 1216 insertions(+), 47 deletions(-) create mode 100644 src/app/mock-api/apps/report/monthly/api.ts create mode 100644 src/app/mock-api/apps/report/monthly/data.ts create mode 100644 src/app/modules/admin/report/monthly/components/index.ts create mode 100644 src/app/modules/admin/report/monthly/components/list.component.html create mode 100644 src/app/modules/admin/report/monthly/components/list.component.ts create mode 100644 src/app/modules/admin/report/monthly/models/monthly-pagination.ts create mode 100644 src/app/modules/admin/report/monthly/models/monthly.ts create mode 100644 src/app/modules/admin/report/monthly/monthly.module.ts create mode 100644 src/app/modules/admin/report/monthly/monthly.routing.ts create mode 100644 src/app/modules/admin/report/monthly/resolvers/monthly.resolver.ts create mode 100644 src/app/modules/admin/report/monthly/services/monthly.service.ts diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index ecae6ef..43fbd45 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -305,6 +305,13 @@ export const appRoutes: Route[] = [ (m: any) => m.DailyModule ), }, + { + path: 'monthly', + loadChildren: () => + import('app/modules/admin/report/monthly/monthly.module').then( + (m: any) => m.MonthlyModule + ), + }, ], }, ], diff --git a/src/app/mock-api/apps/report/daily/api.ts b/src/app/mock-api/apps/report/daily/api.ts index 2f7e710..1809429 100644 --- a/src/app/mock-api/apps/report/daily/api.ts +++ b/src/app/mock-api/apps/report/daily/api.ts @@ -29,7 +29,7 @@ export class ReportDailyMockApi { // @ Dailys - GET // ----------------------------------------------------------------------------------------------------- this._fuseMockApiService - .onGet('api/apps/member/partner-branch/partner-branchs', 300) + .onGet('api/apps/report/daily/dailys', 300) .reply(({ request }) => { // Get available queries const search = request.params.get('search'); @@ -115,7 +115,7 @@ export class ReportDailyMockApi { // @ Daily - GET // ----------------------------------------------------------------------------------------------------- this._fuseMockApiService - .onGet('api/apps/member/partne-branch/partner-branch') + .onGet('api/apps/report/daily/daily') .reply(({ request }) => { // Get the id from the params const id = request.params.get('id'); @@ -133,44 +133,42 @@ export class ReportDailyMockApi { // ----------------------------------------------------------------------------------------------------- // @ Daily - POST // ----------------------------------------------------------------------------------------------------- - this._fuseMockApiService - .onPost('api/apps/member/partner-branch/partner-branch') - .reply(() => { - // Generate a new daily - const newDaily = { - id: FuseMockApiUtils.guid(), - category: '', - name: 'A New User', - description: '', - tags: [], - sku: '', - barcode: '', - brand: '', - vendor: '', - stock: '', - reserved: '', - cost: '', - basePrice: '', - taxPercent: '', - price: '', - weight: '', - thumbnail: '', - images: [], - active: false, - }; + this._fuseMockApiService.onPost('api/apps/report/daily/daily').reply(() => { + // Generate a new daily + const newDaily = { + id: FuseMockApiUtils.guid(), + category: '', + name: 'A New User', + description: '', + tags: [], + sku: '', + barcode: '', + brand: '', + vendor: '', + stock: '', + reserved: '', + cost: '', + basePrice: '', + taxPercent: '', + price: '', + weight: '', + thumbnail: '', + images: [], + active: false, + }; - // Unshift the new daily - this._dailys.unshift(newDaily); + // Unshift the new daily + this._dailys.unshift(newDaily); - // Return the response - return [200, newDaily]; - }); + // Return the response + return [200, newDaily]; + }); // ----------------------------------------------------------------------------------------------------- // @ Daily - PATCH // ----------------------------------------------------------------------------------------------------- this._fuseMockApiService - .onPatch('api/apps/member/partner-branch/partner-branch') + .onPatch('api/apps/report/daily/daily') .reply(({ request }) => { // Get the id and daily const id = request.body.id; @@ -198,7 +196,7 @@ export class ReportDailyMockApi { // @ Daily - DELETE // ----------------------------------------------------------------------------------------------------- this._fuseMockApiService - .onDelete('api/apps/member/partner-branch/partner-branch') + .onDelete('api/apps/report/daily/daily') .reply(({ request }) => { // Get the id const id = request.params.get('id'); diff --git a/src/app/mock-api/apps/report/monthly/api.ts b/src/app/mock-api/apps/report/monthly/api.ts new file mode 100644 index 0000000..e7be7ac --- /dev/null +++ b/src/app/mock-api/apps/report/monthly/api.ts @@ -0,0 +1,217 @@ +import { Injectable } from '@angular/core'; +import { assign, cloneDeep } from 'lodash-es'; +import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api'; +import { monthlys as monthlysData } from './data'; + +@Injectable({ + providedIn: 'root', +}) +export class ReportMonthlyMockApi { + private _monthlys: any[] = monthlysData; + + /** + * Constructor + */ + constructor(private _fuseMockApiService: FuseMockApiService) { + // Register Mock API handlers + this.registerHandlers(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Register Mock API handlers + */ + registerHandlers(): void { + // ----------------------------------------------------------------------------------------------------- + // @ Monthlys - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/report/montyly/monthlys', 300) + .reply(({ request }) => { + // Get available queries + const search = request.params.get('search'); + const sort = request.params.get('sort') || 'name'; + const order = request.params.get('order') || 'asc'; + const page = parseInt(request.params.get('page') ?? '1', 10); + const size = parseInt(request.params.get('size') ?? '10', 10); + + // Clone the monthlys + let monthlys: any[] | null = cloneDeep(this._monthlys); + + // Sort the monthlys + if (sort === 'sku' || sort === 'name' || sort === 'active') { + monthlys.sort((a, b) => { + const fieldA = a[sort].toString().toUpperCase(); + const fieldB = b[sort].toString().toUpperCase(); + return order === 'asc' + ? fieldA.localeCompare(fieldB) + : fieldB.localeCompare(fieldA); + }); + } else { + monthlys.sort((a, b) => + order === 'asc' ? a[sort] - b[sort] : b[sort] - a[sort] + ); + } + + // If search exists... + if (search) { + // Filter the monthlys + monthlys = monthlys.filter( + (contact: any) => + contact.name && + contact.name.toLowerCase().includes(search.toLowerCase()) + ); + } + + // Paginate - Start + const monthlysLength = monthlys.length; + + // Calculate pagination details + const begin = page * size; + const end = Math.min(size * (page + 1), monthlysLength); + const lastPage = Math.max(Math.ceil(monthlysLength / size), 1); + + // Prepare the pagination object + let pagination = {}; + + // If the requested page number is bigger than + // the last possible page number, return null for + // monthlys but also send the last possible page so + // the app can navigate to there + if (page > lastPage) { + monthlys = null; + pagination = { + lastPage, + }; + } else { + // Paginate the results by size + monthlys = monthlys.slice(begin, end); + + // Prepare the pagination mock-api + pagination = { + length: monthlysLength, + size: size, + page: page, + lastPage: lastPage, + startIndex: begin, + endIndex: end - 1, + }; + } + + // Return the response + return [ + 200, + { + monthlys, + pagination, + }, + ]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Monthly - GET + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onGet('api/apps/report/monthly/monthly') + .reply(({ request }) => { + // Get the id from the params + const id = request.params.get('id'); + + // Clone the monthlys + const monthlys = cloneDeep(this._monthlys); + + // Find the monthly + const monthly = monthlys.find((item: any) => item.id === id); + + // Return the response + return [200, monthly]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Monthly - POST + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPost('api/apps/report/monthly/monthly') + .reply(() => { + // Generate a new monthly + const newMonthly = { + id: FuseMockApiUtils.guid(), + category: '', + name: 'A New User', + description: '', + tags: [], + sku: '', + barcode: '', + brand: '', + vendor: '', + stock: '', + reserved: '', + cost: '', + basePrice: '', + taxPercent: '', + price: '', + weight: '', + thumbnail: '', + images: [], + active: false, + }; + + // Unshift the new monthly + this._monthlys.unshift(newMonthly); + + // Return the response + return [200, newMonthly]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Monthly - PATCH + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onPatch('api/apps/report/monthly/monthly') + .reply(({ request }) => { + // Get the id and monthly + const id = request.body.id; + const monthly = cloneDeep(request.body.monthly); + + // Prepare the updated monthly + let updatedMonthly = null; + + // Find the monthly and update it + this._monthlys.forEach((item, index, monthlys) => { + if (item.id === id) { + // Update the monthly + monthlys[index] = assign({}, monthlys[index], monthly); + + // Store the updated monthly + updatedMonthly = monthlys[index]; + } + }); + + // Return the response + return [200, updatedMonthly]; + }); + + // ----------------------------------------------------------------------------------------------------- + // @ Monthly - DELETE + // ----------------------------------------------------------------------------------------------------- + this._fuseMockApiService + .onDelete('api/apps/report/monthly/monthly') + .reply(({ request }) => { + // Get the id + const id = request.params.get('id'); + + // Find the monthly and delete it + this._monthlys.forEach((item, index) => { + if (item.id === id) { + this._monthlys.splice(index, 1); + } + }); + + // Return the response + return [200, true]; + }); + } +} diff --git a/src/app/mock-api/apps/report/monthly/data.ts b/src/app/mock-api/apps/report/monthly/data.ts new file mode 100644 index 0000000..be97b0c --- /dev/null +++ b/src/app/mock-api/apps/report/monthly/data.ts @@ -0,0 +1,33 @@ +/* eslint-disable */ + +export const monthlys = [ + { + id: 'on00', + totalPartnerCount: '5', + totalHoldingMoney: 303675, + totalComp: 108933, + total: 412608, + branchCount: 1, + divisionCount: 1, + officeCount: 1, + storeCount: 1, + memberCount: 1, + nickname: 'on00', + accountHolder: '11', + phoneNumber: '010-1111-1111', + calculateType: '롤링', + ownCash: 50000, + ownComp: 1711, + ownCoupon: 50000, + gameMoney: 0, + todayComp: 0, + totalDeposit: 0, + totalWithdraw: 0, + balance: 0, + registDate: '2022-06-12 15:38', + finalSigninDate: '', + ip: '', + state: '정상', + note: '', + }, +]; diff --git a/src/app/mock-api/common/navigation/data.ts b/src/app/mock-api/common/navigation/data.ts index 469365c..0c88ec9 100644 --- a/src/app/mock-api/common/navigation/data.ts +++ b/src/app/mock-api/common/navigation/data.ts @@ -213,6 +213,13 @@ export const defaultNavigation: FuseNavigationItem[] = [ icon: 'heroicons_outline:academic-cap', link: '/report/daily', }, + { + id: 'report.monthly', + title: 'Monthly', + type: 'basic', + icon: 'heroicons_outline:academic-cap', + link: '/report/monthly', + }, ], }, ]; diff --git a/src/app/mock-api/index.ts b/src/app/mock-api/index.ts index ae2cbc5..d96ff43 100644 --- a/src/app/mock-api/index.ts +++ b/src/app/mock-api/index.ts @@ -41,6 +41,7 @@ import { GameCasinoMockApi } from './apps/game/casino/api'; import { GameEvolutionMockApi } from './apps/game/evolution/api'; import { GameSlotMockApi } from './apps/game/slot/api'; import { ReportDailyMockApi } from './apps/report/daily/api'; +import { ReportMonthlyMockApi } from './apps/report/monthly/api'; export const mockApiServices = [ AcademyMockApi, @@ -86,4 +87,5 @@ export const mockApiServices = [ GameEvolutionMockApi, GameSlotMockApi, ReportDailyMockApi, + ReportMonthlyMockApi, ]; diff --git a/src/app/modules/admin/report/daily/services/daily.service.ts b/src/app/modules/admin/report/daily/services/daily.service.ts index d2b308f..b92c54a 100644 --- a/src/app/modules/admin/report/daily/services/daily.service.ts +++ b/src/app/modules/admin/report/daily/services/daily.service.ts @@ -84,7 +84,7 @@ export class DailyService { .get<{ pagination: DailyPagination; dailys: Daily[]; - }>('api/apps/member/partner-branch/partner-branchs', { + }>('api/apps/repoart/daily/dailys', { params: { page: '' + page, size: '' + size, @@ -134,19 +134,17 @@ export class DailyService { return this.dailys$.pipe( take(1), switchMap((dailys) => - this._httpClient - .post('api/apps/member/partner-branch/product', {}) - .pipe( - map((newDaily) => { - // Update the dailys with the new product - if (!!dailys) { - this.__dailys.next([newDaily, ...dailys]); - } + this._httpClient.post('api/apps/report/daily/product', {}).pipe( + map((newDaily) => { + // Update the dailys with the new product + if (!!dailys) { + this.__dailys.next([newDaily, ...dailys]); + } - // Return the new product - return newDaily; - }) - ) + // Return the new product + return newDaily; + }) + ) ) ); } diff --git a/src/app/modules/admin/report/monthly/components/index.ts b/src/app/modules/admin/report/monthly/components/index.ts new file mode 100644 index 0000000..04759eb --- /dev/null +++ b/src/app/modules/admin/report/monthly/components/index.ts @@ -0,0 +1,3 @@ +import { ListComponent } from './list.component'; + +export const COMPONENTS = [ListComponent]; diff --git a/src/app/modules/admin/report/monthly/components/list.component.html b/src/app/modules/admin/report/monthly/components/list.component.html new file mode 100644 index 0000000..7238dcb --- /dev/null +++ b/src/app/modules/admin/report/monthly/components/list.component.html @@ -0,0 +1,355 @@ +
+ +
+ +
+ +
+ +
월현황
+ +
+ + + + + + + 40 + 60 + 80 + 100 + + + + + LV.1 + LV.2 + LV.3 + LV.4 + + + + + 정상 + 대기 + 탈퇴 + 휴면 + 블랙 + 정지 + + + + + 카지노제한 + 슬롯제한 + + + + + 계좌입금 + + + + + 카지노콤프 + 슬롯콤프 + 배팅콤프 + 첫충콤프 + + + + + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+ + +
+ There are no monthlys! +
+
+
+
+
diff --git a/src/app/modules/admin/report/monthly/components/list.component.ts b/src/app/modules/admin/report/monthly/components/list.component.ts new file mode 100644 index 0000000..8bd7258 --- /dev/null +++ b/src/app/modules/admin/report/monthly/components/list.component.ts @@ -0,0 +1,198 @@ +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { + debounceTime, + map, + merge, + Observable, + Subject, + switchMap, + takeUntil, +} from 'rxjs'; +import { fuseAnimations } from '@fuse/animations'; +import { FuseConfirmationService } from '@fuse/services/confirmation'; + +import { User } from '../../../member/user/models/user'; +import { Monthly } from '../models/monthly'; +import { MonthlyPagination } from '../models/monthly-pagination'; +import { MonthlyService } from '../services/monthly.service'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'partner-branch-list', + templateUrl: './list.component.html', + styles: [ + /* language=SCSS */ + ` + .inventory-grid { + grid-template-columns: 60px auto 40px; + + @screen sm { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px; + } + + @screen md { + grid-template-columns: 60px 60px 60px 60px 60px 60px auto 60px 60px; + } + + @screen lg { + grid-template-columns: 60px 70px 70px 70px 70px 100px 60px 60px auto 60px 60px 60px 60px; + } + } + `, + ], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + animations: fuseAnimations, +}) +export class ListComponent implements OnInit, AfterViewInit, OnDestroy { + @ViewChild(MatPaginator) private _paginator!: MatPaginator; + @ViewChild(MatSort) private _sort!: MatSort; + + monthlys$!: Observable; + users$!: Observable; + + isLoading = false; + searchInputControl = new FormControl(); + selectedMonthly?: Monthly; + pagination?: MonthlyPagination; + + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _changeDetectorRef: ChangeDetectorRef, + private _fuseConfirmationService: FuseConfirmationService, + private _formBuilder: FormBuilder, + private _monthlyService: MonthlyService, + private router: Router + ) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + // Get the pagination + this._monthlyService.pagination$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe((pagination: MonthlyPagination | undefined) => { + // Update the pagination + this.pagination = pagination; + + // Mark for check + this._changeDetectorRef.markForCheck(); + }); + + // Get the products + this.monthlys$ = this._monthlyService.monthlys$; + } + + /** + * After view init + */ + ngAfterViewInit(): void { + if (this._sort && this._paginator) { + // Set the initial sort + this._sort.sort({ + id: 'name', + start: 'asc', + disableClear: true, + }); + + // Mark for check + this._changeDetectorRef.markForCheck(); + + // If the monthly changes the sort order... + this._sort.sortChange + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(() => { + // Reset back to the first page + this._paginator.pageIndex = 0; + }); + + // Get products if sort or page changes + merge(this._sort.sortChange, this._paginator.page) + .pipe( + switchMap(() => { + this.isLoading = true; + return this._monthlyService.getMonthlys( + this._paginator.pageIndex, + this._paginator.pageSize, + this._sort.active, + this._sort.direction + ); + }), + map(() => { + this.isLoading = false; + }) + ) + .subscribe(); + } + } + + /** + * On destroy + */ + ngOnDestroy(): void { + // Unsubscribe from all subscriptions + this._unsubscribeAll.next(null); + this._unsubscribeAll.complete(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + viewUserDetail(id: string): void { + let url: string = 'member/user/' + id; + this.router.navigateByUrl(url); + } + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Create product + */ + __createProduct(): void {} + + /** + * Toggle product details + * + * @param productId + */ + __toggleDetails(productId: string): void {} + + /** + * Track by function for ngFor loops + * + * @param index + * @param item + */ + __trackByFn(index: number, item: any): any { + return item.id || index; + } +} diff --git a/src/app/modules/admin/report/monthly/models/monthly-pagination.ts b/src/app/modules/admin/report/monthly/models/monthly-pagination.ts new file mode 100644 index 0000000..afd5b82 --- /dev/null +++ b/src/app/modules/admin/report/monthly/models/monthly-pagination.ts @@ -0,0 +1,8 @@ +export interface MonthlyPagination { + length: number; + size: number; + page: number; + lastPage: number; + startIndex: number; + endIndex: number; +} diff --git a/src/app/modules/admin/report/monthly/models/monthly.ts b/src/app/modules/admin/report/monthly/models/monthly.ts new file mode 100644 index 0000000..fc883d8 --- /dev/null +++ b/src/app/modules/admin/report/monthly/models/monthly.ts @@ -0,0 +1,29 @@ +export interface Monthly { + id?: string; + totalPartnerCount?: number; + totalHoldingMoney?: number; + totalComp?: number; + total?: number; + branchCount?: number; + divisionCount?: number; + officeCount?: number; + storeCount?: number; + memberCount?: number; + nickname?: string; + accountHolder?: string; + phoneNumber?: string; + calculateType?: string; + ownCash?: number; + ownComp?: number; + ownCoupon?: number; + gameMoney?: number; + todayComp?: number; + totalDeposit?: number; + totalWithdraw?: number; + balance?: number; + registDate?: string; + finalSigninDate?: string; + ip?: string; + state?: string; + note?: string; +} diff --git a/src/app/modules/admin/report/monthly/monthly.module.ts b/src/app/modules/admin/report/monthly/monthly.module.ts new file mode 100644 index 0000000..cb0abdf --- /dev/null +++ b/src/app/modules/admin/report/monthly/monthly.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatRippleModule } from '@angular/material/core'; +import { MatSortModule } from '@angular/material/sort'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +import { TranslocoModule } from '@ngneat/transloco'; + +import { SharedModule } from 'app/shared/shared.module'; + +import { COMPONENTS } from './components'; + +import { monthlyRoutes } from './monthly.routing'; + +@NgModule({ + declarations: [COMPONENTS], + imports: [ + TranslocoModule, + SharedModule, + RouterModule.forChild(monthlyRoutes), + + MatButtonModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatPaginatorModule, + MatProgressBarModule, + MatRippleModule, + MatSortModule, + MatSelectModule, + MatTooltipModule, + MatGridListModule, + MatSlideToggleModule, + MatRadioModule, + MatCheckboxModule, + ], +}) +export class MonthlyModule {} diff --git a/src/app/modules/admin/report/monthly/monthly.routing.ts b/src/app/modules/admin/report/monthly/monthly.routing.ts new file mode 100644 index 0000000..b2ce27c --- /dev/null +++ b/src/app/modules/admin/report/monthly/monthly.routing.ts @@ -0,0 +1,24 @@ +import { Route } from '@angular/router'; + +import { ListComponent } from './components/list.component'; +import { ViewComponent } from '../../member/user/components/view.component'; + +import { MonthlysResolver } from './resolvers/monthly.resolver'; +import { UserResolver } from '../../member/user/resolvers/user.resolver'; + +export const monthlyRoutes: Route[] = [ + { + path: '', + component: ListComponent, + resolve: { + monthlys: MonthlysResolver, + }, + }, + { + path: ':id', + component: ViewComponent, + resolve: { + users: UserResolver, + }, + }, +]; diff --git a/src/app/modules/admin/report/monthly/resolvers/monthly.resolver.ts b/src/app/modules/admin/report/monthly/resolvers/monthly.resolver.ts new file mode 100644 index 0000000..ca31eb8 --- /dev/null +++ b/src/app/modules/admin/report/monthly/resolvers/monthly.resolver.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@angular/core'; +import { + ActivatedRouteSnapshot, + Resolve, + Router, + RouterStateSnapshot, +} from '@angular/router'; +import { catchError, Observable, throwError } from 'rxjs'; + +import { Monthly } from '../models/monthly'; +import { MonthlyPagination } from '../models/monthly-pagination'; +import { MonthlyService } from '../services/monthly.service'; + +@Injectable({ + providedIn: 'root', +}) +export class MonthlyResolver implements Resolve { + /** + * Constructor + */ + constructor( + private _monthlyService: MonthlyService, + private _router: Router + ) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable { + return this._monthlyService.getMonthlyById(route.paramMap.get('id')).pipe( + // Error here means the requested product is not available + catchError((error) => { + // Log the error + console.error(error); + + // Get the parent url + const parentUrl = state.url.split('/').slice(0, -1).join('/'); + + // Navigate to there + this._router.navigateByUrl(parentUrl); + + // Throw an error + return throwError(error); + }) + ); + } +} + +@Injectable({ + providedIn: 'root', +}) +export class MonthlysResolver implements Resolve { + /** + * Constructor + */ + constructor(private _monthlyService: MonthlyService) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Resolver + * + * @param route + * @param state + */ + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable<{ + pagination: MonthlyPagination; + monthlys: Monthly[]; + }> { + return this._monthlyService.getMonthlys(); + } +} diff --git a/src/app/modules/admin/report/monthly/services/monthly.service.ts b/src/app/modules/admin/report/monthly/services/monthly.service.ts new file mode 100644 index 0000000..6c5f7fe --- /dev/null +++ b/src/app/modules/admin/report/monthly/services/monthly.service.ts @@ -0,0 +1,153 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { + BehaviorSubject, + filter, + map, + Observable, + of, + switchMap, + take, + tap, + throwError, +} from 'rxjs'; + +import { Monthly } from '../models/monthly'; +import { MonthlyPagination } from '../models/monthly-pagination'; + +@Injectable({ + providedIn: 'root', +}) +export class MonthlyService { + // Private + private __pagination = new BehaviorSubject( + undefined + ); + private __monthly = new BehaviorSubject(undefined); + private __monthlys = new BehaviorSubject(undefined); + + /** + * Constructor + */ + constructor(private _httpClient: HttpClient) {} + + // ----------------------------------------------------------------------------------------------------- + // @ Accessors + // ----------------------------------------------------------------------------------------------------- + + /** + * Getter for pagination + */ + get pagination$(): Observable { + return this.__pagination.asObservable(); + } + + /** + * Getter for monthly + */ + get monthly$(): Observable { + return this.__monthly.asObservable(); + } + + /** + * Getter for monthlys + */ + get monthlys$(): Observable { + return this.__monthlys.asObservable(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Get Monthlys + * + * + * @param page + * @param size + * @param sort + * @param order + * @param search + */ + getMonthlys( + page: number = 0, + size: number = 10, + sort: string = 'name', + order: 'asc' | 'desc' | '' = 'asc', + search: string = '' + ): Observable<{ + pagination: MonthlyPagination; + monthlys: Monthly[]; + }> { + return this._httpClient + .get<{ + pagination: MonthlyPagination; + monthlys: Monthly[]; + }>('api/apps/report/monthly/monthlys', { + params: { + page: '' + page, + size: '' + size, + sort, + order, + search, + }, + }) + .pipe( + tap((response) => { + this.__pagination.next(response.pagination); + this.__monthlys.next(response.monthlys); + }) + ); + } + + /** + * Get product by id + */ + getMonthlyById(id: string | null): Observable { + return this.__monthlys.pipe( + take(1), + map((monthlys) => { + // Find the product + const monthly = monthlys?.find((item) => item.id === id) || undefined; + + // Update the product + this.__monthly.next(monthly); + + // Return the product + return monthly; + }), + switchMap((product) => { + if (!product) { + return throwError('Could not found product with id of ' + id + '!'); + } + + return of(product); + }) + ); + } + + /** + * Create product + */ + createMonthly(): Observable { + return this.monthlys$.pipe( + take(1), + switchMap((monthlys) => + this._httpClient + .post('api/apps/report/monthly/product', {}) + .pipe( + map((newMonthly) => { + // Update the monthlys with the new product + if (!!monthlys) { + this.__monthlys.next([newMonthly, ...monthlys]); + } + + // Return the new product + return newMonthly; + }) + ) + ) + ); + } +}