Skip to content

Commit

Permalink
Merge pull request #15 from akbarsaputrait/6-api-owner---manage-resta…
Browse files Browse the repository at this point in the history
…urant-staff

Owner - Restaurant manage staff
  • Loading branch information
akbarsaputrait authored Jan 29, 2024
2 parents ca7e9ba + b81472f commit 526d92f
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 17 deletions.
31 changes: 16 additions & 15 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

export NVM_DIR="$HOME/.nvm/nvm.sh"
. "$(dirname $NVM_DIR)/nvm.sh"

export NVM_DIR="$HOME/.nvm"
a=$(nvm ls | grep 'node')
b=${a#*(-> }
v=${b%%[)| ]*}

export PATH="$NVM_DIR/versions/node/$v/bin:$PATH"

echo '🏗️👷 Styling, testing and building your project before committing'
if [[ "$OSTYPE" =~ ^msys ]]; then
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
. "$(dirname "$0")/_/husky.sh"
else
export NVM_DIR="$HOME/.nvm/nvm.sh"
. "$(dirname $NVM_DIR)/nvm.sh"

export NVM_DIR="$HOME/.nvm"
a=$(nvm ls | grep 'node')
b=${a#*(-> }
v=${b%%[)| ]*}

export PATH="$NVM_DIR/versions/node/$v/bin:$PATH"
fi

#fix & format
npx lint-staged
Expand All @@ -24,5 +26,4 @@ npm run lint ||
false;
)

echo '🎉🎉🎉🎉 Yeayy, there is no error in your code... I am committing this now. ✨🚀🏄‍♂️🍻'

echo '🎉🎉🎉🎉 Yeayy, there is no error in your code... I am committing this now. ✨🚀🏄‍♂️🍻'
5 changes: 5 additions & 0 deletions src/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { OwnerModule } from './app/owner/owner.module';
import { OwnerProfileModule } from './app/owner/profile/profile.module';
import { OwnerLocationModule } from './app/owner/restaurant/location/location.module';
import { OwnerRestaurantModule } from './app/owner/restaurant/restaurant.module';
import { OwnerStaffModule } from './app/owner/restaurant/staff/staff.module';
import { OwnerTableModule } from './app/owner/restaurant/table/table.module';

export const routes: Routes = [
Expand All @@ -27,6 +28,10 @@ export const routes: Routes = [
path: '/:restaurant_id/tables',
module: OwnerTableModule,
},
{
path: '/:restaurant_id/staff',
module: OwnerStaffModule,
},
],
},
],
Expand Down
3 changes: 2 additions & 1 deletion src/app/owner/restaurant/restaurant.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';
import { OwnerLocationModule } from './location/location.module';
import { RestaurantController } from './restaurant.controller';
import { OwnerStaffModule } from './staff/staff.module';
import { OwnerTableModule } from './table/table.module';

@Module({
imports: [OwnerLocationModule, OwnerTableModule],
imports: [OwnerLocationModule, OwnerTableModule, OwnerStaffModule],
controllers: [RestaurantController],
providers: [],
})
Expand Down
20 changes: 20 additions & 0 deletions src/app/owner/restaurant/staff/role.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { OwnerAuthGuard } from '@core/guards/auth.guard';
import { OwnerGuard } from '@core/guards/owner.guard';
import { PermAct, PermOwner } from '@core/services/role.service';
import { StaffRole } from '@db/entities/staff/role.entity';
import { RawTransformer } from '@db/transformers/raw.transformer';
import { Permissions } from '@lib/rbac';
import AppDataSource from '@lib/typeorm/datasource.typeorm';
import { Controller, Get, Res, UseGuards } from '@nestjs/common';

