96 switch from azure table storage to sqlite #97

Merged
noahspannbauer merged 7 commits from 96-switch-from-azure-table-storage-to-sqlite into main 2025-11-23 11:42:31 -05:00
51 changed files with 22651 additions and 13863 deletions
Showing only changes of commit e1605992b0 - Show all commits

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -0,0 +1,64 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class InitialMigration1754234179211 implements MigrationInterface {
name = 'InitialMigration1754234179211'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "pilots" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "address" varchar NOT NULL, "city" varchar NOT NULL, "state" varchar NOT NULL, "postalCode" varchar NOT NULL, "email" varchar NOT NULL, "phone" varchar NOT NULL)`);
await queryRunner.query(`CREATE TABLE "logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar)`);
await queryRunner.query(`CREATE TABLE "temporary_certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_05a68997dc2d27dfcc642a4cf51" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_certificates"("id", "type", "number", "issueDate", "pilotId") SELECT "id", "type", "number", "issueDate", "pilotId" FROM "certificates"`);
await queryRunner.query(`DROP TABLE "certificates"`);
await queryRunner.query(`ALTER TABLE "temporary_certificates" RENAME TO "certificates"`);
await queryRunner.query(`CREATE TABLE "temporary_endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_96a69a0bfbdeccce6bffe34002d" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_endorsements"("id", "type", "issueDate", "pilotId") SELECT "id", "type", "issueDate", "pilotId" FROM "endorsements"`);
await queryRunner.query(`DROP TABLE "endorsements"`);
await queryRunner.query(`ALTER TABLE "temporary_endorsements" RENAME TO "endorsements"`);
await queryRunner.query(`CREATE TABLE "temporary_medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_cb1f5d88fa2b105cc77513e9082" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_medical"("id", "class", "expirationDate", "pilotId") SELECT "id", "class", "expirationDate", "pilotId" FROM "medical"`);
await queryRunner.query(`DROP TABLE "medical"`);
await queryRunner.query(`ALTER TABLE "temporary_medical" RENAME TO "medical"`);
await queryRunner.query(`CREATE TABLE "temporary_logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar, CONSTRAINT "FK_19598551658f7625a82c7f029c8" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_logs"("id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId") SELECT "id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId" FROM "logs"`);
await queryRunner.query(`DROP TABLE "logs"`);
await queryRunner.query(`ALTER TABLE "temporary_logs" RENAME TO "logs"`);
await queryRunner.query(`CREATE TABLE "temporary_tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar, CONSTRAINT "FK_71881df31cfff2362e39accc4b2" FOREIGN KEY ("logId") REFERENCES "logs" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_tracks"("id", "url", "order", "logId") SELECT "id", "url", "order", "logId" FROM "tracks"`);
await queryRunner.query(`DROP TABLE "tracks"`);
await queryRunner.query(`ALTER TABLE "temporary_tracks" RENAME TO "tracks"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "tracks" RENAME TO "temporary_tracks"`);
await queryRunner.query(`CREATE TABLE "tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar)`);
await queryRunner.query(`INSERT INTO "tracks"("id", "url", "order", "logId") SELECT "id", "url", "order", "logId" FROM "temporary_tracks"`);
await queryRunner.query(`DROP TABLE "temporary_tracks"`);
await queryRunner.query(`ALTER TABLE "logs" RENAME TO "temporary_logs"`);
await queryRunner.query(`CREATE TABLE "logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "logs"("id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId") SELECT "id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId" FROM "temporary_logs"`);
await queryRunner.query(`DROP TABLE "temporary_logs"`);
await queryRunner.query(`ALTER TABLE "medical" RENAME TO "temporary_medical"`);
await queryRunner.query(`CREATE TABLE "medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "medical"("id", "class", "expirationDate", "pilotId") SELECT "id", "class", "expirationDate", "pilotId" FROM "temporary_medical"`);
await queryRunner.query(`DROP TABLE "temporary_medical"`);
await queryRunner.query(`ALTER TABLE "endorsements" RENAME TO "temporary_endorsements"`);
await queryRunner.query(`CREATE TABLE "endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "endorsements"("id", "type", "issueDate", "pilotId") SELECT "id", "type", "issueDate", "pilotId" FROM "temporary_endorsements"`);
await queryRunner.query(`DROP TABLE "temporary_endorsements"`);
await queryRunner.query(`ALTER TABLE "certificates" RENAME TO "temporary_certificates"`);
await queryRunner.query(`CREATE TABLE "certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "certificates"("id", "type", "number", "issueDate", "pilotId") SELECT "id", "type", "number", "issueDate", "pilotId" FROM "temporary_certificates"`);
await queryRunner.query(`DROP TABLE "temporary_certificates"`);
await queryRunner.query(`DROP TABLE "tracks"`);
await queryRunner.query(`DROP TABLE "logs"`);
await queryRunner.query(`DROP TABLE "pilots"`);
await queryRunner.query(`DROP TABLE "medical"`);
await queryRunner.query(`DROP TABLE "endorsements"`);
await queryRunner.query(`DROP TABLE "certificates"`);
}
}

View File

