From 35193b2de8dd42b3f3b80d6de1b215bbebfcb34b Mon Sep 17 00:00:00 2001 From: MaxHopaytsa <119316701+MaxHopaytsa@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:18:24 +0300 Subject: [PATCH] th-168: Add trucks table (#232) * th-168: * business controller * th-168: + get methods to backend for find all trucks by businessId * th-168: + truck slice * th-168: * migrate generate * th-168: + styles for trucks table * th-168: + add trucks form * th-168: + notifications and sorted table * th-168: * linting * th-168: * linting * th-168: * linting * th-168: * comments * th-168: * endpoint * th-168: * swager * th-168: * resolve comments * th-168: * linting * th-168: * linting * th-168: * comments * th-168: * actions * th-168: * columns * th-168: * error handle in action and slice * th-168: * comments * th-168: * actions and other comennts * th-168: * linting * th-168: + sort by year * th-168: * style * th-168: * comments * th-168: * another comments * th-168: * comments * th-168: * comments --- backend/src/libs/enums/enums.ts | 1 + .../helpers/count-offset-by-query.helper.ts | 7 ++ .../src/libs/helpers/get-sorted-by.helper.ts | 32 +++++ backend/src/libs/helpers/helpers.ts | 2 + .../0012_add_businessId_to_trucks.sql | 9 ++ .../packages/database/schema/tables-schema.ts | 6 +- backend/src/libs/types/types.ts | 3 + .../packages/business/business.controller.ts | 68 +++++++++- .../src/packages/business/business.service.ts | 57 ++++++--- .../src/packages/trucks/libs/types/types.ts | 6 +- .../validation-schemas.ts | 0 .../src/packages/trucks/truck.controller.ts | 60 +-------- backend/src/packages/trucks/truck.entity.ts | 10 ++ .../src/packages/trucks/truck.repository.ts | 67 +++++----- backend/src/packages/trucks/truck.service.ts | 34 ++--- backend/src/packages/trucks/trucks.ts | 9 ++ .../components/page-layout/styles.module.scss | 1 + frontend/src/libs/components/table/table.tsx | 29 ++++- frontend/src/libs/enums/enums.ts | 1 + .../capitalize-first-letter.helper.ts | 10 ++ .../get-sorting-method-value.helper.ts | 9 ++ frontend/src/libs/helpers/helpers.ts | 2 + frontend/src/libs/hooks/hooks.ts | 1 + .../libs/hooks/use-app-table/use-app-table.ts | 33 +++-- .../use-query-parameters.hook.ts | 8 +- .../libs/hooks/use-toggle/use-toggle.hook.ts | 15 +++ .../src/libs/packages/store/store.package.ts | 2 +- frontend/src/libs/types/types.ts | 2 + .../src/packages/business/business-api.ts | 28 ++++- frontend/src/packages/drivers/drivers-api.ts | 10 +- .../src/packages/trucks/libs/enums/enums.ts | 1 + .../enums/truck-successful-message.enum.ts | 5 + .../src/packages/trucks/libs/types/types.ts | 2 + frontend/src/packages/trucks/trucks-api.ts | 21 +--- .../form}/add-driver-form/add-driver-form.tsx | 2 +- .../form}/add-driver-form/libs/constants.ts | 0 .../form}/add-driver-form/libs/fields.ts | 0 .../libs/helpers/get-initial-fields.helper.ts | 0 .../add-driver-form/libs/helpers/helpers.ts | 0 .../form}/add-driver-form/styles.module.scss | 0 .../form/add-truck-form/add-truck-form.tsx | 41 ++++++ .../add-truck-form}/constants/constants.ts | 2 +- .../fields/add-truck.fields.ts | 4 +- .../form}/add-truck-form/styles.module.scss | 10 ++ .../pages/dashboard/components/form/form.ts | 2 + .../libs/helpers/check-active-tab.helper.ts | 7 ++ .../sidebar/libs/helpers/helpers.ts | 1 + .../dashboard/components/sidebar/sidebar.tsx | 14 +-- .../drivers-table/columns/columns.ts | 0 .../drivers-table/driver-table.tsx | 46 +++---- .../drivers-table/styles.module.scss | 0 .../dashboard/components/tables/tables.ts | 2 + .../tables/trucks-table/columns/columns.ts | 55 ++++++++ .../tables/trucks-table/styles.module.scss | 28 +++++ .../tables/trucks-table/trucks-table.tsx | 117 ++++++++++++++++++ frontend/src/pages/dashboard/dashboard.tsx | 10 +- .../add-truck-form/add-truck-form.tsx | 38 ------ frontend/src/slices/drivers/actions.ts | 18 +-- frontend/src/slices/trucks/actions.ts | 70 +++++++---- frontend/src/slices/trucks/trucks.slice.ts | 22 ++-- frontend/src/slices/trucks/trucks.ts | 4 +- readme.md | 2 + shared/src/index.ts | 5 + shared/src/libs/enums/enums.ts | 1 + shared/src/libs/enums/sort-method.enum.ts | 6 + ...agination.ts => entity-pagination.type.ts} | 0 shared/src/libs/types/pagination-payload.ts | 6 - ...pagination-with-sorting-parameters.type.ts | 8 ++ .../src/libs/types/sort-method-value.type.ts | 6 + shared/src/libs/types/types.ts | 4 +- .../libs/enums/business-api-path.enum.ts | 2 +- .../driver-get-all-response-dto.type.ts | 2 +- .../trucks/libs/enums/truck-capacity.enum.ts | 4 +- .../libs/types/truck-add-request-dto.type.ts | 5 + .../trucks/libs/types/truck-entity.type.ts | 1 + .../types/truck-get-all-response-dto.type.ts | 5 + .../src/packages/trucks/libs/types/types.ts | 2 + ...k-create-request-body.validation-schema.ts | 7 +- ...k-update-request-body.validation-schema.ts | 2 +- shared/src/packages/trucks/trucks.ts | 2 + 80 files changed, 802 insertions(+), 312 deletions(-) create mode 100644 backend/src/libs/helpers/count-offset-by-query.helper.ts create mode 100644 backend/src/libs/helpers/get-sorted-by.helper.ts create mode 100644 backend/src/libs/helpers/helpers.ts create mode 100644 backend/src/libs/packages/database/generated-schema/0012_add_businessId_to_trucks.sql rename backend/src/packages/trucks/libs/{validation-schema => validation-schemas}/validation-schemas.ts (100%) create mode 100644 frontend/src/libs/helpers/capitalize-first-letter/capitalize-first-letter.helper.ts create mode 100644 frontend/src/libs/helpers/get-sorting-method-value/get-sorting-method-value.helper.ts create mode 100644 frontend/src/libs/hooks/use-toggle/use-toggle.hook.ts create mode 100644 frontend/src/packages/trucks/libs/enums/truck-successful-message.enum.ts rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/add-driver-form.tsx (95%) rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/libs/constants.ts (100%) rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/libs/fields.ts (100%) rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/libs/helpers/get-initial-fields.helper.ts (100%) rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/libs/helpers/helpers.ts (100%) rename frontend/src/pages/{business/components => dashboard/components/form}/add-driver-form/styles.module.scss (100%) create mode 100644 frontend/src/pages/dashboard/components/form/add-truck-form/add-truck-form.tsx rename frontend/src/pages/{trucks/components/add-truck-form/libs => dashboard/components/form/add-truck-form}/constants/constants.ts (78%) rename frontend/src/pages/{trucks/components/add-truck-form/libs => dashboard/components/form/add-truck-form}/fields/add-truck.fields.ts (94%) rename frontend/src/pages/{trucks/components => dashboard/components/form}/add-truck-form/styles.module.scss (74%) create mode 100644 frontend/src/pages/dashboard/components/form/form.ts create mode 100644 frontend/src/pages/dashboard/components/sidebar/libs/helpers/check-active-tab.helper.ts create mode 100644 frontend/src/pages/dashboard/components/sidebar/libs/helpers/helpers.ts rename frontend/src/pages/dashboard/components/{table => tables}/drivers-table/columns/columns.ts (100%) rename frontend/src/pages/dashboard/components/{table => tables}/drivers-table/driver-table.tsx (71%) rename frontend/src/pages/dashboard/components/{table => tables}/drivers-table/styles.module.scss (100%) create mode 100644 frontend/src/pages/dashboard/components/tables/tables.ts create mode 100644 frontend/src/pages/dashboard/components/tables/trucks-table/columns/columns.ts create mode 100644 frontend/src/pages/dashboard/components/tables/trucks-table/styles.module.scss create mode 100644 frontend/src/pages/dashboard/components/tables/trucks-table/trucks-table.tsx delete mode 100644 frontend/src/pages/trucks/components/add-truck-form/add-truck-form.tsx create mode 100644 shared/src/libs/enums/sort-method.enum.ts rename shared/src/libs/types/{entity-pagination.ts => entity-pagination.type.ts} (100%) delete mode 100644 shared/src/libs/types/pagination-payload.ts create mode 100644 shared/src/libs/types/pagination-with-sorting-parameters.type.ts create mode 100644 shared/src/libs/types/sort-method-value.type.ts create mode 100644 shared/src/packages/trucks/libs/types/truck-add-request-dto.type.ts create mode 100644 shared/src/packages/trucks/libs/types/truck-get-all-response-dto.type.ts diff --git a/backend/src/libs/enums/enums.ts b/backend/src/libs/enums/enums.ts index 73dd521a0..240691680 100644 --- a/backend/src/libs/enums/enums.ts +++ b/backend/src/libs/enums/enums.ts @@ -5,4 +5,5 @@ export { HttpCode, HttpMessage, ServerErrorType, + SortMethod, } from 'shared/build/index.js'; diff --git a/backend/src/libs/helpers/count-offset-by-query.helper.ts b/backend/src/libs/helpers/count-offset-by-query.helper.ts new file mode 100644 index 000000000..b80563dd7 --- /dev/null +++ b/backend/src/libs/helpers/count-offset-by-query.helper.ts @@ -0,0 +1,7 @@ +import { type PaginationParameters } from '../types/types.js'; + +const countOffsetByQuery = ({ page, size }: PaginationParameters): number => { + return page * size; +}; + +export { countOffsetByQuery }; diff --git a/backend/src/libs/helpers/get-sorted-by.helper.ts b/backend/src/libs/helpers/get-sorted-by.helper.ts new file mode 100644 index 000000000..b641a2e5f --- /dev/null +++ b/backend/src/libs/helpers/get-sorted-by.helper.ts @@ -0,0 +1,32 @@ +import { + type AnyColumn, + type SQL, + type SQLWrapper, + asc, + desc, +} from 'drizzle-orm'; + +import { SortMethod } from '../enums/enums.js'; +import { type SortMethodValue } from '../types/types.js'; + +type ColumnToSorted = SQLWrapper | AnyColumn; + +const getSortedBy = ( + sortedMethod: SortMethodValue | null, + requiredColumnToSorted: ColumnToSorted, + optionalColumnToSorted: ColumnToSorted, +): SQL[] => { + const sortedBy = [desc(requiredColumnToSorted)]; + + if (sortedMethod === SortMethod.ASC) { + sortedBy.unshift(asc(optionalColumnToSorted)); + } + + if (sortedMethod === SortMethod.DESC) { + sortedBy.unshift(desc(optionalColumnToSorted)); + } + + return sortedBy; +}; + +export { getSortedBy }; diff --git a/backend/src/libs/helpers/helpers.ts b/backend/src/libs/helpers/helpers.ts new file mode 100644 index 000000000..bb2fc17a1 --- /dev/null +++ b/backend/src/libs/helpers/helpers.ts @@ -0,0 +1,2 @@ +export { countOffsetByQuery } from './count-offset-by-query.helper.js'; +export { getSortedBy } from './get-sorted-by.helper.js'; diff --git a/backend/src/libs/packages/database/generated-schema/0012_add_businessId_to_trucks.sql b/backend/src/libs/packages/database/generated-schema/0012_add_businessId_to_trucks.sql new file mode 100644 index 000000000..cd40de266 --- /dev/null +++ b/backend/src/libs/packages/database/generated-schema/0012_add_businessId_to_trucks.sql @@ -0,0 +1,9 @@ +ALTER TABLE "trucks" +ADD COLUMN "business_id" integer NOT NULL; + +--> statement-breakpoint +DO $$ BEGIN +ALTER TABLE "trucks" ADD CONSTRAINT "trucks_business_id_business_details_id_fk" FOREIGN KEY ("business_id") REFERENCES "business_details"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION +WHEN duplicate_object THEN null; +END $$; diff --git a/backend/src/libs/packages/database/schema/tables-schema.ts b/backend/src/libs/packages/database/schema/tables-schema.ts index 7dbac054f..09da0e258 100644 --- a/backend/src/libs/packages/database/schema/tables-schema.ts +++ b/backend/src/libs/packages/database/schema/tables-schema.ts @@ -128,15 +128,15 @@ const trucks = pgTable( id: serial('id').primaryKey(), createdAt: timestamp('created_at').notNull().defaultNow(), updatedAt: timestamp('updated_at').notNull().defaultNow(), + businessId: integer('business_id') + .notNull() + .references(() => business.id), manufacturer: varchar('manufacturer').notNull(), capacity: integer('capacity').notNull(), pricePerKm: real('price_per_km').notNull(), licensePlateNumber: varchar('license_plate_number').notNull(), year: integer('year').notNull(), towType: varchar('tow_type').notNull(), - businessId: integer('business_id') - .notNull() - .references(() => business.id), }, (trucks) => { return { diff --git a/backend/src/libs/types/types.ts b/backend/src/libs/types/types.ts index a2c9bf1fd..8b37d2be6 100644 --- a/backend/src/libs/types/types.ts +++ b/backend/src/libs/types/types.ts @@ -5,9 +5,12 @@ export { type Id, type NullableProperties, type OperationResult, + type PaginationParameters, + type PaginationWithSortingParameters, type RequireProperty, type ServerCommonErrorResponse, type ServerValidationErrorResponse, + type SortMethodValue, type ValidationSchema, type ValueOf, } from 'shared/build/index.js'; diff --git a/backend/src/packages/business/business.controller.ts b/backend/src/packages/business/business.controller.ts index 9cf5e73bd..c5b35adf4 100644 --- a/backend/src/packages/business/business.controller.ts +++ b/backend/src/packages/business/business.controller.ts @@ -6,6 +6,7 @@ import { } from '~/libs/packages/controller/controller.js'; import { HttpCode } from '~/libs/packages/http/http.js'; import { type ILogger } from '~/libs/packages/logger/logger.js'; +import { type PaginationWithSortingParameters } from '~/libs/types/types.js'; import { AuthStrategy } from '~/packages/auth/libs/enums/enums.js'; import { @@ -16,6 +17,8 @@ import { driverCreateUpdateRequestBody, driverParameters, } from '../drivers/libs/validation-schemas/validation-schemas.js'; +import { type TruckAddRequestDto } from '../trucks/libs/types/types.js'; +import { truckCreateRequestBody } from '../trucks/trucks.js'; import { type UserEntityObjectWithGroupT } from '../users/users.js'; import { type BusinessService } from './business.service.js'; import { BusinessApiPath } from './libs/enums/enums.js'; @@ -392,7 +395,23 @@ class BusinessController extends Controller { handler: (options) => this.findAllTrucks( options as ApiHandlerOptions<{ - query: GetPaginatedPageQuery; + query: PaginationWithSortingParameters; + user: UserEntityObjectWithGroupT; + }>, + ), + }); + + this.addRoute({ + path: BusinessApiPath.TRUCKS, + method: 'POST', + authStrategy: defaultStrategies, + validation: { + body: truckCreateRequestBody, + }, + handler: (request) => + this.createTruck( + request as ApiHandlerOptions<{ + body: TruckAddRequestDto; user: UserEntityObjectWithGroupT; }>, ), @@ -873,11 +892,11 @@ class BusinessController extends Controller { private async findAllTrucks( options: ApiHandlerOptions<{ - query: GetPaginatedPageQuery; + query: PaginationWithSortingParameters; user: UserEntityObjectWithGroupT; }>, ): Promise { - const trucks = await this.businessService.findAllTrucksByOwnerId( + const trucks = await this.businessService.findAllTrucksByBusinessId( options.user.id, options.query, ); @@ -887,6 +906,49 @@ class BusinessController extends Controller { payload: trucks, }; } + + /** + * @swagger + * /trucks: + * post: + * summary: Create a new truck + * tags: + * - business/trucks + * requestBody: + * description: Truck data to be added + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Truck' + * security: + * - bearerAuth: [] + * responses: + * '201': + * description: Truck created successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/TruckResponse' + * '400': + * description: Bad request + * + */ + + private async createTruck( + options: ApiHandlerOptions<{ + body: TruckAddRequestDto; + user: UserEntityObjectWithGroupT; + }>, + ): Promise { + return { + status: HttpCode.CREATED, + payload: await this.businessService.createTruck( + options.body, + options.user.id, + ), + }; + } } export { BusinessController }; diff --git a/backend/src/packages/business/business.service.ts b/backend/src/packages/business/business.service.ts index e6f053d2c..b3dff1914 100644 --- a/backend/src/packages/business/business.service.ts +++ b/backend/src/packages/business/business.service.ts @@ -1,7 +1,7 @@ import { NotFoundError } from '~/libs/exceptions/exceptions.js'; import { type IService } from '~/libs/interfaces/interfaces.js'; import { HttpCode, HttpError, HttpMessage } from '~/libs/packages/http/http.js'; -import { type EntityPagination } from '~/libs/types/types.js'; +import { type PaginationWithSortingParameters } from '~/libs/types/types.js'; import { UserGroupKey } from '~/packages/users/libs/enums/enums.js'; import { type DriverService } from '../drivers/driver.service.js'; @@ -12,7 +12,11 @@ import { type DriverGetAllResponseDto, } from '../drivers/drivers.js'; import { type ShiftEntity } from '../shifts/shift.js'; -import { type TruckEntity } from '../trucks/libs/types/types.js'; +import { + type TruckAddRequestDto, + type TruckEntity, + type TruckGetAllResponseDto, +} from '../trucks/libs/types/types.js'; import { type TruckService } from '../trucks/truck.service.js'; import { type UserEntityT } from '../users/users.js'; import { BusinessEntity } from './business.entity.js'; @@ -211,23 +215,26 @@ class BusinessService implements IService { return await this.driverService.delete(driverId); } - public checkisDriverBelongedToBusiness({ - userId, - driverId, - }: { - userId: UserEntityT['id']; - driverId: ShiftEntity['driverId']; - }): Promise { - return this.businessRepository.checkisDriverBelongedToBusiness( - userId, - driverId, - ); + public async findAllTrucksByBusinessId( + userId: number, + query: PaginationWithSortingParameters, + ): Promise { + const business = await this.findByOwnerId(userId); + + if (!business) { + throw new HttpError({ + status: HttpCode.BAD_REQUEST, + message: HttpMessage.BUSINESS_DOES_NOT_EXIST, + }); + } + + return await this.truckService.findAllByBusinessId(business.id, query); } - public async findAllTrucksByOwnerId( + public async createTruck( + payload: TruckAddRequestDto, userId: number, - query: GetPaginatedPageQuery, - ): Promise> { + ): Promise { const business = await this.findByOwnerId(userId); if (!business) { @@ -237,7 +244,23 @@ class BusinessService implements IService { }); } - return await this.truckService.findAllByBusinessId(business.id, query); + return await this.truckService.create({ + ...payload, + businessId: business.id, + }); + } + + public checkisDriverBelongedToBusiness({ + userId, + driverId, + }: { + userId: UserEntityT['id']; + driverId: ShiftEntity['driverId']; + }): Promise { + return this.businessRepository.checkisDriverBelongedToBusiness( + userId, + driverId, + ); } } diff --git a/backend/src/packages/trucks/libs/types/types.ts b/backend/src/packages/trucks/libs/types/types.ts index c0a96fb12..3ea762cfc 100644 --- a/backend/src/packages/trucks/libs/types/types.ts +++ b/backend/src/packages/trucks/libs/types/types.ts @@ -1,3 +1,7 @@ export { type DriverHaveAccessToTruck } from './driver-have-acces-to-truck.type.js'; export { type TruckDatabaseModel } from './truck-entity-type/truck-database-model.type.js'; -export { type TruckEntity } from 'shared/build/index.js'; +export { + type TruckAddRequestDto, + type TruckEntity, + type TruckGetAllResponseDto, +} from 'shared/build/index.js'; diff --git a/backend/src/packages/trucks/libs/validation-schema/validation-schemas.ts b/backend/src/packages/trucks/libs/validation-schemas/validation-schemas.ts similarity index 100% rename from backend/src/packages/trucks/libs/validation-schema/validation-schemas.ts rename to backend/src/packages/trucks/libs/validation-schemas/validation-schemas.ts diff --git a/backend/src/packages/trucks/truck.controller.ts b/backend/src/packages/trucks/truck.controller.ts index 03da4db88..ecfe110e7 100644 --- a/backend/src/packages/trucks/truck.controller.ts +++ b/backend/src/packages/trucks/truck.controller.ts @@ -10,10 +10,9 @@ import { type ILogger } from '~/libs/packages/logger/logger.js'; import { TruckApiPath } from './libs/enums/enums.js'; import { type TruckEntity } from './libs/types/types.js'; import { - truckCreateRequestBody, truckGetParameters, truckUpdateRequestBody, -} from './libs/validation-schema/validation-schemas.js'; +} from './libs/validation-schemas/validation-schemas.js'; import { type TruckService } from './truck.service.js'; /** @@ -102,6 +101,9 @@ import { type TruckService } from './truck.service.js'; * pricePerKm: * type: number * example: 5 + * businessId: + * type: number + * example: 1 * * ErrorType: * type: object @@ -136,20 +138,6 @@ class TruckController extends Controller { this.truckService = truckService; - this.addRoute({ - path: TruckApiPath.ROOT, - method: 'POST', - validation: { - body: truckCreateRequestBody, - }, - handler: (request) => - this.create( - request as ApiHandlerOptions<{ - body: Omit; - }>, - ), - }); - this.addRoute({ path: TruckApiPath.$ID, method: 'PUT', @@ -160,7 +148,7 @@ class TruckController extends Controller { handler: (request) => this.update( request as ApiHandlerOptions<{ - body: Partial; + body: Partial>; params: { id: number }; }>, ), @@ -201,42 +189,6 @@ class TruckController extends Controller { }); } - /** - * @swagger - * /trucks/: - * post: - * summary: Create a new truck - * tags: - * - truck - * requestBody: - * description: Truck data to be added - * required: true - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/Truck' - * responses: - * '201': - * description: Truck created successfully - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/TruckResponse' - * '400': - * description: Bad request - * - */ - private async create( - options: ApiHandlerOptions<{ - body: Omit; - }>, - ): Promise { - return { - status: HttpCode.CREATED, - payload: await this.truckService.create(options.body), - }; - } - /** * @swagger * /trucks/{id}: @@ -274,7 +226,7 @@ class TruckController extends Controller { private async update( options: ApiHandlerOptions<{ - body: Partial; + body: Partial>; params: { id: number }; }>, ): Promise { diff --git a/backend/src/packages/trucks/truck.entity.ts b/backend/src/packages/trucks/truck.entity.ts index f31bf58dd..6024b5028 100644 --- a/backend/src/packages/trucks/truck.entity.ts +++ b/backend/src/packages/trucks/truck.entity.ts @@ -27,6 +27,8 @@ class TruckEntity implements IEntity { private businessId: number; + private createdAt: string; + private constructor({ id, manufacturer, @@ -36,6 +38,7 @@ class TruckEntity implements IEntity { year, towType, businessId, + createdAt, }: NullableProperties) { this.id = id; this.manufacturer = manufacturer; @@ -45,6 +48,7 @@ class TruckEntity implements IEntity { this.year = year; this.towType = towType; this.businessId = businessId; + this.createdAt = createdAt; } public static initialize({ @@ -56,6 +60,7 @@ class TruckEntity implements IEntity { year, towType, businessId, + createdAt, }: TruckDatabaseModel): TruckEntity { return new TruckEntity({ id, @@ -66,6 +71,7 @@ class TruckEntity implements IEntity { licensePlateNumber, year, businessId, + createdAt: new Date(createdAt).toDateString(), }); } @@ -77,6 +83,7 @@ class TruckEntity implements IEntity { year, towType, businessId, + createdAt, }: Omit): TruckEntity { return new TruckEntity({ id: null, @@ -87,6 +94,7 @@ class TruckEntity implements IEntity { licensePlateNumber, year, businessId, + createdAt: new Date(createdAt).toDateString(), }); } @@ -100,6 +108,7 @@ class TruckEntity implements IEntity { pricePerKm: this.pricePerKm, licensePlateNumber: this.licensePlateNumber, businessId: this.businessId, + createdAt: this.createdAt, }; } @@ -112,6 +121,7 @@ class TruckEntity implements IEntity { pricePerKm: this.pricePerKm, licensePlateNumber: this.licensePlateNumber, businessId: this.businessId, + createdAt: this.createdAt, }; } } diff --git a/backend/src/packages/trucks/truck.repository.ts b/backend/src/packages/trucks/truck.repository.ts index 607362577..5a6731400 100644 --- a/backend/src/packages/trucks/truck.repository.ts +++ b/backend/src/packages/trucks/truck.repository.ts @@ -1,10 +1,11 @@ -import { desc, eq, ilike, placeholder, sql } from 'drizzle-orm'; +import { eq, ilike, placeholder, sql } from 'drizzle-orm'; +import { countOffsetByQuery, getSortedBy } from '~/libs/helpers/helpers.js'; import { type IRepository } from '~/libs/interfaces/interfaces.js'; import { type IDatabase } from '~/libs/packages/database/database.js'; import { type DatabaseSchema } from '~/libs/packages/database/schema/schema.js'; +import { type PaginationWithSortingParameters } from '~/libs/types/types.js'; -import { type GetPaginatedPageQuery } from '../business/libs/types/types.js'; import { type DriverHaveAccessToTruck, type TruckDatabaseModel, @@ -40,8 +41,40 @@ class TruckRepository implements IRepository { return await this.db.driver().select().from(this.trucksSchema); } + public async findAllByBusinessId( + businessId: number, + { size, sort, page }: PaginationWithSortingParameters, + ): Promise { + const offset = countOffsetByQuery({ size, page }); + + const sortedBy = getSortedBy( + sort, + this.trucksSchema.createdAt, + this.trucksSchema.year, + ); + + return await this.db + .driver() + .select() + .from(this.trucksSchema) + .where(eq(this.trucksSchema.businessId, businessId)) + .orderBy(...sortedBy) + .offset(offset) + .limit(size); + } + + public async getTotal(businessId: number): Promise { + const [total] = await this.db + .driver() + .select({ count: sql`count(${this.trucksSchema.businessId})` }) + .from(this.trucksSchema) + .where(eq(this.trucksSchema.businessId, businessId)); + + return total.count; + } + public async create( - entity: Omit, + entity: Omit, ): Promise { const preparedQuery = this.db .driver() @@ -55,7 +88,7 @@ class TruckRepository implements IRepository { public async update( id: number, - payload: Partial, + payload: Partial>, ): Promise { const preparedQuery = this.db .driver() @@ -111,32 +144,6 @@ class TruckRepository implements IRepository { await preparedQuery.execute(); } - - public async findAllByBusinessId( - businessId: number, - query: GetPaginatedPageQuery, - ): Promise { - const index = query.page * query.size; - - return await this.db - .driver() - .select() - .from(this.trucksSchema) - .where(eq(this.trucksSchema.businessId, businessId)) - .orderBy(desc(this.trucksSchema.createdAt)) - .offset(index) - .limit(query.size); - } - - public async getTotal(businessId: number): Promise { - const [total] = await this.db - .driver() - .select({ count: sql`count(${this.trucksSchema.businessId})` }) - .from(this.trucksSchema) - .where(eq(this.trucksSchema.businessId, businessId)); - - return total.count; - } } export { TruckRepository }; diff --git a/backend/src/packages/trucks/truck.service.ts b/backend/src/packages/trucks/truck.service.ts index cf0e30f5f..8e795363a 100644 --- a/backend/src/packages/trucks/truck.service.ts +++ b/backend/src/packages/trucks/truck.service.ts @@ -1,11 +1,11 @@ import { type IService } from '~/libs/interfaces/interfaces.js'; import { HttpCode, HttpError, HttpMessage } from '~/libs/packages/http/http.js'; -import { type EntityPagination } from '~/libs/types/types.js'; +import { type PaginationWithSortingParameters } from '~/libs/types/types.js'; -import { type GetPaginatedPageQuery } from '../business/libs/types/types.js'; import { type DriverHaveAccessToTruck, type TruckEntity as TruckEntityT, + type TruckGetAllResponseDto, } from './libs/types/types.js'; import { TruckEntity } from './truck.entity.js'; import { type TruckRepository } from './truck.repository.js'; @@ -23,8 +23,21 @@ class TruckService implements IService { return truck ? TruckEntity.initialize(truck).toObject() : null; } + public async findAllByBusinessId( + businessId: number, + query: PaginationWithSortingParameters, + ): Promise { + const data = await this.repository.findAllByBusinessId(businessId, query); + + const items = data.map((it) => TruckEntity.initialize(it).toObject()); + + const total = await this.repository.getTotal(businessId); + + return { items, total }; + } + public async create( - payload: Omit, + payload: Omit, ): Promise { const existingTruck = await this.repository.find( payload.licensePlateNumber, @@ -44,7 +57,7 @@ class TruckService implements IService { public async update( id: number, - payload: Partial, + payload: Partial>, ): Promise { const truck = await this.findById(id); @@ -86,19 +99,6 @@ class TruckService implements IService { await this.repository.addTruckToDriver(driverTrucks); } - - public async findAllByBusinessId( - businessId: number, - query: GetPaginatedPageQuery, - ): Promise> { - const data = await this.repository.findAllByBusinessId(businessId, query); - - const items = data.map((it) => TruckEntity.initialize(it).toObject()); - - const total = await this.repository.getTotal(businessId); - - return { items, total }; - } } export { TruckService }; diff --git a/backend/src/packages/trucks/trucks.ts b/backend/src/packages/trucks/trucks.ts index c48899cb7..68f81727e 100644 --- a/backend/src/packages/trucks/trucks.ts +++ b/backend/src/packages/trucks/trucks.ts @@ -15,3 +15,12 @@ const truckService = new TruckService(truckRepository); const truckController = new TruckController(logger, truckService); export { truckController, truckService }; +export { + type TruckDatabaseModel, + type TruckEntity, +} from './libs/types/types.js'; +export { + truckCreateRequestBody, + truckGetParameters, + truckUpdateRequestBody, +} from './libs/validation-schemas/validation-schemas.js'; diff --git a/frontend/src/libs/components/page-layout/styles.module.scss b/frontend/src/libs/components/page-layout/styles.module.scss index bc3d90920..9442afcf3 100644 --- a/frontend/src/libs/components/page-layout/styles.module.scss +++ b/frontend/src/libs/components/page-layout/styles.module.scss @@ -13,6 +13,7 @@ .sidebar { grid-area: sidebar; + padding: 10px 0 10px 10px; overflow-y: hidden; } diff --git a/frontend/src/libs/components/table/table.tsx b/frontend/src/libs/components/table/table.tsx index d567e91b1..2c78817e0 100644 --- a/frontend/src/libs/components/table/table.tsx +++ b/frontend/src/libs/components/table/table.tsx @@ -1,4 +1,9 @@ -import { type OnChangeFn, type SortingState } from '@tanstack/react-table'; +import { + type ColumnSort, + type OnChangeFn, + type SortingState, + type Updater, +} from '@tanstack/react-table'; import { getValidClassNames } from '~/libs/helpers/helpers.js'; import { @@ -26,8 +31,8 @@ type Properties = { pageSize: number; totalRow: number; pageIndex: number; - sorting?: SortingState; - setSorting?: OnChangeFn; + sorting?: ColumnSort | null; + setSorting?: OnChangeFn; changePageIndex: React.Dispatch>; changePageSize: React.Dispatch>; onEditClick?: (rowId: string) => void; @@ -50,6 +55,17 @@ const Table = ({ ...properties }: Properties): JSX.Element => { const pagesRange = Math.ceil(totalRow / pageSize); + + const sortState = sorting ? [sorting] : []; + + const handleSortingChange = (sortingUpdater: Updater): void => { + if (typeof sortingUpdater === 'function' && setSorting) { + const sortingArray = sortingUpdater(sortState); + const [firstItem] = sortingArray; + setSorting(firstItem); + } + }; + const dataAndColumns = useMemo(() => { return isTableEditable ? addIconsToData(data, columns) : { data, columns }; }, [columns, data, isTableEditable]); @@ -58,12 +74,15 @@ const Table = ({ columnResizeMode: 'onChange', defaultColumn: DEFAULT_COLUMN, state: { - sorting, + sorting: sortState, }, manualSorting: true, + onSortingChange: (sortingUpdater) => { + handleSortingChange(sortingUpdater); + }, + enableSortingRemoval: false, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), - onSortingChange: setSorting, initialState: { pagination: { pageSize, diff --git a/frontend/src/libs/enums/enums.ts b/frontend/src/libs/enums/enums.ts index 34d669e42..af3cd75a8 100644 --- a/frontend/src/libs/enums/enums.ts +++ b/frontend/src/libs/enums/enums.ts @@ -15,4 +15,5 @@ export { ContentType, HttpMessage, ServerErrorType, + SortMethod, } from 'shared/build/index.js'; diff --git a/frontend/src/libs/helpers/capitalize-first-letter/capitalize-first-letter.helper.ts b/frontend/src/libs/helpers/capitalize-first-letter/capitalize-first-letter.helper.ts new file mode 100644 index 000000000..c7fa9a9da --- /dev/null +++ b/frontend/src/libs/helpers/capitalize-first-letter/capitalize-first-letter.helper.ts @@ -0,0 +1,10 @@ +const capitalizeFirstLetter = (string: string): string => { + const stringWithoutUnderscores = string.replace(/_/g, ' '); + + return ( + stringWithoutUnderscores.charAt(0).toUpperCase() + + stringWithoutUnderscores.slice(1) + ); +}; + +export { capitalizeFirstLetter }; diff --git a/frontend/src/libs/helpers/get-sorting-method-value/get-sorting-method-value.helper.ts b/frontend/src/libs/helpers/get-sorting-method-value/get-sorting-method-value.helper.ts new file mode 100644 index 000000000..6c92c2aec --- /dev/null +++ b/frontend/src/libs/helpers/get-sorting-method-value/get-sorting-method-value.helper.ts @@ -0,0 +1,9 @@ +import { type ColumnSort } from '@tanstack/react-table'; + +import { type SortMethodValue } from '~/libs/types/types.js'; + +const getSortingMethodValue = (sorting: ColumnSort | null): SortMethodValue => { + return sorting?.desc ? 'desc' : 'asc'; +}; + +export { getSortingMethodValue }; diff --git a/frontend/src/libs/helpers/helpers.ts b/frontend/src/libs/helpers/helpers.ts index b7e592f00..a90d4cfec 100644 --- a/frontend/src/libs/helpers/helpers.ts +++ b/frontend/src/libs/helpers/helpers.ts @@ -1,4 +1,6 @@ +export { capitalizeFirstLetter } from './capitalize-first-letter/capitalize-first-letter.helper.js'; export { getErrorMessage } from './get-error-message/get-error-message.helper.js'; +export { getSortingMethodValue } from './get-sorting-method-value/get-sorting-method-value.helper.js'; export { getValidClassNames } from './get-valid-class-names/get-valid-class-names.helper.js'; export { configureString, diff --git a/frontend/src/libs/hooks/hooks.ts b/frontend/src/libs/hooks/hooks.ts index c54c5b0d9..c3779e7ca 100644 --- a/frontend/src/libs/hooks/hooks.ts +++ b/frontend/src/libs/hooks/hooks.ts @@ -9,6 +9,7 @@ export { useGetCurrentUser } from './use-get-current-user/use-get-current-user.h export { useHomePageSocketService } from './use-homepage-socket-service/use-homepage-socket-service.js'; export { useQueryParameters } from './use-query-parameters/use-query-parameters.hook.js'; export { useServerErrorFromThunk } from './use-server-error-from-thunk/use-server-error-from-thunk.hook.js'; +export { useToggle } from './use-toggle/use-toggle.hook.js'; export { createColumnHelper, flexRender, diff --git a/frontend/src/libs/hooks/use-app-table/use-app-table.ts b/frontend/src/libs/hooks/use-app-table/use-app-table.ts index 8d18bbfc1..734178471 100644 --- a/frontend/src/libs/hooks/use-app-table/use-app-table.ts +++ b/frontend/src/libs/hooks/use-app-table/use-app-table.ts @@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'react'; import { type AsyncThunkConfig, - type PaginationParameters, + type SortMethodValue, } from '~/libs/types/types.js'; import { useQueryParameters } from '../hooks.js'; @@ -11,14 +11,11 @@ import { useAppDispatch } from '../use-app-dispatch/use-app-dispatch.hook.js'; import { DEFAULT_PAGE_INDEX, DEFAULT_PAGE_SIZE } from './libs/constant.js'; type Properties = { - tableFetchCall: AsyncThunk< - T, - (K & PaginationParameters) | PaginationParameters, - AsyncThunkConfig - >; + tableFetchCall: AsyncThunk; payload?: K; initialPageSize?: number | null; initialPageIndex?: number | null; + sort?: SortMethodValue | null; }; type ReturnValue = { @@ -29,11 +26,12 @@ type ReturnValue = { updatePage: () => void; }; -const useAppTable = ({ +const useAppTable = ({ tableFetchCall, payload, initialPageSize, initialPageIndex, + sort, }: Properties): ReturnValue => { const [pageSize, changePageSize] = useState( initialPageSize ?? DEFAULT_PAGE_SIZE, @@ -41,20 +39,31 @@ const useAppTable = ({ const [pageIndex, changePageIndex] = useState( initialPageIndex ?? DEFAULT_PAGE_INDEX, ); - const { setQueryParameters } = useQueryParameters(); + + const { setQueryParameters, searchParameters } = useQueryParameters(); const dispatch = useAppDispatch(); const updatePage = useCallback(() => { - const actionPayload = { ...payload, page: pageIndex, size: pageSize }; - setQueryParameters(actionPayload); - void dispatch(tableFetchCall(actionPayload)); + const actionPayload = { + ...payload, + page: pageIndex, + size: pageSize, + }; + + const queryParameters = sort ? { ...actionPayload, sort } : actionPayload; + + setQueryParameters(queryParameters); + + void dispatch(tableFetchCall(searchParameters.toString())); }, [ dispatch, pageIndex, pageSize, payload, - tableFetchCall, + searchParameters, setQueryParameters, + sort, + tableFetchCall, ]); useEffect(() => { diff --git a/frontend/src/libs/hooks/use-query-parameters/use-query-parameters.hook.ts b/frontend/src/libs/hooks/use-query-parameters/use-query-parameters.hook.ts index 22198fe23..74d78f5bd 100644 --- a/frontend/src/libs/hooks/use-query-parameters/use-query-parameters.hook.ts +++ b/frontend/src/libs/hooks/use-query-parameters/use-query-parameters.hook.ts @@ -9,6 +9,7 @@ type ReturnType = { getQueryParameters: (...keys: string[]) => string | Queries | null; setQueryParameters: (parameters: ParametersT) => void; removeQueryParameters: (...keys: string[]) => void; + searchParameters: URLSearchParams; }; const useQueryParameters = (): ReturnType => { @@ -58,7 +59,12 @@ const useQueryParameters = (): ReturnType => { [searchParameters, setSearchParameters], ); - return { getQueryParameters, setQueryParameters, removeQueryParameters }; + return { + getQueryParameters, + setQueryParameters, + removeQueryParameters, + searchParameters, + }; }; export { type Queries, useQueryParameters }; diff --git a/frontend/src/libs/hooks/use-toggle/use-toggle.hook.ts b/frontend/src/libs/hooks/use-toggle/use-toggle.hook.ts new file mode 100644 index 000000000..20eae0915 --- /dev/null +++ b/frontend/src/libs/hooks/use-toggle/use-toggle.hook.ts @@ -0,0 +1,15 @@ +import { useState } from 'react'; + +type UseToggleHook = [boolean, () => void]; + +const useToggle = (initialValue = false): UseToggleHook => { + const [value, setValue] = useState(initialValue); + + const handleToggle = (): void => { + setValue((previousValue) => !previousValue); + }; + + return [value, handleToggle]; +}; + +export { useToggle }; diff --git a/frontend/src/libs/packages/store/store.package.ts b/frontend/src/libs/packages/store/store.package.ts index 29c57303e..9a32cefd4 100644 --- a/frontend/src/libs/packages/store/store.package.ts +++ b/frontend/src/libs/packages/store/store.package.ts @@ -60,10 +60,10 @@ class Store { filesApi, notification, truckApi, + businessApi, driverApi, ordersApi, localStorage: LocalStorage, - businessApi, }; } } diff --git a/frontend/src/libs/types/types.ts b/frontend/src/libs/types/types.ts index 3d3b0b969..00b602ba0 100644 --- a/frontend/src/libs/types/types.ts +++ b/frontend/src/libs/types/types.ts @@ -20,10 +20,12 @@ export { type DriverWithUserData, type EntityPagination, type PaginationParameters, + type PaginationWithSortingParameters, type ServerCommonErrorResponse, type ServerErrorDetail, type ServerErrorResponse, type ServerValidationErrorResponse, + type SortMethodValue, type TruckEntity, type TruckTowType, type UserEntityObjectWithGroupAndBusinessT, diff --git a/frontend/src/packages/business/business-api.ts b/frontend/src/packages/business/business-api.ts index f77ffc954..0e123c044 100644 --- a/frontend/src/packages/business/business-api.ts +++ b/frontend/src/packages/business/business-api.ts @@ -2,8 +2,12 @@ import { ApiPath, ContentType } from '~/libs/enums/enums.js'; import { HttpApi } from '~/libs/packages/api/http-api.js'; import { type IHttp } from '~/libs/packages/http/http.js'; import { type IStorage } from '~/libs/packages/storage/storage.js'; -import { type EntityPagination, type TruckEntity } from '~/libs/types/types.js'; +import { type TruckEntity } from '~/libs/types/types.js'; +import { + type TruckAddRequestDto, + type TruckGetAllResponseDto, +} from '../trucks/libs/types/types.js'; import { BusinessApiPath } from './libs/enums/enums.js'; type Constructor = { @@ -17,9 +21,11 @@ class BusinessApi extends HttpApi { super({ path: ApiPath.BUSINESS, baseUrl, http, storage }); } - public async getTrucksByBusinessId(): Promise> { + public async findAllTrucksByBusinessId( + queryString = '', + ): Promise { const response = await this.load( - this.getFullEndpoint(BusinessApiPath.TRUCKS, {}), + this.getFullEndpoint(`${BusinessApiPath.TRUCKS}?${queryString}`, {}), { method: 'GET', contentType: ContentType.JSON, @@ -27,7 +33,21 @@ class BusinessApi extends HttpApi { }, ); - return await response.json>(); + return await response.json(); + } + + public async addTruck(payload: TruckAddRequestDto): Promise { + const response = await this.load( + this.getFullEndpoint(BusinessApiPath.TRUCKS, {}), + { + method: 'POST', + contentType: ContentType.JSON, + payload: JSON.stringify(payload), + hasAuth: true, + }, + ); + + return await response.json(); } } diff --git a/frontend/src/packages/drivers/drivers-api.ts b/frontend/src/packages/drivers/drivers-api.ts index 5e387316b..c6d5f2bed 100644 --- a/frontend/src/packages/drivers/drivers-api.ts +++ b/frontend/src/packages/drivers/drivers-api.ts @@ -7,7 +7,6 @@ import { type DriverGetAllResponseDto } from '~/libs/types/types.js'; import { type DriverAddPayload, type DriverAddResponseWithGroup, - type GetPaginatedPageQuery, } from './libs/types/types.js'; type Constructor = { @@ -21,12 +20,11 @@ class DriverApi extends HttpApi { super({ path: ApiPath.BUSINESS, baseUrl, http, storage }); } - public async getPageOfDrivers({ - page, - size, - }: GetPaginatedPageQuery): Promise { + public async getPageOfDrivers( + queryString = '', + ): Promise { const data = await this.load( - this.getFullEndpoint(`${ApiPath.DRIVERS}?page=${page}&size=${size}`, {}), + this.getFullEndpoint(`${ApiPath.DRIVERS}?${queryString}`, {}), { method: 'GET', contentType: ContentType.JSON, diff --git a/frontend/src/packages/trucks/libs/enums/enums.ts b/frontend/src/packages/trucks/libs/enums/enums.ts index 789fdb22b..811f67ffe 100644 --- a/frontend/src/packages/trucks/libs/enums/enums.ts +++ b/frontend/src/packages/trucks/libs/enums/enums.ts @@ -1,3 +1,4 @@ +export { TruckNotificationMessage } from './truck-successful-message.enum.js'; export { TruckApiPath, TruckCapacity, diff --git a/frontend/src/packages/trucks/libs/enums/truck-successful-message.enum.ts b/frontend/src/packages/trucks/libs/enums/truck-successful-message.enum.ts new file mode 100644 index 000000000..6638f718a --- /dev/null +++ b/frontend/src/packages/trucks/libs/enums/truck-successful-message.enum.ts @@ -0,0 +1,5 @@ +const TruckNotificationMessage = { + SUCCESS_ADD_NEW_TRUCK: 'You added new truck!', +} as const; + +export { TruckNotificationMessage }; diff --git a/frontend/src/packages/trucks/libs/types/types.ts b/frontend/src/packages/trucks/libs/types/types.ts index 7269bd1c2..481e89b15 100644 --- a/frontend/src/packages/trucks/libs/types/types.ts +++ b/frontend/src/packages/trucks/libs/types/types.ts @@ -1,4 +1,6 @@ export { + type TruckAddRequestDto, type TruckEntity, + type TruckGetAllResponseDto, type TruckGetItemResponseDto, } from 'shared/build/index.js'; diff --git a/frontend/src/packages/trucks/trucks-api.ts b/frontend/src/packages/trucks/trucks-api.ts index 75d660e3b..7b9083651 100644 --- a/frontend/src/packages/trucks/trucks-api.ts +++ b/frontend/src/packages/trucks/trucks-api.ts @@ -1,11 +1,8 @@ -import { ApiPath, ContentType } from '~/libs/enums/enums.js'; +import { ApiPath } from '~/libs/enums/enums.js'; import { HttpApi } from '~/libs/packages/api/http-api.js'; import { type IHttp } from '~/libs/packages/http/http.js'; import { type IStorage } from '~/libs/packages/storage/storage.js'; -import { TruckApiPath } from './libs/enums/enums.js'; -import { type TruckEntity } from './libs/types/types.js'; - type Constructor = { baseUrl: string; http: IHttp; @@ -16,22 +13,6 @@ class TruckApi extends HttpApi { public constructor({ baseUrl, http, storage }: Constructor) { super({ path: ApiPath.TRUCKS, baseUrl, http, storage }); } - - public async addTruck( - payload: Omit, - ): Promise { - const response = await this.load( - this.getFullEndpoint(TruckApiPath.ROOT, {}), - { - method: 'POST', - contentType: ContentType.JSON, - payload: JSON.stringify(payload), - hasAuth: false, - }, - ); - - return await response.json(); - } } export { TruckApi }; diff --git a/frontend/src/pages/business/components/add-driver-form/add-driver-form.tsx b/frontend/src/pages/dashboard/components/form/add-driver-form/add-driver-form.tsx similarity index 95% rename from frontend/src/pages/business/components/add-driver-form/add-driver-form.tsx rename to frontend/src/pages/dashboard/components/form/add-driver-form/add-driver-form.tsx index 8bd613b91..9ac851af6 100644 --- a/frontend/src/pages/business/components/add-driver-form/add-driver-form.tsx +++ b/frontend/src/pages/dashboard/components/form/add-driver-form/add-driver-form.tsx @@ -25,7 +25,7 @@ const AddDriverForm: React.FC = ({ onSubmit }: Properties) => { const trucks = useAppSelector((state) => state.trucks.trucks); useEffect( - () => void dispatch(truckActions.getTrucksForBusiness()), + () => void dispatch(truckActions.findAllTrucksForBusiness()), [dispatch], ); diff --git a/frontend/src/pages/business/components/add-driver-form/libs/constants.ts b/frontend/src/pages/dashboard/components/form/add-driver-form/libs/constants.ts similarity index 100% rename from frontend/src/pages/business/components/add-driver-form/libs/constants.ts rename to frontend/src/pages/dashboard/components/form/add-driver-form/libs/constants.ts diff --git a/frontend/src/pages/business/components/add-driver-form/libs/fields.ts b/frontend/src/pages/dashboard/components/form/add-driver-form/libs/fields.ts similarity index 100% rename from frontend/src/pages/business/components/add-driver-form/libs/fields.ts rename to frontend/src/pages/dashboard/components/form/add-driver-form/libs/fields.ts diff --git a/frontend/src/pages/business/components/add-driver-form/libs/helpers/get-initial-fields.helper.ts b/frontend/src/pages/dashboard/components/form/add-driver-form/libs/helpers/get-initial-fields.helper.ts similarity index 100% rename from frontend/src/pages/business/components/add-driver-form/libs/helpers/get-initial-fields.helper.ts rename to frontend/src/pages/dashboard/components/form/add-driver-form/libs/helpers/get-initial-fields.helper.ts diff --git a/frontend/src/pages/business/components/add-driver-form/libs/helpers/helpers.ts b/frontend/src/pages/dashboard/components/form/add-driver-form/libs/helpers/helpers.ts similarity index 100% rename from frontend/src/pages/business/components/add-driver-form/libs/helpers/helpers.ts rename to frontend/src/pages/dashboard/components/form/add-driver-form/libs/helpers/helpers.ts diff --git a/frontend/src/pages/business/components/add-driver-form/styles.module.scss b/frontend/src/pages/dashboard/components/form/add-driver-form/styles.module.scss similarity index 100% rename from frontend/src/pages/business/components/add-driver-form/styles.module.scss rename to frontend/src/pages/dashboard/components/form/add-driver-form/styles.module.scss diff --git a/frontend/src/pages/dashboard/components/form/add-truck-form/add-truck-form.tsx b/frontend/src/pages/dashboard/components/form/add-truck-form/add-truck-form.tsx new file mode 100644 index 000000000..d5c29767c --- /dev/null +++ b/frontend/src/pages/dashboard/components/form/add-truck-form/add-truck-form.tsx @@ -0,0 +1,41 @@ +import { Form, Icon } from '~/libs/components/components.js'; +import { IconName } from '~/libs/enums/enums.js'; +import { getValidClassNames } from '~/libs/helpers/helpers.js'; +import { type TruckAddRequestDto } from '~/packages/trucks/libs/types/types.js'; +import { truckCreateRequestBody } from '~/packages/trucks/libs/validation-schemas/validation-schemas.js'; + +import { DEFAULT_TRUCK_PAYLOAD } from './constants/constants.js'; +import { ADD_TRUCK_FIELDS } from './fields/add-truck.fields.js'; +import styles from './styles.module.scss'; + +type Properties = { + onClose: () => void; + onSubmit: (payload: TruckAddRequestDto) => void; +}; + +const AddTruckForm: React.FC = ({ + onClose, + onSubmit, +}: Properties) => { + return ( +
+

+ Add Truck +

+ +
+
+ ); +}; + +export { AddTruckForm }; diff --git a/frontend/src/pages/trucks/components/add-truck-form/libs/constants/constants.ts b/frontend/src/pages/dashboard/components/form/add-truck-form/constants/constants.ts similarity index 78% rename from frontend/src/pages/trucks/components/add-truck-form/libs/constants/constants.ts rename to frontend/src/pages/dashboard/components/form/add-truck-form/constants/constants.ts index 49b846c5d..e1aace60b 100644 --- a/frontend/src/pages/trucks/components/add-truck-form/libs/constants/constants.ts +++ b/frontend/src/pages/dashboard/components/form/add-truck-form/constants/constants.ts @@ -2,7 +2,7 @@ import { type TruckEntity } from '~/packages/trucks/libs/types/types.js'; const DEFAULT_TRUCK_PAYLOAD: Omit< TruckEntity, - 'id' | 'manufacturer' | 'towType' | 'businessId' + 'id' | 'manufacturer' | 'towType' | 'businessId' | 'createdAt' > = { licensePlateNumber: '', capacity: 0, diff --git a/frontend/src/pages/trucks/components/add-truck-form/libs/fields/add-truck.fields.ts b/frontend/src/pages/dashboard/components/form/add-truck-form/fields/add-truck.fields.ts similarity index 94% rename from frontend/src/pages/trucks/components/add-truck-form/libs/fields/add-truck.fields.ts rename to frontend/src/pages/dashboard/components/form/add-truck-form/fields/add-truck.fields.ts index 3b4662d6f..c7228c2d6 100644 --- a/frontend/src/pages/trucks/components/add-truck-form/libs/fields/add-truck.fields.ts +++ b/frontend/src/pages/dashboard/components/form/add-truck-form/fields/add-truck.fields.ts @@ -23,7 +23,9 @@ const convertToSelectOptions = ( })); }; -const ADD_TRUCK_FIELDS: FormField>[] = [ +const ADD_TRUCK_FIELDS: FormField< + Omit +>[] = [ { id: FormLabel.MANUFACTURER, type: 'dropdown', diff --git a/frontend/src/pages/trucks/components/add-truck-form/styles.module.scss b/frontend/src/pages/dashboard/components/form/add-truck-form/styles.module.scss similarity index 74% rename from frontend/src/pages/trucks/components/add-truck-form/styles.module.scss rename to frontend/src/pages/dashboard/components/form/add-truck-form/styles.module.scss index d9090d126..21c5a5b02 100644 --- a/frontend/src/pages/trucks/components/add-truck-form/styles.module.scss +++ b/frontend/src/pages/dashboard/components/form/add-truck-form/styles.module.scss @@ -1,6 +1,7 @@ @import "src/assets/css/vars.scss"; .formWrapper { + position: relative; width: 100%; max-width: 448px; padding: 30px 60px 50px; @@ -25,3 +26,12 @@ text-align: center; text-decoration: none; } + +.closeIcon { + position: absolute; + top: 10px; + right: 10px; + color: $red; + font-size: 24px; + cursor: pointer; +} diff --git a/frontend/src/pages/dashboard/components/form/form.ts b/frontend/src/pages/dashboard/components/form/form.ts new file mode 100644 index 000000000..1910132e3 --- /dev/null +++ b/frontend/src/pages/dashboard/components/form/form.ts @@ -0,0 +1,2 @@ +export { AddDriverForm } from './add-driver-form/add-driver-form.js'; +export { AddTruckForm } from './add-truck-form/add-truck-form.js'; diff --git a/frontend/src/pages/dashboard/components/sidebar/libs/helpers/check-active-tab.helper.ts b/frontend/src/pages/dashboard/components/sidebar/libs/helpers/check-active-tab.helper.ts new file mode 100644 index 000000000..4174e9256 --- /dev/null +++ b/frontend/src/pages/dashboard/components/sidebar/libs/helpers/check-active-tab.helper.ts @@ -0,0 +1,7 @@ +import { AppRoute } from '~/libs/enums/app-route.enum'; + +const checkActiveTab = (path: string, tab: string): boolean => { + return path === `${AppRoute.DASHBOARD}/${tab}`; +}; + +export { checkActiveTab }; diff --git a/frontend/src/pages/dashboard/components/sidebar/libs/helpers/helpers.ts b/frontend/src/pages/dashboard/components/sidebar/libs/helpers/helpers.ts new file mode 100644 index 000000000..1ab817673 --- /dev/null +++ b/frontend/src/pages/dashboard/components/sidebar/libs/helpers/helpers.ts @@ -0,0 +1 @@ +export { checkActiveTab } from './check-active-tab.helper.js'; diff --git a/frontend/src/pages/dashboard/components/sidebar/sidebar.tsx b/frontend/src/pages/dashboard/components/sidebar/sidebar.tsx index 90fce7be1..9c4e92d4d 100644 --- a/frontend/src/pages/dashboard/components/sidebar/sidebar.tsx +++ b/frontend/src/pages/dashboard/components/sidebar/sidebar.tsx @@ -1,10 +1,10 @@ import { Button } from '~/libs/components/components.js'; -import { AppRoute } from '~/libs/enums/app-route.enum'; +import { AppRoute } from '~/libs/enums/enums.js'; import { getValidClassNames } from '~/libs/helpers/helpers.js'; import { useCallback, useLocation, useNavigate } from '~/libs/hooks/hooks.js'; import { type TabName } from '~/libs/types/types.js'; -import { checkActiveTab } from './libs/helpers.js'; +import { checkActiveTab } from './libs/helpers/helpers.js'; import styles from './styles.module.scss'; import { TABS } from './tabs.js'; @@ -35,13 +35,9 @@ const Sidebar: React.FC = ({ isCollapsed = false }: Properties) => {