@Controller('roles')
@UseGuards(OwnerAuthGuard())
export class RoleController {
@Get()
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Role}@${PermAct.R}`)
async index(@Res() response) {
const roles = await AppDataSource.createQueryBuilder(StaffRole, 't1').search().sort().getPaged();
await response.paginate(roles, RawTransformer);
}
}
140 changes: 140 additions & 0 deletions src/app/owner/restaurant/staff/staff.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Quero } from '@core/decorators/quero.decorator';
import { Rest } from '@core/decorators/restaurant.decorator';
import { OwnerAuthGuard } from '@core/guards/auth.guard';
import { OwnerGuard } from '@core/guards/owner.guard';
import { PermAct, PermOwner } from '@core/services/role.service';
import { Location } from '@db/entities/owner/location.entity';
import { StaffRole } from '@db/entities/staff/role.entity';
import { StaffUser, StaffUserStatus } from '@db/entities/staff/user.entity';
import { StaffTransformer } from '@db/transformers/staff.transformer';
import { ValidationException } from '@lib/exceptions/validation.exception';
import { hash } from '@lib/helpers/encrypt.helper';
import { randomChar } from '@lib/helpers/utils.helper';
import { Validator } from '@lib/helpers/validator.helper';
import { Permissions } from '@lib/rbac';
import AppDataSource from '@lib/typeorm/datasource.typeorm';
import { BadRequestException, Body, Controller, Get, Param, Post, Put, Res, UseGuards } from '@nestjs/common';

@Controller()
@UseGuards(OwnerAuthGuard())
export class StaffController {
@Get()
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Staff}@${PermAct.R}`)
async index(@Rest() rest, @Res() response, @Quero() quero) {
const query = AppDataSource.createQueryBuilder(StaffUser, 't1').search().sort();
query.where({ restaurant_id: rest.id });

if (quero.location_id) {
query.andWhere({ location_id: quero.location_id });
}

const staffs = await query.getPaged();
await response.paginate(staffs, StaffTransformer);
}

