Skip to content

Commit

Permalink
Merge pull request #151 from Kleostro/feat/tu-02-33/editing-prices
Browse files Browse the repository at this point in the history
feat(tu-02-34): editing prices
  • Loading branch information
Kleostro authored Aug 24, 2024
2 parents 8881e50 + 624f5be commit aaea018
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 21 deletions.
53 changes: 53 additions & 0 deletions src/app/admin/components/ride-price/ride-price.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@if (priceList(); as priceList) {
<div class="wrapper">
<div class="top">
<span class="title">Price</span>
@if (isEdit()) {
<p-button
icon="pi pi-save"
[rounded]="true"
severity="success"
[disabled]="priceForm.invalid"
(click)="this.isEdit.set(false)"
(click)="submit()"
></p-button>
<p-button
icon="pi pi-arrow-left"
[rounded]="true"
severity="primary"
(click)="this.isEdit.set(false)"
></p-button>
} @else {
<p-button
[disabled]="!isPriceEdited()"
[icon]="!isPriceEdited() ? 'pi pi-spin pi-cog' : 'pi pi-pencil'"
[rounded]="true"
severity="primary"
(click)="this.isEdit.set(true)"
></p-button>
}
</div>
<div class="bottom">
<form [formGroup]="priceForm">
<div class="controls" formArrayName="priceList">
@for (priceControl of priceForm.controls.priceList.controls; track priceControl; let i = $index) {
<div class="control" [formGroupName]="i">
{{ priceList[i].type }}
@if (isEdit()) {
<p-inputNumber
formControlName="price"
[showButtons]="false"
mode="currency"
currency="USD"
locale="en-US"
></p-inputNumber>
} @else {
<span> - {{ priceList[i].price | currency }}</span>
}
</div>
}
</div>
</form>
</div>
</div>
}
22 changes: 22 additions & 0 deletions src/app/admin/components/ride-price/ride-price.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.wrapper {
display: flex;
flex-direction: column;
gap: 1rem;
}

.title {
display: block;
margin-right: auto;
}

.top {
display: flex;
gap: 0.5rem;
align-items: center;
}

.controls {
display: flex;
flex-direction: column;
gap: 1rem;
}
22 changes: 22 additions & 0 deletions src/app/admin/components/ride-price/ride-price.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { RidePriceComponent } from './ride-price.component';

describe('RidePriceComponent', () => {
let component: RidePriceComponent;
let fixture: ComponentFixture<RidePriceComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RidePriceComponent],
}).compileComponents();

