🔥 Unrestrict currency field

This commit is contained in:
itskovacs 2025-08-23 15:58:47 +02:00
parent 706892fcbe
commit 0abb938393
13 changed files with 58 additions and 38 deletions

View File

@ -0,0 +1,27 @@
"""Trip currency
Revision ID: 7e331b851cb7
Revises: 26c89b7466f2
Create Date: 2025-08-23 15:06:50.387366
"""
import sqlalchemy as sa
import sqlmodel.sql.sqltypes
from alembic import op
# revision identifiers, used by Alembic.
revision = "7e331b851cb7"
down_revision = "26c89b7466f2"
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table("trip", schema=None) as batch_op:
batch_op.add_column(sa.Column("currency", sqlmodel.sql.sqltypes.AutoString(), nullable=True))
def downgrade():
with op.batch_alter_table("trip", schema=None) as batch_op:
batch_op.drop_column("currency")

View File

@ -250,6 +250,7 @@ class PlaceRead(PlaceBase):
class TripBase(SQLModel):
name: str
archived: bool | None = None
currency: str | None = settings.DEFAULT_CURRENCY
class Trip(TripBase, table=True):
@ -294,6 +295,7 @@ class TripReadBase(TripBase):
image_id=obj.image_id,
days=len(obj.days),
collaborators=[TripMemberRead.serialize(m) for m in obj.memberships],
currency=obj.currency if obj.currency else settings.DEFAULT_CURRENCY,
)
@ -318,6 +320,7 @@ class TripRead(TripBase):
places=[PlaceRead.serialize(place) for place in obj.places],
collaborators=[TripMemberRead.serialize(m) for m in obj.memberships],
shared=bool(obj.shares),
currency=obj.currency if obj.currency else settings.DEFAULT_CURRENCY,
)

View File

@ -248,8 +248,7 @@
</div>
<div class="mt-4">
<p-floatlabel variant="in" class="md:col-span-2">
<p-select [options]="currencySigns" optionValue="s" optionLabel="c" inputId="currency" id="currency"
class="capitalize" formControlName="currency" [checkmark]="true" [showClear]="true" fluid />
<input id="currency" formControlName="currency" pInputText fluid />
<label for="currency">Currency</label>
</p-floatlabel>
</div>

View File

