diff --git a/desafio-fullstack-backend/.eslintrc.js b/desafio-fullstack-backend/.eslintrc.js new file mode 100644 index 000000000..f6c62bee2 --- /dev/null +++ b/desafio-fullstack-backend/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; diff --git a/desafio-fullstack-backend/.gitignore b/desafio-fullstack-backend/.gitignore new file mode 100644 index 000000000..c4f0a9bb6 --- /dev/null +++ b/desafio-fullstack-backend/.gitignore @@ -0,0 +1,39 @@ +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +.env + +package-lock.json +yarn.lock + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json \ No newline at end of file diff --git a/desafio-fullstack-backend/.prettierrc b/desafio-fullstack-backend/.prettierrc new file mode 100644 index 000000000..dcb72794f --- /dev/null +++ b/desafio-fullstack-backend/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/desafio-fullstack-backend/Diagrama ER.png b/desafio-fullstack-backend/Diagrama ER.png new file mode 100644 index 000000000..c8e41634e Binary files /dev/null and b/desafio-fullstack-backend/Diagrama ER.png differ diff --git a/desafio-fullstack-backend/README.md b/desafio-fullstack-backend/README.md new file mode 100644 index 000000000..e7c48acff --- /dev/null +++ b/desafio-fullstack-backend/README.md @@ -0,0 +1,37 @@ +# DesafioFullstackBackend + +## Instalação + +```bash +$ npm install +``` + +## Inicialização + +```bash +# development +$ npm run start +``` + +## Conexão com o banco de dados +- Crie um arquivo `.env` +- Adicione as seguintes configurações: + +```bash +DATABASE_HOST= +DATABASE_PORT= +DATABASE_USERNAME= +DATABASE_PASSWORD= +DATABASE_NAME= +``` + +## Criar banco de dados +- Automático +```bash +Ao iniciar o projeto, as tabelas vão ser automaticamente criadas. +``` +- Manual +```bash +Importe o arquivo create-database.sql em seu pgadmin (ou algum outro que esteja utilizando). +``` +[Arquivo para criar banco de dado](./create-database.sql) diff --git a/desafio-fullstack-backend/create-database.sql b/desafio-fullstack-backend/create-database.sql new file mode 100644 index 000000000..3e23de119 --- /dev/null +++ b/desafio-fullstack-backend/create-database.sql @@ -0,0 +1,84 @@ +BEGIN; + + +CREATE TABLE public.classroom +( + id integer NOT NULL, + name character varying NOT NULL, + PRIMARY KEY (id) +); + +INSERT INTO public.classroom( + id, name) + VALUES (1, 'Sala 402'); + +CREATE TABLE public.course +( + id integer NOT NULL, + name character varying NOT NULL, + "startTime" character varying NOT NULL, + "endTime" character varying NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE public.course_classroom +( + "courseId" integer NOT NULL, + "classroomId" integer NOT NULL, + PRIMARY KEY ("courseId", "classroomId") +); + +CREATE TABLE public.course_teacher +( + "courseId" integer NOT NULL, + "teacherId" integer NOT NULL, + PRIMARY KEY ("courseId", "teacherId") +); + +CREATE TABLE public.teacher +( + id integer NOT NULL, + name character varying NOT NULL, + PRIMARY KEY (id) +); + +INSERT INTO public.teacher( + id, name) + VALUES (1, 'Pedro Henrique'); + +CREATE TABLE public."user" +( + id integer NOT NULL, + email character varying NOT NULL, + password character varying NOT NULL, + PRIMARY KEY (id) +); + +INSERT INTO public."user"( + id, email, password) + VALUES (1, 'user@test.com', 'testpass'); + +ALTER TABLE public.course_classroom + ADD FOREIGN KEY ("classroomId") + REFERENCES public.classroom (id) + NOT VALID; + + +ALTER TABLE public.course_classroom + ADD FOREIGN KEY ("courseId") + REFERENCES public.course (id) + NOT VALID; + + +ALTER TABLE public.course_teacher + ADD FOREIGN KEY ("teacherId") + REFERENCES public.teacher (id) + NOT VALID; + + +ALTER TABLE public.course_teacher + ADD FOREIGN KEY ("courseId") + REFERENCES public.course (id) + NOT VALID; + +END; \ No newline at end of file diff --git a/desafio-fullstack-backend/nest-cli.json b/desafio-fullstack-backend/nest-cli.json new file mode 100644 index 000000000..56167b36a --- /dev/null +++ b/desafio-fullstack-backend/nest-cli.json @@ -0,0 +1,4 @@ +{ + "collection": "@nestjs/schematics", + "sourceRoot": "src" +} diff --git a/desafio-fullstack-backend/package.json b/desafio-fullstack-backend/package.json new file mode 100644 index 000000000..f1f4ee09c --- /dev/null +++ b/desafio-fullstack-backend/package.json @@ -0,0 +1,77 @@ +{ + "name": "desafio-fullstack-api", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@nestjs/common": "^7.6.15", + "@nestjs/config": "^0.6.3", + "@nestjs/core": "^7.6.15", + "@nestjs/mapped-types": "*", + "@nestjs/platform-express": "^7.6.15", + "@nestjs/typeorm": "^7.1.5", + "@types/passport": "^1.0.6", + "dotenv": "^10.0.0", + "pg": "^8.6.0", + "reflect-metadata": "^0.1.13", + "rimraf": "^3.0.2", + "rxjs": "^6.6.6", + "typeorm": "^0.2.32" + }, + "devDependencies": { + "@nestjs/cli": "^7.6.0", + "@nestjs/schematics": "^7.3.0", + "@nestjs/testing": "^7.6.15", + "@types/express": "^4.17.11", + "@types/jest": "^26.0.22", + "@types/node": "^14.14.36", + "@types/supertest": "^2.0.10", + "@typescript-eslint/eslint-plugin": "^4.19.0", + "@typescript-eslint/parser": "^4.19.0", + "eslint": "^7.22.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-prettier": "^3.3.1", + "jest": "^26.6.3", + "prettier": "^2.2.1", + "supertest": "^6.1.3", + "ts-jest": "^26.5.4", + "ts-loader": "^8.0.18", + "ts-node": "^9.1.1", + "tsconfig-paths": "^3.9.0", + "typescript": "^4.2.3" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/desafio-fullstack-backend/src/app.controller.spec.ts b/desafio-fullstack-backend/src/app.controller.spec.ts new file mode 100644 index 000000000..d22f3890a --- /dev/null +++ b/desafio-fullstack-backend/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/desafio-fullstack-backend/src/app.controller.ts b/desafio-fullstack-backend/src/app.controller.ts new file mode 100644 index 000000000..cce879ee6 --- /dev/null +++ b/desafio-fullstack-backend/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/desafio-fullstack-backend/src/app.module.ts b/desafio-fullstack-backend/src/app.module.ts new file mode 100644 index 000000000..893d124a6 --- /dev/null +++ b/desafio-fullstack-backend/src/app.module.ts @@ -0,0 +1,38 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuthModule } from './auth/auth.module'; +import { UserModule } from './user/user.module'; +import { UserEntity } from './user/entity/user.entity'; +import { CourseModule } from './course/course.module'; +import { TeacherModule } from './teacher/teacher.module'; +import { TeacherEntity } from './teacher/entity/teacher.entity'; +import { ClassroomModule } from './classroom/classroom.module'; +import { ClassroomEntity } from './classroom/entity/classroom.entity'; +import { CourseEntity } from './course/entity/course.entity'; +import { ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forRoot({ + type: 'postgres', + host: process.env.DATABASE_HOST, + port: parseInt(process.env.DATABASE_PORT), + username: process.env.DATABASE_USERNAME, + password: process.env.DATABASE_PASSWORD, + database: process.env.DATABASE_NAME, + synchronize: true, + entities: [UserEntity, TeacherEntity, ClassroomEntity, CourseEntity] + }), + AuthModule, + UserModule, + CourseModule, + TeacherModule, + ClassroomModule, + ], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule { } diff --git a/desafio-fullstack-backend/src/app.service.ts b/desafio-fullstack-backend/src/app.service.ts new file mode 100644 index 000000000..927d7cca0 --- /dev/null +++ b/desafio-fullstack-backend/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/desafio-fullstack-backend/src/auth/auth.controller.ts b/desafio-fullstack-backend/src/auth/auth.controller.ts new file mode 100644 index 000000000..9af98f9a8 --- /dev/null +++ b/desafio-fullstack-backend/src/auth/auth.controller.ts @@ -0,0 +1,26 @@ +import { Body, Controller, HttpStatus, Post, Res } from '@nestjs/common'; + +import { AuthService } from './auth.service'; +import { LoginDto } from './dto/login.dto'; + +@Controller('auth') +export class AuthController { + + constructor(private authService: AuthService) { } + + @Post('/login') + async login(@Body() loginDto: LoginDto, @Res() response) { + + try { + const result = await this.authService.login(loginDto) + if (!result) { + return response.status(HttpStatus.FORBIDDEN).send('invalid credentials') + } + + return response.status(HttpStatus.OK).send(result); + } catch (error) { + return response.status(HttpStatus.INTERNAL_SERVER_ERROR).send(error); + } + } + +} diff --git a/desafio-fullstack-backend/src/auth/auth.module.ts b/desafio-fullstack-backend/src/auth/auth.module.ts new file mode 100644 index 000000000..33596fad7 --- /dev/null +++ b/desafio-fullstack-backend/src/auth/auth.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AuthService } from './auth.service'; +import { AuthController } from './auth.controller'; +import { UserModule } from 'src/user/user.module'; + +@Module({ + imports: [UserModule], + providers: [AuthService], + controllers: [AuthController] +}) +export class AuthModule {} diff --git a/desafio-fullstack-backend/src/auth/auth.service.ts b/desafio-fullstack-backend/src/auth/auth.service.ts new file mode 100644 index 000000000..c46a34ddc --- /dev/null +++ b/desafio-fullstack-backend/src/auth/auth.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { UserDto } from 'src/user/dto/user.dto'; +import { UserService } from 'src/user/user.service'; + +import { LoginDto } from './dto/login.dto'; + +@Injectable() +export class AuthService { + + constructor(private userService: UserService) { } + + async login(loginDto: LoginDto): Promise { + const user = await this.userService.findOne(loginDto.email); + if (user && user.password === loginDto.password) { + delete user.password; + return user; + } + return null; + } + +} diff --git a/desafio-fullstack-backend/src/auth/dto/login.dto.ts b/desafio-fullstack-backend/src/auth/dto/login.dto.ts new file mode 100644 index 000000000..4616492fd --- /dev/null +++ b/desafio-fullstack-backend/src/auth/dto/login.dto.ts @@ -0,0 +1,6 @@ +export class LoginDto { + + email: string; + password: string; + +} diff --git a/desafio-fullstack-backend/src/classroom/classroom.controller.ts b/desafio-fullstack-backend/src/classroom/classroom.controller.ts new file mode 100644 index 000000000..e89e231c4 --- /dev/null +++ b/desafio-fullstack-backend/src/classroom/classroom.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Get } from '@nestjs/common'; +import { ClassroomService } from './classroom.service'; +import { ClassroomDto } from './dto/classroom.dto'; + +@Controller('classroom') +export class ClassroomController { + + constructor(private classroomService: ClassroomService) { } + + @Get('/') + findAll(): Promise { + return this.classroomService.findAll(); + } + +} diff --git a/desafio-fullstack-backend/src/classroom/classroom.module.ts b/desafio-fullstack-backend/src/classroom/classroom.module.ts new file mode 100644 index 000000000..d68297ea9 --- /dev/null +++ b/desafio-fullstack-backend/src/classroom/classroom.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ClassroomService } from './classroom.service'; +import { ClassroomController } from './classroom.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ClassroomEntity } from './entity/classroom.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([ClassroomEntity])], + exports: [ClassroomService], + providers: [ClassroomService], + controllers: [ClassroomController] +}) +export class ClassroomModule {} diff --git a/desafio-fullstack-backend/src/classroom/classroom.service.ts b/desafio-fullstack-backend/src/classroom/classroom.service.ts new file mode 100644 index 000000000..880ec9b34 --- /dev/null +++ b/desafio-fullstack-backend/src/classroom/classroom.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { ClassroomEntity } from './entity/classroom.entity'; + +@Injectable() +export class ClassroomService { + + constructor(@InjectRepository(ClassroomEntity) private classroomRepository: Repository) { } + + findAll(): Promise { + return this.classroomRepository.find() + } + +} diff --git a/desafio-fullstack-backend/src/classroom/dto/classroom.dto.ts b/desafio-fullstack-backend/src/classroom/dto/classroom.dto.ts new file mode 100644 index 000000000..ad0fd20c8 --- /dev/null +++ b/desafio-fullstack-backend/src/classroom/dto/classroom.dto.ts @@ -0,0 +1,6 @@ +export class ClassroomDto { + + id: number; + name: string; + +} diff --git a/desafio-fullstack-backend/src/classroom/entity/classroom.entity.ts b/desafio-fullstack-backend/src/classroom/entity/classroom.entity.ts new file mode 100644 index 000000000..7a139b00d --- /dev/null +++ b/desafio-fullstack-backend/src/classroom/entity/classroom.entity.ts @@ -0,0 +1,16 @@ +import { CourseEntity } from "src/course/entity/course.entity"; +import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from "typeorm"; + +@Entity('classroom') +export class ClassroomEntity { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ nullable: false }) + name: string; + + @ManyToMany(() => CourseEntity, course => course.classrooms) + courses: CourseEntity[]; + +} diff --git a/desafio-fullstack-backend/src/course/course.controller.ts b/desafio-fullstack-backend/src/course/course.controller.ts new file mode 100644 index 000000000..07c8e1341 --- /dev/null +++ b/desafio-fullstack-backend/src/course/course.controller.ts @@ -0,0 +1,43 @@ +import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; +import { CourseService } from './course.service'; +import { CourseDto } from './dto/course.dto'; + +@Controller('course') +export class CourseController { + + constructor(private courseService: CourseService) { } + + @Post('/') + async create(@Body() courseDto: CourseDto) { + try { + const result = await this.courseService.create(courseDto); + + console.log('result ', result); + } catch (error) { + console.log('error ', error); + } + + return true; + } + + @Put('/:id') + async update(@Param('id') id, @Body() courseDto: CourseDto) { + return this.courseService.update(id, courseDto); + } + + @Get('/') + findAll() { + return this.courseService.findAll(); + } + + @Get('/:id') + findOne(@Param('id') id) { + return this.courseService.findOne(id); + } + + @Delete('/:id') + delete(@Param('id') id) { + return this.courseService.delete(id); + } + +} diff --git a/desafio-fullstack-backend/src/course/course.module.ts b/desafio-fullstack-backend/src/course/course.module.ts new file mode 100644 index 000000000..53322bae4 --- /dev/null +++ b/desafio-fullstack-backend/src/course/course.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { CourseController } from './course.controller'; +import { CourseService } from './course.service'; +import { CourseEntity } from './entity/course.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([CourseEntity])], + providers: [CourseService], + controllers: [CourseController] +}) +export class CourseModule { } \ No newline at end of file diff --git a/desafio-fullstack-backend/src/course/course.service.ts b/desafio-fullstack-backend/src/course/course.service.ts new file mode 100644 index 000000000..1be751fc6 --- /dev/null +++ b/desafio-fullstack-backend/src/course/course.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { CourseDto } from './dto/course.dto'; +import { CourseEntity } from './entity/course.entity'; + +@Injectable() +export class CourseService { + + constructor(@InjectRepository(CourseEntity) private courseRepository: Repository) { } + + findAll(): Promise { + return this.courseRepository.find({ relations: ['classrooms', 'teachers'] }) + } + + findOne(id): Promise { + return this.courseRepository.findOne(id, { relations: ['classrooms', 'teachers'] }); + } + + update(id, courseDto: CourseDto) { + return this.courseRepository.save({ id: parseInt(id), ...courseDto }, { reload: true }) + } + + create(courseDto: CourseDto) { + return this.courseRepository.save({ ...courseDto }); + } + + delete(id: number) { + return this.courseRepository.delete(id); + } +} diff --git a/desafio-fullstack-backend/src/course/dto/course.dto.ts b/desafio-fullstack-backend/src/course/dto/course.dto.ts new file mode 100644 index 000000000..404dc7875 --- /dev/null +++ b/desafio-fullstack-backend/src/course/dto/course.dto.ts @@ -0,0 +1,9 @@ +export class CourseDto { + + id: number; + startTime: string; + endTime: string; + teachers: any[]; + classrooms: any[]; + +} diff --git a/desafio-fullstack-backend/src/course/entity/course.entity.ts b/desafio-fullstack-backend/src/course/entity/course.entity.ts new file mode 100644 index 000000000..e987622e9 --- /dev/null +++ b/desafio-fullstack-backend/src/course/entity/course.entity.ts @@ -0,0 +1,36 @@ +import { ClassroomEntity } from "src/classroom/entity/classroom.entity"; +import { TeacherEntity } from "src/teacher/entity/teacher.entity"; +import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from "typeorm"; + +@Entity('course') +export class CourseEntity { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ nullable: false, unique: true }) + name: string; + + @Column({ nullable: false }) + startTime: string; + + @Column({ nullable: false }) + endTime: string; + + @ManyToMany(() => TeacherEntity, teacher => teacher.courses, { cascade: true }) + @JoinTable({ + name: 'course_teacher', + joinColumn: { name: 'courseId', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'teacherId', referencedColumnName: 'id' }, + }) + teachers: TeacherEntity[]; + + @ManyToMany(() => ClassroomEntity, classroom => classroom.courses, { cascade: true }) + @JoinTable({ + name: 'course_classroom', + joinColumn: { name: 'courseId', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'classroomId', referencedColumnName: 'id' }, + }) + classrooms: ClassroomEntity[]; + +} diff --git a/desafio-fullstack-backend/src/main.ts b/desafio-fullstack-backend/src/main.ts new file mode 100644 index 000000000..8d9718c07 --- /dev/null +++ b/desafio-fullstack-backend/src/main.ts @@ -0,0 +1,8 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule, { cors: true }); + await app.listen(3000); +} +bootstrap(); diff --git a/desafio-fullstack-backend/src/teacher/dto/teacher.dto.ts b/desafio-fullstack-backend/src/teacher/dto/teacher.dto.ts new file mode 100644 index 000000000..877b8a407 --- /dev/null +++ b/desafio-fullstack-backend/src/teacher/dto/teacher.dto.ts @@ -0,0 +1,6 @@ +export class TeacherDto { + + id: number; + name: string; + +} diff --git a/desafio-fullstack-backend/src/teacher/entity/teacher.entity.ts b/desafio-fullstack-backend/src/teacher/entity/teacher.entity.ts new file mode 100644 index 000000000..f0e3880be --- /dev/null +++ b/desafio-fullstack-backend/src/teacher/entity/teacher.entity.ts @@ -0,0 +1,16 @@ +import { CourseEntity } from "src/course/entity/course.entity"; +import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from "typeorm"; + +@Entity('teacher') +export class TeacherEntity { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ nullable: false }) + name: string; + + @ManyToMany(() => CourseEntity, course => course.teachers) + courses: CourseEntity[]; + +} diff --git a/desafio-fullstack-backend/src/teacher/teacher.controller.ts b/desafio-fullstack-backend/src/teacher/teacher.controller.ts new file mode 100644 index 000000000..e57cb034a --- /dev/null +++ b/desafio-fullstack-backend/src/teacher/teacher.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Get } from '@nestjs/common'; +import { TeacherDto } from './dto/teacher.dto'; +import { TeacherService } from './teacher.service'; + +@Controller('teacher') +export class TeacherController { + + constructor(private teacherService: TeacherService) { } + + @Get('/') + findAll(): Promise { + return this.teacherService.findAll(); + } + +} diff --git a/desafio-fullstack-backend/src/teacher/teacher.module.ts b/desafio-fullstack-backend/src/teacher/teacher.module.ts new file mode 100644 index 000000000..d3780c43b --- /dev/null +++ b/desafio-fullstack-backend/src/teacher/teacher.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TeacherService } from './teacher.service'; +import { TeacherController } from './teacher.controller'; +import { TeacherEntity } from './entity/teacher.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +@Module({ + imports: [TypeOrmModule.forFeature([TeacherEntity])], + providers: [TeacherService], + controllers: [TeacherController] +}) +export class TeacherModule {} diff --git a/desafio-fullstack-backend/src/teacher/teacher.service.ts b/desafio-fullstack-backend/src/teacher/teacher.service.ts new file mode 100644 index 000000000..a2997e6b6 --- /dev/null +++ b/desafio-fullstack-backend/src/teacher/teacher.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { TeacherEntity } from './entity/teacher.entity'; + +@Injectable() +export class TeacherService { + + constructor(@InjectRepository(TeacherEntity) private teacherRepository: Repository) { } + + findAll(): Promise { + return this.teacherRepository.find() + } + +} diff --git a/desafio-fullstack-backend/src/user/dto/user.dto.ts b/desafio-fullstack-backend/src/user/dto/user.dto.ts new file mode 100644 index 000000000..dfe8a8bf4 --- /dev/null +++ b/desafio-fullstack-backend/src/user/dto/user.dto.ts @@ -0,0 +1,7 @@ +export class UserDto { + + id: number; + email: string; + password: string; + +} diff --git a/desafio-fullstack-backend/src/user/entity/user.entity.ts b/desafio-fullstack-backend/src/user/entity/user.entity.ts new file mode 100644 index 000000000..e11295566 --- /dev/null +++ b/desafio-fullstack-backend/src/user/entity/user.entity.ts @@ -0,0 +1,15 @@ +import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; + +@Entity('user') +export class UserEntity { + + @PrimaryGeneratedColumn('increment') + id: number; + + @Column({ nullable: false, unique: true }) + email: string; + + @Column({ nullable: false }) + password: string; + +} diff --git a/desafio-fullstack-backend/src/user/user.controller.ts b/desafio-fullstack-backend/src/user/user.controller.ts new file mode 100644 index 000000000..1d11659ba --- /dev/null +++ b/desafio-fullstack-backend/src/user/user.controller.ts @@ -0,0 +1,4 @@ +import { Controller, Get } from '@nestjs/common'; + +@Controller('user') +export class UserController { } \ No newline at end of file diff --git a/desafio-fullstack-backend/src/user/user.module.ts b/desafio-fullstack-backend/src/user/user.module.ts new file mode 100644 index 000000000..8e2ecfe61 --- /dev/null +++ b/desafio-fullstack-backend/src/user/user.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { UserService } from './user.service'; +import { UserController } from './user.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserEntity } from './entity/user.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([UserEntity])], + providers: [UserService], + exports: [UserService], + controllers: [UserController] +}) +export class UserModule { } \ No newline at end of file diff --git a/desafio-fullstack-backend/src/user/user.service.ts b/desafio-fullstack-backend/src/user/user.service.ts new file mode 100644 index 000000000..3750aa84d --- /dev/null +++ b/desafio-fullstack-backend/src/user/user.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { UserDto } from './dto/user.dto'; +import { UserEntity } from './entity/user.entity'; + +@Injectable() +export class UserService { + + constructor(@InjectRepository(UserEntity) private userRepository: Repository) { } + + findOne(email: string): Promise { + return this.userRepository.findOne({ email }) + } + +} diff --git a/desafio-fullstack-backend/tsconfig.build.json b/desafio-fullstack-backend/tsconfig.build.json new file mode 100644 index 000000000..64f86c6bd --- /dev/null +++ b/desafio-fullstack-backend/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/desafio-fullstack-backend/tsconfig.json b/desafio-fullstack-backend/tsconfig.json new file mode 100644 index 000000000..bf10a2398 --- /dev/null +++ b/desafio-fullstack-backend/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true + } +} diff --git a/desafio-fullstack-front/.editorconfig b/desafio-fullstack-front/.editorconfig new file mode 100644 index 000000000..6e87a003d --- /dev/null +++ b/desafio-fullstack-front/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/desafio-fullstack-front/.gitignore b/desafio-fullstack-front/.gitignore new file mode 100644 index 000000000..ee5c9d833 --- /dev/null +++ b/desafio-fullstack-front/.gitignore @@ -0,0 +1,39 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git a/desafio-fullstack-front/README.md b/desafio-fullstack-front/README.md new file mode 100644 index 000000000..6d8592bde --- /dev/null +++ b/desafio-fullstack-front/README.md @@ -0,0 +1,22 @@ +# DesafioFullstackFront + +## Instalação + +```bash +$ npm install +``` + +## Inicialização + +```bash +# development +$ ng serve +``` +- Inicializar o projeto utilize o comando `ng serve` + +## Conexão com a API + +```bash +Acesse o arquivo `environment.ts`, e informe o IP da API no campo `baseUrl`. +``` +[Arquivo environment](./src/environments/environment.ts) diff --git a/desafio-fullstack-front/angular.json b/desafio-fullstack-front/angular.json new file mode 100644 index 000000000..62cd18806 --- /dev/null +++ b/desafio-fullstack-front/angular.json @@ -0,0 +1,127 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "desafio-fullstack-front": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": {}, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/desafio-fullstack-front", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "desafio-fullstack-front:build" + }, + "configurations": { + "production": { + "browserTarget": "desafio-fullstack-front:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "desafio-fullstack-front:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.spec.json", + "karmaConfig": "src/karma.conf.js", + "styles": [ + "src/styles.css" + ], + "scripts": [], + "assets": [ + "src/favicon.ico", + "src/assets" + ] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "src/tsconfig.app.json", + "src/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + }, + "desafio-fullstack-front-e2e": { + "root": "e2e/", + "projectType": "application", + "architect": { + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "desafio-fullstack-front:serve" + }, + "configurations": { + "production": { + "devServerTarget": "desafio-fullstack-front:serve:production" + } + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": "e2e/tsconfig.e2e.json", + "exclude": [ + "**/node_modules/**" + ] + } + } + } + } + }, + "defaultProject": "desafio-fullstack-front" +} \ No newline at end of file diff --git a/desafio-fullstack-front/e2e/protractor.conf.js b/desafio-fullstack-front/e2e/protractor.conf.js new file mode 100644 index 000000000..86776a391 --- /dev/null +++ b/desafio-fullstack-front/e2e/protractor.conf.js @@ -0,0 +1,28 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.e2e.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; \ No newline at end of file diff --git a/desafio-fullstack-front/e2e/src/app.e2e-spec.ts b/desafio-fullstack-front/e2e/src/app.e2e-spec.ts new file mode 100644 index 000000000..a0b19ea84 --- /dev/null +++ b/desafio-fullstack-front/e2e/src/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { AppPage } from './app.po'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('Welcome to desafio-fullstack-front!'); + }); +}); diff --git a/desafio-fullstack-front/e2e/src/app.po.ts b/desafio-fullstack-front/e2e/src/app.po.ts new file mode 100644 index 000000000..82ea75ba5 --- /dev/null +++ b/desafio-fullstack-front/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git a/desafio-fullstack-front/e2e/tsconfig.e2e.json b/desafio-fullstack-front/e2e/tsconfig.e2e.json new file mode 100644 index 000000000..a6dd62202 --- /dev/null +++ b/desafio-fullstack-front/e2e/tsconfig.e2e.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} \ No newline at end of file diff --git a/desafio-fullstack-front/package.json b/desafio-fullstack-front/package.json new file mode 100644 index 000000000..5e344cc4b --- /dev/null +++ b/desafio-fullstack-front/package.json @@ -0,0 +1,51 @@ +{ + "name": "desafio-fullstack-front", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^6.1.0", + "@angular/cdk": "^12.0.2", + "@angular/common": "^6.1.0", + "@angular/compiler": "^6.1.0", + "@angular/core": "^6.1.0", + "@angular/forms": "^6.1.0", + "@angular/http": "^6.1.0", + "@angular/platform-browser": "^6.1.0", + "@angular/platform-browser-dynamic": "^6.1.0", + "@angular/router": "^6.1.0", + "@ng-select/ng-select": "^2.20.5", + "core-js": "^2.5.4", + "rxjs": "6.0.0", + "sweetalert2": "^8.14.0", + "zone.js": "~0.8.26" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.7.0", + "@angular/cli": "~6.1.1", + "@angular/compiler-cli": "^6.1.0", + "@angular/language-service": "^6.1.0", + "@types/jasmine": "~2.8.6", + "@types/jasminewd2": "~2.0.3", + "@types/node": "~8.9.4", + "codelyzer": "~4.2.1", + "jasmine-core": "~2.99.1", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~1.7.1", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "~2.0.0", + "karma-jasmine": "~1.1.1", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "~5.3.0", + "ts-node": "~5.0.1", + "tslint": "~5.9.1", + "typescript": "~2.7.2" + } +} diff --git a/desafio-fullstack-front/src/app/app-routing.module.ts b/desafio-fullstack-front/src/app/app-routing.module.ts new file mode 100644 index 000000000..002e7de21 --- /dev/null +++ b/desafio-fullstack-front/src/app/app-routing.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { CourseDetailComponent } from './course-detail/course-detail.component'; +import { CourseComponent } from './course/course.component'; +import { LoginComponent } from './login/login.component'; + +const routes: Routes = [ + { + path: '', + component: LoginComponent, + }, + { + path: 'course', + component: CourseComponent + }, + { + path: 'course-detail', + component: CourseDetailComponent + }, + { + path: 'course-detail/:id', + component: CourseDetailComponent + } +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(routes) + ], + exports: [ + RouterModule + ], + declarations: [] +}) +export class AppRoutingModule { } \ No newline at end of file diff --git a/desafio-fullstack-front/src/app/app.component.css b/desafio-fullstack-front/src/app/app.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/desafio-fullstack-front/src/app/app.component.html b/desafio-fullstack-front/src/app/app.component.html new file mode 100644 index 000000000..6a164d697 --- /dev/null +++ b/desafio-fullstack-front/src/app/app.component.html @@ -0,0 +1,2 @@ + + diff --git a/desafio-fullstack-front/src/app/app.component.ts b/desafio-fullstack-front/src/app/app.component.ts new file mode 100644 index 000000000..4ac4cfdbf --- /dev/null +++ b/desafio-fullstack-front/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'desafio-fullstack-front'; +} diff --git a/desafio-fullstack-front/src/app/app.module.ts b/desafio-fullstack-front/src/app/app.module.ts new file mode 100644 index 000000000..3e9848ca7 --- /dev/null +++ b/desafio-fullstack-front/src/app/app.module.ts @@ -0,0 +1,34 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; + +import { AppComponent } from './app.component'; +import { LoginComponent } from './login/login.component'; +import { AppRoutingModule } from './app-routing.module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; +import { CourseComponent } from './course/course.component'; +import { CourseDetailComponent } from './course-detail/course-detail.component'; +import { NgMultiSelectDropDownModule } from 'ng-multiselect-dropdown'; +import { NgSelectModule } from '@ng-select/ng-select'; + +@NgModule({ + declarations: [ + AppComponent, + LoginComponent, + CourseComponent, + CourseDetailComponent + ], + imports: [ + BrowserModule, + AppRoutingModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + NgSelectModule, + NgMultiSelectDropDownModule.forRoot() + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/desafio-fullstack-front/src/app/course-detail/course-detail.component.css b/desafio-fullstack-front/src/app/course-detail/course-detail.component.css new file mode 100644 index 000000000..a296afa49 --- /dev/null +++ b/desafio-fullstack-front/src/app/course-detail/course-detail.component.css @@ -0,0 +1,158 @@ +.main { + display: flex; + justify-content: center !important; + align-items: center !important; + width: 100% !important; +} + +.grid-container { + display: grid; + grid-template-columns: 10% 80% 10%; +} + +.grid-list-courses { + display: grid; + grid-template-columns: 50% 50%; +} + +.grid-second-line { + display: grid; + grid-template-columns: 50% 50%; +} + +.grid-third-line { + display: grid; + grid-template-columns: 50% 50%; +} + +.ng-select { + border: 1px black solid; + border-radius: none; +} + +.button-container { + padding-top: 10px; + padding-bottom: 10px; + text-align: center; +} + +.button-container button { + height: 40px; + width: 150px; + background-color: black; + color: #fff; +} + +input { + width: 100%; + height: 32px; + background-color: #fff !important; + box-shadow: none !important; +} + +.grid-item { + text-align: center; +} + +.left { + padding-right: 5px !important; +} + +.right { + padding-left: 5px !important; +} + +.container-center { + width: 100% !important; +} + +.container-content { + padding: 40px; +} + +.teachers { + text-align: left; + margin: 0; +} + +.classrooms { + margin: 0; +} + +button { + background: none; + border: none; +} + +.schedule { + margin: 0; +} + +.container-header { + text-align: center; + background-color: #bdbdbd; + width: 100% !important; +} + +.container-header h1 { + text-align: center; +} + +.grid-item { + margin: 10px; +} + +.margin-container-select-left { + margin-right: 3px; +} + +.margin-container-select-right { + margin-left: 0px; +} + +.no-margin { + margin: 0px !important; +} + +.input-left { + margin-right: 2px; +} + +.input-right { + margin-left: 2px; +} + +@media only screen and (max-width: 800px) { + .grid-container { + display: grid; + grid-template-columns: 100%; + } + + .grid-list-courses { + display: grid; + grid-template-columns: 100%; + } + + .grid-second-line { + display: grid; + grid-template-columns: 100%; + } + + .grid-third-line { + display: grid; + grid-template-columns: 46% 46%; + margin-left: 5px; + } + + .left { + margin-left: 5px !important; + } + + .input-left { + margin-right: 0px; + } + + .input-right { + margin-left: 0px; + } +} diff --git a/desafio-fullstack-front/src/app/course-detail/course-detail.component.html b/desafio-fullstack-front/src/app/course-detail/course-detail.component.html new file mode 100644 index 000000000..9a8b2596a --- /dev/null +++ b/desafio-fullstack-front/src/app/course-detail/course-detail.component.html @@ -0,0 +1,44 @@ +
+
+
+
+
+
+

