Skip to content

Commit 2d4c6a2

Browse files
Merge pull request #48 from akbarsaputrait/fix-46-47
Owner & Staff - Dashboard
2 parents 844b703 + 1a0437d commit 2d4c6a2

18 files changed

+346
-20
lines changed

src/app.controller.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Quero } from '@core/decorators/quero.decorator';
2+
import { Owner } from '@db/entities/owner/owner.entity';
23
import base64url from '@lib/helpers/base64.helper';
34
import { config } from '@lib/helpers/config.helper';
5+
import AppDataSource from '@lib/typeorm/datasource.typeorm';
46
import { Controller, Get, Res } from '@nestjs/common';
57
import * as fs from 'fs';
68
import { lookup } from 'mime-types';
@@ -10,6 +12,9 @@ import * as path from 'path';
1012
export class AppController {
1113
@Get()
1214
async getHello(@Res() response) {
15+
const check = await AppDataSource.createQueryBuilder(Owner, 't1').getCount();
16+
console.log({ check });
17+
1318
return response.send('Ordero API V2');
1419
}
1520

src/app.routes.ts

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { OwnerAuthModule } from './app/owner/auth/auth.module';
77
import { OwnerModule } from './app/owner/owner.module';
88
import { OwnerProfileModule } from './app/owner/profile/profile.module';
99
import { OwnerCategoryModule } from './app/owner/restaurant/category/category.module';
10+
import { OwnerDashboardModule } from './app/owner/restaurant/dashboard/dashboard.module';
1011
import { OwnerLocationModule } from './app/owner/restaurant/location/location.module';
1112
import { OwnerNotificationModule } from './app/owner/restaurant/notification/notification.module';
1213
import { OwnerOrderModule } from './app/owner/restaurant/order/order.module';
@@ -20,6 +21,7 @@ import { RestaurantModule } from './app/restaurant/restaurant.module';
2021
import { StaffAuthModule } from './app/staff/auth/auth.module';
2122
import { StaffProfileModule } from './app/staff/profile/profile.module';
2223
import { StaffCategoryModule } from './app/staff/restaurant/category/category.module';
24+
import { StaffDashboardModule } from './app/staff/restaurant/dashboard/dashboard.module';
2325
import { StafffNotificationModule } from './app/staff/restaurant/notification/notification.module';
2426
import { StaffOrderModule } from './app/staff/restaurant/order/order.module';
2527
import { StaffProductModule } from './app/staff/restaurant/product/product.module';
@@ -78,6 +80,10 @@ export const routes: Routes = [
7880
path: '/:restaurant_id/notifications',
7981
module: OwnerNotificationModule,
8082
},
83+
{
84+
path: '/:restaurant_id/dashboard',
85+
module: OwnerDashboardModule,
86+
},
8187
],
8288
},
8389
],
@@ -121,6 +127,10 @@ export const routes: Routes = [
121127
path: '/:restaurant_id/notifications',
122128
module: StafffNotificationModule,
123129
},
130+
{
131+
path: '/:restaurant_id/dashboard',
132+
module: StaffDashboardModule,
133+
},
124134
],
125135
},
126136
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { Loc } from '@core/decorators/location.decorator';
2+
import { Rest } from '@core/decorators/restaurant.decorator';
3+
import { OwnerAuthGuard } from '@core/guards/auth.guard';
4+
import { OwnerGuard } from '@core/guards/owner.guard';
5+
import { PermAct, PermOwner } from '@core/services/role.service';
6+
import { OrderProduct } from '@db/entities/core/order-product.entity';
7+
import { Order, OrderStatus } from '@db/entities/core/order.entity';
8+
import { Location } from '@db/entities/owner/location.entity';
9+
import { ProductVariant } from '@db/entities/owner/product-variant.entity';
10+
import { Product } from '@db/entities/owner/product.entity';
11+
import { getChartData } from '@lib/helpers/utils.helper';
12+
import { Permissions } from '@lib/rbac';
13+
import AppDataSource from '@lib/typeorm/datasource.typeorm';
14+
import { uuid } from '@lib/uid/uuid.library';
15+
import { Controller, Get, Res, UseGuards } from '@nestjs/common';
16+
17+
@Controller()
18+
@UseGuards(OwnerAuthGuard(), OwnerGuard)
19+
export class DashboardController {
20+
@Get('orders/total')
21+
@Permissions(`${PermOwner.Dashboard}@${PermAct.R}`)
22+
async orderTotal(@Rest() rest, @Loc() loc: Location, @Res() response) {
23+
const query = AppDataSource.createQueryBuilder(Order, 't1').where({ restaurant_id: rest.id });
24+
25+
if (loc) {
26+
query.andWhere({ location_id: loc.id });
27+
}
28+
29+
const data = await query.search().dateRange().getCount();
30+
return response.data({ id: uuid(), data });
31+
}
32+
33+
@Get('sales/total')
34+
@Permissions(`${PermOwner.Dashboard}@${PermAct.R}`)
35+
async salesTotal(@Rest() rest, @Loc() loc: Location, @Res() response) {
36+
const query = AppDataSource.createQueryBuilder(Order, 't1').where({ restaurant_id: rest.id });
37+
38+
if (loc) {
39+
query.andWhere({ location_id: loc.id });
40+
}
41+
42+
query.selectWithAlias([
43+
'_UUID() as id',
44+
'_IFNULL(SUM(t1.gross_total),0) AS total',
45+
'_IFNULL(SUM(t1.net_total),0) AS net_total',
46+
]);
47+
48+
const data = await query.search().dateRange().getRawOne();
49+
return response.data({ id: uuid(), data });
50+
}
51+
52+
@Get('sales/chart')
53+
@Permissions(`${PermOwner.Dashboard}@${PermAct.R}`)
54+
async salesChart(@Rest() rest, @Loc() loc: Location, @Res() response) {
55+
const query = AppDataSource.createQueryBuilder(Order, 't1')
56+
.where({ restaurant_id: rest.id })
57+
.andWhere('t1.status = :status', { status: OrderStatus.Completed })
58+
.groupBy('t1.created_at')
59+
.selectWithAlias(['_IFNULL(SUM(t1.net_total),0) AS net_total', 'created_at']);
60+
61+
if (loc) {
62+
query.andWhere({ location_id: loc.id });
63+
}
64+
const orders = await query.dateRange().getRawMany();
65+
66+
const { start, end } = AppDataSource.createQueryBuilder(Order, 't1').dateRange().getParameters();
67+
const results = await getChartData({ start, end }, orders, 'net_total');
68+
69+
return response.data(results);
70+
}
71+
72+
@Get('top/menus')
73+
@Permissions(`${PermOwner.Dashboard}@${PermAct.R}`)
74+
async topMenus(@Rest() rest, @Loc() loc: Location, @Res() response) {
75+
const query = AppDataSource.createQueryBuilder(OrderProduct, 't1')
76+
.leftJoin(Order, 't2', 't1.order_id = t2.id')
77+
.leftJoin(ProductVariant, 't3', 't1.product_variant_id = t3.id')
78+
.leftJoin(Product, 't4', 't3.product_id = t4.id')
79+
.where('t2.restaurant_id = :restId', { restId: rest.id })
80+
.andWhere('t2.status NOT IN (:status)', { status: [OrderStatus.Cancelled] });
81+
82+
if (loc) {
83+
query.andWhere('t2.location_id = :locId', { locId: loc.id });
84+
}
85+
86+
query
87+
.selectWithAlias(['_(t1.product_variant_id) AS id', 't4.name', 't4.sku', '_COUNT(t1.product_variant_id) AS count'])
88+
.groupBy('t1.product_variant_id')
89+
.orderBy('count', 'DESC')
90+
.limit(5);
91+
92+
const results = await query.dateRange().getRawMany();
93+
94+
return response.data(results);
95+
}
96+
97+
@Get('top/locations')
98+
@Permissions(`${PermOwner.Dashboard}@${PermAct.R}`)
99+
async topLocations(@Rest() rest, @Res() response) {
100+
const query = AppDataSource.createQueryBuilder(Order, 't1')
101+
.leftJoin(Location, 't2', 't1.location_id = t2.id')
102+
.where('t1.restaurant_id = :restId', { restId: rest.id })
103+
.andWhere('t1.status NOT IN (:status)', { status: [OrderStatus.Cancelled] })
104+
.selectWithAlias(['_(t2.id) AS id', 't2.name', '_COUNT(t1.id) AS count'])
105+
.groupBy('t2.id')
106+
.orderBy('count', 'DESC')
107+
.limit(5);
108+
109+
const results = await query.dateRange().getRawMany();
110+
111+
return response.data(results);
112+
}
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Module } from '@nestjs/common';
2+
import { DashboardController } from './dashboard.controller';
3+
4+
@Module({
5+
controllers: [DashboardController],
6+
})
7+
export class OwnerDashboardModule {}