@Get('/:id')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Staff}@${PermAct.R}`)
async show(@Rest() rest, @Res() response, @Param() param) {
const staff = await StaffUser.findOrFailByRestaurant(param.id, rest);
await response.item(staff, StaffTransformer);
}

@Post()
@Permissions(`${PermOwner.Staff}@${PermAct.C}`)
async store(@Body() body, @Res() response, @Res() rest) {
const rules = {
name: 'required|safe_text',
email: 'required|email',
phone: 'required|phone',
role_id: 'required|uid',
location_id: 'uid',
};
const validation = Validator.init(body, rules);
if (validation.fails()) {
throw new ValidationException(validation);
}

if (!rest) {
throw new BadRequestException('You must be assigned to a restaurant');
}

const emailExists = await StaffUser.exists({ where: { email: body.email } });
if (emailExists) {
throw new BadRequestException('Email has already been used');
}

const phoneExists = await StaffUser.exists({ where: { phone: body.phone } });
if (phoneExists) {
throw new BadRequestException('Phone has already been used');
}

const role = await StaffRole.findOneByOrFail({ id: body.role_id });

let location: Location = null;
if (body.location_id) {
location = await Location.findOneByOrFail({ id: body.location_id });
}

const plainPass = randomChar(8);
const staff = new StaffUser();
staff.name = body.name;
staff.email = String(body.email).toLowerCase();
staff.phone = body.phone;
staff.password = await hash(plainPass);
staff.status = StaffUserStatus.Active;
staff.role_slug = role.slug;
staff.location_id = location ? location.id : null;
staff.restaurant_id = rest.id;
await staff.save();

// this.mail
// .sendMail({
// to: staff.email,
// subject: 'Your staff account!',
// template: 'staff-register',
// context: {
// name: staff.name,
// password: plainPass,
// },
// })
// .then(() => null)
// .catch((error) => Logger.getInstance().notify(error));

await response.item(staff, StaffTransformer);
}

@Put('/:id')
@Permissions(`${PermOwner.Staff}@${PermAct.U}`)
async update(@Param() param, @Body() body, @Res() response) {
const rules = {
name: 'required|safe_text',
phone: 'required|phone',
status: `required|in:${Object.values(StaffUserStatus).join(',')}`,
role_id: 'required|uid',
location_id: 'uid',
};
const validation = Validator.init(body, rules);
if (validation.fails()) {
throw new ValidationException(validation);
}

const role = await StaffRole.findOneByOrFail({ id: body.role_id });
let location: Location = null;
if (body.location_id) {
location = await Location.findOneByOrFail({ id: body.location_id });
}

const staff = await StaffUser.findOneByOrFail({ id: param.id });

staff.name = body.name;
staff.phone = body.phone;
staff.status = body.status;
staff.role_slug = role.slug;
staff.location_id = location ? location.id : null;
await staff.save();

await response.item(staff, StaffTransformer);
}
}
10 changes: 10 additions & 0 deletions src/app/owner/restaurant/staff/staff.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { RoleController } from './role.controller';
import { StaffController } from './staff.controller';

@Module({
imports: [],
controllers: [StaffController, RoleController],
providers: [],
})
export class OwnerStaffModule {}
4 changes: 4 additions & 0 deletions src/core/services/role.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export enum PermOwner {
Restaurant = 'restaurant',
Location = 'location',
Table = 'table',
Staff = 'staff',
Role = 'role',
}

export const DefaultPerms = [PermOwner.Profile, PermOwner.Restaurant];
Expand All @@ -33,6 +35,8 @@ export class RoleService implements IDynamicStorageRbac {
[PermOwner.Restaurant]: [PermAct.R, PermAct.C, PermAct.U, PermAct.D],
[PermOwner.Location]: [PermAct.R, PermAct.C, PermAct.U, PermAct.D],
[PermOwner.Table]: [PermAct.R, PermAct.C, PermAct.U, PermAct.D],
[PermOwner.Staff]: [PermAct.R, PermAct.C, PermAct.U, PermAct.D],
[PermOwner.Role]: [PermAct.R, PermAct.C, PermAct.U, PermAct.D],
};

return {
Expand Down
2 changes: 1 addition & 1 deletion src/database/entities/base/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class BasicEntity extends Base {
): Promise<T | undefined> {
const obj = await this.findOne<T>({ where: { id, restaurant_id: restaurant.id } } as any);
if (isEmpty(obj)) {
throw new NotFoundException(`Could not find entity ${startCase(this.name).toLowerCase()} on the company`);
throw new NotFoundException(`Could not find entity ${startCase(this.name).toLowerCase()} on the restaurant`);
}

return obj;
Expand Down
68 changes: 68 additions & 0 deletions src/database/transformers/staff.transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Media } from '@db/entities/core/media.entity';
import { StaffUser } from '@db/entities/staff/user.entity';
import { encrypt } from '@lib/helpers/encrypt.helper';
import { RequestHelper } from '@lib/helpers/request.helper';
import { TransformerAbstract } from '@lib/transformer/abstract.transformer';
import { LocationTransformer } from './location.transformer';

export class StaffTransformer extends TransformerAbstract {
get availableInclude() {
return ['location', 'role'];
}

get defaultInclude() {
return [];
}

transform(entity: StaffUser) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { media, ...rest } = entity.toJSON();

return {
...rest,
avatar: Media.getImage(entity.media),
};
}

async transformWithPermission(entity: StaffUser) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { media, ...rest } = entity.toJSON();

const grants = RequestHelper.getPermissionGrants();
const role = await entity.role;
const location = await entity.location;
return {
...rest,
role: {
id: role.id,
name: role.slug,
permissions: encrypt(grants[role.slug]),
},
location: location
? {
id: location.id,
name: location.name,
}
: null,
avatar: Media.getImage(entity.media),
};
}

async includeLocation(entity: StaffUser) {
const location = await entity.location;
if (!location) {
return this.null();
}

return this.item(location, LocationTransformer);
}

async includeRole(entity: StaffUser) {
const role = await entity.role;
if (!role) {
return this.null();
}

return { id: role.id, name: role.slug };
}
}

0 comments on commit 526d92f

Please sign in to comment.