fixing logbook page responsiveness (#116)
* fixing logbook page responsiveness * fixing logbook page responsiveness
This commit was merged in pull request #116.
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
import { JwtPayload } from "jwt-decode";
|
|
||||||
|
|
||||||
export interface CustomJwtPayload extends JwtPayload {
|
|
||||||
permissions: string[];
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ import { LogInterceptor } from './log.interceptor';
|
|||||||
import { FileService } from '../file/file.service';
|
import { FileService } from '../file/file.service';
|
||||||
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
|
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { Logs } from './logs.interface';
|
||||||
|
|
||||||
const reflector = new Reflector();
|
const reflector = new Reflector();
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ export class LogController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Public()
|
@Public()
|
||||||
async findLogsWithCount(@Query('skip') skip?, @Query('take') take?: number,): Promise<{ entities: LogEntity[], total: number, hasNextPage: boolean }> {
|
async findLogsWithCount(@Query('skip') skip?, @Query('take') take?: number,): Promise<Logs> {
|
||||||
try {
|
try {
|
||||||
return await this.logService.findLogsWithCount(skip, take)
|
return await this.logService.findLogsWithCount(skip, take)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -43,13 +44,11 @@ export class LogController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('tracks')
|
@Get('flights')
|
||||||
@Public()
|
@Public()
|
||||||
async findLogsWithTracks(@Query('skip') skip?, @Query('take') take?: number,): Promise<{ entities: LogEntity[], total: number, hasNextPage: boolean }> {
|
async findLogsWithTracks(@Query('skip') skip?, @Query('take') take?: number,): Promise<Logs> {
|
||||||
try {
|
try {
|
||||||
const result = await this.logService.findLogsWithTracks(skip, take)
|
return await this.logService.findLogsWithTracks(skip, take)
|
||||||
console.log(result);
|
|
||||||
return result
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const customError = error as CustomError;
|
const customError = error as CustomError;
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ export class LogInterceptor implements NestInterceptor {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return handler.handle().pipe(
|
return handler.handle().pipe(
|
||||||
map((data: { entities: LogEntity[], total: number, hasNextPage: boolean }) => {
|
map((data: any) => {
|
||||||
const req = context.switchToHttp().getRequest();
|
const req = context.switchToHttp().getRequest();
|
||||||
const limitData = (entities: LogEntity[]) => {
|
const limitData = (log: LogEntity) => {
|
||||||
return entities.map((log: LogEntity) => {
|
|
||||||
return {
|
return {
|
||||||
id: log.id,
|
id: log.id,
|
||||||
pilot: {
|
pilot: {
|
||||||
@@ -31,7 +30,6 @@ export class LogInterceptor implements NestInterceptor {
|
|||||||
durationOfFlight: log.durationOfFlight,
|
durationOfFlight: log.durationOfFlight,
|
||||||
tracks: log.tracks,
|
tracks: log.tracks,
|
||||||
};
|
};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.headers.authorization) {
|
if (req.headers.authorization) {
|
||||||
@@ -41,25 +39,39 @@ export class LogInterceptor implements NestInterceptor {
|
|||||||
const rolesKeyName = Object.keys(jwtPayload).find((key) => key.includes('roles'));
|
const rolesKeyName = Object.keys(jwtPayload).find((key) => key.includes('roles'));
|
||||||
|
|
||||||
if (jwtPayload[rolesKeyName].includes('Flying.Read')) {
|
if (jwtPayload[rolesKeyName].includes('Flying.Read')) {
|
||||||
const logs = limitData(data.entities);
|
if (data.entities) {
|
||||||
|
const logs = data.entities.map((entity) => limitData(data.entities));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities: logs,
|
entities: logs,
|
||||||
total: data.total,
|
total: data.total,
|
||||||
hasNextPage: data.hasNextPage
|
hasNextPage: data.hasNextPage
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
const log = limitData(data);
|
||||||
|
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
} else if (!req.headers.authorization && isPublic) {
|
} else if (!req.headers.authorization && isPublic) {
|
||||||
const publicData = limitData(data.entities)
|
if (data.entities) {
|
||||||
|
const publicData = data.entities.map((entity) => limitData(entity))
|
||||||
const logs = publicData.slice(0, 5)
|
const logs = publicData.slice(0, 5)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities: logs,
|
entities: publicData,
|
||||||
total: data.total,
|
total: data.total,
|
||||||
hasNextPage: data.hasNextPage
|
hasNextPage: false
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
const publicData = limitData(data);
|
||||||
|
|
||||||
|
return publicData
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { PilotService } from '../pilot/pilot.service';
|
|||||||
import { PilotEntity } from '../pilot/pilot.entity';
|
import { PilotEntity } from '../pilot/pilot.entity';
|
||||||
import { CustomError } from '../error/customError';
|
import { CustomError } from '../error/customError';
|
||||||
import { FileService } from '../file/file.service';
|
import { FileService } from '../file/file.service';
|
||||||
|
import { Logs } from './logs.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LogService {
|
export class LogService {
|
||||||
@@ -17,15 +18,13 @@ export class LogService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async find(id: string): Promise<LogEntity> {
|
async find(id: string): Promise<LogEntity> {
|
||||||
const logEntity: LogEntity = await this.logRepository.findOne({
|
return await this.logRepository.findOne({
|
||||||
where: { id: id },
|
where: { id: id },
|
||||||
relations: ['pilot', 'tracks']
|
relations: ['pilot', 'tracks']
|
||||||
});
|
});
|
||||||
|
|
||||||
return logEntity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findLogsWithCount(skip?: number, take?: number): Promise<{entities: LogEntity[], total: number, hasNextPage: boolean}> {
|
async findLogsWithCount(skip?: number, take?: number): Promise<Logs> {
|
||||||
const [entities, total] = await this.logRepository.findAndCount({
|
const [entities, total] = await this.logRepository.findAndCount({
|
||||||
take,
|
take,
|
||||||
skip,
|
skip,
|
||||||
@@ -39,7 +38,7 @@ export class LogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findLogsWithTracks(skip?: number, take?: number): Promise<{entities: LogEntity[], total: number, hasNextPage: boolean}> {
|
async findLogsWithTracks(skip?: number, take?: number): Promise<Logs> {
|
||||||
const [entities, total] = await this.logRepository
|
const [entities, total] = await this.logRepository
|
||||||
.createQueryBuilder('logs')
|
.createQueryBuilder('logs')
|
||||||
.innerJoinAndSelect('logs.tracks', 'track')
|
.innerJoinAndSelect('logs.tracks', 'track')
|
||||||
|
|||||||
7
api/src/log/logs.interface.ts
Normal file
7
api/src/log/logs.interface.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { LogEntity } from "src/log/log.entity";
|
||||||
|
|
||||||
|
export interface Logs {
|
||||||
|
entities: LogEntity[],
|
||||||
|
total: number,
|
||||||
|
hasNextPage: boolean
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { LogService } from "../log/log.service";
|
|||||||
import { LogEntity } from "../log/log.entity";
|
import { LogEntity } from "../log/log.entity";
|
||||||
import { CustomError } from "../error/customError";
|
import { CustomError } from "../error/customError";
|
||||||
import { FileService } from "../file/file.service";
|
import { FileService } from "../file/file.service";
|
||||||
|
import { Logs } from "src/log/logs.interface";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TrackService {
|
export class TrackService {
|
||||||
|
|||||||
@@ -61,10 +61,6 @@ const Flights = () => {
|
|||||||
getFlights(state.pageIndex, state.pageSize);
|
getFlights(state.pageIndex, state.pageSize);
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(screenSize)
|
|
||||||
}, [screenSize])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`max-w-screen-lg mx-auto ${screenSize === ScreenSize.SM || screenSize === ScreenSize.MD? 'mr-4 ml-4' : ''}`}>
|
<div className={`max-w-screen-lg mx-auto ${screenSize === ScreenSize.SM || screenSize === ScreenSize.MD? 'mr-4 ml-4' : ''}`}>
|
||||||
<div className='prose mt-5 mb-5'>
|
<div className='prose mt-5 mb-5'>
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ import { CellContext, ColumnDef, flexRender, getCoreRowModel, HeaderContext, Pag
|
|||||||
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
import { useLogbookContext } from '../../hooks/logbookContext/UseLogbookContext';
|
||||||
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
|
import LogbookDrawer from '../logbookDrawer/LogbookDrawer';
|
||||||
import Alert from '../alert/Alert';
|
import Alert from '../alert/Alert';
|
||||||
|
import TrackMap from '../trackMap/TrackMap';
|
||||||
|
|
||||||
|
interface ActionsProps {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Logbook: React.FC<unknown> = () => {
|
const Logbook: React.FC<unknown> = () => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
@@ -25,6 +30,22 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
const { isUserLoggedIn } = useOidc();
|
const { isUserLoggedIn } = useOidc();
|
||||||
const { userRole } = useUserRole();
|
const { userRole } = useUserRole();
|
||||||
const { screenSize } = useBreakpoints();
|
const { screenSize } = useBreakpoints();
|
||||||
|
const Actions = ({ id }: ActionsProps) => {
|
||||||
|
return (
|
||||||
|
<div className='dropdown dropdown-end'>
|
||||||
|
<div tabIndex={0} role='button' className='btn btn-ghost p-0'><FontAwesomeIcon icon={faEllipsisVertical} /></div>
|
||||||
|
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box w-52 p-2 shadow-sm border border-base-300 !z-[100]">
|
||||||
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
|
<li><a onClick={() => onOpenCloseDrawer(FormMode.EDIT, id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
|
||||||
|
}
|
||||||
|
<li><a onClick={() => onOpenCloseDrawer(FormMode.VIEW, id)}><FontAwesomeIcon icon={faEye} />View</a></li>
|
||||||
|
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
||||||
|
<li><a onClick={() => onDeleteLog(id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
const columnTotal = (info: HeaderContext<LogbookEntry, unknown>): number => {
|
const columnTotal = (info: HeaderContext<LogbookEntry, unknown>): number => {
|
||||||
const values: number[] = info.table.getPaginationRowModel().rows.map((row: any) => Number(row.getValue(info.column.id))).filter((value: any) => !Number.isNaN(value));
|
const values: number[] = info.table.getPaginationRowModel().rows.map((row: any) => Number(row.getValue(info.column.id))).filter((value: any) => !Number.isNaN(value));
|
||||||
let total: number = 0;
|
let total: number = 0;
|
||||||
@@ -116,18 +137,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
},
|
},
|
||||||
cell: (info: CellContext<LogbookEntry, unknown>) => {
|
cell: (info: CellContext<LogbookEntry, unknown>) => {
|
||||||
return (
|
return (
|
||||||
<div className='dropdown dropdown-end'>
|
<Actions id={info.row.original.id} />
|
||||||
<div tabIndex={0} role='button' className='btn btn-ghost'><FontAwesomeIcon icon={faEllipsisVertical} /></div>
|
|
||||||
<ul tabIndex={-1} className="dropdown-content menu bg-base-100 rounded-box w-52 p-2 shadow-sm border border-base-300 !z-[100]">
|
|
||||||
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
|
||||||
<li><a onClick={() => onOpenCloseDrawer(FormMode.EDIT, info.row.original.id)}><FontAwesomeIcon icon={faPen} />Edit</a></li>
|
|
||||||
}
|
|
||||||
<li><a onClick={() => onOpenCloseDrawer(FormMode.VIEW, info.row.original.id)}><FontAwesomeIcon icon={faEye} />View</a></li>
|
|
||||||
{isUserLoggedIn && userRole === UserRole.WRITE &&
|
|
||||||
<li><a onClick={() => onDeleteLog(info.row.original.id)}><FontAwesomeIcon icon={faTrash} />Delete</a></li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,12 +499,11 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onRowsPerPageChanged = (event: any) => {
|
const onRowsPerPageChanged = (event: any) => {
|
||||||
console.log(event.target.value)
|
|
||||||
const newPaginationState: PaginationState = {
|
const newPaginationState: PaginationState = {
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: event.target.value !== 'All' ? Number(event.target.value) : state.totalEntries
|
pageSize: event.target.value !== 'All' ? Number(event.target.value) : state.totalEntries
|
||||||
};
|
};
|
||||||
console.log(newPaginationState)
|
|
||||||
dispatch({ type: 'SET_PAGINATION', payload: newPaginationState })
|
dispatch({ type: 'SET_PAGINATION', payload: newPaginationState })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +529,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='mr-10 ml-10 grid grid-cols-12'>
|
<div className={`${screenSize === ScreenSize.SM ? 'mr-4 ml-4' : 'mr-10 ml-10'} grid grid-cols-12`}>
|
||||||
<div className='prose max-w-none col-span-10 mt-5 mb-5'>
|
<div className='prose max-w-none col-span-10 mt-5 mb-5'>
|
||||||
<h1>Logbook</h1>
|
<h1>Logbook</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -547,7 +556,7 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!state.isLoading && state.entries.length > 0 && screenSize !== ScreenSize.SM && (
|
{!state.isLoading && state.entries.length > 0 && screenSize !== ScreenSize.SM && screenSize !== ScreenSize.MD && (
|
||||||
<div className='col-span-12 pr-5 pb-5 pl-5 bg-base-100 border border-base-100 rounded-lg '>
|
<div className='col-span-12 pr-5 pb-5 pl-5 bg-base-100 border border-base-100 rounded-lg '>
|
||||||
<div className='overflow-x-auto mb-5'>
|
<div className='overflow-x-auto mb-5'>
|
||||||
<div className='col-span-12 justify-self-end self-center mt-1 mr-1 mb-2'>
|
<div className='col-span-12 justify-self-end self-center mt-1 mr-1 mb-2'>
|
||||||
@@ -696,7 +705,47 @@ const Logbook: React.FC<unknown> = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{state.entries.length > 0 && screenSize === ScreenSize.SM &&
|
{state.entries.length > 0 && screenSize === ScreenSize.SM &&
|
||||||
<LogbookCard logs={state.entries} onDelete={onDeleteLog} mode='logbook' onOpenCloseForm={onOpenCloseDrawer} />
|
<div className='col-span-12'>
|
||||||
|
<>
|
||||||
|
{table.getRowModel().rows.map((row) => {
|
||||||
|
const date = new Date(row.original.date.replace('Z', ''));
|
||||||
|
const formattedDate: string = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='card bg-base-100 border border-base-300 mb-5'>
|
||||||
|
<div className={`card-body ${screenSize === ScreenSize.SM || screenSize === ScreenSize.MD ? 'p-4' : ''}`} key={row.id}>
|
||||||
|
<div className={`grid grid-cols-12 gap-3`}>
|
||||||
|
<>
|
||||||
|
<div className='col-span-8'>
|
||||||
|
<h2 className='card-title font-bold text-2xl'>{formattedDate}</h2>
|
||||||
|
</div>
|
||||||
|
<div className='col-span-4 justify-self-end self-center'>
|
||||||
|
<Actions id={row.original.id} />
|
||||||
|
</div>
|
||||||
|
{row.getVisibleCells().map((cell) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{cell.column.columnDef.header !== 'Actions' && cell.column.columnDef.header !== 'Date' && cell.column.columnDef.header !== 'Pilot' &&
|
||||||
|
<>
|
||||||
|
<div className='col-span-8 font-bold'>
|
||||||
|
<span>{cell.getContext().column.columnDef.header?.toString()}</span>
|
||||||
|
</div>
|
||||||
|
<div className='col-span-4'>
|
||||||
|
<span>{flexRender(cell.column.columnDef.cell, cell.getContext())}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
{state.isLoading && !state.alert && (
|
{state.isLoading && !state.alert && (
|
||||||
<div className='col-span-12 p-5 bg-base-100 border border-base-100 rounded-lg'>
|
<div className='col-span-12 p-5 bg-base-100 border border-base-100 rounded-lg'>
|
||||||
|
|||||||
Reference in New Issue
Block a user