@ -96,7 +96,6 @@ export class DashboardComponent implements OnInit, AfterViewInit {
markerClusterGroup?: L.MarkerClusterGroup;
gpxLayerGroup?: L.LayerGroup;
settings?: Settings;
currencySigns = UtilsService.currencySigns();
doNotDisplayOptions: SelectItemGroup[] = [];
places: Place[] = [];
@ -117,8 +116,6 @@ export class DashboardComponent implements OnInit, AfterViewInit {
private router: Router,
private fb: FormBuilder,
) {
this.currencySigns = UtilsService.currencySigns();
this.settingsForm = this.fb.group({
map_lat: [
"",

View File

@ -19,7 +19,7 @@
severity="help" />
<span
class="bg-gray-100 text-gray-800 text-xs md:text-sm font-medium me-2 px-2.5 py-0.5 rounded min-w-fit dark:bg-gray-400">{{
(totalPrice | number:'1.0-2') || '-' }} {{ currency$ | async }}</span>
(totalPrice | number:'1.0-2') || '-' }} @if (totalPrice) { {{ trip.currency }} }</span>
</div>
</div>
</section>
@ -131,7 +131,7 @@
</td>}
@if (tripTableSelectedColumns.includes('price')) {<td class="truncate">@if (tripitem.price) {<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded">{{
tripitem.price }} {{ currency$ | async }}</span>}</td>}
tripitem.price }} @if (tripitem.price) { {{ trip.currency }} }</span>}</td>}
@if (tripTableSelectedColumns.includes('status')) {<td class="truncate">@if (tripitem.status) {<span
[style.background]="tripitem.status.color+'1A'" [style.color]="tripitem.status.color"
class="text-xs font-medium me-2 px-2.5 py-0.5 rounded">{{
@ -180,7 +180,7 @@
</td>}
@if (tripTableSelectedColumns.includes('price')) {<td class="truncate">@if (tripitem.price) {<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded">{{
tripitem.price }} {{ currency$ | async }}</span>}</td>}
tripitem.price }} @if (tripitem.price) { {{ trip.currency }} }</span>}</td>}
@if (tripTableSelectedColumns.includes('status')) {<td class="truncate">@if (tripitem.status) {<span
[style.background]="tripitem.status.color+'1A'" [style.color]="tripitem.status.color"
class="text-xs font-medium me-2 px-2.5 py-0.5 rounded">{{
@ -267,7 +267,7 @@
@if (selectedItem.price) {
<div class="rounded-md shadow p-4">
<p class="font-bold mb-1">Price</p>
<p class="text-sm text-gray-500">{{ selectedItem.price }} {{ currency$ | async }}</p>
<p class="text-sm text-gray-500">{{ selectedItem.price }} @if (selectedItem.price) { {{ trip.currency }} }</p>
</div>
}
@ -353,7 +353,7 @@
<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-100/85">{{
p.price || '-'
}} {{ currency$ | async }}</span>
}} @if (p.price) { {{ trip.currency }} }</span>
@if (trip.collaborators.length) {
<span class="bg-gray-100 text-gray-800 text-sm me-2 px-2.5 py-0.5 rounded dark:bg-gray-100/85">{{ p.user
@ -402,7 +402,7 @@
</div>
<div class="flex items-center gap-2 flex-none">
<span class="bg-gray-100 text-gray-800 text-sm px-2.5 py-0.5 rounded-md min-w-fit dark:bg-gray-100/85">{{
getDayStats(d).price || '-' }} {{ currency$ | async }}</span>
getDayStats(d).price || '-' }} @if (getDayStats(d).price) { {{ trip.currency }} }</span>
<span class="bg-blue-100 text-blue-800 text-sm px-2.5 py-0.5 rounded-md dark:bg-blue-100/85">{{
getDayStats(d).places }}</span>
</div>

View File

@ -54,14 +54,12 @@ import { InputTextModule } from "primeng/inputtext";
FormsModule,
MultiSelectModule,
CheckboxModule,
AsyncPipe,
],
templateUrl: "./shared-trip.component.html",
styleUrls: ["./shared-trip.component.scss"],
})
export class SharedTripComponent implements AfterViewInit {
token?: string;
currency$: Observable<string>;
statuses: TripStatus[] = [];
trip?: Trip;
places: Place[] = [];
@ -180,7 +178,6 @@ export class SharedTripComponent implements AfterViewInit {
private utilsService: UtilsService,
private route: ActivatedRoute,
) {
this.currency$ = this.utilsService.currency$;
this.statuses = this.utilsService.statuses;
this.tripTableSearchInput.valueChanges
.pipe(takeUntilDestroyed(), debounceTime(300))

View File

@ -35,7 +35,7 @@
<span
class="bg-gray-100 text-gray-800 text-xs md:text-sm font-medium me-2 px-2.5 py-0.5 rounded min-w-fit dark:bg-gray-400">{{
(totalPrice | number:'1.0-2') || '-' }} {{ currency$ | async }}</span>
(totalPrice | number:'1.0-2') || '-' }} @if (totalPrice) { {{ trip?.currency }} }</span>
</div>
</div>
</section>
@ -161,7 +161,7 @@
</td>}
@if (tripTableSelectedColumns.includes('price')) {<td class="truncate">@if (tripitem.price) {<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded">{{
tripitem.price }} {{ currency$ | async }}</span>}</td>}
tripitem.price }} @if (tripitem.price) { {{ trip?.currency }} }</span>}</td>}
@if (tripTableSelectedColumns.includes('status')) {<td class="truncate">@if (tripitem.status) {<span
[style.background]="tripitem.status.color+'1A'" [style.color]="tripitem.status.color"
class="text-xs font-medium me-2 px-2.5 py-0.5 rounded">{{
@ -210,7 +210,7 @@
</td>}
@if (tripTableSelectedColumns.includes('price')) {<td class="truncate">@if (tripitem.price) {<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded">{{
tripitem.price }} {{ currency$ | async }}</span>}</td>}
tripitem.price }} @if (tripitem.price) { {{ trip?.currency }} }</span>}</td>}
@if (tripTableSelectedColumns.includes('status')) {<td class="truncate">@if (tripitem.status) {<span
[style.background]="tripitem.status.color+'1A'" [style.color]="tripitem.status.color"
class="text-xs font-medium me-2 px-2.5 py-0.5 rounded">{{
@ -310,7 +310,8 @@
@if (selectedItem.price) {
<div class="rounded-md shadow p-4">
<p class="font-bold mb-1">Price</p>
<p class="text-sm text-gray-500">{{ selectedItem.price }} {{ currency$ | async }}</p>
<p class="text-sm text-gray-500">{{ selectedItem.price }} @if (selectedItem.price) { {{ trip?.currency }} }
</p>
</div>
}
@ -401,7 +402,7 @@
<span
class="bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-100/85">{{
p.price || '-'
}} {{ currency$ | async }}</span>
}} @if (p.price) { {{ trip?.currency }} }</span>
@if (trip?.collaborators?.length) {
<span class="bg-gray-100 text-gray-800 text-sm me-2 px-2.5 py-0.5 rounded dark:bg-gray-100/85">{{ p.user
@ -454,7 +455,7 @@
<div class="flex items-center gap-2 flex-none">
<span
class="bg-gray-100 text-gray-800 text-sm px-2.5 py-0.5 rounded-md min-w-fit group-hover:hidden dark:bg-gray-100/85">{{
getDayStats(d).price || '-' }} {{ currency$ | async }}</span>
getDayStats(d).price || '-' }} @if (getDayStats(d).price) { {{ trip?.currency }} }</span>
<span
class="bg-blue-100 text-blue-800 text-sm px-2.5 py-0.5 rounded-md group-hover:hidden dark:bg-blue-100/85">{{
getDayStats(d).places }}</span>
@ -649,7 +650,7 @@
<div class="flex items-center gap-2">
<span
class="text-center bg-gray-100 text-gray-800 text-xs px-2.5 py-0.5 rounded-md group-hover:hidden dark:bg-gray-100/85">-
{{ currency$ | async }}</span>
{{ trip?.currency }}</span>
<div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
@if (!m.invited_at) {

View File

@ -86,7 +86,6 @@ import { TripInviteMemberModalComponent } from "../../modals/trip-invite-member-
styleUrls: ["./trip.component.scss"],
})
export class TripComponent implements AfterViewInit {
currency$: Observable<string>;
tripSharedURL$?: Observable<string>;
statuses: TripStatus[] = [];
trip?: Trip;
@ -284,7 +283,6 @@ export class TripComponent implements AfterViewInit {
private utilsService: UtilsService,
private route: ActivatedRoute,
) {
this.currency$ = this.utilsService.currency$;
this.statuses = this.utilsService.statuses;
this.tripTableSearchInput.valueChanges
.pipe(takeUntilDestroyed(), debounceTime(300))

View File

@ -1,11 +1,16 @@
<div pFocusTrap class="grid items-center gap-4" [formGroup]="tripForm">
<p-floatlabel variant="in">
<div pFocusTrap class="grid md:grid-cols-4 items-center gap-4" [formGroup]="tripForm">
<p-floatlabel class="md:col-span-3" variant="in">
<input id="name" formControlName="name" pInputText fluid (keyup.enter)="closeDialog()" />
<label for="name">Name</label>
</p-floatlabel>
<p-floatlabel variant="in">
<input id="currency" formControlName="currency" pInputText fluid />
<label for="currency">Currency</label>
</p-floatlabel>
@if (tripForm.get("id")?.value === -1) {
<div class="grid grid-cols-2 gap-2">
<div class="grid md:col-span-4 grid-cols-2 gap-2">
<p-floatlabel variant="in">
<p-datepicker id="from" formControlName="from" [iconDisplay]="'input'" [showIcon]="true" appendTo="body" fluid />
<label for="from">From</label>
@ -18,7 +23,7 @@
</div>
}
<div class="grid place-items-center">
<div class="md:col-span-4 grid place-items-center">
@if (tripForm.get("image_id")?.value) {
<div class="max-w-80 max-h-80 relative group cursor-pointer" (click)="fileInput.click()">
<img [src]="tripForm.get('image')?.value"

View File

@ -40,6 +40,7 @@ export class TripCreateModalComponent {
id: -1,
name: ["", Validators.required],
image: "",
currency: null,
image_id: null,
from: null,
to: null,

View File

@ -61,14 +61,4 @@ export class UtilsService {
const latlng = `${latMatch[1]},${lngMatch[1]}`;
return [place, latlng];
}
static currencySigns(): { c: string; s: string }[] {
return [
{ c: "EUR", s: "€" },
{ c: "GBP", s: "£" },
{ c: "JPY", s: "¥" },
{ c: "USD", s: "$" },
{ c: "CHF", s: "CHF" },
];
}
}

View File

@ -77,7 +77,7 @@
<div class="flex flex-col mb-4">
<span class="text-gray-500">Price</span>
<span>{{ selectedPlace.price || '-' }} {{ currency$ | async }}</span>
<span>{{ selectedPlace.price || '-' }} @if (selectedPlace.price) { {{ currency$ | async }} }</span>
</div>
<div class="flex flex-col mb-4">

View File

@ -8,6 +8,7 @@ export interface TripBase {
user: string;
days: number;
collaborators: TripMember[];
currency: string;
}
export interface Trip {
@ -18,6 +19,7 @@ export interface Trip {
user: string;
days: TripDay[];
collaborators: TripMember[];
currency: string;
// POST / PUT
places: Place[];