Detalhe do curso

+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/desafio-fullstack-front/src/app/course-detail/course-detail.component.ts b/desafio-fullstack-front/src/app/course-detail/course-detail.component.ts new file mode 100644 index 000000000..7356f1b47 --- /dev/null +++ b/desafio-fullstack-front/src/app/course-detail/course-detail.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Teacher } from '../models/teacher.model'; +import { ClassroomService } from '../services/classroom.service'; +import { TeacherService } from '../services/teacher.service'; + +import Swal from 'sweetalert2'; +import { CourseService } from '../services/course.service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Classroom } from '../models/classroom.model'; + +@Component({ + selector: 'app-course-detail', + templateUrl: './course-detail.component.html', + styleUrls: ['./course-detail.component.css'] +}) +export class CourseDetailComponent implements OnInit { + + teachers: Teacher[]; + classrooms: Classroom[] + + formGroup: FormGroup; + + loading: boolean = false; + + id = null; + + constructor(private teacherService: TeacherService, private classroomService: ClassroomService, + private fb: FormBuilder, private courseService: CourseService, private router: Router, private activatedRoute: ActivatedRoute) { + this.activatedRoute.paramMap.subscribe(params => { + this.id = params.get('id'); + }); + } + + ngOnInit() { + this.createFormGroup() + this.loadData() + } + + createFormGroup() { + this.formGroup = this.fb.group({ + id: [''], + name: ['', Validators.required], + startTime: ['', Validators.required], + endTime: ['', Validators.required], + teachers: [[], Validators.required], + classrooms: [[], Validators.required] + }) + } + + async loadData() { + + if (this.id) { + const course = await this.courseService.getById(this.id); + this.formGroup.setValue(course) + } + + this.teachers = await this.teacherService.getAll(); + this.classrooms = await this.classroomService.getAll(); + + } + + async save() { + try { + if (!this.formGroup.valid) { + return Swal.fire('Erro', 'É necessário preencher todos os campos para continuar.', 'error'); + } + + this.loading = true; + + const object = { + name: this.formGroup.get('name').value, + startTime: this.formGroup.get('startTime').value, + endTime: this.formGroup.get('endTime').value, + teachers: this.formGroup.get('teachers').value.map((x) => { return { id: x.id } }), + classrooms: this.formGroup.get('classrooms').value.map((x) => { return { id: x.id } }) + } + + this.id ? await this.courseService.update(this.id, object) : await this.courseService.save(object); + + await Swal.fire('Sucesso', `Curso ${this.id ? 'alterado' : 'cadastrado'} com sucesso`, 'success'); + + this.loading = false; + this.router.navigate(['/course']) + } catch (error) { + Swal.fire('Erro', `Ocorreu um erro ao tentar salvar o curso`, 'error'); + } finally { + this.loading = false; + } + + } + + + +} diff --git a/desafio-fullstack-front/src/app/course/course.component.css b/desafio-fullstack-front/src/app/course/course.component.css new file mode 100644 index 000000000..cc07318a3 --- /dev/null +++ b/desafio-fullstack-front/src/app/course/course.component.css @@ -0,0 +1,83 @@ +.main { + display: flex; + justify-content: center !important; + align-items: center !important; + width: 100% !important; +} + +.grid-container { + display: grid; + grid-template-columns: 10% 80% 10%; +} + +.grid-list-courses { + display: grid; + grid-template-columns: 50% 50%; +} + +.grid-content-course { + display: grid; + grid-template-columns: auto auto; +} + +.grid-item { + text-align: center; +} + +.left { + text-align: left !important; +} + +.right { + text-align: right !important; +} + +.grid-container .grid-item img { + margin-top: 14px; +} + +.container-center { + width: 100% !important; +} + +.container-content { + padding: 40px; +} + +.teachers { + text-align: left; + margin: 0; +} + +.classrooms { + margin: 0; +} + +button { + background: none; + border: none; +} + +.schedule { + margin: 0; +} + +.card { + margin: 10px; + padding: 20px; + border: 2px black solid; + border-radius: 5px; +} + +.container-header { + text-align: center; + background-color: #bdbdbd; + width: 100% !important; +} + +.container-header h1 { + text-align: center; +} + +@media only screen and (max-width: 600px) { +} diff --git a/desafio-fullstack-front/src/app/course/course.component.html b/desafio-fullstack-front/src/app/course/course.component.html new file mode 100644 index 000000000..6c2ac52fb --- /dev/null +++ b/desafio-fullstack-front/src/app/course/course.component.html @@ -0,0 +1,56 @@ +
+
+
+
+
+
+