fixture = TestBed.createComponent(RidePriceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
61 changes: 61 additions & 0 deletions src/app/admin/components/ride-price/ride-price.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { CurrencyPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, EventEmitter, inject, input, OnInit, Output, signal } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

import { ButtonModule } from 'primeng/button';
import { InputNumberModule } from 'primeng/inputnumber';
import { RippleModule } from 'primeng/ripple';

import { RidePrice } from '../../models/ride.model';

@Component({
selector: 'app-ride-price',
standalone: true,
imports: [CurrencyPipe, ButtonModule, RippleModule, InputNumberModule, ReactiveFormsModule],
providers: [CurrencyPipe],
templateUrl: './ride-price.component.html',
styleUrl: './ride-price.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RidePriceComponent implements OnInit {
private fb = inject(FormBuilder);
public priceList = input<RidePrice[] | null>(null);
public isPriceEdited = input(false);
public isEdit = signal(false);

public priceForm = this.fb.group({
priceList: this.fb.array<FormGroup<{ price: FormControl<number> }>>([]),
});

@Output() public priceChanged = new EventEmitter<RidePrice[]>();

public ngOnInit(): void {
this.priceForm.controls.priceList.clear();
this.priceList()?.forEach((price) => {
this.priceForm.controls.priceList.push(
this.fb.nonNullable.group({
price: [price.price, [Validators.required.bind(this), Validators.min(0.01)]],
}),
);
});
}

public submit(): void {
this.priceForm.markAsTouched();
this.priceForm.updateValueAndValidity();

if (!this.priceForm.valid) {
return;
}

const priceList = this.priceList();

if (priceList) {
const updatedPriceList = priceList.map((price, index) => ({
...price,
price: this.priceForm.controls.priceList.controls[index].controls.price.value,
}));
this.priceChanged.emit(updatedPriceList);
}
}
}
2 changes: 2 additions & 0 deletions src/app/admin/components/ride-time/ride-time.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

.wrapper {
display: flex;
gap: 1rem;
align-items: flex-start;
justify-content: space-between;
}
Expand All @@ -22,4 +23,5 @@
.control {
display: flex;
gap: 0.5rem;
align-items: center;
}
2 changes: 1 addition & 1 deletion src/app/admin/components/ride-time/ride-time.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class RideTimeComponent {
private fb = inject(FormBuilder);
public to = input<string | null>(null);
public from = input<string | null>(null);
public isEdit = signal(false);
public isTimeEdited = input(true);
public isEdit = signal(false);
public minDate = new Date();
public fromTime = false;
public toTime = false;
Expand Down
13 changes: 7 additions & 6 deletions src/app/admin/components/ride/ride.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ <h2>Ride {{ ride()?.rideId }}</h2>
</div>
<div class="time">
<app-ride-time
[to]="ride.path.to | date: 'yyyy-MM-dd hh:mm a'"
[from]="ride.path.from | date: 'yyyy-MM-dd hh:mm a'"
[to]="ride.path.to"
[from]="ride.path.from"
[isTimeEdited]="isTimeEdited()"
(timeChanged)="handleTimeChanged($event, i)"
></app-ride-time>
</div>
<div class="price">
@if (ride.price?.length) {
<span>Price</span>
@for (price of ride.price; let i = $index; track i) {
<span>{{ price.type }} - {{ price.price | currency }}</span>
}
<app-ride-price
[isPriceEdited]="isPriceEdited()"
[priceList]="ride.price"
(priceChanged)="handlePriceChanged($event, i)"
></app-ride-price>
}
</div>
</div>
Expand Down
39 changes: 33 additions & 6 deletions src/app/admin/components/ride/ride.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CurrencyPipe, DatePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, inject, input, OnDestroy, signal } from '@angular/core';

import { ButtonModule } from 'primeng/button';
Expand All @@ -12,15 +11,20 @@ import { StationsService } from '@/app/api/stationsService/stations.service';
import { USER_MESSAGE } from '@/app/shared/services/userMessage/constants/user-messages';
import { UserMessageService } from '@/app/shared/services/userMessage/user-message.service';

import { RidePath } from '../../models/ride.model';
import { collectAllRideData, exrtactRideData } from '../../utils/collectAllRideData';
import { RidePath, RidePrice } from '../../models/ride.model';
import {
collectAllRideData,
exrtactRideDataWithUpdatePrice,
exrtactRideDataWithUpdateTime,
} from '../../utils/collectAllRideData';
import { RidePriceComponent } from '../ride-price/ride-price.component';
import { RideTimeComponent } from '../ride-time/ride-time.component';

@Component({
selector: 'app-ride',
standalone: true,
imports: [CurrencyPipe, DatePipe, ButtonModule, RippleModule, RideTimeComponent],
providers: [CurrencyPipe, DatePipe],
imports: [ButtonModule, RippleModule, RideTimeComponent, RidePriceComponent],
providers: [],
templateUrl: './ride.component.html',
styleUrl: './ride.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
Expand All @@ -36,6 +40,7 @@ export class RideComponent implements OnDestroy {
);
public fullRideData = computed(() => collectAllRideData(this.stations(), this.ride()?.segments ?? []));
public isTimeEdited = signal(true);
public isPriceEdited = signal(true);
private subscription = new Subscription();

public handleTimeChanged(event: RidePath, index: number): void {
Expand All @@ -47,7 +52,7 @@ export class RideComponent implements OnDestroy {
.updateRide(
this.rideService.currentRouteId(),
this.ride()?.rideId ?? 0,
exrtactRideData(currentRide, event, index),
exrtactRideDataWithUpdateTime(currentRide, event, index),
)
.pipe(take(1))
.subscribe(() => {
Expand All @@ -60,6 +65,28 @@ export class RideComponent implements OnDestroy {
}
}

public handlePriceChanged(event: RidePrice[], index: number): void {
const currentRide = this.ride();
if (currentRide) {
this.isPriceEdited.set(false);
this.subscription.add(
this.rideService
.updateRide(
this.rideService.currentRouteId(),
this.ride()?.rideId ?? 0,
exrtactRideDataWithUpdatePrice(currentRide, event, index),
)
.pipe(take(1))
.subscribe(() => {
this.rideService.getRouteById(this.rideService.currentRouteId()).subscribe(() => {
this.userMessageService.showSuccessMessage(USER_MESSAGE.RIDE_PRICE_UPDATED_SUCCESSFULLY);
this.isPriceEdited.set(true);
});
}),
);
}
}

public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
Expand Down
12 changes: 6 additions & 6 deletions src/app/admin/models/ride.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ export interface RidePath {
to: string | null;
}

export interface RidePrice {
type: string;
price: number;
}

interface FullRide {
station: Station | null;
path: RidePath;
price:
| {
type: string;
price: number;
}[]
| null;
price: RidePrice[] | null;
}

export default FullRide;
21 changes: 19 additions & 2 deletions src/app/admin/utils/collectAllRideData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Station } from '@/app/api/models/stations';

import { CustomSchedule, OmitedSegment } from '../../api/models/schedule';
import FullRide from '../models/ride.model';
import FullRide, { RidePrice } from '../models/ride.model';

export const collectAllRideData = (stations: (Station | null)[], segments: OmitedSegment[]): FullRide[] => {
const rides: FullRide[] = [];
Expand Down Expand Up @@ -33,7 +33,7 @@ export const collectAllRideData = (stations: (Station | null)[], segments: Omite
return rides;
};

export const exrtactRideData = (
export const exrtactRideDataWithUpdateTime = (
currentRide: CustomSchedule,
event: { from: string | null; to: string | null },
index: number,
Expand All @@ -54,3 +54,20 @@ export const exrtactRideData = (

return updatedData;
};

export const exrtactRideDataWithUpdatePrice = (
currentRide: CustomSchedule,
event: RidePrice[],
index: number,
): CustomSchedule => {
const updatedData: CustomSchedule = { rideId: currentRide.rideId ?? 0, segments: currentRide.segments ?? [] };
if (index === 0) {
updatedData.segments[0].price = event.reduce((acc, curr) => ({ ...acc, [curr.type]: curr.price }), {});
} else if (index === currentRide.segments.length) {
updatedData.segments[index - 1].price = event.reduce((acc, curr) => ({ ...acc, [curr.type]: curr.price }), {});
} else {
updatedData.segments[index].price = event.reduce((acc, curr) => ({ ...acc, [curr.type]: curr.price }), {});
}

return updatedData;
};
Empty file removed src/app/shared/directives/.gitkeep
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export const USER_MESSAGE = {
ROUTE_CREATED_ERROR: "We couldn't create your route. Please try again.",
ROUTE_CREATION_UPDATED_ERROR: 'A minimum of 3 stations and 3 carriages must be specified!',
RIDE_DATA_UPDATED_SUCCESSFULLY: 'Ride data updated successfully!',
RIDE_PRICE_UPDATED_SUCCESSFULLY: 'Ride price updated successfully!',
};

0 comments on commit aaea018

Please sign in to comment.