@@ -16,7 +16,11 @@
"test:watch": "jest --watch", "test:watch": "jest --watch",
"test:cov": "jest --coverage", "test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "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" "test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "npm run build && npx typeorm -d dist/config/typeorm-cli.config.js",
"migration:generate": "npm run typeorm -- migration:generate",
"migration:run": "npm run typeorm -- migration:run",
"migration:revert": "npm run typeorm -- migration:revert"
}, },
"dependencies": { "dependencies": {
"@azure/storage-blob": "^12.27.0", "@azure/storage-blob": "^12.27.0",
@@ -32,7 +36,7 @@
"@noahspan/noahspan-modules": "^1.1.5", "@noahspan/noahspan-modules": "^1.1.5",
"@schematics/angular": "^17.3.7", "@schematics/angular": "^17.3.7",
"@types/multer": "^1.4.12", "@types/multer": "^1.4.12",
"dotenv": "^16.4.7", "dotenv": "^16.6.1",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"sqlite3": "^5.1.7", "sqlite3": "^5.1.7",

View File

@@ -8,6 +8,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthGuard, AuthModule, UserModule } from '@noahspan/noahspan-modules'; import { AuthGuard, AuthModule, UserModule } from '@noahspan/noahspan-modules';
import configuration from './config/configuration'; import configuration from './config/configuration';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { dataSourceOptions } from './config/typeorm-cli.config';
import { TrackModule } from './track/track.module';
@Module({ @Module({
imports: [ imports: [
@@ -23,20 +25,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
}, },
}), }),
ConfigModule.forRoot({ ConfigModule.forRoot({
isGlobal: true,
load: [configuration] load: [configuration]
}), }),
FeatureFlagModule, FeatureFlagModule,
LogModule, LogModule,
PilotModule, PilotModule,
TypeOrmModule.forRootAsync({ TrackModule,
inject: [ConfigService], TypeOrmModule.forRoot(dataSourceOptions),
useFactory: (configService: ConfigService) => ({
type: 'sqlite',
database: configService.get<string>('dbPath'),
entities: [__dirname + "/**/*.entity{.ts,.js}"],
synchronize: false
})
}),
UserModule.registerAsync({ UserModule.registerAsync({
inject: [ConfigService], inject: [ConfigService],
imports: [ConfigModule], imports: [ConfigModule],

View File

View File

@@ -1,6 +1,5 @@
export default () => ({ export default () => ({
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING, azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
dbPath: process.env.DB_PATH,
clientId: process.env.CLIENT_ID, clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET, clientSecret: process.env.CLIENT_SECRET,
tenantId: process.env.TENANT_ID tenantId: process.env.TENANT_ID

View File

@@ -0,0 +1,20 @@
import { DataSource, DataSourceOptions } from 'typeorm';
import { config } from 'dotenv';
import { ConfigService } from '@nestjs/config';
import configuration from './configuration';
config();
const configService = new ConfigService();
export const dataSourceOptions: DataSourceOptions = {
type: 'sqlite',
database: configService.get<string>('DB_PATH'),
entities: ['dist/**/*.entity.js'],
migrations: ['dist/migrations/*.js'],
synchronize: configService.get<boolean>('DB_SYNC')
}
const dataSource = new DataSource(dataSourceOptions);
export default dataSource;

View File

@@ -0,0 +1,65 @@
import { Body, Controller, Delete, Get, HttpException, Param, Post, Put } from "@nestjs/common";
import { EndorsementService } from "./endorsement.service";
import { CustomError } from "@noahspan/noahspan-modules";
import { EndorsementDto } from "./endorsement.dto";
@Controller('endorsements')
export class EndorsementController {
constructor(private readonly endorsementService: EndorsementService) {}
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.endorsementService.find(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get()
async findAll() {
try {
return await this.endorsementService.findAll();
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Post()
async create(@Body() endorsementDto: EndorsementDto) {
try {
return await this.endorsementService.create(endorsementDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Put(':id')
async update(@Param('id') id: string, @Body() endorsementDto: EndorsementDto) {
try {
return await this.endorsementService.update(id, endorsementDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode)
}
}
@Delete(':id')
async delete(@Param('id') id: string) {
try {
return await this.endorsementService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode)
}
}
}

View File

@@ -0,0 +1,3 @@
export class EndorsementDto {
}

View File

@@ -0,0 +1,32 @@
import { InjectRepository } from "@nestjs/typeorm";
import { EndorsementEntity } from "./endorsement.entity";
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { Injectable } from "@nestjs/common";
import { EndorsementDto } from "./endorsement.dto";
@Injectable()
export class EndorsementService {
constructor(
@InjectRepository(EndorsementEntity) private readonly endorsementRepository: Repository<EndorsementEntity>
) {}
async find(id: string): Promise<EndorsementEntity> {
return await this.endorsementRepository.findOneBy({ id });
}
async findAll(): Promise<EndorsementEntity[]> {
return await this.endorsementRepository.find();
}
async create(endorsement: EndorsementDto): Promise<InsertResult> {
return await this.endorsementRepository.insert(endorsement);
}
async update(id: string, endorsement: EndorsementDto): Promise<UpdateResult> {
return await this.endorsementRepository.update(id, endorsement);
}
async delete(id: string): Promise<DeleteResult> {
return await this.endorsementRepository.delete({ id });
}
}

View File

@@ -37,10 +37,11 @@ import { ConfigService } from '@nestjs/config';
return blockBlobClient; return blockBlobClient;
} }
async uploadFile(file: Express.Multer.File, containerName: string, rowKey: string): Promise<string> { async uploadFile(file: Express.Multer.File, containerName: string, logId: string): Promise<string> {
console.log(file)
this.containerName = containerName; this.containerName = containerName;
const blockBlobClient = await this.getBlobClient(`${rowKey}/${file.originalname}`); const blockBlobClient = await this.getBlobClient(`${logId}/${file.originalname}`);
const fileUrl = blockBlobClient.url; const fileUrl = blockBlobClient.url;
await blockBlobClient.uploadData(file.buffer); await blockBlobClient.uploadData(file.buffer);

View File

@@ -7,20 +7,17 @@ import {
Param, Param,
Post, Post,
Put, Put,
Query,
StreamableFile,
UploadedFile,
UseGuards, UseGuards,
UseInterceptors UseInterceptors
} from '@nestjs/common'; } from '@nestjs/common';
import { LogDto } from './log.dto'; import { LogDto } from './log.dto';
import { Log } from './log.entity'; import { LogEntity } from './log.entity';
import { LogService } from './log.service'; import { LogService } from './log.service';
import { CustomError } from '../error/customError'; import { CustomError } from '../error/customError';
import { AuthGuard } from '@noahspan/noahspan-modules'; import { AuthGuard } from '@noahspan/noahspan-modules';
import { LogInterceptor } from './interceptors/log.interceptor'; import { LogInterceptor } from './interceptors/log.interceptor';
import { FileService } from '../file/file.service'; import { FileService } from '../file/file.service';
import { FileInterceptor } from '@nestjs/platform-express'; import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
@Controller('logs') @Controller('logs')
export class LogController { export class LogController {
@@ -30,14 +27,13 @@ export class LogController {
) {} ) {}
@Get(':partitionKey/:rowKey') @Get(':id')
@UseInterceptors(new LogInterceptor()) @UseInterceptors(new LogInterceptor())
async find( async find(
@Param('partitionKey') partitionKey: string, @Param('id') id: string,
@Param('rowKey') rowKey: string ): Promise<LogEntity> {
): Promise<Log> {
try { try {
return await this.logService.find(partitionKey, rowKey); return await this.logService.find(id);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -47,7 +43,7 @@ export class LogController {
@Get() @Get()
@UseInterceptors(new LogInterceptor()) @UseInterceptors(new LogInterceptor())
async findAll(): Promise<Log[]> { async findAll(): Promise<LogEntity[]> {
try { try {
return await this.logService.findAll(); return await this.logService.findAll();
} catch (error) { } catch (error) {
@@ -57,35 +53,26 @@ export class LogController {
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Post() @Post()
async create(@Body() logDto: LogDto): Promise<Log> { async create(@Body() logDto: LogDto): Promise<InsertResult> {
try { try {
const log = new Log(); return await this.logService.create(logDto);
Object.assign(log, logDto);
return await this.logService.create(log);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
console.log(error)
throw new HttpException(customError.message, customError.statusCode); throw new HttpException(customError.message, customError.statusCode);
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Put(':partitionKey/:rowKey') @Put(':id')
async update( async update(
@Param('partitionKey') partitionKey: string, @Param('id') id: string,
@Param('rowKey') rowKey: string,
@Body() logDto: LogDto @Body() logDto: LogDto
): Promise<Log> { ): Promise<UpdateResult> {
try { try {
const log = new Log(); return await this.logService.update(id, logDto);
Object.assign(log, logDto);
return await this.logService.update(partitionKey, rowKey, log);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -93,52 +80,13 @@ export class LogController {
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey') @Delete(':id')
async delete( async delete(
@Param('partitionKey') partitionKey: string, @Param('id') id: string,
@Param('rowKey') rowKey: string ): Promise<DeleteResult> {
): Promise<void> {
try { try {
return await this.logService.delete(partitionKey, rowKey); return await this.logService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post(':partitionKey/:rowKey/track')
@UseInterceptors(FileInterceptor('file'))
async createTrack(@Param('rowKey') rowKey: string, @UploadedFile() file: Express.Multer.File) {
try {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, rowKey);
return { url }
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get(':partitionKey/:rowKey/track')
async downloadTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<string> {
const containerName = 'tracks';
const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName)
return downloadedFile;
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey/track')
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
try {
const containerName = 'tracks';
return await this.fileService.deleteFile(containerName, rowKey, fileName)
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;

View File

@@ -1,27 +1,30 @@
import { PilotEntity } from "src/pilot/pilot.entity";
import { TrackEntity } from "src/track/track.entity";
export class LogDto { export class LogDto {
pilotId: string; pilotId: string;
pilotName: string; date: Date;
date: string;
aircraftMakeModel: string; aircraftMakeModel: string;
aircraftIdentity: string; aircraftIdentity: string;
routeFrom: string; routeFrom: string;
routeTo: string; routeTo: string;
durationOfFlight: number; durationOfFlight?: number;
singleEngineLand: string; singleEngineLand?: number;
simulatorAtd: number; simulatorAtd?: number;
landingsDay: number; landingsDay?: number;
landingsNight: number; landingsNight?: number;
instrumentActual: number; instrumentActual?: number;
instrumentSimulated: number; instrumentSimulated?: number;
instrumentApproaches: number; instrumentApproaches?: number;
instrumentHolds: number; instrumentHolds?: number;
instrumentNavTrack: number; instrumentNavTrack?: number;
groundTrainingReceived: number; groundTrainingReceived?: number;
flightTrainingReceived: number; flightTrainingReceived?: number;
crossCountry: number; crossCountry?: number;
night: number; night?: number;
solo: number; solo?: number;
pilotInCommand: number; pilotInCommand?: number;
tracks: string[]; notes?: string;
notes: string; pilot?: PilotEntity;
tracks?: TrackEntity[];
} }

View File

@@ -1,35 +1,36 @@
export class Log { // export class Log {
partitionKey: string; // partitionKey: string;
rowKey: string; // rowKey: string;
pilotId: string; // pilotId: string;
pilotName: string; // pilotName: string;
date: string; // date: string;
aircraftMakeModel: string; // aircraftMakeModel: string;
aircraftIdentity: string; // aircraftIdentity: string;
routeFrom: string; // routeFrom: string;
routeTo: string; // routeTo: string;
durationOfFlight: number | null; // durationOfFlight: number | null;
singleEngineLand: number | null; // singleEngineLand: number | null;
simulatorAtd?: number | null; // simulatorAtd?: number | null;
landingsDay?: number | null; // landingsDay?: number | null;
landingsNight?: number | null; // landingsNight?: number | null;
groundTrainingReceived?: number; // groundTrainingReceived?: number;
flightTrainingReceived?: number; // flightTrainingReceived?: number;
crossCountry?: number | null; // crossCountry?: number | null;
night?: number | null; // night?: number | null;
solo?: number | null; // solo?: number | null;
pilotInCommand?: number | null; // pilotInCommand?: number | null;
instrumentActual?: number | null; // instrumentActual?: number | null;
instrumentSimulated?: number | null; // instrumentSimulated?: number | null;
instrumentApproaches?: number | null; // instrumentApproaches?: number | null;
instrumentHolds?: number | null; // instrumentHolds?: number | null;
instrumentNavTrack?: number | null; // instrumentNavTrack?: number | null;
tracks?: string[]; // tracks?: string[];
notes?: string; // notes?: string;
} // }
import { PilotEntity } from 'src/pilot/pilot.entity'; import { PilotEntity } from 'src/pilot/pilot.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'; import { TrackEntity } from 'src/track/track.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
@Entity({ name: 'logs' }) @Entity({ name: 'logs' })
export class LogEntity { export class LogEntity {
@@ -54,53 +55,56 @@ export class LogEntity {
@Column() @Column()
durationOfFlight: number; durationOfFlight: number;
@Column() @Column({ nullable: true })
singleEngineLand: number; singleEngineLand: number | null;
@Column() @Column({ nullable: true })
simulatorAtd: number; simulatorAtd: number | null;
@Column() @Column({ nullable: true })
landingsDay: number; landingsDay: number | null;
@Column() @Column({ nullable: true })
landingsNight: number; landingsNight: number | null;
@Column() @Column({ nullable: true })
groundTrainingReceived: number; groundTrainingReceived: number | null;
@Column() @Column({ nullable: true })
flightTrainingReceived: number; flightTrainingReceived: number | null;
@Column() @Column({ nullable: true })
crossCountry: number; crossCountry: number | null;
@Column() @Column({ nullable: true })
night: number; night: number | null;
@Column() @Column({ nullable: true })
solo: number; solo: number | null;
@Column() @Column({ nullable: true })
pilotInCommand: number; pilotInCommand: number | null;
@Column() @Column({ nullable: true })
instrumentActual: number; instrumentActual: number | null;
@Column() @Column({ nullable: true })
instrumentSimulated: number; instrumentSimulated: number | null;
@Column() @Column({ nullable: true })
instrumentApproaches: number; instrumentApproaches: number | null;
@Column() @Column({ nullable: true })
instrumentHolds: number; instrumentHolds: number | null;
@Column() @Column({ nullable: true })
instrumentNavTrack: number; instrumentNavTrack: number | null;
@Column() @Column({ nullable: true })
notes: string; notes: string | null;
@OneToMany(() => TrackEntity, (track: TrackEntity) => track.log, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
tracks: TrackEntity[]
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.logs) @ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.logs)
@JoinColumn({ name: 'pilotId' }) @JoinColumn({ name: 'pilotId' })

View File

@@ -1,28 +1,33 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { LogController } from './log.controller'; import { LogController } from './log.controller';
import { LogService } from './log.service'; import { LogService } from './log.service';
import { AzureTableStorageModule } from '@noahspan/azure-database'; // import { AzureTableStorageModule } from '@noahspan/azure-database';
import { Log } from './log.entity'; import { LogEntity } from './log.entity';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { FileService } from '../file/file.service'; import { FileService } from '../file/file.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PilotModule } from 'src/pilot/pilot.module';
@Module({ @Module({
imports: [ imports: [
AzureTableStorageModule.forRootAsync({ // AzureTableStorageModule.forRootAsync({
imports: [ConfigModule], // imports: [ConfigModule],
useFactory: async (configService: ConfigService) => { // useFactory: async (configService: ConfigService) => {
return { // return {
connectionString: configService.get<string>('azureStorageConnectionString') // connectionString: configService.get<string>('azureStorageConnectionString')
}; // };
}, // },
inject: [ConfigService] // inject: [ConfigService]
}), // }),
AzureTableStorageModule.forFeature(Log, { // AzureTableStorageModule.forFeature(Log, {
createTableIfNotExists: false, // createTableIfNotExists: false,
table: 'logs' // table: 'logs'
}), // }),
PilotModule,
TypeOrmModule.forFeature([LogEntity])
], ],
controllers: [LogController], controllers: [LogController],
exports: [LogService],
providers: [ providers: [
ConfigService, ConfigService,
FileService, FileService,

View File

@@ -1,34 +1,58 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository, Repository } from '@noahspan/azure-database'; import { InjectRepository } from '@nestjs/typeorm';
import { Log } from './log.entity'; import { LogEntity } from './log.entity';
import { v4 as uuidv4 } from 'uuid'; import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { LogDto } from './log.dto';
import { PilotService } from 'src/pilot/pilot.service';
import { PilotEntity } from 'src/pilot/pilot.entity';
import { CustomError } from 'src/error/customError';
import { TrackService } from 'src/track/track.service';
@Injectable() @Injectable()
export class LogService { export class LogService {
constructor( constructor(
@InjectRepository(Log) private readonly logRepository: Repository<Log> @InjectRepository(LogEntity) private readonly logRepository: Repository<LogEntity>,
private readonly pilotService: PilotService
) {} ) {}
async find(partitionKey: string, rowKey: string): Promise<Log> { async find(id: string): Promise<LogEntity> {
return await this.logRepository.find(partitionKey, rowKey); const logEntity: LogEntity = await this.logRepository.findOne({
where: { id: id },
relations: ['pilot', 'tracks']
});
console.log(logEntity);
return logEntity;
} }
async findAll(): Promise<Log[]> { async findAll(): Promise<LogEntity[]> {
return await this.logRepository.findAll(); return await this.logRepository.find();
} }
async create(log: Log): Promise<Log> { async create(logDto: LogDto): Promise<InsertResult> {
log.partitionKey = 'log'; try{
log.rowKey = uuidv4(); const pilotEntity: PilotEntity = await this.pilotService.find(logDto.pilotId);
return await this.logRepository.create(log); if (pilotEntity) {
const { pilotId, ...newLogDto } = logDto;
const log = this.logRepository.create({
...newLogDto,
pilot: pilotEntity
})
return this.logRepository.insert(log);
} else {
throw new CustomError('Pilot not found', 'Not found', 404);
}
} catch (error) {
throw error
}
} }
async update(partitionKey: string, rowKey: string, log: Log): Promise<Log> { async update(id: string, log: LogDto): Promise<UpdateResult> {
return await this.logRepository.update(partitionKey, rowKey, log); return await this.logRepository.update(id, log);
} }
async delete(partitionKey: string, rowKey: string): Promise<void> { async delete(id: string): Promise<DeleteResult> {
await this.logRepository.delete(partitionKey, rowKey); return await this.logRepository.delete({ id });
} }
} }

View File

@@ -0,0 +1,71 @@
import { Body, Controller, Delete, Get, HttpException, Param, Post, Put, UseGuards } from "@nestjs/common";
import { MedicalService } from "./medical.service";
import { CustomError } from "src/error/customError";
import { MedicalDto } from "./medical.dto";
import { AuthGuard } from "@noahspan/noahspan-modules";
@Controller('medical')
export class MedicalController {
constructor(private readonly medicalService: MedicalService) {}
@UseGuards(AuthGuard)
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.medicalService.find(id)
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Get()
async findAll() {
try {
return await this.medicalService.findAll();
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post()
async create(@Body() medicalDto: MedicalDto) {
try {
return await this.medicalService.create(medicalDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Put(':id')
async update(@Param('id') id: string, @Body() medicalDto: MedicalDto) {
try {
return await this.medicalService.update(id, medicalDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Delete(':id')
async delete(@Param('id') id: string) {
try {
return await this.medicalService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
}

View File

@@ -0,0 +1,6 @@
export class MedicalDto {
id: string;
class: string;
expirationDate: string;
pilotId: string;
}

View File

View File

@@ -0,0 +1,33 @@
import { MedicalEntity } from "./medical.entity";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { MedicalDto } from "./medical.dto";
@Injectable()
export class MedicalService {
constructor(
@InjectRepository(MedicalEntity) private readonly medicalRepository: Repository<MedicalEntity>
) {}
async find(id: string): Promise<MedicalEntity> {
return await this.medicalRepository.findOneBy({ id });
}
async findAll(): Promise<MedicalEntity[]> {
return await this.medicalRepository.find();
}
async create(medical: MedicalDto): Promise<InsertResult> {
return await this.medicalRepository.insert(medical)
}
async update(id: string, medical: MedicalDto): Promise<UpdateResult> {
return await this.medicalRepository.update(id, medical);
}
async delete(id: string): Promise<DeleteResult> {
return await this.medicalRepository.delete({ id });
}
}

View File

@@ -11,24 +11,21 @@ import {
UseInterceptors, UseInterceptors,
} from '@nestjs/common'; } from '@nestjs/common';
import { PilotDto } from './pilot.dto'; import { PilotDto } from './pilot.dto';
import { Pilot } from './pilot.entity'; import { PilotEntity } from './pilot.entity';
import { PilotService } from './pilot.service'; import { PilotService } from './pilot.service';
import { CustomError } from '../error/customError'; import { CustomError } from '../error/customError';
import { AuthGuard } from '@noahspan/noahspan-modules' import { AuthGuard } from '@noahspan/noahspan-modules'
import { PilotInterceptor } from './interceptors/pilot.interceptor'; import { PilotInterceptor } from './interceptors/pilot.interceptor';
@Controller('pilots') @Controller('pilots')
@UseInterceptors(new PilotInterceptor()) // @UseInterceptors(new PilotInterceptor())
export class PilotController { export class PilotController {
constructor(private readonly pilotService: PilotService) {} constructor(private readonly pilotService: PilotService) {}
@Get(':partitionKey/:rowKey') @Get(':id')
async find( async find(@Param('id') id: string) {
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string
) {
try { try {
return await this.pilotService.find(partitionKey, rowKey); return await this.pilotService.find(id);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -47,30 +44,11 @@ export class PilotController {
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Post() @Post()
async create(@Body() pilotDto: PilotDto) { async create(@Body() pilotDto: PilotDto) {
try { try {
let pilot = new Pilot(); return await this.pilotService.create(pilotDto);
pilot = {
partitionKey: pilotDto.partitionKey,
rowKey: pilotDto.rowKey,
id: pilotDto.id,
name: pilotDto.name,
address: pilotDto.address,
city: pilotDto.city,
state: pilotDto.state,
postalCode: pilotDto.postalCode,
email: pilotDto.email,
phone: pilotDto.phone,
medicalClass: pilotDto.medicalClass,
medicalExpiration: pilotDto.medicalExpiration,
certificates: JSON.stringify(pilotDto.certificates),
endorsements: JSON.stringify(pilotDto.endorsements)
}
return await this.pilotService.create(pilot);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -78,34 +56,14 @@ export class PilotController {
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Put(':partitionKey/:rowKey') @Put(':id')
async update( async update(
@Param('partitionKey') partitionKey: string, @Param('id') id: string,
@Param('rowKey') rowKey: string,
@Body() pilotDto: PilotDto @Body() pilotDto: PilotDto
) { ) {
try { try {
let pilot = new Pilot(); return await this.pilotService.update(id, pilotDto);
pilot = {
partitionKey: pilotDto.partitionKey,
rowKey: pilotDto.rowKey,
id: pilotDto.id,
name: pilotDto.name,
address: pilotDto.address,
city: pilotDto.city,
state: pilotDto.state,
postalCode: pilotDto.postalCode,
email: pilotDto.email,
phone: pilotDto.phone,
medicalClass: pilotDto.medicalClass,
medicalExpiration: pilotDto.medicalExpiration,
certificates: JSON.stringify(pilotDto.certificates),
endorsements: JSON.stringify(pilotDto.endorsements)
}
return await this.pilotService.update(partitionKey, rowKey, pilot);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;
@@ -113,14 +71,13 @@ export class PilotController {
} }
} }
@UseGuards(AuthGuard) // @UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey') @Delete(':id')
async delete( async delete(
@Param('partitionKey') partitionKey: string, @Param('id') id: string,
@Param('rowKey') rowKey: string
) { ) {
try { try {
return await this.pilotService.delete(partitionKey, rowKey); return await this.pilotService.delete(id);
} catch (error) { } catch (error) {
const customError = error as CustomError; const customError = error as CustomError;

View File

@@ -1,10 +1,6 @@
import { Certificate } from "./certificate/certificate.entity"; import { LogEntity } from "src/log/log.entity";
import { Endorsement } from "./endorsement/endorsement.entity";
export class PilotDto { export class PilotDto {
partitionKey: string;
rowKey: string;
id: string;
name: string; name: string;
address: string; address: string;
city: string; city: string;
@@ -12,8 +8,4 @@ export class PilotDto {
postalCode: string; postalCode: string;
email?: string; email?: string;
phone?: string; phone?: string;
medicalClass?: string;
medicalExpiration?: string;
certificates: Certificate;
endorsements: Endorsement
} }

View File

@@ -1,41 +1,44 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { PilotController } from './pilot.controller'; import { PilotController } from './pilot.controller';
import { PilotService } from './pilot.service'; import { PilotService } from './pilot.service';
import { AzureTableStorageModule } from '@noahspan/azure-database'; // import { AzureTableStorageModule } from '@noahspan/azure-database';
import { Pilot } from './pilot.entity'; import { PilotEntity } from './pilot.entity';
import { ConfigModule, ConfigService } from '@nestjs/config'; // import { ConfigModule, ConfigService } from '@nestjs/config';
import { Log } from 'src/log/log.entity'; // import { Log } from 'src/log/log.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
imports: [ imports: [
AzureTableStorageModule.forRootAsync({ // AzureTableStorageModule.forRootAsync({
imports: [ConfigModule], // imports: [ConfigModule],
useFactory: async (configService: ConfigService) => { // useFactory: async (configService: ConfigService) => {
return { // return {
connectionString: configService.get<string>('azureStorageConnectionString') // connectionString: configService.get<string>('azureStorageConnectionString')
}; // };
}, // },
inject: [ConfigService] // inject: [ConfigService]
}), // }),
AzureTableStorageModule.forFeature(Log, { // AzureTableStorageModule.forFeature(Log, {
createTableIfNotExists: false, // createTableIfNotExists: false,
table: 'logs' // table: 'logs'
}), // }),
AzureTableStorageModule.forRootAsync({ // AzureTableStorageModule.forRootAsync({
imports: [ConfigModule], // imports: [ConfigModule],
useFactory: async (configService: ConfigService) => { // useFactory: async (configService: ConfigService) => {
return { // return {
connectionString: configService.get<string>('azureStorageConnectionString') // connectionString: configService.get<string>('azureStorageConnectionString')
}; // };
}, // },
inject: [ConfigService] // inject: [ConfigService]
}), // }),
AzureTableStorageModule.forFeature(Pilot, { // AzureTableStorageModule.forFeature(PilotEntity, {
createTableIfNotExists: false, // createTableIfNotExists: false,
table: 'pilots' // table: 'pilots'
}) // }),
TypeOrmModule.forFeature([PilotEntity])
], ],
controllers: [PilotController], controllers: [PilotController],
exports: [PilotService],
providers: [PilotService] providers: [PilotService]
}) })
export class PilotModule {} export class PilotModule {}

View File

@@ -1,54 +1,46 @@
import { InjectRepository, Repository } from '@noahspan/azure-database'; import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common'; import { PilotEntity } from './pilot.entity';
import { Pilot } from './pilot.entity'; import { InjectRepository } from '@nestjs/typeorm';
import { Log } from 'src/log/log.entity'; import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { LogService } from 'src/log/log.service'; import { PilotDto } from './pilot.dto';
import { CustomError } from 'src/error/customError';
@Injectable() @Injectable()
export class PilotService { export class PilotService {
constructor( constructor(
@InjectRepository(Pilot) private readonly pilotRepository: Repository<Pilot>, @InjectRepository(PilotEntity) private readonly pilotRepository: Repository<PilotEntity>
@InjectRepository(Log) private readonly logRepository: Repository<Log>
) {} ) {}
async find(partitionKey: string, rowKey: string): Promise<Pilot> { async find(id: string): Promise<PilotEntity> {
return await this.pilotRepository.find(partitionKey, rowKey); try {
const pilotEntity = await this.pilotRepository.findOneBy({ id });
if (pilotEntity) {
return pilotEntity
} else {
throw new CustomError('Pilot not found', 'Not found', 404)
}
} catch (error) {
throw error
}
} }
async findAll(): Promise<Pilot[]> { async findAll(): Promise<PilotEntity[]> {
return await this.pilotRepository.findAll(); return await this.pilotRepository.find();
} }
async create(pilot: Pilot): Promise<Pilot> { async create(pilot: PilotDto): Promise<InsertResult> {
// try { return await this.pilotRepository.insert(pilot);
// return await this.pilotRepository.create(pilot);
// } catch (error) {
// throw new Error(error);
// }
return await this.pilotRepository.create(pilot);
} }
async update( async update(
partitionKey: string, id: string,
rowKey: string, pilot: PilotDto
pilot: Pilot ): Promise<UpdateResult> {
): Promise<Pilot> { return await this.pilotRepository.update(id, pilot);
return await this.pilotRepository.update(partitionKey, rowKey, pilot);
} }
async delete(partitionKey: string, rowKey: string): Promise<void> { async delete(id: string): Promise<DeleteResult> {
const pilotLogs: Log[] = await this.logRepository.findAll({ return await this.pilotRepository.delete({ id });
queryOptions: {
filter: `pilotId eq '${rowKey}'`
}
})
for (const pilotLog of pilotLogs) {
await this.logRepository.delete(pilotLog.partitionKey, pilotLog.rowKey);
}
await this.pilotRepository.delete(partitionKey, rowKey);
return
} }
} }

View File

@@ -0,0 +1,133 @@
import {
Body,
Controller,
Delete,
Get,
HttpException,
Param,
Post,
Put,
Query,
UploadedFile,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { AuthGuard } from '@noahspan/noahspan-modules';
import { CustomError } from '../error/customError';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileService } from '../file/file.service';
import { TrackService } from './track.service';
import { TrackEntity } from './track.entity';
import { TrackDto } from './track.dto';
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
@Controller('tracks')
export class TrackController {
constructor(
private readonly fileService: FileService,
private readonly trackService: TrackService
) {}
// @Get('id')
// async fin(@Param('id') id: string): Promise<TrackEntity> {
// try {
// return await this.trackService.find(id);
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode);
// }
// }
// @Get()
// async findAdd(): Promise<TrackEntity[]> {
// try {
// return await this.trackService.findAll();
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode)
// }
// }
@Post(':logId/:order')
async create(@Param('logId') logId: string, @Param('order') order: number, @UploadedFile() file: Express.Multer.File): Promise<InsertResult> {
try {
console.log(file)
return await this.trackService.create(logId, order, file);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
// @Put(':id')
// async update(@Param('id') id: string, @Body() trackDto: TrackDto): Promise<UpdateResult> {
// try {
// return await this.trackService.update(id, trackDto);
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode)
// }
// }
@Delete(':id')
async delete(@Param('id') id: string): Promise<DeleteResult> {
try {
return await this.trackService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post(':partitionKey/:rowKey/track')
@UseInterceptors(FileInterceptor('file'))
async createTrack(@Param('rowKey') rowKey: string, @UploadedFile() file: Express.Multer.File) {
try {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, rowKey);
return { url }
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get(':partitionKey/:rowKey/track')
async downloadTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<string> {
const containerName = 'tracks';
const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName)
return downloadedFile;
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey/track')
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
try {
const containerName = 'tracks';
return await this.fileService.deleteFile(containerName, rowKey, fileName)
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
}

View File

@@ -0,0 +1,5 @@
export class TrackDto {
logId: string;
order: number;
url?: string;
}

View File

@@ -0,0 +1,18 @@
import { LogEntity } from 'src/log/log.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity({ name: 'tracks' })
export class TrackEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
url: string;
@Column()
order: number;
@ManyToOne(() => LogEntity, (log: LogEntity) => log.tracks)
@JoinColumn({ name: 'logId' })
log: LogEntity;
}

View File

@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from "@nestjs/typeorm";
import { TrackEntity } from "./track.entity";
import { TrackController } from "./track.controller";
import { FileService } from "../file/file.service";
import { TrackService } from './track.service';
import { LogModule } from 'src/log/log.module';
@Module({
imports: [
LogModule,
TypeOrmModule.forFeature([TrackEntity])
],
controllers: [TrackController],
providers: [
FileService,
TrackService
]
})
export class TrackModule {}

View File

@@ -0,0 +1,88 @@
import { DeleteResult, InsertResult, Repository, UpdateResult } from "typeorm";
import { TrackDto } from "./track.dto";
import { TrackEntity } from "./track.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { Injectable } from "@nestjs/common";
import { LogService } from "src/log/log.service";
import { LogEntity } from "src/log/log.entity";
import { CustomError } from "src/error/customError";
import { FileService } from "src/file/file.service";
@Injectable()
export class TrackService {
constructor(
@InjectRepository(TrackEntity) private readonly trackRepository: Repository<TrackEntity>,
private readonly fileService: FileService,
private readonly logService: LogService
) {}
// async find(id: string): Promise<TrackEntity> {
// return await this.trackRepository.findOneBy({ id });
// }
// async findAll(logId: string): Promise<TrackEntity[]> {
// try {
// await this.trackRepository.findOneBy({ })
// } catch (error) {
// }
// }
async create(logId: string, order: number, file: Express.Multer.File): Promise<InsertResult> {
try {
const logEntity: LogEntity = await this.logService.find(logId);
if (logEntity) {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, logId);
const track = this.trackRepository.create({
log: logEntity,
order: order,
url: url
});
return await this.trackRepository.insert(track);
} else {
throw new CustomError('Log not found', 'Not found', 404)
}
} catch (error) {
console.log(error)
throw error;
}
}
// async update(id: string, track: TrackDto): Promise<UpdateResult> {
// return await this.trackRepository.update(id, track);
// }
async delete(id: string): Promise<DeleteResult> {
try {
} catch (error) {
}
return await this.trackRepository.delete({ id });
}
// async createTrack()
// async downloadTrack(): Promise<string> {
// const containerName = 'tracks';
// const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName);
// return downloadedFile;
// }
// async deleteTrack(): Promsie<void> {
// try {
// const containerName = 'tracks';
// return await this.fileService.deleteFile(containerName, rowKey, fileName)
// } catch (error) {
// const customError = error as CustomError;
// throw customError;
// }
// }
}

View File

@@ -12,15 +12,15 @@
}, },
"dependencies": { "dependencies": {
"@azure/msal-browser": "^4.0.1", "@azure/msal-browser": "^4.0.1",
"@azure/msal-react": "^3.0.1", "@azure/msal-react": "3.0.1",
"@noahspan/noahspan-components": "^1.9.1", "@noahspan/noahspan-components": "^1.9.1",
"axios": "^1.7.2", "axios": "^1.7.2",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"react": "19.0.0-rc.1", "react": "^18",
"react-dom": "19.0.0-rc.1", "react-dom": "^18",
"react-hook-form": "^7.51.4", "react-hook-form": "^7.51.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^4",
"react-leaflet-kml": "^2.1.2", "react-leaflet-kml": "^2.1.2",
"react-router-dom": "^6.23.0", "react-router-dom": "^6.23.0",
"swiper": "^11.2.6" "swiper": "^11.2.6"

View File

@@ -1,7 +1,7 @@
import { FormMode } from '../../enums/formMode'; import { FormMode } from '../../enums/formMode';
export interface ILogFormProps { export interface ILogFormProps {
entryId?: string; logId?: string;
isDrawerOpen: boolean; isDrawerOpen: boolean;
mode: FormMode; mode: FormMode;
onOpenClose: (mode: FormMode) => void; onOpenClose: (mode: FormMode) => void;

View File

@@ -5,5 +5,5 @@ export interface ILogFormState {
isDisabled: boolean; isDisabled: boolean;
isLoading: boolean; isLoading: boolean;
pilotOptions: { label: string; value: string }[]; pilotOptions: { label: string; value: string }[];
selectedEntryPilotName: string; selectedPilotName: string;
} }

View File

@@ -28,7 +28,7 @@ import { FormMode } from '../../enums/formMode';
import { usePilots } from '../../hooks/pilots/UsePilots'; import { usePilots } from '../../hooks/pilots/UsePilots';
const LogForm: React.FC<ILogFormProps> = ({ const LogForm: React.FC<ILogFormProps> = ({
entryId, logId,
isDrawerOpen, isDrawerOpen,
mode, mode,
onOpenClose onOpenClose
@@ -39,7 +39,6 @@ const LogForm: React.FC<ILogFormProps> = ({
const isAuthenticated = useIsAuthenticated(); const isAuthenticated = useIsAuthenticated();
const defaultValues = { const defaultValues = {
pilotId: '', pilotId: '',
pilotName: '',
date: null, date: null,
aircraftMakeModel: '', aircraftMakeModel: '',
aircraftIdentity: '', aircraftIdentity: '',
@@ -79,14 +78,14 @@ const LogForm: React.FC<ILogFormProps> = ({
const accessToken: string = await getAccessToken(); const accessToken: string = await getAccessToken();
if (!entryId) { if (!logId) {
await httpClient.post(`api/logs`, data, { await httpClient.post(`api/logs`, data, {
headers: { headers: {
Authorization: accessToken Authorization: accessToken
} }
}); });
} else { } else {
await httpClient.put(`api/logs/log/${entryId}`, data, { await httpClient.put(`api/logs/${logId}`, data, {
headers: { headers: {
Authorization: accessToken Authorization: accessToken
} }
@@ -120,21 +119,13 @@ const LogForm: React.FC<ILogFormProps> = ({
? { headers: { Authorization: await getAccessToken() } } ? { headers: { Authorization: await getAccessToken() } }
: {}; : {};
const response: AxiosResponse = await httpClient.get( const response: AxiosResponse = await httpClient.get(
`api/logs/log/${entryId}`, `api/logs/${logId}`,
config config
); );
const entry = response.data; const log = response.data;
// if (mode !== FormMode.ADD) { log.pilotId = log.pilot.id
// const pilot = pilots?.find((pilot) => pilot.id === entry.pilotId); methods.reset(log);
// console.log(pilot.name)
// dispatch({
// type: 'SET_SELECTED_ENTRY_PILOT_NAME',
// payload: pilot.name
// });
// }
methods.reset(entry);
} catch (error) { } catch (error) {
const axiosError = error as AxiosError; const axiosError = error as AxiosError;
@@ -144,10 +135,10 @@ const LogForm: React.FC<ILogFormProps> = ({
} }
}; };
if (entryId && isDrawerOpen) { if (logId && isDrawerOpen) {
getEntry(); getEntry();
} }
}, [entryId]); }, [logId]);
useEffect(() => { useEffect(() => {
if (pilots && FormMode.ADD) { if (pilots && FormMode.ADD) {
@@ -214,10 +205,6 @@ const LogForm: React.FC<ILogFormProps> = ({
(pilot) => (pilot.id = event.target.value) (pilot) => (pilot.id = event.target.value)
); );
if (pilot) {
methods.setValue('pilotName', pilot.name);
}
methods.setValue('pilotId', event.target.value); methods.setValue('pilotId', event.target.value);
}} }}
options={ options={

View File

@@ -6,14 +6,14 @@ type Action =
| { type: 'SET_IS_DISABLED'; payload: boolean } | { type: 'SET_IS_DISABLED'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean } | { type: 'SET_IS_LOADING'; payload: boolean }
| { type: 'SET_PILOT_OPTIONS'; payload: { label: string; value: string }[] } | { type: 'SET_PILOT_OPTIONS'; payload: { label: string; value: string }[] }
| { type: 'SET_SELECTED_ENTRY_PILOT_NAME'; payload: string }; | { type: 'SET_SELECTED_PILOT_NAME'; payload: string };
export const initialState: ILogFormState = { export const initialState: ILogFormState = {
alert: undefined, alert: undefined,
isDisabled: false, isDisabled: false,
isLoading: true, isLoading: true,
pilotOptions: [], pilotOptions: [],
selectedEntryPilotName: '' selectedPilotName: ''
}; };
export const reducer = ( export const reducer = (
@@ -45,10 +45,10 @@ export const reducer = (
pilotOptions: action.payload pilotOptions: action.payload
}; };
} }
case 'SET_SELECTED_ENTRY_PILOT_NAME': { case 'SET_SELECTED_PILOT_NAME': {
return { return {
...state, ...state,
selectedEntryPilotName: action.payload selectedPilotName: action.payload
}; };
} }
default: { default: {

View File

@@ -29,7 +29,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
const getLog = async (): Promise<ILogbookEntry> => { const getLog = async (): Promise<ILogbookEntry> => {
const logResponse: AxiosResponse = await httpClient.get( const logResponse: AxiosResponse = await httpClient.get(
`api/logs/log/${selectedRowKey}`, `api/logs/${selectedRowKey}`,
await getConfig() await getConfig()
); );
const logData: ILogbookEntry = logResponse.data; const logData: ILogbookEntry = logResponse.data;

View File

@@ -1,8 +1,4 @@
import { ColumnDef } from "@noahspan/noahspan-components";
export interface ILogbookEntry { export interface ILogbookEntry {
partitionKey: string;
rowKey: string;
id: string; id: string;
pilotId: string; pilotId: string;
pilotName: string; pilotName: string;

View File

@@ -13,6 +13,6 @@ export interface ILogbookState {
isFormOpen: boolean; isFormOpen: boolean;
isLoading: boolean; isLoading: boolean;
isTracksOpen: boolean; isTracksOpen: boolean;
selectedEntryId: string | undefined; selectedLogId: string | undefined;
tracksMode: FormMode; tracksMode: FormMode;
} }

View File

@@ -42,9 +42,9 @@ const Logbook: React.FC<unknown> = () => {
}, },
cell: (info: any) => ( cell: (info: any) => (
<ActionMenu <ActionMenu
id={info.row.original.rowKey} id={info.row.original.id}
onDelete={onDeleteEntry} onDelete={onDeleteLog}
onOpenCloseForm={onOpenCloseEntryForm} onOpenCloseForm={onOpenCloseLogForm}
onOpenCloseTracks={onOpenCloseTracks} onOpenCloseTracks={onOpenCloseTracks}
/> />
) )
@@ -94,16 +94,16 @@ const Logbook: React.FC<unknown> = () => {
} }
}; };
const onOpenCloseEntryForm = (mode: FormMode, entryId?: string) => { const onOpenCloseLogForm = (mode: FormMode, logId?: string) => {
switch (mode) { switch (mode) {
case FormMode.ADD: case FormMode.ADD:
case FormMode.EDIT: case FormMode.EDIT:
case FormMode.VIEW: case FormMode.VIEW:
dispatch({ dispatch({
type: 'SET_OPEN_CLOSE_ENTRY_FORM', type: 'SET_OPEN_CLOSE_LOG_FORM',
payload: { payload: {
formMode: mode, formMode: mode,
selectedEntryId: entryId, selectedLogId: logId,
isFormOpen: true isFormOpen: true
} }
}); });
@@ -111,10 +111,10 @@ const Logbook: React.FC<unknown> = () => {
break; break;
case FormMode.CANCEL: case FormMode.CANCEL:
dispatch({ dispatch({
type: 'SET_OPEN_CLOSE_ENTRY_FORM', type: 'SET_OPEN_CLOSE_LOG_FORM',
payload: { payload: {
formMode: mode, formMode: mode,
selectedEntryId: undefined, selectedLogId: undefined,
isFormOpen: false isFormOpen: false
} }
}); });
@@ -151,10 +151,10 @@ const Logbook: React.FC<unknown> = () => {
} }
} }
const onDeleteEntry = (entryId: string) => { const onDeleteLog = (logId: string) => {
dispatch({ dispatch({
type: 'SET_DELETE', type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: true, selectedEntryId: entryId } payload: { isConfirmationDialogOpen: true, selectedLogId: logId }
}); });
}; };
@@ -167,11 +167,11 @@ const Logbook: React.FC<unknown> = () => {
? { headers: { Authorization: `${token}` } } ? { headers: { Authorization: `${token}` } }
: {}; : {};
await httpClient.delete(`api/logs/log/${state.selectedEntryId}`, config); await httpClient.delete(`api/logs/${state.selectedLogId}`, config);
dispatch({ dispatch({
type: 'SET_DELETE', type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: false, selectedEntryId: undefined } payload: { isConfirmationDialogOpen: false, selectedLogId: undefined }
}); });
await getLogbookEntries(); await getLogbookEntries();
} catch (error) { } catch (error) {
@@ -189,7 +189,7 @@ const Logbook: React.FC<unknown> = () => {
const onConfirmationDialogCancel = () => { const onConfirmationDialogCancel = () => {
dispatch({ dispatch({
type: 'SET_DELETE', type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: false, selectedEntryId: undefined } payload: { isConfirmationDialogOpen: false, selectedLogId: undefined }
}); });
}; };
@@ -233,7 +233,7 @@ const Logbook: React.FC<unknown> = () => {
<Grid display="flex" justifyContent="right" size={isMedium ? 1 : 6}> <Grid display="flex" justifyContent="right" size={isMedium ? 1 : 6}>
{isAuthenticated && {isAuthenticated &&
<Button <Button
onClick={() => onOpenCloseEntryForm(FormMode.ADD)} onClick={() => onOpenCloseLogForm(FormMode.ADD)}
startIcon={<Icon iconName={IconName.PLUS} />} startIcon={<Icon iconName={IconName.PLUS} />}
variant="contained" variant="contained"
data-testid="pilot-add-button" data-testid="pilot-add-button"
@@ -261,7 +261,7 @@ const Logbook: React.FC<unknown> = () => {
<Table columns={state.columns} data={state.entries} /> <Table columns={state.columns} data={state.entries} />
)} )}
{!isMedium && state.entries.length > 0 && {!isMedium && state.entries.length > 0 &&
<LogbookCard logs={state.entries} onDelete={onDeleteEntry} mode='logbook' onOpenCloseForm={onOpenCloseEntryForm} /> <LogbookCard logs={state.entries} onDelete={onDeleteLog} mode='logbook' onOpenCloseForm={onOpenCloseLogForm} />
} }
</Grid> </Grid>
)} )}
@@ -278,10 +278,10 @@ const Logbook: React.FC<unknown> = () => {
</Grid> </Grid>
{state.isFormOpen && ( {state.isFormOpen && (
<LogForm <LogForm
entryId={state.selectedEntryId} logId={state.selectedLogId}
isDrawerOpen={state.isFormOpen} isDrawerOpen={state.isFormOpen}
mode={state.formMode} mode={state.formMode}
onOpenClose={(mode) => onOpenCloseEntryForm(mode)} onOpenClose={(mode) => onOpenCloseLogForm(mode)}
/> />
)} )}
{state.isConfirmDialogOpen && ( {state.isConfirmDialogOpen && (
@@ -299,7 +299,7 @@ const Logbook: React.FC<unknown> = () => {
isDrawerOpen={state.isTracksOpen} isDrawerOpen={state.isTracksOpen}
mode={state.tracksMode} mode={state.tracksMode}
onOpenClose={(mode) => onOpenCloseTracks(mode)} onOpenClose={(mode) => onOpenCloseTracks(mode)}
selectedRowKey={state.selectedEntryId} selectedRowKey={state.selectedLogId}
/> />
} }
</Box> </Box>

View File

@@ -10,7 +10,7 @@ type Action =
type: 'SET_DELETE'; type: 'SET_DELETE';
payload: { payload: {
isConfirmationDialogOpen: boolean; isConfirmationDialogOpen: boolean;
selectedEntryId: string | undefined; selectedLogId: string | undefined;
}; };
} }
| { type: 'SET_ENTRIES'; payload: ILogbookEntry[] } | { type: 'SET_ENTRIES'; payload: ILogbookEntry[] }
@@ -19,10 +19,10 @@ type Action =
| { type: 'SET_IS_CONFIRMATION_DIALOG_LOADING'; payload: boolean } | { type: 'SET_IS_CONFIRMATION_DIALOG_LOADING'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean } | { type: 'SET_IS_LOADING'; payload: boolean }
| { | {
type: 'SET_OPEN_CLOSE_ENTRY_FORM'; type: 'SET_OPEN_CLOSE_LOG_FORM';
payload: { payload: {
formMode: FormMode; formMode: FormMode;
selectedEntryId: string | undefined; selectedLogId: string | undefined;
isFormOpen: boolean; isFormOpen: boolean;
}; };
} }
@@ -38,7 +38,7 @@ export const initialState: ILogbookState = {
isFormOpen: false, isFormOpen: false,
isLoading: false, isLoading: false,
isTracksOpen: false, isTracksOpen: false,
selectedEntryId: undefined, selectedLogId: undefined,
tracksMode: FormMode.CANCEL tracksMode: FormMode.CANCEL
}; };
@@ -57,7 +57,7 @@ export const reducer = (
return { return {
...state, ...state,
isConfirmDialogOpen: action.payload.isConfirmationDialogOpen, isConfirmDialogOpen: action.payload.isConfirmationDialogOpen,
selectedEntryId: action.payload.selectedEntryId selectedLogId: action.payload.selectedLogId
}; };
} }
case 'SET_ENTRIES': { case 'SET_ENTRIES': {
@@ -90,12 +90,12 @@ export const reducer = (
isLoading: action.payload isLoading: action.payload
}; };
} }
case 'SET_OPEN_CLOSE_ENTRY_FORM': { case 'SET_OPEN_CLOSE_LOG_FORM': {
return { return {
...state, ...state,
formMode: action.payload.formMode, formMode: action.payload.formMode,
isFormOpen: action.payload.isFormOpen, isFormOpen: action.payload.isFormOpen,
selectedEntryId: action.payload.selectedEntryId selectedLogId: action.payload.selectedLogId
}; };
} }
case 'SET_OPEN_CLOSE_TRACKS': { case 'SET_OPEN_CLOSE_TRACKS': {
@@ -103,7 +103,7 @@ export const reducer = (
...state, ...state,
tracksMode: action.payload.tracksMode, tracksMode: action.payload.tracksMode,
isTracksOpen: action.payload.isTracksOpen, isTracksOpen: action.payload.isTracksOpen,
selectedEntryId: action.payload.selectedRowKey selectedLogId: action.payload.selectedRowKey
} }
} }
default: { default: {

View File

@@ -36,16 +36,12 @@ const PilotForm: React.FC<IPilotFormProps> = ({
const [isPeoplePickerLoading, setIsPeoplePickerLoading] = const [isPeoplePickerLoading, setIsPeoplePickerLoading] =
useState<boolean>(false); useState<boolean>(false);
const [selectedPerson, setSelectedPerson] = useState<Person>({ const [selectedPerson, setSelectedPerson] = useState<Person>({
userPrincipalName: '',
displayName: '' displayName: ''
}); });
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const { getAccessToken } = useAccessToken(); const { getAccessToken } = useAccessToken();
const isAuthenticated = useIsAuthenticated(); const isAuthenticated = useIsAuthenticated();
const defaultValues = { const defaultValues = {
partitionKey: 'pilot',
rowKey: 'noah@noahspannbauer.com',
id: '',
name: '', name: '',
address: '', address: '',
city: '', city: '',
@@ -53,10 +49,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
postalCode: '', postalCode: '',
email: '', email: '',
phone: '', phone: '',
medicalClass: '',
medicalExpiration: '',
certificates: [],
endorsements: []
}; };
const methods = useForm({ const methods = useForm({
defaultValues: defaultValues defaultValues: defaultValues
@@ -100,7 +92,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
value: Person, value: Person,
_reason: string _reason: string
) => { ) => {
methods.setValue('id', value.userPrincipalName!.toString());
methods.setValue('name', value.displayName!.toString()); methods.setValue('name', value.displayName!.toString());
setSelectedPerson(value); setSelectedPerson(value);
}; };
@@ -159,16 +150,12 @@ const PilotForm: React.FC<IPilotFormProps> = ({
? { headers: { Authorization: await getAccessToken() } } ? { headers: { Authorization: await getAccessToken() } }
: {}; : {};
const response: AxiosResponse = await httpClient.get( const response: AxiosResponse = await httpClient.get(
`api/pilots/pilot/${pilotId}`, `api/pilots/${pilotId}`,
config config
); );
const pilot = response.data; const pilot = response.data;
pilot.certificates = JSON.parse(pilot.certificates);
pilot.endorsements = JSON.parse(pilot.endorsements)
setSelectedPerson({ setSelectedPerson({
userPrincipalName: pilot.id,
displayName: pilot.name displayName: pilot.name
}); });
methods.reset(pilot); methods.reset(pilot);
@@ -380,7 +367,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
</Grid> </Grid>
</> </>
} }
{isAuthenticated && {/* {isAuthenticated &&
<Grid size={12}> <Grid size={12}>
<PilotFormMedical <PilotFormMedical
isDisabled={isDisabled} isDisabled={isDisabled}
@@ -392,7 +379,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
</Grid> </Grid>
<Grid size={12}> <Grid size={12}>
<PilotFormEndorsements isDisabled={isDisabled} mode={mode} /> <PilotFormEndorsements isDisabled={isDisabled} mode={mode} />
</Grid> </Grid> */}
<Grid display="flex" gap={2} justifyContent="right" size={12}> <Grid display="flex" gap={2} justifyContent="right" size={12}>
<Button <Button
disabled={ disabled={

View File

@@ -1,6 +1,4 @@
export interface Pilot { export interface Pilot {
partitionKey: string;
rowKey: string;
id: string; id: string;
name: string; name: string;
}; };

View File

@@ -36,7 +36,7 @@ const Pilots: React.FC<unknown> = () => {
const response: AxiosResponse = await httpClient.get( const response: AxiosResponse = await httpClient.get(
`api/pilots` `api/pilots`
); );
console.log(response);
if (response.data.length > 0) { if (response.data.length > 0) {
dispatch({ type: 'SET_PILOTS', payload: response.data }); dispatch({ type: 'SET_PILOTS', payload: response.data });
@@ -136,13 +136,14 @@ const Pilots: React.FC<unknown> = () => {
}, },
{ {
header: 'Actions', header: 'Actions',
cell: (info: any) => ( cell: (info: any) => {
<ActionMenu console.log(info)
id={info.row.original.rowKey} return <ActionMenu
id={info.row.original.id}
onDelete={onDeleteEntry} onDelete={onDeleteEntry}
onOpenCloseForm={onOpenClosePilotForm} onOpenCloseForm={onOpenClosePilotForm}
/> />
) }
} }
]; ];

View File

@@ -1,16 +1,16 @@
version: '3.8' version: '3.8'
services: services:
# azurite: azurite:
# container_name: azurite-flying container_name: azurite-flying
# image: mcr.microsoft.com/azure-storage/azurite image: mcr.microsoft.com/azure-storage/azurite
# ports: ports:
# - '10000:10000' - '10000:10000'
# - '10001:10001' - '10001:10001'
# - '10002:10002' - '10002:10002'
# command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck' command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
# volumes: volumes:
# - ./azurite-flying:/data - ./azurite-flying:/data
restore: restore:
container_name: restore container_name: restore

21759
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -25,5 +25,10 @@
}, },
"lint-staged": { "lint-staged": {
"**/*": "prettier --write \"**/src/**/*.ts\" \"**/test/**/*.ts\" --ignore-unknown" "**/*": "prettier --write \"**/src/**/*.ts\" \"**/test/**/*.ts\" --ignore-unknown"
} },
"workspaces": [
"api",
"app",
"tests"
]
} }

13428
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
packages:
- 'api'
- 'app'