Cursos

+
+
+ +
+
+
+
+
+
+
+
+
+ {{course.name}} +
+
+ +
+
+
+

+ + Prof. {{teacher.name}} + {{ i == (course.teachers.length - 1) ? '' : course.teachers.length > 1 && course.teachers.length < 3 + ? ' e ' : course.teachers.length> 2 ? ', ' : '' }} + +

+
+
+

+ + {{classroom.name}} + {{ i == (course.classrooms.length - 1) ? '' : course.classrooms.length > 1 && + course.classrooms.length < 3 ? ' e ' : course.classrooms.length> 2 ? ', ' : '' }} + +

+
+
+

+ {{ course.startTime }} às {{ course.endTime }} +

+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/desafio-fullstack-front/src/app/course/course.component.ts b/desafio-fullstack-front/src/app/course/course.component.ts new file mode 100644 index 000000000..35a3a311d --- /dev/null +++ b/desafio-fullstack-front/src/app/course/course.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit } from '@angular/core'; +import { CourseService } from '../services/course.service'; +import Swal from 'sweetalert2'; +import { Course } from '../models/course.model'; + +@Component({ + selector: 'app-course', + templateUrl: './course.component.html', + styleUrls: ['./course.component.css'] +}) +export class CourseComponent implements OnInit { + + courses: Course[] + + constructor(private courseService: CourseService) { } + + ngOnInit() { + this.loadData() + } + + async loadData() { + const courses: any = await this.courseService.getAll(); + this.courses = courses + } + + async removeCourse(id) { + try { + await this.courseService.delete(id); + await Swal.fire('Sucesso', `Curso removido com sucesso`, 'success'); + this.loadData(); + } catch (error) { + Swal.fire('Erro', `Ocorreu um erro ao remover o curso`, 'error'); + } + } + +} diff --git a/desafio-fullstack-front/src/app/login/login.component.css b/desafio-fullstack-front/src/app/login/login.component.css new file mode 100644 index 000000000..4ffbf538f --- /dev/null +++ b/desafio-fullstack-front/src/app/login/login.component.css @@ -0,0 +1,49 @@ +.main { + display: flex; + justify-content: center !important; + align-items: center !important; +} + +.login { + text-align: center; + width: 400px; +} + +.login h1 { + text-align: center; +} + +.input-container { + padding-top: 10px; + padding-bottom: 10px; +} + +.input-container input { + width: 400px; + height: 30px; + background-color: #fff !important; + box-shadow: none !important; +} + +.button-container { + padding-top: 10px; + padding-bottom: 10px; +} + +.button-container button { + height: 40px; + width: 150px; + background-color: black; + color: #fff; +} + +@media only screen and (max-width: 600px) { + .login { + text-align: center; + width: 250px; + } + + .input-container input { + width: 250px; + } +} diff --git a/desafio-fullstack-front/src/app/login/login.component.html b/desafio-fullstack-front/src/app/login/login.component.html new file mode 100644 index 000000000..5dc795fee --- /dev/null +++ b/desafio-fullstack-front/src/app/login/login.component.html @@ -0,0 +1,18 @@ +
+
+ +
+
\ No newline at end of file diff --git a/desafio-fullstack-front/src/app/login/login.component.ts b/desafio-fullstack-front/src/app/login/login.component.ts new file mode 100644 index 000000000..f56a8db41 --- /dev/null +++ b/desafio-fullstack-front/src/app/login/login.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AuthService } from '../services/auth.service'; +import Swal from 'sweetalert2'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] +}) +export class LoginComponent implements OnInit { + + formGroup: FormGroup; + loading: boolean = false; + + constructor(private fb: FormBuilder, private authService: AuthService, private router: Router) { } + + ngOnInit() { + this.createFormGroup() + } + + createFormGroup() { + this.formGroup = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', Validators.required] + }) + } + + async login() { + try { + + if (!this.formGroup.valid) { + return Swal.fire('Erro', 'É necessário informar email e senha para continuar.', 'error'); + } + + this.loading = true; + + const data = { + email: this.formGroup.get('email').value, + password: this.formGroup.get('password').value + } + + await this.authService.login(data); + + this.router.navigate(['/course']) + } catch (error) { + + let msg = 'Ocorreu um erro ao realizar o login, tente novamente.'; + + if (error.status === 403) { + msg = 'Email e/ou senha inválido(s)'; + } + + Swal.fire('Erro', msg, 'error'); + } finally { + this.loading = false; + } + } + +} diff --git a/desafio-fullstack-front/src/app/models/classroom.model.ts b/desafio-fullstack-front/src/app/models/classroom.model.ts new file mode 100644 index 000000000..2501544bb --- /dev/null +++ b/desafio-fullstack-front/src/app/models/classroom.model.ts @@ -0,0 +1,6 @@ +export interface Classroom { + + id: number; + name: string; + +} \ No newline at end of file diff --git a/desafio-fullstack-front/src/app/models/course.model.ts b/desafio-fullstack-front/src/app/models/course.model.ts new file mode 100644 index 000000000..dbf9fd347 --- /dev/null +++ b/desafio-fullstack-front/src/app/models/course.model.ts @@ -0,0 +1,13 @@ +import { Classroom } from "./classroom.model"; +import { Teacher } from "./teacher.model"; + +export interface Course { + + id: number; + name: string; + startTime: string; + endTime: string; + teachers: Teacher[]; + classrooms: Classroom[]; + +} \ No newline at end of file diff --git a/desafio-fullstack-front/src/app/models/teacher.model.ts b/desafio-fullstack-front/src/app/models/teacher.model.ts new file mode 100644 index 000000000..3689714cc --- /dev/null +++ b/desafio-fullstack-front/src/app/models/teacher.model.ts @@ -0,0 +1,6 @@ +export interface Teacher { + + id: number; + name: string; + +} \ No newline at end of file diff --git a/desafio-fullstack-front/src/app/services/auth.service.ts b/desafio-fullstack-front/src/app/services/auth.service.ts new file mode 100644 index 000000000..e9c0b1040 --- /dev/null +++ b/desafio-fullstack-front/src/app/services/auth.service.ts @@ -0,0 +1,15 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + constructor(private http: HttpClient) { } + + login(data) { + return this.http.post(`${environment.baseUrl}/auth/login`, { ...data }).toPromise() + } +} diff --git a/desafio-fullstack-front/src/app/services/classroom.service.ts b/desafio-fullstack-front/src/app/services/classroom.service.ts new file mode 100644 index 000000000..69f1ce6b4 --- /dev/null +++ b/desafio-fullstack-front/src/app/services/classroom.service.ts @@ -0,0 +1,17 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; +import { Classroom } from '../models/classroom.model'; + +@Injectable({ + providedIn: 'root' +}) +export class ClassroomService { + + constructor(private http: HttpClient) { } + + getAll(): Promise { + return this.http.get(`${environment.baseUrl}/classroom`).toPromise() + } + +} diff --git a/desafio-fullstack-front/src/app/services/course.service.ts b/desafio-fullstack-front/src/app/services/course.service.ts new file mode 100644 index 000000000..1aa4dd98a --- /dev/null +++ b/desafio-fullstack-front/src/app/services/course.service.ts @@ -0,0 +1,32 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; +import { Course } from '../models/course.model'; + +@Injectable({ + providedIn: 'root' +}) +export class CourseService { + + constructor(private http: HttpClient) { } + + getAll() { + return this.http.get(`${environment.baseUrl}/course`).toPromise() + } + + getById(id) { + return this.http.get(`${environment.baseUrl}/course/${id}`).toPromise() + } + + update(id, course) { + return this.http.put(`${environment.baseUrl}/course/${id}`, { ...course }).toPromise(); + } + + save(course) { + return this.http.post(`${environment.baseUrl}/course`, { ...course }).toPromise() + } + + delete(id) { + return this.http.delete(`${environment.baseUrl}/course/${id}`).toPromise() + } +} diff --git a/desafio-fullstack-front/src/app/services/teacher.service.ts b/desafio-fullstack-front/src/app/services/teacher.service.ts new file mode 100644 index 000000000..773a4b4c6 --- /dev/null +++ b/desafio-fullstack-front/src/app/services/teacher.service.ts @@ -0,0 +1,17 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; +import { Teacher } from '../models/teacher.model'; + +@Injectable({ + providedIn: 'root' +}) +export class TeacherService { + + constructor(private http: HttpClient) { } + + getAll(): Promise { + return this.http.get(`${environment.baseUrl}/teacher`).toPromise() + } + +} diff --git a/desafio-fullstack-front/src/assets/.gitkeep b/desafio-fullstack-front/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/desafio-fullstack-front/src/assets/images/add-outline.svg b/desafio-fullstack-front/src/assets/images/add-outline.svg new file mode 100644 index 000000000..e66e1656e --- /dev/null +++ b/desafio-fullstack-front/src/assets/images/add-outline.svg @@ -0,0 +1 @@ +Add \ No newline at end of file diff --git a/desafio-fullstack-front/src/assets/images/trash-outline.svg b/desafio-fullstack-front/src/assets/images/trash-outline.svg new file mode 100644 index 000000000..4920b56a6 --- /dev/null +++ b/desafio-fullstack-front/src/assets/images/trash-outline.svg @@ -0,0 +1 @@ +Trash \ No newline at end of file diff --git a/desafio-fullstack-front/src/browserslist b/desafio-fullstack-front/src/browserslist new file mode 100644 index 000000000..8e09ab492 --- /dev/null +++ b/desafio-fullstack-front/src/browserslist @@ -0,0 +1,9 @@ +# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# For IE 9-11 support, please uncomment the last line of the file and adjust as needed +> 0.5% +last 2 versions +Firefox ESR +not dead +# IE 9-11 \ No newline at end of file diff --git a/desafio-fullstack-front/src/environments/environment.prod.ts b/desafio-fullstack-front/src/environments/environment.prod.ts new file mode 100644 index 000000000..3612073bc --- /dev/null +++ b/desafio-fullstack-front/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/desafio-fullstack-front/src/environments/environment.ts b/desafio-fullstack-front/src/environments/environment.ts new file mode 100644 index 000000000..5193b0038 --- /dev/null +++ b/desafio-fullstack-front/src/environments/environment.ts @@ -0,0 +1,17 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + + baseUrl: 'Adicione a URL da API' +}; + +/* + * In development mode, to ignore zone related error stack frames such as + * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can + * import the following file, but please comment it out in production mode + * because it will have performance impact when throw error + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/desafio-fullstack-front/src/favicon.ico b/desafio-fullstack-front/src/favicon.ico new file mode 100644 index 000000000..8081c7cea Binary files /dev/null and b/desafio-fullstack-front/src/favicon.ico differ diff --git a/desafio-fullstack-front/src/index.html b/desafio-fullstack-front/src/index.html new file mode 100644 index 000000000..b5cdfaec0 --- /dev/null +++ b/desafio-fullstack-front/src/index.html @@ -0,0 +1,14 @@ + + + + + DesafioFullstackFront + + + + + + + + + diff --git a/desafio-fullstack-front/src/karma.conf.js b/desafio-fullstack-front/src/karma.conf.js new file mode 100644 index 000000000..b6e00421c --- /dev/null +++ b/desafio-fullstack-front/src/karma.conf.js @@ -0,0 +1,31 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../coverage'), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; \ No newline at end of file diff --git a/desafio-fullstack-front/src/main.ts b/desafio-fullstack-front/src/main.ts new file mode 100644 index 000000000..91ec6da5f --- /dev/null +++ b/desafio-fullstack-front/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); diff --git a/desafio-fullstack-front/src/polyfills.ts b/desafio-fullstack-front/src/polyfills.ts new file mode 100644 index 000000000..d310405a6 --- /dev/null +++ b/desafio-fullstack-front/src/polyfills.ts @@ -0,0 +1,80 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + */ + + // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + + /* + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + */ +// (window as any).__Zone_enable_cross_context_check = true; + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/desafio-fullstack-front/src/styles.css b/desafio-fullstack-front/src/styles.css new file mode 100644 index 000000000..421e573c4 --- /dev/null +++ b/desafio-fullstack-front/src/styles.css @@ -0,0 +1,2 @@ +/* You can add global styles to this file, and also import other style files */ +@import "~@ng-select/ng-select/themes/default.theme.css"; \ No newline at end of file diff --git a/desafio-fullstack-front/src/test.ts b/desafio-fullstack-front/src/test.ts new file mode 100644 index 000000000..16317897b --- /dev/null +++ b/desafio-fullstack-front/src/test.ts @@ -0,0 +1,20 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/desafio-fullstack-front/src/tsconfig.app.json b/desafio-fullstack-front/src/tsconfig.app.json new file mode 100644 index 000000000..8ea061ea1 --- /dev/null +++ b/desafio-fullstack-front/src/tsconfig.app.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "types": [] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/desafio-fullstack-front/src/tsconfig.spec.json b/desafio-fullstack-front/src/tsconfig.spec.json new file mode 100644 index 000000000..de7733630 --- /dev/null +++ b/desafio-fullstack-front/src/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/desafio-fullstack-front/src/tslint.json b/desafio-fullstack-front/src/tslint.json new file mode 100644 index 000000000..52e2c1a5a --- /dev/null +++ b/desafio-fullstack-front/src/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +} diff --git a/desafio-fullstack-front/tsconfig.json b/desafio-fullstack-front/tsconfig.json new file mode 100644 index 000000000..916247e4c --- /dev/null +++ b/desafio-fullstack-front/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "module": "es2015", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2017", + "dom" + ] + } +} diff --git a/desafio-fullstack-front/tslint.json b/desafio-fullstack-front/tslint.json new file mode 100644 index 000000000..3ea984c77 --- /dev/null +++ b/desafio-fullstack-front/tslint.json @@ -0,0 +1,130 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "deprecation": { + "severity": "warn" + }, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "no-output-on-prefix": true, + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true + } +}