src/app/owner/restaurant/restaurant.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Module } from '@nestjs/common';
22
import { OwnerCategoryModule } from './category/category.module';
3+
import { OwnerDashboardModule } from './dashboard/dashboard.module';
34
import { OwnerLocationModule } from './location/location.module';
45
import { OwnerNotificationModule } from './notification/notification.module';
56
import { OwnerOrderModule } from './order/order.module';
@@ -12,6 +13,7 @@ import { OwnerVariantModule } from './variant/variant.module';
1213

1314
@Module({
1415
imports: [
16+
OwnerDashboardModule,
1517
OwnerLocationModule,
1618
OwnerTableModule,
1719
OwnerStaffModule,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Loc } from '@core/decorators/location.decorator';
2+
import { Rest } from '@core/decorators/restaurant.decorator';
3+
import { StaffAuthGuard } from '@core/guards/auth.guard';
4+
import { StaffGuard } from '@core/guards/staff.guard';
5+
import { PermAct, PermStaff } from '@core/services/role.service';
6+
import { OrderProduct } from '@db/entities/core/order-product.entity';
7+
import { Order, OrderStatus } from '@db/entities/core/order.entity';
8+
import { Location } from '@db/entities/owner/location.entity';
9+
import { ProductVariant } from '@db/entities/owner/product-variant.entity';
10+
import { Product } from '@db/entities/owner/product.entity';
11+
import { getChartData } from '@lib/helpers/utils.helper';
12+
import { Permissions } from '@lib/rbac';
13+
import AppDataSource from '@lib/typeorm/datasource.typeorm';
14+
import { uuid } from '@lib/uid/uuid.library';
15+
import { Controller, Get, Res, UseGuards } from '@nestjs/common';
16+
17+
@Controller()
18+
@UseGuards(StaffAuthGuard(), StaffGuard)
19+
export class DashboardController {
20+
@Get('orders/total')
21+
@Permissions(`${PermStaff.Dashboard}@${PermAct.R}`)
22+
async orderTotal(@Rest() rest, @Loc() loc: Location, @Res() response) {
23+
const query = AppDataSource.createQueryBuilder(Order, 't1').where({ restaurant_id: rest.id });
24+
25+
if (loc) {
26+
query.andWhere({ location_id: loc.id });
27+
}
28+
29+
const data = await query.search().dateRange().getCount();
30+
return response.data({ id: uuid(), data });
31+
}
32+
33+
@Get('sales/total')
34+
@Permissions(`${PermStaff.Dashboard}@${PermAct.R}`)
35+
async salesTotal(@Rest() rest, @Loc() loc: Location, @Res() response) {
36+
const query = AppDataSource.createQueryBuilder(Order, 't1').where({ restaurant_id: rest.id });
37+
38+
if (loc) {
39+
query.andWhere({ location_id: loc.id });
40+
}
41+
42+
query.selectWithAlias([
43+
'_UUID() as id',
44+
'_IFNULL(SUM(t1.gross_total),0) AS total',
45+
'_IFNULL(SUM(t1.net_total),0) AS net_total',
46+
]);
47+
48+
const data = await query.search().dateRange().getRawOne();
49+
return response.data({ id: uuid(), data });
50+
}
51+
52+
@Get('sales/chart')
53+
@Permissions(`${PermStaff.Dashboard}@${PermAct.R}`)
54+
async salesChart(@Rest() rest, @Loc() loc: Location, @Res() response) {
55+
const query = AppDataSource.createQueryBuilder(Order, 't1')
56+
.where({ restaurant_id: rest.id })
57+
.andWhere('t1.status = :status', { status: OrderStatus.Completed })
58+
.groupBy('t1.created_at')
59+
.selectWithAlias(['_IFNULL(SUM(t1.net_total),0) AS net_total', 'created_at']);
60+
61+
if (loc) {
62+
query.andWhere({ location_id: loc.id });
63+
}
64+
const orders = await query.dateRange().getRawMany();
65+
66+
const { start, end } = AppDataSource.createQueryBuilder(Order, 't1').dateRange().getParameters();
67+
const results = await getChartData({ start, end }, orders, 'net_total');
68+
69+
return response.data(results);
70+
}
71+
72+
@Get('top/menus')
73+
@Permissions(`${PermStaff.Dashboard}@${PermAct.R}`)
74+
async topMenus(@Rest() rest, @Loc() loc: Location, @Res() response) {
75+
const query = AppDataSource.createQueryBuilder(OrderProduct, 't1')
76+
.leftJoin(Order, 't2', 't1.order_id = t2.id')
77+
.leftJoin(ProductVariant, 't3', 't1.product_variant_id = t3.id')
78+
.leftJoin(Product, 't4', 't3.product_id = t4.id')
79+
.where('t2.restaurant_id = :restId', { restId: rest.id })
80+
.andWhere('t2.status NOT IN (:status)', { status: [OrderStatus.Cancelled] });
81+
82+
if (loc) {
83+
query.andWhere('t2.location_id = :locId', { locId: loc.id });
84+
}
85+
86+
query
87+
.selectWithAlias(['_(t1.product_variant_id) AS id', 't4.name', 't4.sku', '_COUNT(t1.product_variant_id) AS count'])
88+
.groupBy('t1.product_variant_id')
89+
.orderBy('count', 'DESC')
90+
.limit(5);
91+
92+
const results = await query.dateRange().getRawMany();
93+
94+
return response.data(results);
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Module } from '@nestjs/common';
2+
import { DashboardController } from './dashboard.controller';
3+
4+
@Module({
5+
controllers: [DashboardController],
6+
})
7+
export class StaffDashboardModule {}

src/app/staff/restaurant/restaurant.module.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
import { Module } from '@nestjs/common';
2+
import { StaffCategoryModule } from './category/category.module';
3+
import { StaffDashboardModule } from './dashboard/dashboard.module';
24
import { StafffNotificationModule } from './notification/notification.module';
35
import { StaffOrderModule } from './order/order.module';
6+
import { StaffProductModule } from './product/product.module';
47
import { StaffRestaurantController } from './restaurant.controller';
8+
import { StaffStockModule } from './stock/stock.module';
9+
import { StaffTableModule } from './table/table.module';
10+
import { StaffVariantModule } from './variant/variant.module';
511

612
@Module({
7-
imports: [StaffOrderModule, StafffNotificationModule],
13+
imports: [
14+
StaffStockModule,
15+
StaffTableModule,
16+
StaffCategoryModule,
17+
StaffVariantModule,
18+
StaffProductModule,
19+
StaffOrderModule,
20+
StafffNotificationModule,
21+
StaffDashboardModule,
22+
],
823
controllers: [StaffRestaurantController],
924
providers: [],
1025
})

src/app/staff/staff.module.ts

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
11
import { Module } from '@nestjs/common';
22
import { StaffAuthModule } from './auth/auth.module';
33
import { StaffProfileModule } from './profile/profile.module';
4-
import { StaffCategoryModule } from './restaurant/category/category.module';
5-
import { StaffProductModule } from './restaurant/product/product.module';
64
import { StaffRestaurantModule } from './restaurant/restaurant.module';
7-
import { StaffStockModule } from './restaurant/stock/stock.module';
8-
import { StaffTableModule } from './restaurant/table/table.module';
9-
import { StaffVariantModule } from './restaurant/variant/variant.module';
105

116
@Module({
12-
imports: [
13-
StaffAuthModule,
14-
StaffProfileModule,
15-
StaffRestaurantModule,
16-
StaffStockModule,
17-
StaffTableModule,
18-
StaffCategoryModule,
19-
StaffVariantModule,
20-
StaffProductModule,
21-
],
7+
imports: [StaffAuthModule, StaffProfileModule, StaffRestaurantModule],
228
controllers: [],
239
providers: [],
2410
})

src/config/database.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ if (isTrue(config.get('REDIS_ENABLED'))) {
3939
};
4040
}
4141

42+
// Production
4243
const production: TypeOrmModuleOptions = {
4344
...defaultConfigs,
4445
logging: false,
@@ -63,6 +64,7 @@ const production: TypeOrmModuleOptions = {
6364
},
6465
};
6566

67+
// Development (Local)
6668
const development: TypeOrmModuleOptions = {
6769
...defaultConfigs,
6870
logging: config.isDebugging(),
@@ -71,7 +73,6 @@ const development: TypeOrmModuleOptions = {
7173
username: config.get('DATABASE_USER'),
7274
password: config.get('DATABASE_PASSWORD'),
7375
database: config.get('DATABASE_NAME'),
74-
synchronize: true,
7576
};
7677

7778
export const database: TypeOrmModuleOptions = config.isDevelopment() ? development : production;

src/core/guards/owner.guard.ts

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export class OwnerGuard implements CanActivate {
7575
const request = context.switchToHttp().getRequest();
7676

7777
const permissions = this.reflector.get<string[]>('Permissions', context.getHandler());
78+
console.log({ permissions });
7879
if (!permissions) {
7980
throw new GuardException('Bad permission');
8081
}

0 commit comments

Comments
 (0)