From fc3cd00662fa615170df248efa11897624f52d57 Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Tue, 15 Sep 2026 11:29:27 +0000 Subject: [PATCH] Feature: Added sale voucher to check sale import. Chore: Frontend moved from Moment to date-fns and from dd-MMM-yyyy to ISO Date format. --- brewman/brewman/main.py | 2 + brewman/brewman/routers/__init__.py | 2 +- brewman/brewman/routers/account.py | 7 +- brewman/brewman/routers/batch.py | 12 +- brewman/brewman/routers/credit_salary.py | 9 +- .../brewman/routers/employee_attendance.py | 2 +- brewman/brewman/routers/fingerprint_report.py | 13 +- brewman/brewman/routers/rate_contract.py | 12 +- brewman/brewman/routers/recipe.py | 8 +- brewman/brewman/routers/reports/__init__.py | 50 +- .../brewman/routers/reports/product_ledger.py | 8 +- .../brewman/routers/reports/trial_balance.py | 2 +- brewman/brewman/routers/sale.py | 83 ++ brewman/brewman/schemas/attendance.py | 21 +- brewman/brewman/schemas/balance_sheet.py | 23 +- brewman/brewman/schemas/batch_integrity.py | 32 +- brewman/brewman/schemas/blank_voucher_info.py | 21 +- brewman/brewman/schemas/cash_flow.py | 36 +- brewman/brewman/schemas/client.py | 32 +- brewman/brewman/schemas/closing_stock.py | 45 +- brewman/brewman/schemas/daybook.py | 49 +- brewman/brewman/schemas/employee.py | 29 +- .../brewman/schemas/employee_attendance.py | 47 +- brewman/brewman/schemas/entries.py | 41 +- brewman/brewman/schemas/fingerprint.py | 21 +- brewman/brewman/schemas/input.py | 54 +- brewman/brewman/schemas/ledger.py | 49 +- .../brewman/schemas/mozimo_daily_register.py | 34 +- .../schemas/mozimo_product_register.py | 54 +- brewman/brewman/schemas/net_transactions.py | 36 +- .../brewman/schemas/non_contract_purchase.py | 21 +- brewman/brewman/schemas/period.py | 33 +- brewman/brewman/schemas/product_ledger.py | 47 +- brewman/brewman/schemas/profit_loss.py | 32 +- brewman/brewman/schemas/purchase_entries.py | 44 +- brewman/brewman/schemas/purchases.py | 36 +- brewman/brewman/schemas/rate_contract.py | 78 +- brewman/brewman/schemas/raw_material_cost.py | 36 +- brewman/brewman/schemas/recipe.py | 23 +- brewman/brewman/schemas/reconcile.py | 60 +- brewman/brewman/schemas/sale.py | 18 + brewman/brewman/schemas/settings.py | 49 +- brewman/brewman/schemas/stock_movement.py | 36 +- brewman/brewman/schemas/temporal_product.py | 56 +- brewman/brewman/schemas/trial_balance.py | 23 +- brewman/brewman/schemas/user.py | 22 +- brewman/brewman/schemas/voucher.py | 64 +- overlord/package.json | 6 +- overlord/src/app/app.config.ts | 9 +- overlord/src/app/app.environment.ts | 10 +- overlord/src/app/app.routes.ts | 4 + .../app/attendance/attendance.component.ts | 8 +- .../balance-sheet/balance-sheet.component.ts | 6 +- .../app/cash-flow/cash-flow.component.html | 6 +- .../src/app/cash-flow/cash-flow.component.ts | 12 +- .../closing-stock/closing-stock.component.ts | 8 +- overlord/src/app/core/date-utils.ts | 54 + overlord/src/app/daybook/daybook.component.ts | 10 +- .../employee-attendance.component.ts | 16 +- .../employee-benefits.component.ts | 8 +- .../employee-functions.component.ts | 48 +- .../employee-detail.component.ts | 11 +- .../src/app/entries/entries.component.html | 12 +- overlord/src/app/entries/entries.component.ts | 10 +- .../src/app/incentive/incentive.component.ts | 10 +- overlord/src/app/issue/issue.component.ts | 12 +- overlord/src/app/journal/journal.component.ts | 16 +- overlord/src/app/ledger/ledger.component.ts | 10 +- .../mozimo-daily-register.component.ts | 8 +- .../mozimo-product-register.component.ts | 14 +- .../net-transactions.component.html | 6 +- .../net-transactions.component.ts | 10 +- overlord/src/app/payment/payment.component.ts | 16 +- .../period-detail/period-detail.component.ts | 10 +- .../product-ledger.component.html | 4 +- .../product-ledger.component.ts | 14 +- .../app/profit-loss/profit-loss.component.ts | 10 +- .../purchase-entries.component.ts | 10 +- .../purchase-return.component.ts | 12 +- .../src/app/purchase/purchase.component.ts | 10 +- .../src/app/purchases/purchases.component.ts | 10 +- .../rate-contract-detail.component.ts | 14 +- .../raw-material-cost.component.ts | 12 +- overlord/src/app/receipt/receipt.component.ts | 16 +- .../recipe-detail/recipe-detail.component.ts | 8 +- .../role/role-detail/role-detail.component.ts | 17 +- overlord/src/app/sale/sale.component.css | 306 +++++ overlord/src/app/sale/sale.component.html | 299 +++++ overlord/src/app/sale/sale.component.ts | 196 +++ overlord/src/app/sale/sale.routes.ts | 17 + overlord/src/app/sale/sale.service.ts | 22 + overlord/src/app/sale/sale.ts | 17 + .../sales-import/sales-import.component.css | 23 + .../sales-import/sales-import.component.html | 110 +- .../sales-import/sales-import.component.ts | 15 +- .../src/app/settings/settings.component.ts | 23 +- overlord/src/app/shared/local-time.pipe.ts | 29 +- .../stock-movement.component.ts | 10 +- .../temporal-product-detail.component.html | 10 +- .../temporal-product-detail.component.ts | 39 +- .../trial-balance/trial-balance.component.ts | 6 +- overlord/yarn.lock | 1103 +++++++++-------- 102 files changed, 2121 insertions(+), 2103 deletions(-) create mode 100644 brewman/brewman/routers/sale.py create mode 100644 brewman/brewman/schemas/sale.py create mode 100644 overlord/src/app/core/date-utils.ts create mode 100644 overlord/src/app/sale/sale.component.css create mode 100644 overlord/src/app/sale/sale.component.html create mode 100644 overlord/src/app/sale/sale.component.ts create mode 100644 overlord/src/app/sale/sale.routes.ts create mode 100644 overlord/src/app/sale/sale.service.ts create mode 100644 overlord/src/app/sale/sale.ts diff --git a/brewman/brewman/main.py b/brewman/brewman/main.py index e0c6ba7c..2dee92f6 100644 --- a/brewman/brewman/main.py +++ b/brewman/brewman/main.py @@ -45,6 +45,7 @@ from .routers import ( rebase, recipe, role, + sale, sales_import, tag, temporal_product, @@ -165,6 +166,7 @@ app.include_router(credit_salary.router, prefix="/api/credit-salary", tags=["vou app.include_router(voucher.router, prefix="/api", tags=["vouchers"]) app.include_router(transaction_import.router, prefix="/api/transaction-import", tags=["vouchers"]) app.include_router(sales_import.router, prefix="/api/sales-import", tags=["vouchers"]) +app.include_router(sale.router, prefix="/api/sale", tags=["vouchers"]) app.include_router(lock_information.router, prefix="/api/lock-information", tags=["settings"]) app.include_router(maintenance.router, prefix="/api/maintenance", tags=["settings"]) diff --git a/brewman/brewman/routers/__init__.py b/brewman/brewman/routers/__init__.py index 2ba85871..bc7c217d 100644 --- a/brewman/brewman/routers/__init__.py +++ b/brewman/brewman/routers/__init__.py @@ -86,7 +86,7 @@ def effective_date(d: str | None = None) -> date: datetime.now(UTC).replace(tzinfo=None) + timedelta(minutes=330) # Adjust for IST (Indian Standard Time) ).date() if d is None - else datetime.strptime(d, "%d-%b-%Y").date() + else date.fromisoformat(d) ) diff --git a/brewman/brewman/routers/account.py b/brewman/brewman/routers/account.py index 7f80101d..9c402dc8 100644 --- a/brewman/brewman/routers/account.py +++ b/brewman/brewman/routers/account.py @@ -1,6 +1,6 @@ import uuid -from datetime import date, datetime +from datetime import date from decimal import Decimal from typing import Annotated @@ -142,10 +142,9 @@ async def show_balance( id_: uuid.UUID, user: Annotated[UserToken, Security(get_user)], db: SessionDep, - d: str | None = None, + d: date | None = None, ) -> AccountBalance: - date_ = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y") - return AccountBalance(date_=balance(id_, date_, db), total=balance(id_, None, db)) + return AccountBalance(date_=balance(id_, d, db), total=balance(id_, None, db)) @router.get("/{id_}", response_model=schemas.Account) diff --git a/brewman/brewman/routers/batch.py b/brewman/brewman/routers/batch.py index ccba7588..0f432daa 100644 --- a/brewman/brewman/routers/batch.py +++ b/brewman/brewman/routers/batch.py @@ -1,5 +1,4 @@ -import datetime - +from datetime import date from typing import Annotated from fastapi import APIRouter, Security @@ -24,18 +23,17 @@ router = APIRouter() @router.get("", response_model=list[schemas.Batch]) def batch_term( - q: str | None, d: str, user: Annotated[UserToken, Security(get_user)], db: SessionDep + q: str | None, d: date, user: Annotated[UserToken, Security(get_user)], db: SessionDep ) -> list[schemas.Batch]: - date_ = datetime.datetime.strptime(d, "%d-%b-%Y") list_: list[schemas.Batch] = [] query = ( select(Batch) .join(Batch.sku) .join(StockKeepingUnit.product) - .join(ProductVersion, onclause=_pv_onclause(date_)) - .join(SkuVersion, onclause=_sv_onclause(date_)) - .where(Batch.quantity_remaining > 0, Batch.name <= date_) + .join(ProductVersion, onclause=_pv_onclause(d)) + .join(SkuVersion, onclause=_sv_onclause(d)) + .where(Batch.quantity_remaining > 0, Batch.name <= d) .options( contains_eager(Batch.sku).contains_eager(StockKeepingUnit.product).contains_eager(Product.versions), contains_eager(Batch.sku).contains_eager(StockKeepingUnit.versions), diff --git a/brewman/brewman/routers/credit_salary.py b/brewman/brewman/routers/credit_salary.py index c0d67dfe..4eebc468 100644 --- a/brewman/brewman/routers/credit_salary.py +++ b/brewman/brewman/routers/credit_salary.py @@ -1,5 +1,5 @@ from calendar import monthrange -from datetime import date, datetime +from datetime import date from decimal import Decimal from typing import Annotated @@ -28,11 +28,10 @@ router = APIRouter() def credit_salary( user: Annotated[UserToken, Security(get_user, scopes=["attendance"])], db: SessionDep, - month: str = Body(..., embed=True), + month: date = Body(..., embed=True), ) -> Message: - month_ = datetime.strptime(month, "%d-%b-%Y").date() - start_date = get_first_day(month_) - finish_date = get_last_day(month_) + start_date = get_first_day(month) + finish_date = get_last_day(month) voucher = Voucher( date_=finish_date, narration="Auto Generated Salary Entry", diff --git a/brewman/brewman/routers/employee_attendance.py b/brewman/brewman/routers/employee_attendance.py index e9f5004e..03127987 100644 --- a/brewman/brewman/routers/employee_attendance.py +++ b/brewman/brewman/routers/employee_attendance.py @@ -82,7 +82,7 @@ def employee_attendance( prints, hours_worked, full_day = get_prints(employee.id, item, db) list_.append( schemas.EmployeeAttendanceItem( - date_=item.strftime("%d-%b-%Y"), # type: ignore + date_=item, attendance_type=AttendanceTypeSchema(id_=at_type), prints=prints, hours_worked=hours_worked, diff --git a/brewman/brewman/routers/fingerprint_report.py b/brewman/brewman/routers/fingerprint_report.py index 88f72b5b..dcfc302b 100644 --- a/brewman/brewman/routers/fingerprint_report.py +++ b/brewman/brewman/routers/fingerprint_report.py @@ -1,7 +1,7 @@ import csv import io -from datetime import date, datetime +from datetime import date from fastapi import APIRouter from fastapi.responses import StreamingResponse @@ -19,19 +19,14 @@ router = APIRouter() @router.get("", response_class=StreamingResponse) def get_report( - s: str, - f: str, + s: date, + f: date, # user: Annotated[UserToken, Security(get_user, scopes=["attendance"])] ## removed as jwt headers are a pain in the ass db: SessionDep, ) -> StreamingResponse: try: output = io.StringIO() - attendance_record( - datetime.strptime(s, "%d-%b-%Y"), - datetime.strptime(f, "%d-%b-%Y"), - output, - db, - ) + attendance_record(s, f, output, db) headers = {"Content-Disposition": "attachment; filename = fingerprint-record.csv"} output.seek(0) return StreamingResponse(output, media_type="text/csv", headers=headers) diff --git a/brewman/brewman/routers/rate_contract.py b/brewman/brewman/routers/rate_contract.py index ebc833e0..5341d8e5 100644 --- a/brewman/brewman/routers/rate_contract.py +++ b/brewman/brewman/routers/rate_contract.py @@ -45,11 +45,11 @@ async def save( ) db.add(item) add_items(item, data.items, db) - report_date(request, data.date_.strftime("%d-%b-%Y")) + report_date(request, data.date_) if data.valid_from is not None: - report_start_date(request, data.valid_from.strftime("%d-%b-%Y")) + report_start_date(request, data.valid_from) if data.valid_till is not None: - report_finish_date(request, data.valid_till.strftime("%d-%b-%Y")) + report_finish_date(request, data.valid_till) db.commit() return rate_contract_info(item, db) @@ -77,11 +77,11 @@ async def update_route( item.narration = data.narration item.user_id = user.id_ update_items(item, data.items, db) - report_date(request, data.date_.strftime("%d-%b-%Y")) + report_date(request, data.date_) if data.valid_from is not None: - report_start_date(request, data.valid_from.strftime("%d-%b-%Y")) + report_start_date(request, data.valid_from) if data.valid_till is not None: - report_finish_date(request, data.valid_till.strftime("%d-%b-%Y")) + report_finish_date(request, data.valid_till) db.commit() return rate_contract_info(item, db) diff --git a/brewman/brewman/routers/recipe.py b/brewman/brewman/routers/recipe.py index b6a633a0..8c6e1db8 100644 --- a/brewman/brewman/routers/recipe.py +++ b/brewman/brewman/routers/recipe.py @@ -732,10 +732,10 @@ def _parse_import_date(value: str) -> date: return datetime.strptime(value.strip(), fmt).date() except ValueError: continue - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail="Invalid date; use DD-MMM-YYYY or YYYY-MM-DD", - ) + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="Invalid date; use DD-MMM-YYYY or YYYY-MM-DD", + ) def _import_options(date_: date, create: str, sale_prices: bool, master_data: bool, tags: bool) -> ImportOptions: diff --git a/brewman/brewman/routers/reports/__init__.py b/brewman/brewman/routers/reports/__init__.py index f6313feb..8e70ef3c 100644 --- a/brewman/brewman/routers/reports/__init__.py +++ b/brewman/brewman/routers/reports/__init__.py @@ -1,4 +1,4 @@ -from datetime import date, datetime, timedelta +from datetime import date, timedelta from fastapi import Request @@ -6,40 +6,43 @@ from fastapi import Request def report_start_date(request: Request, s: str | date | None = None) -> date: session = request.session if isinstance(s, date): - session["start"] = s.strftime("%d-%b-%Y") + session["start"] = s.isoformat() return s if s: - session["start"] = s - return datetime.strptime(s, "%d-%b-%Y").date() + d = date.fromisoformat(s) + session["start"] = d.isoformat() + return d if "start" not in session: - session["start"] = get_first_day(date.today()).strftime("%d-%b-%Y") - return datetime.strptime(session["start"], "%d-%b-%Y").date() + session["start"] = get_first_day(date.today()).isoformat() + return date.fromisoformat(session["start"]) def report_finish_date(request: Request, f: str | date | None = None) -> date: session = request.session if isinstance(f, date): - session["finish"] = f.strftime("%d-%b-%Y") + session["finish"] = f.isoformat() return f if f: - session["finish"] = f - return datetime.strptime(f, "%d-%b-%Y").date() + d = date.fromisoformat(f) + session["finish"] = d.isoformat() + return d if "finish" not in session: - session["finish"] = get_last_day(date.today()).strftime("%d-%b-%Y") - return datetime.strptime(session["finish"], "%d-%b-%Y").date() + session["finish"] = get_last_day(date.today()).isoformat() + return date.fromisoformat(session["finish"]) def report_date(request: Request, d: str | date | None = None) -> date: session = request.session if isinstance(d, date): - session["date"] = d.strftime("%d-%b-%Y") + session["date"] = d.isoformat() return d if d: - session["date"] = d - return datetime.strptime(d, "%d-%b-%Y").date() + parsed_date = date.fromisoformat(d) + session["date"] = parsed_date.isoformat() + return parsed_date if "date" not in session: - session["date"] = get_last_day(date.today()).strftime("%d-%b-%Y") - return datetime.strptime(session["date"], "%d-%b-%Y").date() + session["date"] = get_last_day(date.today()).isoformat() + return date.fromisoformat(session["date"]) def get_first_day(dt: date, d_years: int = 0, d_months: int = 0) -> date: @@ -56,20 +59,23 @@ def get_last_day(dt: date) -> date: def op_report_start_date(request: Request, s: str | None = None) -> date | None: session = request.session if s: - session["start"] = s - return datetime.strptime(s, "%d-%b-%Y").date() + d = date.fromisoformat(s) + session["start"] = d.isoformat() + return d return None def op_report_finish_date(request: Request, f: str | None = None) -> date | None: session = request.session if f: - session["finish"] = f - return datetime.strptime(f, "%d-%b-%Y").date() + d = date.fromisoformat(f) + session["finish"] = d.isoformat() + return d return None def path_date(request: Request, date_: str) -> date: session = request.session - session["date"] = date_ - return datetime.strptime(date_, "%d-%b-%Y").date() + d = date.fromisoformat(date_) + session["date"] = d.isoformat() + return d diff --git a/brewman/brewman/routers/reports/product_ledger.py b/brewman/brewman/routers/reports/product_ledger.py index 75b18f12..23c708a4 100644 --- a/brewman/brewman/routers/reports/product_ledger.py +++ b/brewman/brewman/routers/reports/product_ledger.py @@ -102,11 +102,11 @@ def build_report( ) for voucher, inventory, journal, stockKeepingUnit in query: + # We are tracking at purchase cost centre, but the journal is of the cost centre so we reverse the debit direction. journal_debit = journal.debit * -1 if voucher.voucher_type == VoucherType.SALE: - print(journal_debit, " pre") + # Else sale will be seen as a debit since journal_debit = -1 * journal_debit - print(journal_debit) name = ( journal.cost_centre.name if voucher.voucher_type in [VoucherType.ISSUE, VoucherType.CLOSING_STOCK, VoucherType.SALE] @@ -130,9 +130,7 @@ def build_report( url=[ "/", voucher.voucher_type.name.replace("_", "-").lower(), - str(voucher.id) - if voucher.voucher_type != VoucherType.CLOSING_STOCK - else voucher.date_.strftime("%d-%b-%Y"), + str(voucher.id) if voucher.voucher_type != VoucherType.CLOSING_STOCK else voucher.date_.isoformat(), ], type_=voucher.voucher_type.name.replace("_", " ").title(), narration=voucher.narration, diff --git a/brewman/brewman/routers/reports/trial_balance.py b/brewman/brewman/routers/reports/trial_balance.py index 0636c08c..5fccc2ce 100644 --- a/brewman/brewman/routers/reports/trial_balance.py +++ b/brewman/brewman/routers/reports/trial_balance.py @@ -35,7 +35,7 @@ def report_data( user: Annotated[UserToken, Security(get_user, scopes=["trial-balance"])], db: SessionDep, ) -> schemas.TrialBalance: - report_finish_date(request, date_.strftime("%d-%b-%Y")) + report_finish_date(request, date_) return schemas.TrialBalance( date_=date_, body=build_report(date_, db), diff --git a/brewman/brewman/routers/sale.py b/brewman/brewman/routers/sale.py new file mode 100644 index 00000000..09059b9b --- /dev/null +++ b/brewman/brewman/routers/sale.py @@ -0,0 +1,83 @@ +import uuid + +from datetime import date +from decimal import Decimal +from typing import Annotated + +from fastapi import APIRouter, Depends, HTTPException, Security, status +from sqlalchemy import select + +from ..core.security import get_current_active_user as get_user +from ..db.session import SessionDep +from ..models.voucher import Voucher +from ..models.voucher_type import VoucherType +from ..schemas import sale as schemas +from ..schemas import voucher as output +from ..schemas.user import UserToken +from .reports import report_date +from .voucher import voucher_info + + +router = APIRouter() + + +def compute_total_amount(vouchers: list[output.Voucher]) -> Decimal: + total = Decimal(0) + for v in vouchers: + for inv in v.inventories: + if inv.amount is not None: + total += inv.amount + return total + + +@router.get("/{id_}", response_model=schemas.SaleDayResponse) +def get_sale_day_by_id( + id_: uuid.UUID, + user: Annotated[UserToken, Security(get_user)], + db: SessionDep, +) -> schemas.SaleDayResponse: + voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one_or_none() + if voucher is None or voucher.voucher_type != VoucherType.SALE: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sale voucher not found") + vouchers = ( + db.execute( + select(Voucher) + .where(Voucher.date_ == voucher.date_, Voucher.voucher_type == VoucherType.SALE) + .order_by(Voucher.creation_date) + ) + .scalars() + .unique() + .all() + ) + voucher_infos = [voucher_info(v, db) for v in vouchers] + return schemas.SaleDayResponse( + date_=voucher.date_, + selected_voucher_id=id_, + total_amount=compute_total_amount(voucher_infos), + vouchers=voucher_infos, + ) + + +@router.get("", response_model=schemas.SaleDayResponse) +def get_sale_day_by_date( + d: Annotated[date, Depends(report_date)], + user: Annotated[UserToken, Security(get_user)], + db: SessionDep, +) -> schemas.SaleDayResponse: + vouchers = ( + db.execute( + select(Voucher) + .where(Voucher.date_ == d, Voucher.voucher_type == VoucherType.SALE) + .order_by(Voucher.creation_date) + ) + .scalars() + .unique() + .all() + ) + voucher_infos = [voucher_info(v, db) for v in vouchers] + return schemas.SaleDayResponse( + date_=d, + selected_voucher_id=None, + total_amount=compute_total_amount(voucher_infos), + vouchers=voucher_infos, + ) diff --git a/brewman/brewman/schemas/attendance.py b/brewman/brewman/schemas/attendance.py index 86deb026..f66474a3 100644 --- a/brewman/brewman/schemas/attendance.py +++ b/brewman/brewman/schemas/attendance.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel from .attendance_type import AttendanceType @@ -31,14 +25,3 @@ class Attendance(BaseModel): date_: date | None = None body: list[AttendanceItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/balance_sheet.py b/brewman/brewman/schemas/balance_sheet.py index 5aa0f4ed..b1180e59 100644 --- a/brewman/brewman/schemas/balance_sheet.py +++ b/brewman/brewman/schemas/balance_sheet.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -21,18 +15,7 @@ class BalanceSheetItem(BaseModel): class BalanceSheet(BaseModel): - date_: date | str + date_: date body: list[BalanceSheetItem] footer: BalanceSheetItem | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/batch_integrity.py b/brewman/brewman/schemas/batch_integrity.py index 891d91e4..66b4daf4 100644 --- a/brewman/brewman/schemas/batch_integrity.py +++ b/brewman/brewman/schemas/batch_integrity.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -21,17 +15,6 @@ class BatchIntegrityItem(BaseModel): price: Daf model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class BatchIntegrity(BaseModel): id_: uuid.UUID @@ -42,14 +25,3 @@ class BatchIntegrity(BaseModel): actual: Daf details: list[BatchIntegrityItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/blank_voucher_info.py b/brewman/brewman/schemas/blank_voucher_info.py index 73ebf695..9984a241 100644 --- a/brewman/brewman/schemas/blank_voucher_info.py +++ b/brewman/brewman/schemas/blank_voucher_info.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from ..models.voucher_type import VoucherType from . import to_camel @@ -21,14 +15,3 @@ class BlankVoucherInfo(BaseModel): source: CostCentreLink | None = None destination: CostCentreLink | None = None model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/cash_flow.py b/brewman/brewman/schemas/cash_flow.py index fe1e1a35..302bd883 100644 --- a/brewman/brewman/schemas/cash_flow.py +++ b/brewman/brewman/schemas/cash_flow.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -27,30 +21,8 @@ class CashFlowBody(BaseModel): class CashFlow(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: CashFlowBody footer: list[CashFlowItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/client.py b/brewman/brewman/schemas/client.py index 70720d06..6ffc044b 100644 --- a/brewman/brewman/schemas/client.py +++ b/brewman/brewman/schemas/client.py @@ -2,13 +2,7 @@ import uuid from datetime import datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel @@ -25,32 +19,8 @@ class Client(ClientIn): creation_date: datetime model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("creation_date", mode="before") - @classmethod - def parse_creation_date(cls, value: datetime | str) -> datetime | None: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("creation_date") - def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") - class ClientList(Client): last_user: str last_date: datetime | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("last_date", mode="before") - @classmethod - def parse_last_date(cls, value: datetime | str | None) -> datetime | None: - if value is None or value == "": - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_date") - def serialize_last_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/closing_stock.py b/brewman/brewman/schemas/closing_stock.py index 0e594d08..aab3fc2f 100644 --- a/brewman/brewman/schemas/closing_stock.py +++ b/brewman/brewman/schemas/closing_stock.py @@ -3,13 +3,7 @@ import uuid from datetime import date, datetime from decimal import Decimal -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict, field_validator from . import Daf, to_camel from .cost_centre import CostCentreLink @@ -59,40 +53,3 @@ class ClosingStock(BaseModel): posted: bool poster: UserLink | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("creation_date", mode="before") - @classmethod - def parse_creation_date(cls, value: None | datetime | str) -> datetime | None: - if value is None: - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("creation_date") - def serialize_creation_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") - - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: - if value is None: - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/daybook.py b/brewman/brewman/schemas/daybook.py index 717a8c29..a64b0fad 100644 --- a/brewman/brewman/schemas/daybook.py +++ b/brewman/brewman/schemas/daybook.py @@ -1,21 +1,15 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel class DaybookItem(BaseModel): id_: uuid.UUID | None = None - date_: date | str + date_: date url: list[str] type_: str narration: str @@ -26,42 +20,9 @@ class DaybookItem(BaseModel): posted: bool model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class Daybook(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[DaybookItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/employee.py b/brewman/brewman/schemas/employee.py index cfda1d29..44128070 100644 --- a/brewman/brewman/schemas/employee.py +++ b/brewman/brewman/schemas/employee.py @@ -1,14 +1,11 @@ import uuid -from datetime import date, datetime +from datetime import date from pydantic import ( BaseModel, ConfigDict, Field, - FieldSerializationInfo, - field_serializer, - field_validator, model_validator, ) @@ -25,30 +22,6 @@ class EmployeeIn(AccountBase): leaving_date: date | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("joining_date", mode="before") - @classmethod - def parse_joining_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("joining_date") - def serialize_joining_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("leaving_date", mode="before") - @classmethod - def parse_leaving_date(cls, value: None | date | str) -> date | None: - if isinstance(value, date): - return value - if value is None: - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("leaving_date") - def serialize_leaving_date(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - @model_validator(mode="after") def leaving_date_more_than_joining_date(self) -> EmployeeIn: if self.is_active: diff --git a/brewman/brewman/schemas/employee_attendance.py b/brewman/brewman/schemas/employee_attendance.py index ab6bcdac..e9aa9e78 100644 --- a/brewman/brewman/schemas/employee_attendance.py +++ b/brewman/brewman/schemas/employee_attendance.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel from .account import AccountLink @@ -21,17 +15,6 @@ class EmployeeAttendanceItem(BaseModel): full_day: bool | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class EmployeeAttendance(BaseModel): start_date: date | None = None @@ -39,29 +22,3 @@ class EmployeeAttendance(BaseModel): employee: AccountLink | None = None body: list[EmployeeAttendanceItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/entries.py b/brewman/brewman/schemas/entries.py index 47a6bbdd..a6e8589a 100644 --- a/brewman/brewman/schemas/entries.py +++ b/brewman/brewman/schemas/entries.py @@ -2,13 +2,7 @@ import uuid from datetime import date, datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .user_link import UserLink @@ -29,39 +23,6 @@ class Entries(BaseModel): user: UserLink model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("creation_date", mode="before") - @classmethod - def parse_creation_date(cls, value: datetime | str) -> datetime: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("creation_date") - def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") - - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: datetime | str) -> datetime: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") - class Report(BaseModel): counts: int diff --git a/brewman/brewman/schemas/fingerprint.py b/brewman/brewman/schemas/fingerprint.py index 1af2d1f8..42f00158 100644 --- a/brewman/brewman/schemas/fingerprint.py +++ b/brewman/brewman/schemas/fingerprint.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel @@ -18,14 +12,3 @@ class Fingerprint(BaseModel): employee_id: uuid.UUID date_: datetime model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: datetime | str) -> datetime: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/input.py b/brewman/brewman/schemas/input.py index bc582239..ec5ea65f 100644 --- a/brewman/brewman/schemas/input.py +++ b/brewman/brewman/schemas/input.py @@ -1,10 +1,10 @@ import json import uuid -from datetime import date, datetime +from typing import Self from fastapi import Form -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, model_validator from . import Daf, to_camel from .account import AccountLink @@ -20,17 +20,10 @@ class JournalIn(VoucherIn): journals: list[Journal] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class PurchaseIn(VoucherIn): @@ -38,17 +31,10 @@ class PurchaseIn(VoucherIn): inventories: list[Inventory] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class IssueIn(VoucherIn): @@ -58,49 +44,35 @@ class IssueIn(VoucherIn): model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) @model_validator(mode="after") - def source_destination_unique(self) -> IssueIn: + def source_destination_unique(self) -> Self: if self.source.id_ == self.destination.id_: raise ValueError("Source and destination cannot be the same") return self @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class EmployeeBenefitIn(VoucherIn): employee_benefits: list[EmployeeBenefit] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class IncentiveIn(VoucherIn): incentives: list[Incentive] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class IncentiveEmployee(BaseModel): diff --git a/brewman/brewman/schemas/ledger.py b/brewman/brewman/schemas/ledger.py index 3afdd049..9cf0601e 100644 --- a/brewman/brewman/schemas/ledger.py +++ b/brewman/brewman/schemas/ledger.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .account import AccountLink @@ -16,7 +10,7 @@ from .account import AccountLink class LedgerItem(BaseModel): id_: uuid.UUID | None = None - date_: date | str + date_: date name: str url: list[str] type_: str @@ -27,43 +21,10 @@ class LedgerItem(BaseModel): tags: list[str] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class Ledger(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date account: AccountLink | None = None body: list[LedgerItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/mozimo_daily_register.py b/brewman/brewman/schemas/mozimo_daily_register.py index b69ce81c..ed5ea40f 100644 --- a/brewman/brewman/schemas/mozimo_daily_register.py +++ b/brewman/brewman/schemas/mozimo_daily_register.py @@ -2,13 +2,7 @@ import uuid from datetime import date, datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .product import ProductLink @@ -26,32 +20,8 @@ class MozimoDailyRegisterItem(BaseModel): last_edit_date: datetime | None = None model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: - if value is None or value == "": - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") - class MozimoDailyRegister(BaseModel): - date_: date | str + date_: date body: list[MozimoDailyRegisterItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/mozimo_product_register.py b/brewman/brewman/schemas/mozimo_product_register.py index 2dcd5116..a8a4e8ca 100644 --- a/brewman/brewman/schemas/mozimo_product_register.py +++ b/brewman/brewman/schemas/mozimo_product_register.py @@ -2,13 +2,7 @@ import uuid from datetime import date, datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .product import ProductLink @@ -26,30 +20,6 @@ class MozimoProductRegisterItem(BaseModel): last_edit_date: datetime | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: - if value is None or value == "": - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") - class MozimoProductRegister(BaseModel): start_date: date @@ -57,25 +27,3 @@ class MozimoProductRegister(BaseModel): product: ProductLink | None = None body: list[MozimoProductRegisterItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/net_transactions.py b/brewman/brewman/schemas/net_transactions.py index 485e5e93..8fc87e8b 100644 --- a/brewman/brewman/schemas/net_transactions.py +++ b/brewman/brewman/schemas/net_transactions.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -20,29 +14,7 @@ class NetTransactionsItem(BaseModel): class NetTransactions(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[NetTransactionsItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/non_contract_purchase.py b/brewman/brewman/schemas/non_contract_purchase.py index aee4461d..00241e31 100644 --- a/brewman/brewman/schemas/non_contract_purchase.py +++ b/brewman/brewman/schemas/non_contract_purchase.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -19,14 +13,3 @@ class NonContractPurchase(BaseModel): contract_price: Daf purchase_price: Daf model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/period.py b/brewman/brewman/schemas/period.py index 012267c9..44ec577a 100644 --- a/brewman/brewman/schemas/period.py +++ b/brewman/brewman/schemas/period.py @@ -1,15 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - Field, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict, Field from . import to_camel @@ -19,28 +12,6 @@ class PeriodIn(BaseModel): valid_till: date model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("valid_from", mode="before") - @classmethod - def parse_valid_from(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_from") - def serialize_valid_from(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("valid_till", mode="before") - @classmethod - def parse_valid_till(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_till") - def serialize_valid_till(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class Period(PeriodIn): id_: uuid.UUID diff --git a/brewman/brewman/schemas/product_ledger.py b/brewman/brewman/schemas/product_ledger.py index a26e2d80..3647c4d7 100644 --- a/brewman/brewman/schemas/product_ledger.py +++ b/brewman/brewman/schemas/product_ledger.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .product import ProductLink @@ -32,43 +26,10 @@ class ProductLedgerItem(BaseModel): posted: bool model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - class ProductLedger(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date product: ProductLink | None = None body: list[ProductLedgerItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/profit_loss.py b/brewman/brewman/schemas/profit_loss.py index 7c81dcda..f2b1a2cc 100644 --- a/brewman/brewman/schemas/profit_loss.py +++ b/brewman/brewman/schemas/profit_loss.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -26,25 +20,3 @@ class ProfitLoss(BaseModel): body: list[ProfitLossItem] footer: ProfitLossItem model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/purchase_entries.py b/brewman/brewman/schemas/purchase_entries.py index 8b15b8ba..51818fd9 100644 --- a/brewman/brewman/schemas/purchase_entries.py +++ b/brewman/brewman/schemas/purchase_entries.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -21,41 +15,11 @@ class PurchaseEntriesItem(BaseModel): tax: Daf discount: Daf amount: Daf - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) class PurchaseEntries(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[PurchaseEntriesItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/purchases.py b/brewman/brewman/schemas/purchases.py index 0cc4b633..6687a59e 100644 --- a/brewman/brewman/schemas/purchases.py +++ b/brewman/brewman/schemas/purchases.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -21,30 +15,8 @@ class PurchasesItem(BaseModel): class Purchases(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[PurchasesItem] footer: PurchasesItem model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/rate_contract.py b/brewman/brewman/schemas/rate_contract.py index e6ad0080..5007105d 100644 --- a/brewman/brewman/schemas/rate_contract.py +++ b/brewman/brewman/schemas/rate_contract.py @@ -2,13 +2,7 @@ import uuid from datetime import date, datetime -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel from .account import AccountLink @@ -18,61 +12,17 @@ from .user_link import UserLink class RateContractBlank(BaseModel): date_: date - valid_from: date | None - valid_till: date | None + valid_from: date | None = None + valid_till: date | None = None narration: str items: list[RateContractItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("valid_from", mode="before") - @classmethod - def parse_valid_from(cls, value: date | str | None) -> date | None: - if value is None: - return value - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_from") - def serialize_valid_from(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("valid_till", mode="before") - @classmethod - def parse_valid_till(cls, value: date | str | None) -> date | None: - if value is None: - return value - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_till") - def serialize_valid_till(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class RateContractIn(RateContractBlank): vendor: AccountLink model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - class RateContract(RateContractIn): id_: uuid.UUID @@ -80,25 +30,3 @@ class RateContract(RateContractIn): last_edit_date: datetime user: UserLink model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("creation_date", mode="before") - @classmethod - def parse_creation_date(cls, value: datetime | str) -> datetime | None: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("creation_date") - def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") - - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: datetime | str) -> datetime | None: - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/raw_material_cost.py b/brewman/brewman/schemas/raw_material_cost.py index 366cddb1..a09b77a6 100644 --- a/brewman/brewman/schemas/raw_material_cost.py +++ b/brewman/brewman/schemas/raw_material_cost.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -32,30 +26,8 @@ class RawMaterialCostItem(BaseModel): class RawMaterialCost(BaseModel): id_: uuid.UUID | None = None - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[RawMaterialCostItem] footer: RawMaterialCostItem | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/recipe.py b/brewman/brewman/schemas/recipe.py index 231cec2a..ded56be4 100644 --- a/brewman/brewman/schemas/recipe.py +++ b/brewman/brewman/schemas/recipe.py @@ -1,6 +1,8 @@ +import json import uuid -from datetime import date, datetime +from datetime import date +from typing import Self from fastapi import Form from pydantic import ( @@ -8,9 +10,6 @@ from pydantic import ( BaseModel, ConfigDict, Field, - FieldSerializationInfo, - field_serializer, - field_validator, ) from . import Daf, to_camel @@ -44,23 +43,9 @@ class RecipeIn(BaseModel): model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: - import json - + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - # pydantic v2 return cls.model_validate(json_data) diff --git a/brewman/brewman/schemas/reconcile.py b/brewman/brewman/schemas/reconcile.py index 394b9a78..35331c6f 100644 --- a/brewman/brewman/schemas/reconcile.py +++ b/brewman/brewman/schemas/reconcile.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .account import AccountLink @@ -28,56 +22,10 @@ class ReconcileItem(BaseModel): reconcile_date: date | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("reconcile_date", mode="before") - @classmethod - def parse_reconcile_date(cls, value: None | date | str) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("reconcile_date") - def serialize_reconcile_date(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class Reconcile(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date account: AccountLink | None = None body: list[ReconcileItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/sale.py b/brewman/brewman/schemas/sale.py new file mode 100644 index 00000000..6f649a5a --- /dev/null +++ b/brewman/brewman/schemas/sale.py @@ -0,0 +1,18 @@ +import uuid + +from datetime import date +from decimal import Decimal + +from pydantic import BaseModel, ConfigDict + +from . import to_camel +from .voucher import Voucher + + +class SaleDayResponse(BaseModel): + date_: date + selected_voucher_id: uuid.UUID | None = None + total_amount: Decimal + vouchers: list[Voucher] + + model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) diff --git a/brewman/brewman/schemas/settings.py b/brewman/brewman/schemas/settings.py index a881fe4d..d16c39dd 100644 --- a/brewman/brewman/schemas/settings.py +++ b/brewman/brewman/schemas/settings.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import to_camel @@ -18,19 +12,6 @@ class LockDate(BaseModel): date_: date | None = None model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: None | date | str) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class VoucherTypesSelected(BaseModel): id_: int @@ -59,32 +40,6 @@ class LockInformation(BaseModel): index: int model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - @field_validator("valid_from", mode="before") - @classmethod - def parse_valid_from(cls, value: date | str | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_from") - def serialize_valid_from(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("valid_till", mode="before") - @classmethod - def parse_valid_till(cls, value: date | str | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_till") - def serialize_valid_till(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class Maintenance(BaseModel): enabled: bool diff --git a/brewman/brewman/schemas/stock_movement.py b/brewman/brewman/schemas/stock_movement.py index 4194d0b6..eb195bc4 100644 --- a/brewman/brewman/schemas/stock_movement.py +++ b/brewman/brewman/schemas/stock_movement.py @@ -1,14 +1,8 @@ import uuid -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -26,29 +20,7 @@ class StockMovementItem(BaseModel): class StockMovement(BaseModel): - start_date: date | str - finish_date: date | str + start_date: date + finish_date: date body: list[StockMovementItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("start_date", mode="before") - @classmethod - def parse_start_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("start_date") - def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") - - @field_validator("finish_date", mode="before") - @classmethod - def parse_finish_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("finish_date") - def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/temporal_product.py b/brewman/brewman/schemas/temporal_product.py index f681c412..13f4a51e 100644 --- a/brewman/brewman/schemas/temporal_product.py +++ b/brewman/brewman/schemas/temporal_product.py @@ -1,10 +1,10 @@ import uuid -from datetime import date, datetime +from datetime import date from decimal import Decimal from typing import Annotated -from pydantic import BaseModel, ConfigDict, Field, SerializationInfo, field_serializer, field_validator +from pydantic import BaseModel, ConfigDict, Field from . import Daf, to_camel from .product_group import ProductGroupLink @@ -43,32 +43,6 @@ class Product(BaseModel): valid_till: date | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("valid_from", mode="before") - @classmethod - def parse_valid_from(cls, value: str | date | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_from") - def serialize_valid_from(self, value: date | None, _info: SerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("valid_till", mode="before") - @classmethod - def parse_valid_till(cls, value: str | date | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_till") - def serialize_valid_till(self, value: date | None, _info: SerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class StockKeepingUnit(BaseModel): id_: uuid.UUID @@ -83,32 +57,6 @@ class StockKeepingUnit(BaseModel): valid_till: date | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("valid_from", mode="before") - @classmethod - def parse_valid_from(cls, value: str | date | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_from") - def serialize_valid_from(self, value: date | None, _info: SerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("valid_till", mode="before") - @classmethod - def parse_valid_till(cls, value: str | date | None) -> date | None: - if value is None: - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("valid_till") - def serialize_valid_till(self, value: date | None, _info: SerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - class TemporalProduct(BaseModel): products: list[Product] diff --git a/brewman/brewman/schemas/trial_balance.py b/brewman/brewman/schemas/trial_balance.py index a548a39d..3af74c14 100644 --- a/brewman/brewman/schemas/trial_balance.py +++ b/brewman/brewman/schemas/trial_balance.py @@ -1,12 +1,6 @@ -from datetime import date, datetime +from datetime import date -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel @@ -20,17 +14,6 @@ class TrialBalanceItem(BaseModel): class TrialBalance(BaseModel): - date_: date | str + date_: date body: list[TrialBalanceItem] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") diff --git a/brewman/brewman/schemas/user.py b/brewman/brewman/schemas/user.py index e0867021..d49d227a 100644 --- a/brewman/brewman/schemas/user.py +++ b/brewman/brewman/schemas/user.py @@ -2,14 +2,7 @@ import uuid from datetime import datetime -from pydantic import ( - BaseModel, - ConfigDict, - Field, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict, Field from . import to_camel from .role import RoleItem @@ -37,19 +30,6 @@ class UserList(BaseModel): last_date: datetime | None = None model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - @field_validator("last_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: datetime | str | None) -> datetime | None: - if value is None: - return value - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") - class UserToken(BaseModel): id_: uuid.UUID diff --git a/brewman/brewman/schemas/voucher.py b/brewman/brewman/schemas/voucher.py index 89dcc74d..374222de 100644 --- a/brewman/brewman/schemas/voucher.py +++ b/brewman/brewman/schemas/voucher.py @@ -2,15 +2,10 @@ import json import uuid from datetime import date, datetime +from typing import Self from fastapi import Form -from pydantic import ( - BaseModel, - ConfigDict, - FieldSerializationInfo, - field_serializer, - field_validator, -) +from pydantic import BaseModel, ConfigDict from . import Daf, to_camel from .account import AccountLink @@ -34,22 +29,12 @@ class VoucherIn(BaseModel): type_: str files: list[ImageUpload] tags: list[Tag] - - @field_validator("date_", mode="before") - @classmethod - def parse_date(cls, value: date | str) -> date: - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("date_") - def serialize_date(self, value: date, info: FieldSerializationInfo) -> str: - return value.strftime("%d-%b-%Y") + model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) @classmethod - def load_form(cls, data: str = Form(...)) -> BaseModel: + def load_form(cls, data: str = Form(...)) -> Self: json_data = json.loads(data) - return cls.parse_obj(json_data) + return cls.model_validate(json_data) class Voucher(VoucherIn): @@ -71,42 +56,3 @@ class Voucher(VoucherIn): employee_benefits: list[EmployeeBenefit] files: list[ImageUpload] model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True) - - @field_validator("reconcile_date", mode="before") - @classmethod - def parse_reconcile_date(cls, value: None | date | str) -> date | None: - if value is None or value == "": - return None - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - @field_serializer("reconcile_date") - def serialize_reconcile_date(self, value: date | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y") - - @field_validator("creation_date", mode="before") - @classmethod - def parse_creation_date(cls, value: None | datetime | str) -> datetime | None: - if value is None or value == "": - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("creation_date") - def serialize_creation_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") - - @field_validator("last_edit_date", mode="before") - @classmethod - def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: - if value is None or value == "": - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%d-%b-%Y %H:%M") - - @field_serializer("last_edit_date") - def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str | None: - return None if value is None else value.strftime("%d-%b-%Y %H:%M") diff --git a/overlord/package.json b/overlord/package.json index 5a3bdf88..658b10fa 100644 --- a/overlord/package.json +++ b/overlord/package.json @@ -14,21 +14,19 @@ }, "private": true, "dependencies": { - "@angular/animations": "^22.1.2", "@angular/cdk": "^22.1.2", "@angular/common": "^22.1.2", "@angular/compiler": "^22.1.2", "@angular/core": "^22.1.2", "@angular/forms": "^22.1.2", "@angular/material": "^22.1.2", - "@angular/material-moment-adapter": "^22.1.2", + "@angular/material-date-fns-adapter": "^22.1.2", "@angular/platform-browser": "^22.1.2", - "@angular/platform-browser-dynamic": "^22.1.2", "@angular/router": "^22.1.2", + "date-fns": "^4.4.0", "html2canvas": "^1.4.1", "jspdf": "^4.2.0", "mathjs": "^15.1.1", - "moment": "^2.30.1", "rxjs": "~7.8.0", "tslib": "^2.6.0" }, diff --git a/overlord/src/app/app.config.ts b/overlord/src/app/app.config.ts index 6b51cce5..13ebd90c 100644 --- a/overlord/src/app/app.config.ts +++ b/overlord/src/app/app.config.ts @@ -6,11 +6,12 @@ import { LOCALE_ID, provideZonelessChangeDetection, } from '@angular/core'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; -import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; +import { provideDateFnsAdapter } from '@angular/material-date-fns-adapter'; +import { MAT_DATE_LOCALE } from '@angular/material/core'; import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; import { BrowserModule } from '@angular/platform-browser'; import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { enIN } from 'date-fns/locale'; import { dateFormat } from './app.environment'; import { routes } from './app.routes'; @@ -24,6 +25,7 @@ export const appConfig: ApplicationConfig = { providers: [ importProvidersFrom(BrowserModule), { provide: LOCALE_ID, useValue: 'en-IN' }, + { provide: MAT_DATE_LOCALE, useValue: enIN }, AuthService, provideHttpClient( withInterceptors([ @@ -34,8 +36,7 @@ export const appConfig: ApplicationConfig = { ]), ), provideRouter(routes, withComponentInputBinding()), - { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, - { provide: MAT_DATE_FORMATS, useValue: dateFormat }, + provideDateFnsAdapter(dateFormat), { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 3000 } }, provideZonelessChangeDetection(), ], diff --git a/overlord/src/app/app.environment.ts b/overlord/src/app/app.environment.ts index 575175ae..c32a51d2 100644 --- a/overlord/src/app/app.environment.ts +++ b/overlord/src/app/app.environment.ts @@ -6,12 +6,12 @@ export const environment = { export const dateFormat = { parse: { - dateInput: 'DD-MMM-YYYY', + dateInput: ['dd-MMM-yyyy', 'dd/MM/yyyy', 'dd-MM-yyyy', 'yyyy-MM-dd'], }, display: { - dateInput: 'DD-MMM-YYYY', - monthYearLabel: 'MMM YYYY', - dateA11yLabel: 'DD-MMM-YYYY', - monthYearA11yLabel: 'MMM YYYY', + dateInput: 'dd-MMM-yyyy', + monthYearLabel: 'MMM yyyy', + dateA11yLabel: 'dd-MMM-yyyy', + monthYearA11yLabel: 'MMMM yyyy', }, }; diff --git a/overlord/src/app/app.routes.ts b/overlord/src/app/app.routes.ts index 5ba487ee..10c42f01 100644 --- a/overlord/src/app/app.routes.ts +++ b/overlord/src/app/app.routes.ts @@ -153,6 +153,10 @@ export const routes: Routes = [ path: 'sales-import', loadChildren: () => import('./sales-import/sales-import.routes').then((mod) => mod.routes), }, + { + path: 'sale', + loadChildren: () => import('./sale/sale.routes').then((mod) => mod.routes), + }, { path: 'settings', loadChildren: () => import('./settings/settings.routes').then((mod) => mod.routes), diff --git a/overlord/src/app/attendance/attendance.component.ts b/overlord/src/app/attendance/attendance.component.ts index 2cb01ce2..770fad24 100644 --- a/overlord/src/app/attendance/attendance.component.ts +++ b/overlord/src/app/attendance/attendance.component.ts @@ -12,9 +12,9 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { Attendance } from './attendance'; @@ -66,7 +66,7 @@ export class AttendanceComponent { model = linkedSignal({ source: this.info, computation: (infoVal): AttendanceFormData => ({ - date: moment(infoVal.date, 'DD-MMM-YYYY').toDate(), + date: parseDate(infoVal.date), attendances: infoVal.body.map((x) => ({ attendanceType: x.attendanceType.id, })), @@ -98,7 +98,7 @@ export class AttendanceComponent { } show() { - const date = moment(this.model().date).format('DD-MMM-YYYY'); + const date = formatIsoDate(this.model().date); if (this.date() === date) { this.infoResource.reload(); } else { @@ -120,7 +120,7 @@ export class AttendanceComponent { getAttendance(): Attendance { const formModel = this.model(); const currentInfo = this.info(); - currentInfo.date = moment(formModel.date).format('DD-MMM-YYYY'); + currentInfo.date = formatIsoDate(formModel.date); const array = formModel.attendances; if (array) { currentInfo.body.forEach((item, index) => { diff --git a/overlord/src/app/balance-sheet/balance-sheet.component.ts b/overlord/src/app/balance-sheet/balance-sheet.component.ts index 4a6b1b14..1cd1cbcf 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.component.ts +++ b/overlord/src/app/balance-sheet/balance-sheet.component.ts @@ -12,8 +12,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ClearPipe } from '../shared/clear.pipe'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -58,7 +58,7 @@ export class BalanceSheetComponent { formModel = linkedSignal({ source: this.info, computation: (info: BalanceSheet): BalanceSheetFormData => ({ - date: info.date ? moment(info.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(info.date), }), }); @@ -121,7 +121,7 @@ export class BalanceSheetComponent { getDate(): string { const formModel = this.formModel(); - return moment(formModel.date).format('DD-MMM-YYYY'); + return formatIsoDate(formModel.date); } handlePageEvent(e: PageEvent) { diff --git a/overlord/src/app/cash-flow/cash-flow.component.html b/overlord/src/app/cash-flow/cash-flow.component.html index 41332dfe..ad5d98d3 100644 --- a/overlord/src/app/cash-flow/cash-flow.component.html +++ b/overlord/src/app/cash-flow/cash-flow.component.html @@ -4,9 +4,9 @@
Start Date - - - + + + Finish Date diff --git a/overlord/src/app/cash-flow/cash-flow.component.ts b/overlord/src/app/cash-flow/cash-flow.component.ts index cb34de88..b6d1b94c 100644 --- a/overlord/src/app/cash-flow/cash-flow.component.ts +++ b/overlord/src/app/cash-flow/cash-flow.component.ts @@ -12,8 +12,8 @@ import { MatInputModule } from '@angular/material/input'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ClearPipe } from '../shared/clear.pipe'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -78,11 +78,11 @@ export class CashFlowComponent { dataSource = computed(() => this.sortedList()); - model = linkedSignal({ + model = linkedSignal({ source: this.info, computation: (info: CashFlow): CashFlowFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -121,8 +121,8 @@ export class CashFlowComponent { const formModel = this.model(); return new CashFlow({ - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/closing-stock/closing-stock.component.ts b/overlord/src/app/closing-stock/closing-stock.component.ts index 27a50e20..2a767a9d 100644 --- a/overlord/src/app/closing-stock/closing-stock.component.ts +++ b/overlord/src/app/closing-stock/closing-stock.component.ts @@ -14,10 +14,10 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; import { CostCentre } from '../core/cost-centre'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { User } from '../core/user'; import { CostCentreService } from '../cost-centre/cost-centre.service'; import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component'; @@ -85,7 +85,7 @@ export class ClosingStockComponent { model = linkedSignal({ source: computed(() => ({ info: this.info(), costCentres: this.costCentres() })), computation: ({ info, costCentres }): ClosingStockFormData => ({ - date: moment(info.date, 'DD-MMM-YYYY').toDate(), + date: parseDate(info.date), costCentre: costCentres.find((c) => c.id === info.costCentre?.id) || null, stocks: info.items.map((x) => ({ physical: x.physical, @@ -176,7 +176,7 @@ export class ClosingStockComponent { getClosingStock(): ClosingStock { const formModel = this.model(); const currentInfo = this.info(); - currentInfo.date = moment(formModel.date).format('DD-MMM-YYYY'); + currentInfo.date = formatIsoDate(formModel.date); const array = formModel.stocks; currentInfo.items.forEach((item, index) => { item.physical = array[index].physical ?? 0; @@ -189,7 +189,7 @@ export class ClosingStockComponent { const formModel = this.model(); return new ClosingStock({ - date: moment(formModel.date).format('DD-MMM-YYYY'), + date: formatIsoDate(formModel.date), costCentre: formModel.costCentre ?? new CostCentre(), }); } diff --git a/overlord/src/app/core/date-utils.ts b/overlord/src/app/core/date-utils.ts new file mode 100644 index 00000000..c1c4e63d --- /dev/null +++ b/overlord/src/app/core/date-utils.ts @@ -0,0 +1,54 @@ +import { endOfMonth, format, getDaysInMonth, isValid, parse, parseISO, startOfMonth } from 'date-fns'; + +export const ISO_DATE_FORMAT = 'yyyy-MM-dd'; +export const DISPLAY_DATE_FORMAT = 'dd-MMM-yyyy'; +export const DISPLAY_DATETIME_FORMAT = 'dd-MMM-yyyy HH:mm'; + +export function isValidDate(value: unknown): value is Date { + return value instanceof Date && isValid(value); +} + +export function formatIsoDate(date: Date | null | undefined): string { + if (!date || !isValid(date)) return ''; + return format(date, ISO_DATE_FORMAT); +} + +export function formatDisplayDate(date: Date | null | undefined): string { + if (!date || !isValid(date)) return ''; + return format(date, DISPLAY_DATE_FORMAT); +} + +export function formatDisplayDateTime(date: Date | null | undefined): string { + if (!date || !isValid(date)) return ''; + return format(date, DISPLAY_DATETIME_FORMAT); +} + +export function parseDate(value: string | Date | null | undefined): Date; +export function parseDate(value: string | Date | null | undefined, defaultValue: null): Date | null; +export function parseDate(value: string | Date | null | undefined, defaultValue: Date): Date; +export function parseDate( + value: string | Date | null | undefined, + defaultValue: Date | null = new Date(), +): Date | null { + if (!value) return defaultValue; + if (value instanceof Date) return value; + const trimmed = value.trim(); + if (!trimmed) return defaultValue; + + // ISO 8601 strings (YYYY-MM-DD or full ISO datetime) + if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) { + return parseISO(trimmed); + } + + // Common UI format: dd-MMM-yyyy (e.g. 15-Sep-2026) + const parsedCustom = parse(trimmed, 'dd-MMM-yyyy', new Date()); + if (isValid(parsedCustom)) { + return parsedCustom; + } + + // Fallback to native date parsing + const fallback = new Date(trimmed); + return fallback; +} + +export { endOfMonth, getDaysInMonth, startOfMonth }; diff --git a/overlord/src/app/daybook/daybook.component.ts b/overlord/src/app/daybook/daybook.component.ts index 9cec6941..65be3d67 100644 --- a/overlord/src/app/daybook/daybook.component.ts +++ b/overlord/src/app/daybook/daybook.component.ts @@ -13,8 +13,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { Daybook } from './daybook'; @@ -98,8 +98,8 @@ export class DaybookComponent { model = linkedSignal({ source: this.info, computation: (info: Daybook): DaybookFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -143,8 +143,8 @@ export class DaybookComponent { const formModel = this.model(); return { - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), body: [], }; } diff --git a/overlord/src/app/employee-attendance/employee-attendance.component.ts b/overlord/src/app/employee-attendance/employee-attendance.component.ts index d78c96b7..1dd5ec50 100644 --- a/overlord/src/app/employee-attendance/employee-attendance.component.ts +++ b/overlord/src/app/employee-attendance/employee-attendance.component.ts @@ -12,9 +12,9 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AttendanceTypeService } from '../attendance/attendance-type.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { Employee } from '../employee/employee'; import { EmployeeService } from '../employee/employee.service'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; @@ -69,9 +69,9 @@ export class EmployeeAttendanceComponent { model = linkedSignal({ source: this.info, - computation: (info): EmployeeAttendanceFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + computation: (info: EmployeeAttendance): EmployeeAttendanceFormData => ({ + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), employee: info.employee ?? null, attendances: info.body.map((x) => ({ attendanceType: x.attendanceType.id, @@ -121,8 +121,8 @@ export class EmployeeAttendanceComponent { show() { const formValue = this.model(); const currentInfo = this.info(); - currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY'); - currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY'); + currentInfo.startDate = formatIsoDate(formValue.startDate); + currentInfo.finishDate = formatIsoDate(formValue.finishDate); currentInfo.employee = typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined; if (currentInfo.employee) { @@ -157,8 +157,8 @@ export class EmployeeAttendanceComponent { getEmployeeAttendance(): EmployeeAttendance { const formValue = this.model(); const currentInfo = this.info(); - currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY'); - currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY'); + currentInfo.startDate = formatIsoDate(formValue.startDate); + currentInfo.finishDate = formatIsoDate(formValue.finishDate); currentInfo.employee = typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined; const array = formValue.attendances; diff --git a/overlord/src/app/employee-benefits/employee-benefits.component.ts b/overlord/src/app/employee-benefits/employee-benefits.component.ts index 79ac1dcd..1ab15deb 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.component.ts +++ b/overlord/src/app/employee-benefits/employee-benefits.component.ts @@ -12,9 +12,9 @@ import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; +import { formatIsoDate, getDaysInMonth, parseDate } from '../core/date-utils'; import { EmployeeBenefit } from '../core/employee-benefit'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; @@ -80,7 +80,7 @@ export class EmployeeBenefitsComponent { formModel = linkedSignal({ source: this.item, computation: (v): EmployeeBenefitsFormData => ({ - date: v.date ? moment(v.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(v.date), addRow: { employee: null as Employee | string | null, grossSalary: '', @@ -168,7 +168,7 @@ export class EmployeeBenefitsComponent { } const grossSalary = Number(formValue?.grossSalary ?? 0); const daysWorked = Number(formValue?.daysWorked ?? 0); - const daysInMonth = moment(this.formModel().date).daysInMonth(); + const daysInMonth = getDaysInMonth(this.formModel().date); const esi = EmployeeBenefitsComponent.getEsi(grossSalary, daysWorked, daysInMonth); const pf = EmployeeBenefitsComponent.getPf(grossSalary, daysWorked, daysInMonth); @@ -245,7 +245,7 @@ export class EmployeeBenefitsComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); v.employeeBenefits = m.benefits.map( (b) => new EmployeeBenefit({ diff --git a/overlord/src/app/employee-functions/employee-functions.component.ts b/overlord/src/app/employee-functions/employee-functions.component.ts index f65e834f..fda0b0d8 100644 --- a/overlord/src/app/employee-functions/employee-functions.component.ts +++ b/overlord/src/app/employee-functions/employee-functions.component.ts @@ -6,8 +6,8 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTabsModule } from '@angular/material/tabs'; -import moment from 'moment'; +import { endOfMonth, formatIsoDate } from '../core/date-utils'; import { EmployeeFunctionsService } from './employee-functions.service'; @Component({ @@ -28,15 +28,15 @@ export class EmployeeFunctionsComponent { private snackBar = inject(MatSnackBar); private ser = inject(EmployeeFunctionsService); - creditSalaryModel = signal({ - date: moment().date(moment().daysInMonth()), + creditSalaryModel = signal<{ date: Date }>({ + date: endOfMonth(new Date()), }); creditSalaryForm = createForm(this.creditSalaryModel); - attendanceRecordModel = signal({ - startDate: moment(), - finishDate: moment(), + attendanceRecordModel = signal<{ startDate: Date; finishDate: Date }>({ + startDate: new Date(), + finishDate: new Date(), }); attendanceRecordForm = createForm(this.attendanceRecordModel); @@ -44,28 +44,24 @@ export class EmployeeFunctionsComponent { fingerprintFile: File | null = null; constructor() { - const startDate = moment().date(1); - const finishDate = moment().date(startDate.daysInMonth()); - this.creditSalaryModel.set({ date: finishDate }); + this.creditSalaryModel.set({ date: endOfMonth(new Date()) }); } - chosenYearHandler(normalizedYear: moment.Moment) { - const ctrlValue = moment(this.creditSalaryModel().date); - ctrlValue.year(normalizedYear.year()); - ctrlValue.date(ctrlValue.daysInMonth()); - this.creditSalaryModel.update((m) => ({ ...m, date: ctrlValue })); + chosenYearHandler(normalizedYear: Date) { + const current = this.creditSalaryModel().date; + const updated = new Date(normalizedYear.getFullYear(), current.getMonth(), 1); + this.creditSalaryModel.update((m) => ({ ...m, date: endOfMonth(updated) })); } - chosenMonthHandler(normlizedMonth: moment.Moment, datepicker: MatDatepicker) { - const ctrlValue = moment(this.creditSalaryModel().date); - ctrlValue.month(normlizedMonth.month()); - ctrlValue.date(ctrlValue.daysInMonth()); - this.creditSalaryModel.update((m) => ({ ...m, date: ctrlValue })); + chosenMonthHandler(normalizedMonth: Date, datepicker: MatDatepicker) { + const current = this.creditSalaryModel().date; + const updated = new Date(current.getFullYear(), normalizedMonth.getMonth(), 1); + this.creditSalaryModel.update((m) => ({ ...m, date: endOfMonth(updated) })); datepicker.close(); } creditSalary() { - const date = (this.creditSalaryModel().date ?? moment()).format('DD-MMM-YYYY'); + const date = formatIsoDate(this.creditSalaryModel().date); if (!date) { this.snackBar.open('Please choose a valid date.', 'Danger'); return; @@ -81,20 +77,20 @@ export class EmployeeFunctionsComponent { } attendanceRecordUrl() { - const startDate = (this.attendanceRecordModel().startDate ?? moment()).format('DD-MMM-YYYY'); - const finishDate = (this.attendanceRecordModel().finishDate ?? moment()).format('DD-MMM-YYYY'); + const startDate = formatIsoDate(this.attendanceRecordModel().startDate ?? new Date()); + const finishDate = formatIsoDate(this.attendanceRecordModel().finishDate ?? new Date()); if (!startDate || !finishDate) { - // this.snackBar.open('Please choose a start and finish date.', 'Danger'); + this.snackBar.open('Please choose a start and finish date.', 'Danger'); return ''; } return `/attendance-report?s=${startDate}&f=${finishDate}`; } fingerPrintUrl() { - const startDate = (this.attendanceRecordModel().startDate ?? moment()).format('DD-MMM-YYYY'); - const finishDate = (this.attendanceRecordModel().finishDate ?? moment()).format('DD-MMM-YYYY'); + const startDate = formatIsoDate(this.attendanceRecordModel().startDate); + const finishDate = formatIsoDate(this.attendanceRecordModel().finishDate); if (!startDate || !finishDate) { - // this.snackBar.open('Please choose a start and finish date.', 'Danger'); + this.snackBar.open('Please choose a start and finish date.', 'Danger'); return ''; } return `/fingerprint-report?s=${startDate}&f=${finishDate}`; diff --git a/overlord/src/app/employee/employee-detail/employee-detail.component.ts b/overlord/src/app/employee/employee-detail/employee-detail.component.ts index f036dc8d..8561890f 100644 --- a/overlord/src/app/employee/employee-detail/employee-detail.component.ts +++ b/overlord/src/app/employee/employee-detail/employee-detail.component.ts @@ -11,8 +11,8 @@ import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../../core/date-utils'; import { CostCentreService } from '../../cost-centre/cost-centre.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ErrorStateComponent } from '../../shared/error-state/error-state.component'; @@ -70,8 +70,8 @@ export class EmployeeDetailComponent { points: itemVal.points || 0, isActive: itemVal.isActive ?? true, costCentre: itemVal.costCentre?.id ?? '', - joiningDate: itemVal.joiningDate ? moment(itemVal.joiningDate, 'DD-MMM-YYYY').toDate() : new Date(), - leavingDate: itemVal.leavingDate ? moment(itemVal.leavingDate, 'DD-MMM-YYYY').toDate() : null, + joiningDate: parseDate(itemVal.joiningDate), + leavingDate: parseDate(itemVal.leavingDate, null), }), }); @@ -147,9 +147,8 @@ export class EmployeeDetailComponent { item.points = Number(formValue.points); item.isActive = formValue.isActive; item.costCentre.id = formValue.costCentre ?? ''; - item.joiningDate = moment(formValue.joiningDate).format('DD-MMM-YYYY'); - item.leavingDate = - item.isActive || !formValue.leavingDate ? null : moment(formValue.leavingDate).format('DD-MMM-YYYY'); + item.joiningDate = formatIsoDate(formValue.joiningDate); + item.leavingDate = item.isActive || !formValue.leavingDate ? null : formatIsoDate(formValue.leavingDate); return item; } } diff --git a/overlord/src/app/entries/entries.component.html b/overlord/src/app/entries/entries.component.html index 80534ef9..4a5ef73d 100644 --- a/overlord/src/app/entries/entries.component.html +++ b/overlord/src/app/entries/entries.component.html @@ -4,15 +4,9 @@
Create / Last Edit Date From - - - + + + Create / Last Edit Date Till diff --git a/overlord/src/app/entries/entries.component.ts b/overlord/src/app/entries/entries.component.ts index 379b3124..fe63ee06 100644 --- a/overlord/src/app/entries/entries.component.ts +++ b/overlord/src/app/entries/entries.component.ts @@ -19,8 +19,8 @@ import { MatRadioChange, MatRadioModule } from '@angular/material/radio'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { LocalTimePipe } from '../shared/local-time.pipe'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -103,8 +103,8 @@ export class EntriesComponent { issue: this.issue(), }), computation: (src): EntriesFormData => ({ - startDate: src.startDate ? moment(src.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: src.finishDate ? moment(src.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(src.startDate), + finishDate: parseDate(src.finishDate), posted: src.posted, issue: src.issue, }), @@ -127,8 +127,8 @@ export class EntriesComponent { refresh() { const formModel = this.formModel(); - const startDate = moment(formModel.startDate).format('DD-MMM-YYYY'); - const finishDate = moment(formModel.finishDate).format('DD-MMM-YYYY'); + const startDate = formatIsoDate(formModel.startDate); + const finishDate = formatIsoDate(formModel.finishDate); this.router.navigate([], { relativeTo: this.route, queryParams: { s: startDate, f: finishDate }, diff --git a/overlord/src/app/incentive/incentive.component.ts b/overlord/src/app/incentive/incentive.component.ts index e4352edb..f9bda83d 100644 --- a/overlord/src/app/incentive/incentive.component.ts +++ b/overlord/src/app/incentive/incentive.component.ts @@ -10,11 +10,11 @@ import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountBalance } from '../core/account-balance'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { Incentive } from '../core/incentive'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; @@ -23,7 +23,7 @@ import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog. import { LocalTimePipe } from '../shared/local-time.pipe'; export interface IncentiveFormData { - date: moment.Moment; + date: Date; incentives: Incentive[]; } @@ -62,7 +62,7 @@ export class IncentiveComponent { model = linkedSignal({ source: this.voucher, computation: (v): IncentiveFormData => ({ - date: v.date ? moment(v.date, 'DD-MMM-YYYY') : moment(new Date()), + date: parseDate(v.date), incentives: v.incentives.map((x) => new Incentive({ ...x })), }), }); @@ -71,7 +71,7 @@ export class IncentiveComponent { constructor() { effect(async () => { - const date = this.form.date().value().format('DD-MMM-YYYY'); + const date = formatIsoDate(this.form.date().value()); if (date !== this.d() && !this.id()) { this.router.navigate(['/incentive'], { queryParams: { d: date }, replaceUrl: true }); } @@ -167,7 +167,7 @@ export class IncentiveComponent { getVoucher(): Voucher { const formModel = this.model(); const v = this.voucher(); - v.date = moment(formModel.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(formModel.date); v.incentives = formModel.incentives; return v; } diff --git a/overlord/src/app/issue/issue.component.ts b/overlord/src/app/issue/issue.component.ts index 11d7c071..c000828d 100644 --- a/overlord/src/app/issue/issue.component.ts +++ b/overlord/src/app/issue/issue.component.ts @@ -15,12 +15,12 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; import { Batch } from '../core/batch'; import { BatchService } from '../core/batch.service'; import { CostCentre } from '../core/cost-centre'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { Inventory } from '../core/inventory'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; @@ -118,7 +118,7 @@ export class IssueComponent { 2, ); return { - date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(voucher.date), source: (voucher.source as CostCentre)?.id ?? '', destination: (voucher.destination as CostCentre)?.id ?? '', amount, @@ -150,7 +150,7 @@ export class IssueComponent { displayedColumns = ['product', 'batch', 'quantity', 'rate', 'amount', 'action']; gridColumns = ['source', 'destination', 'gridAmount', 'load']; - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); batchSearch = computed(() => { const v = this.form.addRow.batch().value(); @@ -200,7 +200,7 @@ export class IssueComponent { if (!d) return; const url = new URL(window.location.href); - url.searchParams.set('d', moment(d).format('DD-MMM-YYYY')); + url.searchParams.set('d', formatIsoDate(d)); window.history.replaceState({}, '', url.toString()); }); } @@ -291,7 +291,7 @@ export class IssueComponent { amount: row.amount, batch: row.batch, }), - date: moment(this.formModel().date).format('DD-MMM-YYYY'), + date: formatIsoDate(this.formModel().date), }, }); @@ -337,7 +337,7 @@ export class IssueComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); v.source = new CostCentre({ id: m.source }); v.destination = new CostCentre({ id: m.destination }); v.narration = m.narration ?? ''; diff --git a/overlord/src/app/journal/journal.component.ts b/overlord/src/app/journal/journal.component.ts index 56bb7c96..2e53a9e3 100644 --- a/overlord/src/app/journal/journal.component.ts +++ b/overlord/src/app/journal/journal.component.ts @@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { zip } from 'rxjs'; import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; import { CostCentre } from '../core/cost-centre'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { User } from '../core/user'; @@ -135,7 +135,7 @@ export class JournalComponent { event.preventDefault(); this.router.navigate(['payment'], { queryParams: { - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -145,7 +145,7 @@ export class JournalComponent { event.preventDefault(); this.router.navigate(['receipt'], { queryParams: { - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -175,7 +175,7 @@ export class JournalComponent { source: this.item, computation: (voucher): JournalFormModel => ({ isStarred: voucher.isStarred, - date: moment(voucher.date, 'DD-MMM-YYYY').toDate(), + date: parseDate(voucher.date), addRow: { debit: 1, account: null as Account | string | null, @@ -218,7 +218,7 @@ export class JournalComponent { tagsResource = this.tagSer.autocomplete(this.tagQuery); tags = computed(() => this.tagsResource.value() ?? []); - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); accountId = computed((): string | null => { const account = this.form.addRow.account().value(); return typeof account === 'string' ? null : (account?.id ?? null); @@ -237,7 +237,7 @@ export class JournalComponent { dateChanged(newDate: Date) { this.router.navigate([], { - queryParams: { d: moment(newDate).format('DD-MMM-YYYY') }, + queryParams: { d: formatIsoDate(newDate) }, replaceUrl: true, queryParamsHandling: 'merge', }); @@ -292,7 +292,7 @@ export class JournalComponent { width: '750px', data: { journal: new Journal({ ...row }), - date: moment(this.formModel().date).format('DD-MMM-YYYY'), + date: formatIsoDate(this.formModel().date), }, }); @@ -328,7 +328,7 @@ export class JournalComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); v.narration = m.narration ?? ''; v.journals = m.journals.map( (j) => new Journal({ id: j.id, debit: j.debit, amount: j.amount, account: j.account, costCentre: j.costCentre }), diff --git a/overlord/src/app/ledger/ledger.component.ts b/overlord/src/app/ledger/ledger.component.ts index d5fc3179..cb23c336 100644 --- a/overlord/src/app/ledger/ledger.component.ts +++ b/overlord/src/app/ledger/ledger.component.ts @@ -17,10 +17,10 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router, RouterModule } from '@angular/router'; -import moment from 'moment'; import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { AccountingPipe } from '../shared/accounting.pipe'; import { ClearPipe } from '../shared/clear.pipe'; import { ToCsvService } from '../shared/to-csv.service'; @@ -148,8 +148,8 @@ export class LedgerComponent { model = linkedSignal({ source: this.info, computation: (info: Ledger): LedgerFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), account: info.account ?? null, tags: this.tags(), }), @@ -283,8 +283,8 @@ export class LedgerComponent { return new Ledger({ account, - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/mozimo-daily-register/mozimo-daily-register.component.ts b/overlord/src/app/mozimo-daily-register/mozimo-daily-register.component.ts index 78f6793d..c01b7510 100644 --- a/overlord/src/app/mozimo-daily-register/mozimo-daily-register.component.ts +++ b/overlord/src/app/mozimo-daily-register/mozimo-daily-register.component.ts @@ -13,9 +13,9 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { LocalTimePipe } from '../shared/local-time.pipe'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -78,7 +78,7 @@ export class MozimoDailyRegisterComponent { model = linkedSignal({ source: this.info, computation: (v): MozimoDailyRegisterFormData => ({ - date: moment(v.date, 'DD-MMM-YYYY').toDate(), + date: parseDate(v.date), items: v.body.map((x) => ({ received: x.received, sale: x.sale, @@ -110,7 +110,7 @@ export class MozimoDailyRegisterComponent { } show() { - const date = moment(this.model().date).format('DD-MMM-YYYY'); + const date = formatIsoDate(this.model().date); if (this.date() === date) { this.infoResource.reload(); } else { @@ -132,7 +132,7 @@ export class MozimoDailyRegisterComponent { getMozimoDailyRegister(): MozimoDailyRegister { const formModel = this.model(); const info = this.info(); - info.date = moment(formModel.date).format('DD-MMM-YYYY'); + info.date = formatIsoDate(formModel.date); const array = formModel.items; if (array) { diff --git a/overlord/src/app/mozimo-product-register/mozimo-product-register.component.ts b/overlord/src/app/mozimo-product-register/mozimo-product-register.component.ts index 584cdec1..5f01670f 100644 --- a/overlord/src/app/mozimo-product-register/mozimo-product-register.component.ts +++ b/overlord/src/app/mozimo-product-register/mozimo-product-register.component.ts @@ -13,9 +13,9 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Router } from '@angular/router'; -import moment from 'moment'; import { AuthService } from '../auth/auth.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { Product } from '../core/product'; import { ProductService } from '../product/product.service'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; @@ -84,8 +84,8 @@ export class MozimoProductRegisterComponent { model = linkedSignal({ source: this.info, computation: (info): MozimoProductRegisterFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), product: info.product ?? '', items: info.body.map((x) => ({ received: x.received, @@ -167,8 +167,8 @@ export class MozimoProductRegisterComponent { getMozimoProductRegister(): MozimoProductRegister { const formModel = this.model(); const info = this.info(); - info.startDate = moment(formModel.startDate).format('DD-MMM-YYYY'); - info.finishDate = moment(formModel.finishDate).format('DD-MMM-YYYY'); + info.startDate = formatIsoDate(formModel.startDate); + info.finishDate = formatIsoDate(formModel.finishDate); const array = formModel.items; if (array) { @@ -190,8 +190,8 @@ export class MozimoProductRegisterComponent { return new MozimoProductRegister({ product: formModel.product as Product, - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/net-transactions/net-transactions.component.html b/overlord/src/app/net-transactions/net-transactions.component.html index c9793f1a..92d055d9 100644 --- a/overlord/src/app/net-transactions/net-transactions.component.html +++ b/overlord/src/app/net-transactions/net-transactions.component.html @@ -4,9 +4,9 @@
Start Date - - - + + + Finish Date diff --git a/overlord/src/app/net-transactions/net-transactions.component.ts b/overlord/src/app/net-transactions/net-transactions.component.ts index 2ec8cc6b..c07202a5 100644 --- a/overlord/src/app/net-transactions/net-transactions.component.ts +++ b/overlord/src/app/net-transactions/net-transactions.component.ts @@ -13,8 +13,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { AccountingPipe } from '../shared/accounting.pipe'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -92,8 +92,8 @@ export class NetTransactionsComponent { model = linkedSignal({ source: this.info, computation: (info: NetTransactions): NetTransactionsFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -133,8 +133,8 @@ export class NetTransactionsComponent { const formModel = this.model(); return new NetTransactions({ - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/payment/payment.component.ts b/overlord/src/app/payment/payment.component.ts index 48b3a745..873c0c34 100644 --- a/overlord/src/app/payment/payment.component.ts +++ b/overlord/src/app/payment/payment.component.ts @@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { zip } from 'rxjs'; import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; import { CostCentre } from '../core/cost-centre'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { User } from '../core/user'; @@ -133,7 +133,7 @@ export class PaymentComponent { ); return { isStarred: voucher.isStarred, - date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(voucher.date), paymentAccount: pj?.account?.id ?? this.a() ?? '', paymentAmount, addRow: { @@ -180,7 +180,7 @@ export class PaymentComponent { tagsResource = this.tagSer.autocomplete(this.tagQuery); tags = computed(() => this.tagsResource.value() ?? []); - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); accountId = computed((): string | null => { const account = this.form.addRow.account().value(); return typeof account === 'string' ? null : (account?.id ?? null); @@ -206,7 +206,7 @@ export class PaymentComponent { this.router.navigate(['receipt'], { queryParams: { a: this.formModel().paymentAccount, - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -216,7 +216,7 @@ export class PaymentComponent { event.preventDefault(); this.router.navigate(['journal'], { queryParams: { - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -246,7 +246,7 @@ export class PaymentComponent { if (!d) return; const url = new URL(window.location.href); - url.searchParams.set('d', moment(d).format('DD-MMM-YYYY')); + url.searchParams.set('d', formatIsoDate(d)); window.history.replaceState({}, '', url.toString()); }); @@ -319,7 +319,7 @@ export class PaymentComponent { account: row.account, costCentre: row.costCentre, }), - date: moment(this.formModel().date).format('DD-MMM-YYYY'), + date: formatIsoDate(this.formModel().date), }, }); @@ -358,7 +358,7 @@ export class PaymentComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); v.narration = m.narration ?? ''; const existingPj = base.journals.find((x) => x.debit === -1); diff --git a/overlord/src/app/period/period-detail/period-detail.component.ts b/overlord/src/app/period/period-detail/period-detail.component.ts index d1f8a20b..b603e1fa 100644 --- a/overlord/src/app/period/period-detail/period-detail.component.ts +++ b/overlord/src/app/period/period-detail/period-detail.component.ts @@ -11,8 +11,8 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../../core/date-utils'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ErrorStateComponent } from '../../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../../shared/skeleton-loader/skeleton-loader.component'; @@ -48,8 +48,8 @@ export class PeriodDetailComponent { model = linkedSignal({ source: this.item, computation: (item): PeriodFormData => ({ - validFrom: item.validFrom ? moment(item.validFrom, 'DD-MMM-YYYY').toDate() : new Date(), - validTill: item.validTill ? moment(item.validTill, 'DD-MMM-YYYY').toDate() : new Date(), + validFrom: parseDate(item.validFrom), + validTill: parseDate(item.validTill), }), }); @@ -100,8 +100,8 @@ export class PeriodDetailComponent { getItem(): Period { const formValue = this.model(); const item = this.item(); - item.validFrom = moment(formValue.validFrom).format('DD-MMM-YYYY'); - item.validTill = moment(formValue.validTill).format('DD-MMM-YYYY'); + item.validFrom = formatIsoDate(formValue.validFrom); + item.validTill = formatIsoDate(formValue.validTill); return item; } } diff --git a/overlord/src/app/product-ledger/product-ledger.component.html b/overlord/src/app/product-ledger/product-ledger.component.html index 514d6f57..a66f45cd 100644 --- a/overlord/src/app/product-ledger/product-ledger.component.html +++ b/overlord/src/app/product-ledger/product-ledger.component.html @@ -15,6 +15,7 @@ + Finish Date @@ -39,9 +40,6 @@ [displayWith]="displayFn" (optionSelected)="selected($event)" > - @for (product of products(); track product.id) { - {{ product.name }} - } diff --git a/overlord/src/app/product-ledger/product-ledger.component.ts b/overlord/src/app/product-ledger/product-ledger.component.ts index 26da2ee0..b6577d61 100644 --- a/overlord/src/app/product-ledger/product-ledger.component.ts +++ b/overlord/src/app/product-ledger/product-ledger.component.ts @@ -11,9 +11,9 @@ import { MatInputModule } from '@angular/material/input'; import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; -import { ActivatedRoute, Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { Router, RouterModule } from '@angular/router'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { Product } from '../core/product'; import { ProductSku } from '../core/product-sku'; import { ProductService } from '../product/product.service'; @@ -23,6 +23,7 @@ export interface ProductLedgerFormData { finishDate: Date; product: Product | string | null; } + import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { ProductLedger } from './product-ledger'; @@ -57,7 +58,6 @@ import { ProductLedgerService } from './product-ledger.service'; ], }) export class ProductLedgerComponent { - private route = inject(ActivatedRoute); private router = inject(Router); private toCsv = inject(ToCsvService); private ser = inject(ProductLedgerService); @@ -152,8 +152,8 @@ export class ProductLedgerComponent { model = linkedSignal({ source: this.info, computation: (info: ProductLedger): ProductLedgerFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), product: info.product ?? null, }), }); @@ -234,8 +234,8 @@ export class ProductLedgerComponent { const product = prodVal && typeof prodVal !== 'string' ? prodVal : undefined; return new ProductLedger({ product, - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/profit-loss/profit-loss.component.ts b/overlord/src/app/profit-loss/profit-loss.component.ts index e52f7387..2ff6dc57 100644 --- a/overlord/src/app/profit-loss/profit-loss.component.ts +++ b/overlord/src/app/profit-loss/profit-loss.component.ts @@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { ProfitLoss } from './profit-loss'; @@ -58,8 +58,8 @@ export class ProfitLossComponent { formModel = linkedSignal({ source: this.info, computation: (info: ProfitLoss): ProfitLossFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -133,8 +133,8 @@ export class ProfitLossComponent { const formModel = this.formModel(); return new ProfitLoss({ - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), footer: this.info().footer, body: this.info().body, }); diff --git a/overlord/src/app/purchase-entries/purchase-entries.component.ts b/overlord/src/app/purchase-entries/purchase-entries.component.ts index d7f7ccac..d1d41ef6 100644 --- a/overlord/src/app/purchase-entries/purchase-entries.component.ts +++ b/overlord/src/app/purchase-entries/purchase-entries.component.ts @@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { PurchaseEntries } from './purchase-entries'; @@ -103,8 +103,8 @@ export class PurchaseEntriesComponent { formModel = linkedSignal({ source: () => this.info(), computation: (info: PurchaseEntries): PurchaseEntriesFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -137,8 +137,8 @@ export class PurchaseEntriesComponent { const formModel = this.formModel(); return { - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), body: [], }; } diff --git a/overlord/src/app/purchase-return/purchase-return.component.ts b/overlord/src/app/purchase-return/purchase-return.component.ts index 3b056010..f7517368 100644 --- a/overlord/src/app/purchase-return/purchase-return.component.ts +++ b/overlord/src/app/purchase-return/purchase-return.component.ts @@ -16,7 +16,6 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { zip } from 'rxjs'; import { AuthService } from '../auth/auth.service'; @@ -24,6 +23,7 @@ import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; import { Batch } from '../core/batch'; import { BatchService } from '../core/batch.service'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { DbFile } from '../core/db-file'; import { Inventory } from '../core/inventory'; import { User } from '../core/user'; @@ -134,7 +134,7 @@ export class PurchaseReturnComponent { ); return { isStarred: voucher.isStarred, - date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(voucher.date), account: voucher.vendor ?? (this.a() ? new Account({ id: this.a()! }) : null), amount, addRow: { @@ -184,7 +184,7 @@ export class PurchaseReturnComponent { tagsResource = this.tagSer.autocomplete(this.tagQuery); tags = computed(() => this.tagsResource.value() ?? []); - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); accountId = computed((): string | null => { const account = this.form.account().value(); return typeof account === 'string' ? null : (account?.id ?? null); @@ -240,7 +240,7 @@ export class PurchaseReturnComponent { if (!d) return; const url = new URL(window.location.href); - url.searchParams.set('d', moment(d).format('DD-MMM-YYYY')); + url.searchParams.set('d', formatIsoDate(d)); window.history.replaceState({}, '', url.toString()); }); @@ -326,7 +326,7 @@ export class PurchaseReturnComponent { amount: row.amount, batch: row.batch, }), - date: moment(this.formModel().date).format('DD-MMM-YYYY'), + date: formatIsoDate(this.formModel().date), }, }); @@ -372,7 +372,7 @@ export class PurchaseReturnComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); const account = m.account; v.vendor = !account || typeof account === 'string' ? undefined : account; v.narration = m.narration ?? ''; diff --git a/overlord/src/app/purchase/purchase.component.ts b/overlord/src/app/purchase/purchase.component.ts index d6b71293..2d8754b4 100644 --- a/overlord/src/app/purchase/purchase.component.ts +++ b/overlord/src/app/purchase/purchase.component.ts @@ -16,13 +16,13 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { zip } from 'rxjs'; import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; import { Batch } from '../core/batch'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { DbFile } from '../core/db-file'; import { Inventory } from '../core/inventory'; import { Product } from '../core/product'; @@ -139,7 +139,7 @@ export class PurchaseComponent { ); return { isStarred: voucher.isStarred, - date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(voucher.date), account: voucher.vendor ?? (this.a() ? new Account({ id: this.a()! }) : null), amount, addRow: { @@ -197,7 +197,7 @@ export class PurchaseComponent { return typeof v === 'string' ? v : null; }); - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); accountId = computed((): string | null => { const account = this.form.account().value(); return typeof account === 'string' ? null : (account?.id ?? null); @@ -254,7 +254,7 @@ export class PurchaseComponent { if (!d) return; const url = new URL(window.location.href); - url.searchParams.set('d', moment(d).format('DD-MMM-YYYY')); + url.searchParams.set('d', formatIsoDate(d)); window.history.replaceState({}, '', url.toString()); }); @@ -409,7 +409,7 @@ export class PurchaseComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); const account = m.account; v.vendor = !account || typeof account === 'string' ? undefined : account; v.narration = m.narration ?? ''; diff --git a/overlord/src/app/purchases/purchases.component.ts b/overlord/src/app/purchases/purchases.component.ts index 75c8d47c..114b9056 100644 --- a/overlord/src/app/purchases/purchases.component.ts +++ b/overlord/src/app/purchases/purchases.component.ts @@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { Purchases } from './purchases'; @@ -97,8 +97,8 @@ export class PurchasesComponent { formModel = linkedSignal({ source: () => this.info(), computation: (info: Purchases): PurchasesFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -131,8 +131,8 @@ export class PurchasesComponent { const formModel = this.formModel(); return { - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), body: [], footer: new PurchasesItem(), }; diff --git a/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts b/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts index b20df634..73d81a4c 100644 --- a/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts +++ b/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts @@ -13,10 +13,10 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; import { Account } from '../../core/account'; import { AccountService } from '../../core/account.service'; +import { formatIsoDate, parseDate } from '../../core/date-utils'; import { Product } from '../../core/product'; import { ProductService } from '../../product/product.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; @@ -88,9 +88,9 @@ export class RateContractDetailComponent { formModel = linkedSignal({ source: this.item, computation: (itemVal): RateContractDetailFormData => ({ - date: itemVal.date ? moment(itemVal.date, 'DD-MMM-YYYY').toDate() : new Date(), - validFrom: itemVal.validFrom ? moment(itemVal.validFrom, 'DD-MMM-YYYY').toDate() : new Date(), - validTill: itemVal.validTill ? moment(itemVal.validTill, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(itemVal.date), + validFrom: parseDate(itemVal.validFrom), + validTill: parseDate(itemVal.validTill), account: itemVal.vendor?.name ?? '', addRow: { product: '', @@ -240,9 +240,9 @@ export class RateContractDetailComponent { getItem(): RateContract { const formModel = this.formModel(); const item = this.item(); - item.date = moment(formModel.date).format('DD-MMM-YYYY'); - item.validFrom = moment(formModel.validFrom).format('DD-MMM-YYYY'); - item.validTill = moment(formModel.validTill).format('DD-MMM-YYYY'); + item.date = formatIsoDate(formModel.date); + item.validFrom = formatIsoDate(formModel.validFrom); + item.validTill = formatIsoDate(formModel.validTill); item.vendor = this.account ?? new Account(); item.narration = formModel.narration ?? ''; item.items = formModel.items.map( diff --git a/overlord/src/app/raw-material-cost/raw-material-cost.component.ts b/overlord/src/app/raw-material-cost/raw-material-cost.component.ts index 4e235e3b..715c6471 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost.component.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost.component.ts @@ -10,8 +10,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router, RouterModule } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { ToCsvService } from '../shared/to-csv.service'; @@ -107,9 +107,9 @@ export class RawMaterialCostComponent { formModel = linkedSignal({ source: () => this.info(), - computation: (info): RawMaterialCostFormData => ({ - startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), - finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), + computation: (info: RawMaterialCost): RawMaterialCostFormData => ({ + startDate: parseDate(info.startDate), + finishDate: parseDate(info.finishDate), }), }); @@ -147,8 +147,8 @@ export class RawMaterialCostComponent { const formModel = this.formModel(); return new RawMaterialCost({ - startDate: moment(formModel.startDate).format('DD-MMM-YYYY'), - finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'), + startDate: formatIsoDate(formModel.startDate), + finishDate: formatIsoDate(formModel.finishDate), }); } diff --git a/overlord/src/app/receipt/receipt.component.ts b/overlord/src/app/receipt/receipt.component.ts index c0732734..b4dcbb08 100644 --- a/overlord/src/app/receipt/receipt.component.ts +++ b/overlord/src/app/receipt/receipt.component.ts @@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import { round } from 'mathjs'; -import moment from 'moment'; import { zip } from 'rxjs'; import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountService } from '../core/account.service'; import { CostCentre } from '../core/cost-centre'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { User } from '../core/user'; @@ -135,7 +135,7 @@ export class ReceiptComponent { ); return { isStarred: voucher.isStarred, - date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(voucher.date), receiptAccount: rj?.account?.id ?? this.a() ?? '', receiptAmount, addRow: { @@ -182,7 +182,7 @@ export class ReceiptComponent { tagsResource = this.tagSer.autocomplete(this.tagQuery); tags = computed(() => this.tagsResource.value() ?? []); - balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY')); + balanceDate = computed(() => formatIsoDate(this.form.date().value())); accountId = computed((): string | null => { const account = this.form.addRow.account().value(); return typeof account === 'string' ? null : (account?.id ?? null); @@ -209,7 +209,7 @@ export class ReceiptComponent { this.router.navigate(['payment'], { queryParams: { a: this.formModel().receiptAccount, - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -223,7 +223,7 @@ export class ReceiptComponent { event.preventDefault(); this.router.navigate(['journal'], { queryParams: { - d: moment(this.formModel().date).format('DD-MMM-YYYY'), + d: formatIsoDate(this.formModel().date), }, queryParamsHandling: 'merge', }); @@ -253,7 +253,7 @@ export class ReceiptComponent { if (!d) return; const url = new URL(window.location.href); - url.searchParams.set('d', moment(d).format('DD-MMM-YYYY')); + url.searchParams.set('d', formatIsoDate(d)); window.history.replaceState({}, '', url.toString()); }); @@ -326,7 +326,7 @@ export class ReceiptComponent { account: row.account, costCentre: row.costCentre, }), - date: moment(this.formModel().date).format('DD-MMM-YYYY'), + date: formatIsoDate(this.formModel().date), }, }); @@ -365,7 +365,7 @@ export class ReceiptComponent { const m = this.formModel(); const base = this.item(); const v = new Voucher({ ...base }); - v.date = moment(m.date).format('DD-MMM-YYYY'); + v.date = formatIsoDate(m.date); v.narration = m.narration ?? ''; const existingRj = base.journals.find((x) => x.debit === 1); diff --git a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts index 846cc653..211fb686 100644 --- a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts +++ b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts @@ -23,8 +23,8 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../../core/date-utils'; import { Product } from '../../core/product'; import { ProductSku } from '../../core/product-sku'; import { ProductService } from '../../product/product.service'; @@ -63,7 +63,7 @@ export interface IngredientFormData { export interface RecipeDetailFormData { basics: { - date: string; + date: Date; source: string; recipeYield: string; product: ProductSku | string | null; @@ -136,7 +136,7 @@ export class RecipeDetailComponent { source: this.item, computation: (itemVal): RecipeDetailFormData => ({ basics: { - date: itemVal.date, + date: parseDate(itemVal.date), source: itemVal.source ?? '', recipeYield: `${itemVal.recipeYield ?? ''}`, product: itemVal.sku ?? null, @@ -488,7 +488,7 @@ export class RecipeDetailComponent { const base = this.item(); const item = new Recipe({ ...base }); - item.date = moment(basics.date).format('DD-MMM-YYYY'); + item.date = formatIsoDate(basics.date); item.source = basics.source ?? ''; item.recipeYield = this.math.parseAmount(basics.recipeYield ?? '1', 2); item.garnishing = notes.garnishing ?? ''; diff --git a/overlord/src/app/role/role-detail/role-detail.component.ts b/overlord/src/app/role/role-detail/role-detail.component.ts index 508964d1..68553012 100644 --- a/overlord/src/app/role/role-detail/role-detail.component.ts +++ b/overlord/src/app/role/role-detail/role-detail.component.ts @@ -1,13 +1,4 @@ -import { - Component, - computed, - effect, - inject, - input, - linkedSignal, - untracked, - afterNextRender, -} from '@angular/core'; +import { Component, computed, effect, inject, input, linkedSignal, untracked, afterNextRender } from '@angular/core'; import { form as createForm, FormField, FormRoot } from '@angular/forms/signals'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -143,11 +134,7 @@ export class RoleDetailComponent { const newPermission = isImplied ? true : currentDirect; const newDisabled = isImplied; - if ( - p.direct !== currentDirect || - p.permission !== newPermission || - p.disabled !== newDisabled - ) { + if (p.direct !== currentDirect || p.permission !== newPermission || p.disabled !== newDisabled) { changed = true; return { ...p, diff --git a/overlord/src/app/sale/sale.component.css b/overlord/src/app/sale/sale.component.css new file mode 100644 index 00000000..14a7fe7d --- /dev/null +++ b/overlord/src/app/sale/sale.component.css @@ -0,0 +1,306 @@ +.right { + display: flex; + justify-content: flex-end; +} + +.center { + display: flex; + justify-content: center; +} + +.font-semibold { + font-weight: 600; +} + +.text-accent { + color: var(--mat-sys-primary, #0284c7); +} + +.header-row { + display: flex; + align-items: center; + margin-bottom: 8px; + flex-wrap: wrap; + gap: 12px; +} + +.title-with-back { + display: flex; + align-items: center; + gap: 8px; +} + +.back-button { + margin-right: 4px; +} + +.title-group { + display: flex; + align-items: center; + gap: 10px; +} + +.title-group h2 { + margin: 0; + font-size: 1.5rem; + font-weight: 600; +} + +.readonly-badge { + display: inline-flex; + align-items: center; + font-size: 0.75rem; + font-weight: 500; + padding: 3px 8px; + border-radius: 6px; + background: var(--mat-sys-surface-variant, #f1f5f9); + color: var(--mat-sys-on-surface-variant, #64748b); + border: 1px solid var(--mat-sys-outline-variant, #e2e8f0); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.day-total-badge { + display: inline-flex; + align-items: baseline; + gap: 8px; + padding: 6px 14px; + border-radius: 8px; + background: var(--mat-sys-secondary-container, #e0f2fe); + color: var(--mat-sys-on-secondary-container, #0369a1); + border: 1px solid rgba(2, 132, 199, 0.2); +} + +.day-total-label { + font-size: 0.85rem; + font-weight: 500; +} + +.day-total-value { + font-size: 1.15rem; + font-weight: 700; +} + +.day-total-sub { + font-size: 0.75rem; + opacity: 0.8; +} + +.date-bar { + display: flex; + align-items: center; + margin-bottom: 12px; +} + +.date-field { + max-width: 220px; +} + +/* Tabs */ +.cost-centre-tabs { + margin-top: 8px; +} + +.tab-label-container { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; +} + +.tab-icon { + font-size: 18px; + width: 18px; + height: 18px; + vertical-align: middle; +} + +.tab-cc-name { + font-weight: 500; +} + +.tab-cc-badge { + font-size: 0.75rem; + font-weight: 600; + padding: 2px 7px; + border-radius: 12px; + background: rgba(2, 132, 199, 0.1); + color: var(--mat-sys-primary, #0284c7); +} + +/* Voucher Details */ +.voucher-detail-section { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 0; +} + +.voucher-info-card { + display: flex; + flex-wrap: wrap; + gap: 24px; + padding: 14px 18px; + border-radius: 10px; + background: var(--mat-sys-surface-container-low, #f8fafc); + border: 1px solid var(--mat-sys-outline-variant, #e2e8f0); +} + +.info-item { + display: flex; + flex-direction: column; + gap: 3px; +} + +.info-label { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--mat-sys-on-surface-variant, #64748b); + font-weight: 500; +} + +.info-value { + font-size: 1rem; +} + +.status-pill { + display: inline-block; + font-size: 0.75rem; + font-weight: 600; + padding: 2px 8px; + border-radius: 4px; + width: fit-content; +} + +.status-pill.posted { + background: #dcfce7; + color: #166534; +} + +/* Tables */ +.full-width { + width: 100%; +} + +.items-table, +.journals-table, +.summary-table { + border-radius: 8px; + overflow: hidden; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + border: 1px solid var(--mat-sys-outline-variant, #e2e8f0); +} + +.product-name { + font-weight: 500; +} + +/* Narration */ +.narration-box { + display: flex; + gap: 8px; + padding: 12px 16px; + border-radius: 8px; + background: var(--mat-sys-surface-container-lowest, #ffffff); + border: 1px solid var(--mat-sys-outline-variant, #e2e8f0); + font-size: 0.9rem; +} + +.narration-label { + font-weight: 600; + color: var(--mat-sys-on-surface-variant, #64748b); + white-space: nowrap; +} + +.narration-text { + color: var(--mat-sys-on-surface, #1e293b); +} + +/* Journals Section */ +.journals-section { + margin-top: 8px; +} + +.section-title { + margin: 0 0 8px 0; + font-size: 1rem; + font-weight: 600; + color: var(--mat-sys-on-surface, #1e293b); +} + +/* Metadata Footer */ +.metadata-footer { + display: flex; + justify-content: flex-end; + padding: 12px 0 6px; + border-top: 1px solid var(--mat-sys-outline-variant, #e2e8f0); + margin-top: 8px; +} + +.metadata-text { + font-size: 0.85rem; + color: var(--mat-sys-on-surface-variant, #64748b); +} + +/* Day Summary Tab */ +.summary-header-card { + display: flex; + flex-wrap: wrap; + gap: 20px; + padding: 16px 20px; + border-radius: 10px; + background: var(--mat-sys-surface-container-low, #f8fafc); + border: 1px solid var(--mat-sys-outline-variant, #e2e8f0); + margin-bottom: 8px; +} + +.summary-metric { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 140px; +} + +.metric-label { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--mat-sys-on-surface-variant, #64748b); + font-weight: 500; +} + +.metric-value { + font-size: 1.25rem; +} + +/* Empty State */ +.empty-state-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 16px; + text-align: center; + color: var(--mat-sys-on-surface-variant, #64748b); +} + +.empty-icon { + font-size: 48px; + width: 48px; + height: 48px; + margin-bottom: 12px; + opacity: 0.5; +} + +.empty-state-container h3 { + margin: 0 0 6px 0; + font-size: 1.2rem; + font-weight: 600; + color: var(--mat-sys-on-surface, #1e293b); +} + +.empty-state-container p { + margin: 0; + font-size: 0.95rem; +} diff --git a/overlord/src/app/sale/sale.component.html b/overlord/src/app/sale/sale.component.html new file mode 100644 index 00000000..a12ef12b --- /dev/null +++ b/overlord/src/app/sale/sale.component.html @@ -0,0 +1,299 @@ +
+
+ +
+

Sale Voucher

+ Read-only +
+
+ + @if (dayTotalAmount() > 0) { +
+ Day Total: + {{ dayTotalAmount() | currency: 'INR' }} + + ({{ costCentres().length }} cost centre{{ costCentres().length === 1 ? '' : 's' }}) + +
+ } +
+ +
+ + Business Date + + + + +
+ +@if (dayResource.isLoading()) { + +} @else if (dayResource.error(); as err) { + +} @else if (costCentres().length === 0) { +
+ receipt_long +

No Sale Vouchers

+

There are no sale vouchers recorded for the day.

+
+} @else { + + @for (tab of costCentres(); track trackByTabId($index, tab)) { + + +
+ {{ tab.costCentreName }} + {{ tab.amount | currency: 'INR' }} +
+
+ +
+ +
+
+ Cost Centre + {{ tab.costCentreName }} +
+
+ Date + {{ tab.voucher.date }} +
+
+ Amount + {{ tab.amount | currency: 'INR' }} +
+
+ Status + Posted +
+
+ + + + + + Product + + {{ row.batch?.sku?.name || row.batch?.name }} + + Total + + + + + Quantity + + {{ row.quantity | number: '1.2-2' }} + + + {{ activeVoucherQuantity() | number: '1.2-2' }} + + + + + + Rate + + {{ row.rate | currency: 'INR' }} + + + + + + + Tax + + {{ row.tax | percent: '1.0-2' }} + + + + + + + Discount + + {{ row.discount | percent: '1.0-2' }} + + + + + + + Amount + + {{ row.amount | currency: 'INR' }} + + + {{ tab.amount | currency: 'INR' }} + + + + + + + + + + @if (tab.voucher.narration) { +
+ Narration: + {{ tab.voucher.narration }} +
+ } + + +
+

Accounting Entries

+ + + Account + {{ j.account?.name }} + Total + + + + Cost Centre + {{ j.costCentre?.name }} + + + + + Debit + + {{ j.debit === 1 ? (j.amount | currency: 'INR') : '' }} + + + {{ tab.amount | currency: 'INR' }} + + + + + Credit + + {{ j.debit === -1 ? (j.amount | currency: 'INR') : '' }} + + + {{ tab.amount | currency: 'INR' }} + + + + + + + +
+ + +
+ +
+
+
+ } + + + + +
+ analytics + Day Summary +
+
+ +
+
+
+ Business Date + {{ dayData()?.date }} +
+
+ Total Cost Centres + {{ costCentres().length }} +
+
+ Total Items Sold + {{ dayTotalItems() | number: '1.0-0' }} +
+
+ Grand Total Sales + {{ dayTotalAmount() | currency: 'INR' }} +
+
+ + + + + Cost Centre + {{ row.costCentreName }} + Grand Total + + + + + Line Items + {{ row.itemCount | number: '1.0-0' }} + + {{ dayTotalItems() | number: '1.0-0' }} + + + + + + Total Sale + + {{ row.amount | currency: 'INR' }} + + + {{ dayTotalAmount() | currency: 'INR' }} + + + + + + Action + + + + + + + + + + +
+
+
+} diff --git a/overlord/src/app/sale/sale.component.ts b/overlord/src/app/sale/sale.component.ts new file mode 100644 index 00000000..66d2136d --- /dev/null +++ b/overlord/src/app/sale/sale.component.ts @@ -0,0 +1,196 @@ +import { CurrencyPipe, DecimalPipe, Location, PercentPipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, inject, input, linkedSignal, signal } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDatepickerInputEvent, MatDatepickerModule } from '@angular/material/datepicker'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; + +import { formatIsoDate } from '../core/date-utils'; +import { Inventory } from '../core/inventory'; +import { Journal } from '../core/journal'; +import { ErrorStateComponent } from '../shared/error-state/error-state.component'; +import { LocalTimePipe } from '../shared/local-time.pipe'; +import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; +import { CostCentreTab } from './sale'; +import { SaleService } from './sale.service'; + +@Component({ + selector: 'app-sale', + templateUrl: './sale.component.html', + styleUrls: ['./sale.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + DecimalPipe, + PercentPipe, + CurrencyPipe, + LocalTimePipe, + MatButtonModule, + MatIconModule, + MatTableModule, + MatSortModule, + MatTabsModule, + MatFormFieldModule, + MatInputModule, + MatDatepickerModule, + SkeletonLoaderComponent, + ErrorStateComponent, + ], +}) +export class SaleComponent { + private saleSer = inject(SaleService); + private location = inject(Location); + + id = input(null, { transform: (v: string | null | undefined) => v ?? null }); + dateParam = signal(null); + + dayResource = this.saleSer.getDay(this.id, this.dateParam); + dayData = computed(() => this.dayResource.value() ?? null); + + vouchers = computed(() => this.dayData()?.vouchers ?? []); + + costCentres = computed(() => { + return this.vouchers().map((v) => { + const amount = v.inventories?.reduce((acc, inv) => acc + (inv.amount || 0), 0) ?? 0; + let ccName = v.source?.name || v.journals?.find((j) => j.debit === -1)?.costCentre?.name || ''; + if (!ccName && v.narration) { + const match = v.narration.match(/\(([^)]+)\)/); + if (match) { + ccName = match[1]; + } + } + if (!ccName) { + ccName = 'Cost Centre'; + } + return { + id: v.id ?? '', + costCentreId: v.source?.id ?? '', + costCentreName: ccName, + amount, + itemCount: v.inventories?.length ?? 0, + voucher: v, + }; + }); + }); + + activeCostCentreIndex = linkedSignal({ + source: this.costCentres, + computation: (tabs) => { + const selectedId = this.dayData()?.selectedVoucherId ?? this.id(); + if (selectedId) { + const idx = tabs.findIndex((t) => t.id === selectedId); + if (idx !== -1) return idx; + } + return 0; + }, + }); + + activeTab = computed(() => { + const tabs = this.costCentres(); + const idx = this.activeCostCentreIndex(); + return idx >= 0 && idx < tabs.length ? tabs[idx] : null; + }); + + activeVoucher = computed(() => this.activeTab()?.voucher ?? null); + + dayTotalAmount = computed(() => { + return this.costCentres().reduce((acc, c) => acc + c.amount, 0); + }); + + dayTotalItems = computed(() => { + return this.costCentres().reduce((acc, c) => acc + c.itemCount, 0); + }); + + activeVoucherQuantity = computed(() => { + const v = this.activeVoucher(); + return v?.inventories?.reduce((acc, inv) => acc + (inv.quantity || 0), 0) ?? 0; + }); + + // Sorting + sortActive = signal('product'); + sortDirection = signal<'asc' | 'desc' | ''>('asc'); + + sortedInventories = computed(() => { + const items = [...(this.activeVoucher()?.inventories ?? [])]; + const active = this.sortActive(); + const direction = this.sortDirection(); + if (!active || !direction) return items; + + return items.sort((a, b) => { + let valA: string | number; + let valB: string | number; + switch (active) { + case 'product': + valA = (a.batch?.sku?.name ?? '').toLowerCase(); + valB = (b.batch?.sku?.name ?? '').toLowerCase(); + break; + case 'quantity': + valA = a.quantity ?? 0; + valB = b.quantity ?? 0; + break; + case 'rate': + valA = a.rate ?? 0; + valB = b.rate ?? 0; + break; + case 'tax': + valA = a.tax ?? 0; + valB = b.tax ?? 0; + break; + case 'discount': + valA = a.discount ?? 0; + valB = b.discount ?? 0; + break; + case 'amount': + valA = a.amount ?? 0; + valB = b.amount ?? 0; + break; + default: + return 0; + } + const cmp = valA < valB ? -1 : valA > valB ? 1 : 0; + return direction === 'asc' ? cmp : -cmp; + }); + }); + + inventoryColumns = ['product', 'quantity', 'rate', 'tax', 'discount', 'amount']; + journalColumns = ['account', 'costCentre', 'debit', 'credit']; + summaryColumns = ['costCentre', 'itemCount', 'amount', 'action']; + + sortData(sort: Sort): void { + this.sortActive.set(sort.active); + this.sortDirection.set(sort.direction); + } + + onDateChange(event: MatDatepickerInputEvent): void { + if (event.value) { + this.dateParam.set(formatIsoDate(event.value)); + } + } + + selectCostCentreTab(index: number): void { + this.activeCostCentreIndex.set(index); + } + + goBack(): void { + this.location.back(); + } + + retry(): void { + this.dayResource.reload(); + } + + trackByTabId(_index: number, item: CostCentreTab): string { + return item.id; + } + + trackByInventoryId(index: number, item: Inventory): string { + return item.id ?? `inv-${index}`; + } + + trackByJournalId(index: number, item: Journal): string { + return item.id ?? `j-${index}`; + } +} diff --git a/overlord/src/app/sale/sale.routes.ts b/overlord/src/app/sale/sale.routes.ts new file mode 100644 index 00000000..ad41772b --- /dev/null +++ b/overlord/src/app/sale/sale.routes.ts @@ -0,0 +1,17 @@ +import { Routes } from '@angular/router'; + +import { authGuard } from '../auth/auth-guard.service'; +import { SaleComponent } from './sale.component'; + +export const routes: Routes = [ + { + path: '', + component: SaleComponent, + canActivate: [authGuard], + }, + { + path: ':id', + component: SaleComponent, + canActivate: [authGuard], + }, +]; diff --git a/overlord/src/app/sale/sale.service.ts b/overlord/src/app/sale/sale.service.ts new file mode 100644 index 00000000..77183b04 --- /dev/null +++ b/overlord/src/app/sale/sale.service.ts @@ -0,0 +1,22 @@ +import { httpResource } from '@angular/common/http'; +import { Injectable, ResourceRef, Signal } from '@angular/core'; + +import { SaleDayResponse } from './sale'; + +const url = '/api/sale'; + +@Injectable({ + providedIn: 'root', +}) +export class SaleService { + getDay(id: Signal, date: Signal): ResourceRef { + return httpResource(() => { + const i = id(); + if (i) { + return `${url}/${i}`; + } + const d = date(); + return d ? `${url}?d=${d}` : `${url}`; + }); + } +} diff --git a/overlord/src/app/sale/sale.ts b/overlord/src/app/sale/sale.ts new file mode 100644 index 00000000..c48507f7 --- /dev/null +++ b/overlord/src/app/sale/sale.ts @@ -0,0 +1,17 @@ +import { Voucher } from '../core/voucher'; + +export interface SaleDayResponse { + date: string; + selectedVoucherId: string | null; + totalAmount: number; + vouchers: Voucher[]; +} + +export interface CostCentreTab { + id: string; + costCentreId: string; + costCentreName: string; + amount: number; + itemCount: number; + voucher: Voucher; +} diff --git a/overlord/src/app/sales-import/sales-import.component.css b/overlord/src/app/sales-import/sales-import.component.css index e1ff5cbd..4848f35e 100644 --- a/overlord/src/app/sales-import/sales-import.component.css +++ b/overlord/src/app/sales-import/sales-import.component.css @@ -68,6 +68,29 @@ background-color: rgba(0, 0, 0, 0.04); } +tr.selected td { + border-bottom-color: transparent; +} + +tr.detail-row { + height: 0; +} + +tr.detail-row.collapsed { + display: none; +} + +.detail-cell { + padding: 12px 16px 16px !important; + background-color: rgba(0, 0, 0, 0.02); +} + +.vouchers-section { + display: flex; + flex-direction: column; + gap: 8px; +} + .visually-hidden { position: absolute; width: 1px; diff --git a/overlord/src/app/sales-import/sales-import.component.html b/overlord/src/app/sales-import/sales-import.component.html index 61acd715..a53f5f56 100644 --- a/overlord/src/app/sales-import/sales-import.component.html +++ b/overlord/src/app/sales-import/sales-import.component.html @@ -62,7 +62,7 @@
- +
+ + + +
Expand @@ -95,59 +95,69 @@ Amount {{ day.amount | number: '1.2-2' }} + @if (expanded() === day) { +
+ @for (voucher of day.vouchers; track trackVoucher($index, voucher)) { + + + {{ voucher.saleCategoryName }} → {{ voucher.costCentre.name }} + + {{ voucher.action }}  + {{ voucher.amount | number: '1.2-2' }} + + +

{{ voucher.narration }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Product{{ line.name }} ({{ line.units }})Quantity{{ line.quantity }}Rate{{ line.rate | number: '1.2-2' }}Tax{{ line.taxRate }}Discount{{ line.discount }}Amount{{ line.amount | number: '1.2-2' }}Happy Hour{{ line.isHappyHour ? 'Yes' : '' }}
+
+ } +
+ } +
- @if (expanded(); as day) { -
- @for (voucher of day.vouchers; track trackVoucher($index, voucher)) { - - - {{ voucher.saleCategoryName }} → {{ voucher.costCentre.name }} - - {{ voucher.action }} - {{ voucher.amount | number: '1.2-2' }} - - -

{{ voucher.narration }}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Product{{ line.name }} ({{ line.units }})Quantity{{ line.quantity }}Rate{{ line.rate | number: '1.2-2' }}Tax{{ line.taxRate }}Discount{{ line.discount }}Amount{{ line.amount | number: '1.2-2' }}Happy Hour{{ line.isHappyHour ? 'Yes' : '' }}
-
- } -
- } -
- @if (selectedProductGroup()?.nutritional ?? false) { + @if (form.productForm.productGroup().value().nutritional ?? false) {

Nutritional Information

@@ -103,7 +103,7 @@
} - @if (selectedProductGroup()?.iceCream ?? false) { + @if (form.productForm.productGroup().value().iceCream ?? false) {

Ice Cream Information

@@ -173,7 +173,7 @@ Product Group - {{ getPGName(p.productGroup) }} + {{ p.productGroup.name }} diff --git a/overlord/src/app/temporal-product/temporal-product-detail/temporal-product-detail.component.ts b/overlord/src/app/temporal-product/temporal-product-detail/temporal-product-detail.component.ts index c11eab60..f08610a6 100644 --- a/overlord/src/app/temporal-product/temporal-product-detail/temporal-product-detail.component.ts +++ b/overlord/src/app/temporal-product/temporal-product-detail/temporal-product-detail.component.ts @@ -13,8 +13,8 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; -import moment from 'moment'; +import { formatDisplayDate, formatIsoDate, parseDate } from '../../core/date-utils'; import { ProductGroup } from '../../core/product-group'; import { ProductGroupService } from '../../product-group/product-group.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; @@ -32,7 +32,7 @@ export interface TemporalProductFormData { fractionUnits: string; isPurchased: boolean; isSold: boolean; - productGroup: string; + productGroup: ProductGroup; allergen: string; protein: number; carbohydrate: number; @@ -78,7 +78,7 @@ function createEmptyProductForm(): TemporalProductFormData { fractionUnits: '', isPurchased: true, isSold: false, - productGroup: '', + productGroup: new ProductGroup(), allergen: '', protein: 0, carbohydrate: 0, @@ -170,7 +170,7 @@ export class TemporalProductDetailComponent { fractionUnits: p.fractionUnits || '', isPurchased: p.isPurchased ?? true, isSold: p.isSold ?? false, - productGroup: p.productGroup?.id ?? '', + productGroup: p.productGroup ?? new ProductGroup(), allergen: p.allergen || '', protein: p.protein || 0, carbohydrate: p.carbohydrate || 0, @@ -185,8 +185,8 @@ export class TemporalProductDetailComponent { otherSolids: p.otherSolids || 0, totalSolids: p.totalSolids || 0, water: p.water || 0, - validFrom: p.validFrom ? moment(p.validFrom, 'DD-MMM-YYYY').toDate() : null, - validTill: p.validTill ? moment(p.validTill, 'DD-MMM-YYYY').toDate() : null, + validFrom: parseDate(p.validFrom, null), + validTill: parseDate(p.validTill, null), })); const skus: TemporalProductSkuFormData[] = (itemVal.skus || []).map((s) => ({ @@ -197,8 +197,8 @@ export class TemporalProductDetailComponent { productYield: s.productYield ?? 1, costPrice: s.costPrice ?? 0, salePrice: s.salePrice ?? 0, - validFrom: s.validFrom ? moment(s.validFrom, 'DD-MMM-YYYY').toDate() : null, - validTill: s.validTill ? moment(s.validTill, 'DD-MMM-YYYY').toDate() : null, + validFrom: parseDate(s.validFrom, null), + validTill: parseDate(s.validTill, null), })); return { @@ -215,25 +215,14 @@ export class TemporalProductDetailComponent { productGroupsResource = this.pgSer.list(); productGroups = computed(() => this.productGroupsResource.value() || []); - selectedProductGroup = computed(() => { - const pgId = this.form.productForm.productGroup().value(); - return this.productGroups().find((x: ProductGroup) => x.id === pgId); - }); - constructor() { afterNextRender(() => { this.form.productForm.name().focusBoundControl(); }); } - getPGName(pgId?: string): string { - if (!pgId) return ''; - return this.productGroups().find((x: ProductGroup) => x.id === pgId)?.name ?? ''; - } - formatDateOrNull(d: Date | string | null | undefined): string { - if (!d) return ''; - return moment(d).format('DD-MMM-YYYY'); + return formatDisplayDate(parseDate(d, null)); } editProduct(row: TemporalProductFormData) { @@ -338,7 +327,7 @@ export class TemporalProductDetailComponent { prod.fractionUnits = p.fractionUnits ?? ''; prod.isPurchased = p.isPurchased ?? true; prod.isSold = p.isSold ?? false; - prod.productGroup = this.productGroups().find((x: ProductGroup) => x.id === p.productGroup); + prod.productGroup = p.productGroup; prod.allergen = p.allergen ?? ''; prod.protein = p.protein || 0; @@ -356,8 +345,8 @@ export class TemporalProductDetailComponent { prod.totalSolids = p.totalSolids || 0; prod.water = p.water || 0; - prod.validFrom = p.validFrom ? this.formatDateOrNull(p.validFrom) : null; - prod.validTill = p.validTill ? this.formatDateOrNull(p.validTill) : null; + prod.validFrom = p.validFrom ? formatIsoDate(p.validFrom) : null; + prod.validTill = p.validTill ? formatIsoDate(p.validTill) : null; return prod; }); @@ -370,8 +359,8 @@ export class TemporalProductDetailComponent { sku.productYield = Number(s.productYield) || 1; sku.costPrice = Number(s.costPrice) || 0; sku.salePrice = Number(s.salePrice) || 0; - sku.validFrom = s.validFrom ? this.formatDateOrNull(s.validFrom) : null; - sku.validTill = s.validTill ? this.formatDateOrNull(s.validTill) : null; + sku.validFrom = s.validFrom ? formatIsoDate(s.validFrom) : null; + sku.validTill = s.validTill ? formatIsoDate(s.validTill) : null; return sku; }); diff --git a/overlord/src/app/trial-balance/trial-balance.component.ts b/overlord/src/app/trial-balance/trial-balance.component.ts index 05867820..79dc66aa 100644 --- a/overlord/src/app/trial-balance/trial-balance.component.ts +++ b/overlord/src/app/trial-balance/trial-balance.component.ts @@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatSortModule, Sort } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import moment from 'moment'; +import { formatIsoDate, parseDate } from '../core/date-utils'; import { AccountingPipe } from '../shared/accounting.pipe'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; @@ -61,7 +61,7 @@ export class TrialBalanceComponent { formModel = linkedSignal({ source: this.info, computation: (v): TrialBalanceFormData => ({ - date: v.date ? moment(v.date, 'DD-MMM-YYYY').toDate() : new Date(), + date: parseDate(v.date), }), }); @@ -120,7 +120,7 @@ export class TrialBalanceComponent { const formModel = this.formModel(); return new TrialBalance({ - date: moment(formModel.date).format('DD-MMM-YYYY'), + date: formatIsoDate(formModel.date), }); } diff --git a/overlord/yarn.lock b/overlord/yarn.lock index c5593b9d..1955c100 100644 --- a/overlord/yarn.lock +++ b/overlord/yarn.lock @@ -10,18 +10,18 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@angular-devkit/architect@0.2201.4", "@angular-devkit/architect@>= 0.2200.0 < 0.2300.0": - version "0.2201.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.2201.4.tgz#55784db27897aaeaed48b1d93fce1a0007542a62" - integrity sha512-a2J9hdBYFobhMx9G0KwCdo6+LrWIOOyIeijrA/QSZiPOjg8dAprhMh7BEQkF3eT6R1q7wDGP+IElVR9LsBlXQw== +"@angular-devkit/architect@0.2201.8", "@angular-devkit/architect@>= 0.2200.0 < 0.2300.0": + version "0.2201.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.2201.8.tgz#87c8592ea017044243396f3b5372c760af7231e4" + integrity sha512-EUQo8RDS1my2Bo5FRS+gBYgz1/klfIp9XESfMpTBO04nBkjF6DkPCeOxeAf1CYjWy9nCXcWn968eSdEhV5jXiA== dependencies: - "@angular-devkit/core" "22.1.4" + "@angular-devkit/core" "22.1.8" rxjs "7.8.2" -"@angular-devkit/core@22.1.4", "@angular-devkit/core@>= 22.0.0 < 23.0.0": - version "22.1.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-22.1.4.tgz#ac3401d204e761e7cb7f22f02d31bc9cf5d1e161" - integrity sha512-40jG/6chng9fQuWtpg1kadPO17sRTurNh0aav6Uxebn65PfNpTpvhPIYVIcRgv4tCKdMd++YH95A8SiaIOwqig== +"@angular-devkit/core@22.1.8", "@angular-devkit/core@>= 22.0.0 < 23.0.0": + version "22.1.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-22.1.8.tgz#7563f8dddc62831055fdade259e2abfc815ee8f4" + integrity sha512-34lsgg2FwMVBX7nR/ZqzRUcdvdYPvSB8XAEr2GudXAyZwLI9ehzfQlagAWR/gEb8p4uCFZW0r7uU0toxydq4Rw== dependencies: ajv "8.20.0" ajv-formats "3.0.1" @@ -30,91 +30,84 @@ rxjs "7.8.2" source-map "0.7.6" -"@angular-devkit/schematics@22.1.4", "@angular-devkit/schematics@>= 22.0.0 < 23.0.0": - version "22.1.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-22.1.4.tgz#087ef5b69e3d52d458d44fb0738584f53badef41" - integrity sha512-UBJ7jc7B0vRfmrVwlvpmuudo1D7AfpO6ugf1BxvkM4sw5m8cJpZtaIJFKBorv0QZo78CyDOroQfQdXaijjB2ZA== +"@angular-devkit/schematics@22.1.8", "@angular-devkit/schematics@>= 22.0.0 < 23.0.0": + version "22.1.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-22.1.8.tgz#5030d37343d1eb919af8686c51ec1320cb541bf2" + integrity sha512-Pv3cPa/44kvEwYcqwx4Ns5dhIDmcFNSHhbpheqiEG1Z/u4e2t4zHKDtE3eHZB+8o+IcC7xJj+d+AqGR44RoZDA== dependencies: - "@angular-devkit/core" "22.1.4" + "@angular-devkit/core" "22.1.8" jsonc-parser "3.3.1" magic-string "1.0.0" ora "9.4.1" rxjs "7.8.2" -"@angular-eslint/builder@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-22.1.0.tgz#d38388a397239e2c65addb0ca6428dd4e7199ace" - integrity sha512-gmdk06PK0whNJlapIQjRnhud2/n+oIIDBIoLUHWFFxhHRs+ZstVc6+81gf+sYDOm1Ehmb0V3nZAg3YpT1Mk0Eg== +"@angular-eslint/builder@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-22.5.0.tgz#5406574811b65ea5a8b4e131d90260e45c46eb2d" + integrity sha512-bChGIHOApVk3eXaAfCOxxb54tToIttSzADEUpuBN6cs7Jp7OO96lNpMktv9IPvxfyVrhbriocwNy33jhpRSt0Q== dependencies: "@angular-devkit/architect" ">= 0.2200.0 < 0.2300.0" "@angular-devkit/core" ">= 22.0.0 < 23.0.0" -"@angular-eslint/bundled-angular-compiler@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-22.1.0.tgz#603becba751c43afcfa19503022e129584e49056" - integrity sha512-iOtOQ2jtrtko1rIQo6i+g3ezxGL0lyYv80j4GccFTK1JGh4K+AqYkmaBvfUfNtqoE/7VcKsOoyxaFt6iIpKhaQ== +"@angular-eslint/bundled-angular-compiler@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-22.5.0.tgz#4feaf8de27df90ca0876a58bdd0d97118b0933c8" + integrity sha512-AdvtopHjjenM02LqsgqQbpTWTbE2DqFS4ZJT8YxEIzUKUdLNwkGUgZn9WpKHBHGBvaiA3hj2BLWRCJs/vJmV3g== -"@angular-eslint/eslint-plugin-template@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-22.1.0.tgz#30642c56aa8a634dc45e2dc103840ad27937c142" - integrity sha512-GK/Mwhwvj+MX6DefD29/IfuYjcai5CsCbYJ6Qb9P6IAzZaQqw+EN+CiXxIwzMnzm2edLcJzAsX8iBk4X7sVi2Q== +"@angular-eslint/eslint-plugin-template@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-22.5.0.tgz#b22e6fa4698fa69ac30060bb7c6af7ffa3816a7d" + integrity sha512-qHi292XBe5TIFpPjEKY+NtiFGEZLrYzD8GPVzBuIruaKQz1Y//eqPp3Ve/MYYBbwdj0gD8rPZRjDIXAcsRUMVg== dependencies: - "@angular-eslint/bundled-angular-compiler" "22.1.0" - "@angular-eslint/utils" "22.1.0" + "@angular-eslint/bundled-angular-compiler" "22.5.0" + "@angular-eslint/utils" "22.5.0" aria-query "5.3.2" axobject-query "4.1.0" -"@angular-eslint/eslint-plugin@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-22.1.0.tgz#d445da381aaf79a6afbdab76a825141bddbf995a" - integrity sha512-nRwdbUiW7vF0gbwtxw2hRGPZYZCNWOTLZKw4HM+I37o5YFIA0CFLtbyRKgZWs6JZCIqzTYSwCyKNsW41Qw0FwQ== +"@angular-eslint/eslint-plugin@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-22.5.0.tgz#e966dfd185fdf9f412a0f4405bc8ca41b4d6eb10" + integrity sha512-3MooyC+F156Aa0XmNLwNMeQFVmSPhJYqPdX8FditvzSYuo5qANZTSbBABMU8XepvmxXcpPmEsj5sQUpbTdRl9Q== dependencies: - "@angular-eslint/bundled-angular-compiler" "22.1.0" - "@angular-eslint/utils" "22.1.0" + "@angular-eslint/bundled-angular-compiler" "22.5.0" + "@angular-eslint/utils" "22.5.0" ts-api-utils "^2.1.0" -"@angular-eslint/schematics@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-22.1.0.tgz#e9e7bd786d8496177d85dd9180c501e69338afb7" - integrity sha512-/Z7MEO9ys9P5w5itM2mpNW4R/QNAVxtDdcU5LVFulSx/lo7zCDx1rZ/tLDuzbN5k5RGQy5ZkdRIs0MoscsGXQQ== +"@angular-eslint/schematics@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-22.5.0.tgz#62ef0264648fcd87aa479b2ea16d8b534d6467c5" + integrity sha512-4B7Bpph/H6vNI0Wfi3p4w4AKw6VJrTG9IGNbSx6NKbPzcwBSaFSuJUpIUxiuzlsaFy6csbRWU2E3UPyXjEfFew== dependencies: "@angular-devkit/core" ">= 22.0.0 < 23.0.0" "@angular-devkit/schematics" ">= 22.0.0 < 23.0.0" - "@angular-eslint/eslint-plugin" "22.1.0" - "@angular-eslint/eslint-plugin-template" "22.1.0" - ignore "7.0.5" + "@angular-eslint/eslint-plugin" "22.5.0" + "@angular-eslint/eslint-plugin-template" "22.5.0" + ignore "7.0.8" semver "7.8.5" strip-json-comments "3.1.1" -"@angular-eslint/template-parser@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-22.1.0.tgz#ff92e9a4809bc8fb0b3904c3b4aa8dcf0075fa18" - integrity sha512-gcufZLI/Rl2fOWtBk2MgMRkH1t+OrbJGxIsfT0w3pVjKm0zwi7njM/dtPbVjCHzsGhx7szQMvY0i0UwTBVYkSw== +"@angular-eslint/template-parser@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-22.5.0.tgz#f4bcedb3c3e18025a0ea4ec8994f05e9dd0ba873" + integrity sha512-p0C48htmzd1/Qum3TIQz3FZMWivbkm7x8z3Q6GY3CzNOIzVRKQU2MwZtpocXPkctJGjYnC5wJ58d8Ow4E5LVbA== dependencies: - "@angular-eslint/bundled-angular-compiler" "22.1.0" + "@angular-eslint/bundled-angular-compiler" "22.5.0" eslint-scope "9.1.2" -"@angular-eslint/utils@22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-22.1.0.tgz#d85addc71f157dc75c578f2e1909b752518ba46e" - integrity sha512-zDPJqgOxlkG20UJWsVDSvnEtC2MfIP0+yKMaQUBL+2nrEknV/fVLrz7ApnerWiDrGTcAms5si49KV10MarMpRA== +"@angular-eslint/utils@22.5.0": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-22.5.0.tgz#4a41c85705e154c1f1b7458dc6b61613c114eb6d" + integrity sha512-i106BkmVnXM3ub5nsaXmAZNk1bnajzVOwrGrEVt9U0mX22zcVlunLMWC49z2dqGjl91Ppw0Xr5ed+QrKWm9Ldg== dependencies: - "@angular-eslint/bundled-angular-compiler" "22.1.0" - -"@angular/animations@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-22.1.2.tgz#b57265dfa352c1c07396aceabf0b6e2a32a498d4" - integrity sha512-SHvopFG8I92isKE6lgoWVVmmt6fSGKt76bfrBuJ9qvrKWYqWmWUh87Z5J3XRPUM8zdzuEkXjcTbn+GTGOGRiaA== - dependencies: - tslib "^2.3.0" + "@angular-eslint/bundled-angular-compiler" "22.5.0" "@angular/build@^22.1.4": - version "22.1.4" - resolved "https://registry.yarnpkg.com/@angular/build/-/build-22.1.4.tgz#99fff97ee69d1bd912fc901e335f60dc06a7576b" - integrity sha512-y5wxiyUu1DjYNG5jiFjjgFp7O5bkyYjWzy2kfyTGq+DjKMEveTzNH3tBkZrE33EJ4wYyfx+rQ01IG9nZbhfdvA== + version "22.1.8" + resolved "https://registry.yarnpkg.com/@angular/build/-/build-22.1.8.tgz#4998b1195dd680f391fd2f93e2a5a50b6af0bd77" + integrity sha512-tw+Evk0EITb8p8dTu743ONoFztCEiyDkdRbtZsh7C0vBS7Q9ElsOcFeg2Z9OAAwrmtZMIBuW3jrdPRQp7OCSdw== dependencies: "@ampproject/remapping" "2.3.0" - "@angular-devkit/architect" "0.2201.4" + "@angular-devkit/architect" "0.2201.8" "@babel/core" "8.0.1" "@babel/helper-annotate-as-pure" "8.0.0" "@babel/helper-split-export-declaration" "7.24.7" @@ -125,7 +118,7 @@ esbuild "0.28.2" https-proxy-agent "9.1.0" jsonc-parser "3.3.1" - listr2 "10.2.2" + listr2 "11.0.0" magic-string "1.0.0" mrmime "2.0.1" oxc-parser "0.142.0" @@ -143,27 +136,27 @@ lmdb "3.5.6" "@angular/cdk@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-22.1.2.tgz#660037c01a024584eb26269e1062421860f5ffb8" - integrity sha512-YwTzVnTgZIQDLq3lnnPLINIB24e0SiDn7XxBxHSrOzD/+Y0zLAid5XIiMALXWOqa7rfdhfVh1kEeM93RRLnllQ== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-22.1.6.tgz#5e7f6aa07bc7a87ca922458438cef66c11447bac" + integrity sha512-4eHSs/sIIp1mesNKFDkDTYDhfB/3/Bpfjzv/PbybsKVd642FTa3IAbjWf3uqHS/V7mpucF3Q5gSpirZHDgxwrg== dependencies: parse5 "^8.0.0" tslib "^2.3.0" "@angular/cli@^22.1.4": - version "22.1.4" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-22.1.4.tgz#2d143bb4eed672efa2b69c1ade9fd0215b4ecc72" - integrity sha512-Yt6q7a1vXh0ICFHnRdUOmx/gEXZ87zpkvE1eUWREUqeQ3ARkwqNZVzF3IP+s9NpDDrnc+EB9cqel9SnGZ/0z2Q== + version "22.1.8" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-22.1.8.tgz#0137f4d7925df031050405c864aea9a935763ffc" + integrity sha512-MFN/FydqDndai8vF12hDxG5RlBt1HUUheeJgJNmuHYIne4bOAfOT+xuMpIK8+gLSRwCemdqR3OMM38pnsFJ/nA== dependencies: - "@angular-devkit/architect" "0.2201.4" - "@angular-devkit/core" "22.1.4" - "@angular-devkit/schematics" "22.1.4" + "@angular-devkit/architect" "0.2201.8" + "@angular-devkit/core" "22.1.8" + "@angular-devkit/schematics" "22.1.8" "@inquirer/prompts" "8.5.2" - "@listr2/prompt-adapter-inquirer" "4.2.4" + "@listr2/prompt-adapter-inquirer" "4.2.5" "@modelcontextprotocol/sdk" "1.30.0" - "@schematics/angular" "22.1.4" + "@schematics/angular" "22.1.8" jsonc-parser "3.3.1" - listr2 "10.2.2" + listr2 "11.0.0" npm-package-arg "14.0.0" parse5-html-rewriting-stream "8.0.1" semver "7.8.5" @@ -171,16 +164,16 @@ zod "4.4.3" "@angular/common@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-22.1.2.tgz#da972b75818a8985bc464f39b0ceb1e14997e189" - integrity sha512-8TzyYPBDvSItV8Vqq8k5u8a9fWaomC9bMEr8z1vVLRZDIPhNQ/UcCyuTYbC7GZkeqrrKGu5m8tjUrA09RCwuaQ== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-22.1.6.tgz#8c63099c4d11ce684cf36add62484b3e1a831963" + integrity sha512-giuH+jJvo6YbBxbKofJCXvq6k8g1Z/xCAvh4piNFSSS6/toXTLCeZ+snr6Stw5b2wRbArM5Q5nDeuto3NqVPnQ== dependencies: tslib "^2.3.0" "@angular/compiler-cli@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-22.1.2.tgz#eb9d27221decdf25cc39bc3d2bc5acc8f29b4c41" - integrity sha512-GWRtvQQz/LvKejF/Zpls+hGnnJkvaPPtd3TZnxfZQ1fYCs7b7gpi0YM9PP02TJz51vhlsgkPYJwgtJAKDJEHNQ== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-22.1.6.tgz#986f94cdcf508e411cb5349eedc0d72a5fc24cae" + integrity sha512-C1fQuaSLnibhfbb7Im/vurdBEcfQk+/GqPkY4+dgEKd4EPleO0xWlD+k5DwyNFX8a9w7HebWfo0Zl66HuaEwOQ== dependencies: "@babel/core" "8.0.1" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -192,65 +185,58 @@ yargs "^18.0.0" "@angular/compiler@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-22.1.2.tgz#e57b92326ff025ede2d372195177a601a72cc83f" - integrity sha512-aQv0p5MeXuguCeftUUxK4H8Hbw1hC5Zyu+cFsGbsi025LZ9Ngw+BW+iUHDQZAcqHvWDw2wgGOdKh4e7IkcfRyQ== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-22.1.6.tgz#3b9dda84d85b16538b981b2106a8feb0e85c78cf" + integrity sha512-JjOUm/qD338+fGfZvxSNn/vTUiVqNwOiPzacInVUq1eVp7Jev+cnvEwXA2cFJkYZoy3Imz6wHoMvTUZNN8cbKQ== dependencies: tslib "^2.3.0" "@angular/core@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-22.1.2.tgz#bfaa88718aac9ec195014ee80807cdd71ba6af0e" - integrity sha512-t7BrgfdeqbdSbFe3CsGX8ySMeKDlZNBPxFMCOanAAqd6W0H1Kh1WFILBs3DIg0NfjpybfSRJAzKcagOFQWNtiw== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-22.1.6.tgz#bf0da7b172079b8040eb9719a05376dd9c5bafcc" + integrity sha512-3Ln9YYOhsaU2vPufnpcu6C4dlmX4e/nJTlggVcKMT7bGZH5KlEtw3h0uh9YfANv8YhQCEk1AcuRI7KpBnh5ing== dependencies: tslib "^2.3.0" "@angular/forms@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-22.1.2.tgz#4ba15aec5c11c9d80107881b34930a8252aea125" - integrity sha512-pKDYSNKje/jox74KzswBdX4sL7kJ8OFkJwLnqng8oc7a/NVl/Vf4/Xq4kyowRAdtPcZfLTUnZG6s0qbegfV5mg== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-22.1.6.tgz#4bfbace13354fde045ca3278facd71c3ecf0a85b" + integrity sha512-rfV4G4UB4l69yXSRvhaHPzTMIruvBlRO+ak9NtTcYMnsoj8O5ZCyPv0ledGIrLTBwGSI3X1j4nr9aoV+Rj/n+Q== dependencies: "@standard-schema/spec" "^1.0.0" tslib "^2.3.0" zod "^4.0.10" "@angular/language-service@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-22.1.2.tgz#83d27a8ce32c710357b2c00c1ddf014d03419d7b" - integrity sha512-5TIeUhO3xJVaAOEfwq8xFLlAy7Jlg2W62daw2LE+uoZOxsdabQLk+sBrzyy8hYf0HOYuKDiT1FxDC4C00imH8A== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-22.1.6.tgz#c36c18231c208e3d6551f11884aadfa8d83ef7d8" + integrity sha512-qsOgDUGR62lsdyJF+hoLx1kTSZtBoc+r2GJ64o6O+HqlsTPw0Q6aI6Y1qQkAurQbGa0s7qfhuXEHtKmG9MFiXA== -"@angular/material-moment-adapter@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/material-moment-adapter/-/material-moment-adapter-22.1.2.tgz#7d3901323311ef572186d37a9c5963bae9dedb82" - integrity sha512-huMm6Ev7UlVJYMie6VmkP0/Xkk+8HnoCJXblfLM0untw+7YDVTQKnft0SHCBjgjlqU6SAlOr27U2MqjBTz6/DQ== +"@angular/material-date-fns-adapter@^22.1.2": + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/material-date-fns-adapter/-/material-date-fns-adapter-22.1.6.tgz#b1ec88e4bf3e6994c9233d37046a6fe86b370002" + integrity sha512-huzuvKrjJqmy+G1SkoksUv1+RS1L/kHc+YytMF5yRCCisMajC3gBh+yqBcRUO4at2+DTEqC/NeSanqLRI4awlg== dependencies: tslib "^2.3.0" "@angular/material@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-22.1.2.tgz#18439da01958a4fae93364b133e58c1a49c05d46" - integrity sha512-KX0bVUppfwjDfUepsjpG1CAQGUGHI0l5e0QYpFcskB+1wNRCocBGsIO1br5z6neYu0rzsbhtWocjCeH05gwi7w== - dependencies: - tslib "^2.3.0" - -"@angular/platform-browser-dynamic@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-22.1.2.tgz#6f81f7c9e863bd5266193e60508f567546aae34a" - integrity sha512-nNARbs9zXfoJL72zsTso6GHt1VuIGXWEpyZtzxcC32qQFG1YbzVqGkQTwZ0mZ2/Ctv8dAZ6PPiDo8sbYvAvUDw== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/material/-/material-22.1.6.tgz#f57cc224b66a875582a24a9c6658f7ff9caedf7a" + integrity sha512-NpURtI21B7D6+DVltihVLxQWOm/cshlbJKqlUuSmyoGGO8Fvf4RxMtx6IZ5YAfVSQsuZvCvdXPc4BKUlJrqnHw== dependencies: tslib "^2.3.0" "@angular/platform-browser@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-22.1.2.tgz#0026cf8d0222dc63de306b1cac0bee1a383568d2" - integrity sha512-MQO735ve/tk4gLsWbw8NyBD/DxPfbbmf1pM/vp6K7tZz2xyysMXilqF/k8PBdQBZVOofST8S13inZHQ35nHuRA== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-22.1.6.tgz#064058a3dec346a65a485cf9b83e745281635b93" + integrity sha512-jrRi6zpdz+jOle5l0OW7QL0a8xPgdnxWT5FrJabbiNKfYzQfqKcaAu02l8uJoJxtGb8fLQ8pbjkPpZEvKO2z+w== dependencies: tslib "^2.3.0" "@angular/router@^22.1.2": - version "22.1.2" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-22.1.2.tgz#7f89569d73b7450b747800c86d3c9caedd1c2a13" - integrity sha512-C2c6NJ9HZUfb1L07M554Zpgvi0TvpOBO6Og9L36v/jsv42m7tpOenvqQVPa8R3va7eqLEszCL7wLFcWeJ8naew== + version "22.1.6" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-22.1.6.tgz#242cdb63a069bc1a4f7413dbc3d4efe83f2e6938" + integrity sha512-ex0vrkcVyJJdn7NzZXun+5ctaPPLTZJ/gE7A3g1dun7BM3g4Z6zT738mODFeJgtPtJNf4bTs2gpgvyegvzWH7w== dependencies: tslib "^2.3.0" @@ -262,10 +248,10 @@ "@babel/helper-validator-identifier" "^8.0.0" js-tokens "^10.0.0" -"@babel/compat-data@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-8.0.0.tgz#e780eb6052d8ceeaeadd3a6cd82dda470a945622" - integrity sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw== +"@babel/compat-data@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-8.0.5.tgz#b5e933559f0830bd4d71f167a7b24cbb4b0f3fd7" + integrity sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw== "@babel/core@8.0.1": version "8.0.1" @@ -289,15 +275,15 @@ obug "^2.1.1" semver "^7.7.3" -"@babel/generator@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-8.0.0.tgz#24b7b53a74fa8e74cc2377e054fecef4dcdada96" - integrity sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g== +"@babel/generator@^8.0.0", "@babel/generator@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-8.0.5.tgz#45f1aaddfc41bad6aa63ea81b59891a4cfd2e772" + integrity sha512-f/TuhuMAxJqhwxEGNsJrswuG9VHmh0oNFoQoo6TbpgtFAz9wYZXcTAcWZMHfp7ljesr0RG04bp3Aos9GI59L7w== dependencies: - "@babel/parser" "^8.0.0" - "@babel/types" "^8.0.0" - "@jridgewell/gen-mapping" "^0.3.12" - "@jridgewell/trace-mapping" "^0.3.28" + "@babel/parser" "^8.0.5" + "@babel/types" "^8.0.5" + "@jridgewell/gen-mapping" "0.4.0-beta.0" + "@jridgewell/trace-mapping" "^0.3.31" "@types/jsesc" "^2.5.0" jsesc "^3.0.2" @@ -309,15 +295,15 @@ "@babel/types" "^8.0.0" "@babel/helper-compilation-targets@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.0.tgz#63f230f427c9ea82323d829c935f6190d93b3736" - integrity sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew== + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.5.tgz#b62d771e2dff7405b1d751f60259911f198a24db" + integrity sha512-Qk8ahMGooH5mz6uuhoDvfZGkUf/Mf3RTBucVVl4MKx4LKMTv872TeW8O92h15iVtlN8wAROBIpI1aV6x1z0LCQ== dependencies: - "@babel/compat-data" "^8.0.0" + "@babel/compat-data" "^8.0.5" "@babel/helper-validator-option" "^8.0.0" browserslist "^4.24.0" lru-cache "^11.0.0" - semver "^7.7.3" + verkit "^0.3.2" "@babel/helper-globals@^8.0.0": version "8.0.0" @@ -357,19 +343,19 @@ integrity sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q== "@babel/helpers@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-8.0.0.tgz#2c612d4b7fdbd5f1d2fa7321015e36b02d1a2d79" - integrity sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg== + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-8.0.5.tgz#164c8c914eb19965e073c6193060b44edf28b9bf" + integrity sha512-fQtPOXjYOYv85PIdwotp2TJGVYOycX0PQq+l844fFAxOULtBy8BVF35GyeueX0r4KvDthqPH5xAI1clQPk/2uA== dependencies: "@babel/template" "^8.0.0" - "@babel/types" "^8.0.0" + "@babel/types" "^8.0.5" -"@babel/parser@^8.0.0", "@babel/parser@^8.0.4": - version "8.0.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-8.0.4.tgz#244e21ca23ab54c6f5373af2122cb3a618f9dd39" - integrity sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g== +"@babel/parser@^8.0.0", "@babel/parser@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-8.0.5.tgz#5b383445d883730fe5d71a3cbb45a18b62790287" + integrity sha512-51RXvQNFakaS0bTpYiGkxNbUVwkPO4kONv6EVLorZABxsx+KZ6Z7uSYvi/wmKS/+X+rfj9RvOw0/ZNh+cmI0Rw== dependencies: - "@babel/types" "^8.0.4" + "@babel/types" "^8.0.5" "@babel/runtime@^7.12.5", "@babel/runtime@^7.26.10", "@babel/runtime@^7.28.6": version "7.29.7" @@ -386,16 +372,16 @@ "@babel/types" "^8.0.0" "@babel/traverse@^8.0.0": - version "8.0.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-8.0.4.tgz#71fc3f8861a4548e7f2938fdecac73e25d1db1ed" - integrity sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg== + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-8.0.5.tgz#eb732ef01acc13f9f9393e87cc49160b3f91eb09" + integrity sha512-XFfnuvapSc/vJOcUO7kwORSvpBIvraofKEZ2dhT0PjiF21BRCD7YbAFC8UEeDJNeLoQz82/gVqzgX5hCzkCbdg== dependencies: "@babel/code-frame" "^8.0.0" - "@babel/generator" "^8.0.0" + "@babel/generator" "^8.0.5" "@babel/helper-globals" "^8.0.0" - "@babel/parser" "^8.0.4" + "@babel/parser" "^8.0.5" "@babel/template" "^8.0.0" - "@babel/types" "^8.0.4" + "@babel/types" "^8.0.5" obug "^2.1.1" "@babel/types@^7.24.7": @@ -406,14 +392,32 @@ "@babel/helper-string-parser" "^7.29.7" "@babel/helper-validator-identifier" "^7.29.7" -"@babel/types@^8.0.0", "@babel/types@^8.0.4": - version "8.0.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-8.0.4.tgz#8c208701320c9f0323b5b6a233d093e52c1e3b41" - integrity sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g== +"@babel/types@^8.0.0", "@babel/types@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-8.0.5.tgz#24f537825b6708c0f3ac98a11b3113edd26b9012" + integrity sha512-eVdMqi3ej5aHhyQ2Si6yD2cAWeV8FJK9UrhK5aL0Sd8hu5GhT+YswhVNbVheOGVYMg8kuGuMaUpkB3stjj4z8A== dependencies: "@babel/helper-string-parser" "^8.0.0" "@babel/helper-validator-identifier" "^8.0.4" +"@cacheable/memory@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cacheable/memory/-/memory-2.2.0.tgz#72aeb8b051f6d597d10ac8595b94ad8302bd2239" + integrity sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ== + dependencies: + "@cacheable/utils" "^2.5.0" + "@keyv/bigmap" "^1.3.1" + hookified "^1.15.1" + keyv "^5.6.0" + +"@cacheable/utils@^2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@cacheable/utils/-/utils-2.5.0.tgz#534c91113aa48fe43baedb169550b6ee070ef303" + integrity sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA== + dependencies: + hashery "^1.5.1" + keyv "^5.6.0" + "@emnapi/core@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.1.tgz#b9e1064f3a6b1631e241e638eb48d736bfd372a6" @@ -626,10 +630,10 @@ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091" integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw== -"@eslint/plugin-kit@^0.7.2": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz#4b0962f3f2c7ce8bc98b3ecfe34525c09d2cb729" - integrity sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A== +"@eslint/plugin-kit@^0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz#cc7268cc36405b331ef92db1bc37971f21d66fe1" + integrity sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q== dependencies: "@eslint/core" "^1.2.1" levn "^0.4.1" @@ -675,22 +679,22 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== -"@inquirer/ansi@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.7.tgz#86de22810cac3ed406ec10f8d66016815b8226b4" - integrity sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q== +"@inquirer/ansi@^2.0.7", "@inquirer/ansi@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.8.tgz#0308f3ed790dfa960f0f1f60045fa67e804de38e" + integrity sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ== "@inquirer/checkbox@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-5.2.1.tgz#7f148b3153a776cee202015b10f9a985068d188d" - integrity sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw== + version "5.2.5" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-5.2.5.tgz#336f455e5bd8167a7f3a40eeec5ff50cc9e63f27" + integrity sha512-bRt8J8m+Fot9CXv+zNQGXUq2ET0MggR1fPz7v6edN6MFYmsbfGnMmkmWZJEegMKqrAC8ej/o1sqisHZXZJMAfQ== dependencies: - "@inquirer/ansi" "^2.0.7" - "@inquirer/core" "^11.2.1" - "@inquirer/figures" "^2.0.7" - "@inquirer/type" "^4.0.7" + "@inquirer/ansi" "^2.0.8" + "@inquirer/core" "^12.0.3" + "@inquirer/figures" "^2.0.9" + "@inquirer/type" "4.1.1" -"@inquirer/confirm@6.1.1", "@inquirer/confirm@^6.1.1": +"@inquirer/confirm@6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-6.1.1.tgz#9c6a7d79c6132b2af57fdb75747f056204e55356" integrity sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ== @@ -698,6 +702,14 @@ "@inquirer/core" "^11.2.1" "@inquirer/type" "^4.0.7" +"@inquirer/confirm@^6.1.1": + version "6.3.2" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-6.3.2.tgz#c4691ec8e852f4619496606376a2061490b99e26" + integrity sha512-Xvr/0HggjddPtGppuqVmxhTw+Hr8PvsZ/k0HmOEaAqQEt80OITNkFWnsdNmyT0/eM4Ab+iJLx2R8rctlEyfSVg== + dependencies: + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" + "@inquirer/core@^11.2.1": version "11.2.1" resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-11.2.1.tgz#54ccd8f7d47852140b6066cbd77d63b2c2b168fd" @@ -711,60 +723,73 @@ mute-stream "^3.0.0" signal-exit "^4.1.0" -"@inquirer/editor@^5.2.2": - version "5.2.2" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-5.2.2.tgz#7c73e2fc0e7bd4c40cfd38a180ae5bbd24d32b90" - integrity sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg== +"@inquirer/core@^12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-12.0.3.tgz#8bbcfb31c3e04008102429d88a2d66b84257fb55" + integrity sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/external-editor" "^3.0.3" - "@inquirer/type" "^4.0.7" + "@inquirer/ansi" "^2.0.8" + "@inquirer/figures" "^2.0.9" + "@inquirer/type" "4.1.1" + cli-width "^4.1.0" + fast-wrap-ansi "^0.2.0" + mute-stream "^3.0.0" + signal-exit "^4.1.0" + +"@inquirer/editor@^5.2.2": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-5.3.3.tgz#e0d3b15e175cf59106ba2719c9b7adde54dcc835" + integrity sha512-YsKkS2q63IiLtaDK/9nqzdComN97SDQrmKiyNggN+ceP4ty+Z6VwyTz3FpjeUWeW1Efss2xHFKCC9sx7hnrsxg== + dependencies: + "@inquirer/core" "^12.0.3" + "@inquirer/external-editor" "^3.0.5" + "@inquirer/type" "4.1.1" "@inquirer/expand@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-5.1.1.tgz#e2afeac247d97dd64ee18aa81e902bdd1fe0ea70" - integrity sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g== + version "5.1.5" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-5.1.5.tgz#fbc6827c9500a9218df240b0516104a4b08069fd" + integrity sha512-uHuXLmXW+TtIfT/9vSBotypAkqn1n34Ul+CLGPos/xANyO4Ff5xZzkYhbKR4NEcfVK4a9mHQOpwVZzluSHFRGw== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/type" "^4.0.7" + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" -"@inquirer/external-editor@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-3.0.3.tgz#d79e772542cf8d340642e9dabd3a1ea7f5a30104" - integrity sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA== +"@inquirer/external-editor@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-3.0.5.tgz#e05198b6d5dc87a8993fead6d9bcf92cd4863726" + integrity sha512-f3QQJRIX5ZEneBHNUIuPjmbdzHnmRFJA8r2dkcb8q+OM5Uv5KtnuAttQumnrjcBVBM3mcTX1CkmtAkU58VRZxg== dependencies: chardet "^2.1.1" iconv-lite "^0.7.2" -"@inquirer/figures@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-2.0.7.tgz#f5cc5843732a81304d06a0db4b53cc7dbda15541" - integrity sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw== +"@inquirer/figures@^2.0.7", "@inquirer/figures@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-2.0.9.tgz#8c04fdba3a78af0e57c0b3127cbd319ce604b1c1" + integrity sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg== "@inquirer/input@^5.1.2": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-5.1.2.tgz#9305cb170dfc3a5323e5eac885a945e7cddd5c4b" - integrity sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg== + version "5.1.6" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-5.1.6.tgz#3d5ce158a9e9210f83fefbffeee0099185983c0f" + integrity sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/type" "^4.0.7" + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" "@inquirer/number@^4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-4.1.1.tgz#b133668d8e0e099b4133abb915221501e0ff75d7" - integrity sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA== + version "4.2.3" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-4.2.3.tgz#671940419283fc8e534e557d675799901e184c1b" + integrity sha512-6Yuwh1NGSbu1Lo4N1EWjXs1jKRntLg/ZCwhmeorEHde90v1XxAozdbd4Iu30eOQLW+6h1hp2O9ujNfLSbTPJnA== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/type" "^4.0.7" + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" "@inquirer/password@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-5.1.1.tgz#f21efb614da9c905095262f51781fd2a721fceac" - integrity sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg== + version "5.2.2" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-5.2.2.tgz#8f4cd7741bd5e7b5aa732d75d143269d0d43f523" + integrity sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw== dependencies: - "@inquirer/ansi" "^2.0.7" - "@inquirer/core" "^11.2.1" - "@inquirer/type" "^4.0.7" + "@inquirer/ansi" "^2.0.8" + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" "@inquirer/prompts@8.5.2": version "8.5.2" @@ -783,38 +808,46 @@ "@inquirer/select" "^5.2.1" "@inquirer/rawlist@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-5.3.1.tgz#66f6b8e6aa82d47399c433b8262128e7c1a4f9ce" - integrity sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og== + version "5.3.5" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-5.3.5.tgz#0f0e7d71fb55511cc80e2b7fe65dfed2e1e99096" + integrity sha512-1oHky1ONfCOwNrnkQGDE1oaSij/3fI6HFMSf2H/WsGO2lEyDX9My82iggITSy9ddSZ8yk8j9v41OI0fVoSIoaA== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/type" "^4.0.7" + "@inquirer/core" "^12.0.3" + "@inquirer/type" "4.1.1" "@inquirer/search@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-4.2.1.tgz#c8f4b78ab3f866fdf0503fac0cd08c4a6661c11e" - integrity sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g== + version "4.3.3" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-4.3.3.tgz#ab0f1157cc4cf922ac918b1926468bc982fae2f6" + integrity sha512-fyuIU1Nbpvwlikjg3gXwJFDI11+EFjqQ7P+iByfmivIKQ1vmaykNrD/vy5unHuUqUpsOsnvJ25//tPF7E/RBRA== dependencies: - "@inquirer/core" "^11.2.1" - "@inquirer/figures" "^2.0.7" - "@inquirer/type" "^4.0.7" + "@inquirer/core" "^12.0.3" + "@inquirer/figures" "^2.0.9" + "@inquirer/type" "4.1.1" "@inquirer/select@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-5.2.1.tgz#3a05e76e58d9e1bb095e912c3e7093aa04cd4604" - integrity sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw== + version "5.2.5" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-5.2.5.tgz#4a1c888ba3a8f6dcb78712b99e36c6462a4759db" + integrity sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q== dependencies: - "@inquirer/ansi" "^2.0.7" - "@inquirer/core" "^11.2.1" - "@inquirer/figures" "^2.0.7" - "@inquirer/type" "^4.0.7" + "@inquirer/ansi" "^2.0.8" + "@inquirer/core" "^12.0.3" + "@inquirer/figures" "^2.0.9" + "@inquirer/type" "4.1.1" -"@inquirer/type@^4.0.6", "@inquirer/type@^4.0.7": - version "4.0.7" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-4.0.7.tgz#9c6f0d857fe6ad549a3a932343b64e76acb34b10" - integrity sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g== +"@inquirer/type@4.1.1", "@inquirer/type@^4.0.7": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-4.1.1.tgz#f865ee99f39e0951a0279f9c45ad1d260c0ce0dd" + integrity sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A== -"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": +"@jridgewell/gen-mapping@0.4.0-beta.0": + version "0.4.0-beta.0" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.4.0-beta.0.tgz#af8672fd994cd5d598f0ac59f47eca6bc62c9895" + integrity sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w== + dependencies: + "@jridgewell/sourcemap-codec" "^1.6.0-beta.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/gen-mapping@^0.3.5": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== @@ -827,12 +860,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5", "@jridgewell/sourcemap-codec@^1.6.0-beta.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz#f4c663e862f06dc98ca4d453862c46902789a18d" + integrity sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw== -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.31": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -840,12 +873,25 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@listr2/prompt-adapter-inquirer@4.2.4": - version "4.2.4" - resolved "https://registry.yarnpkg.com/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-4.2.4.tgz#28fcb5727d88e364bb4d404fefc34134ff62a41e" - integrity sha512-/KRI2DMD7JGSYaREF0Ygl7AefJ/2ase4Gc5cBiKqT5l4tFjsSJfhFGcc5nSkgl0Sp9LkCQNzl/cqbVJYP2L3dw== +"@keyv/bigmap@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@keyv/bigmap/-/bigmap-1.3.1.tgz#fc82fa83947e7ff68c6798d08907db842771ef2c" + integrity sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ== dependencies: - "@inquirer/type" "^4.0.6" + hashery "^1.4.0" + hookified "^1.15.0" + +"@keyv/serialize@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@keyv/serialize/-/serialize-1.1.1.tgz#0c01dd3a3483882af7cf3878d4e71d505c81fc4a" + integrity sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA== + +"@listr2/prompt-adapter-inquirer@4.2.5": + version "4.2.5" + resolved "https://registry.yarnpkg.com/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-4.2.5.tgz#ad8c358dda9857a9d6d803d554a6dc3448f89585" + integrity sha512-pYGy9dTdTwXdasPgyohkr0HoQ4FrkAzFnsUZl/gcnadDArbpZ8e+fgr+F9WBdNEl2y00mb9bCM4WgmoBkZJ27A== + dependencies: + "@inquirer/type" "^4.0.7" "@lmdb/lmdb-darwin-arm64@3.5.6": version "3.5.6" @@ -1044,9 +1090,9 @@ "@napi-rs/nice-win32-x64-msvc" "1.1.1" "@napi-rs/wasm-runtime@^1.1.6": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz#97e3d45d7424dc5da1d4e32f3bf3b292f6c1b44c" - integrity sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q== + version "1.2.4" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz#ae38d9ea8483942054c5d82f00c0163374cd1e5f" + integrity sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g== dependencies: "@tybys/wasm-util" "^0.10.3" @@ -1420,13 +1466,13 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@schematics/angular@22.1.4": - version "22.1.4" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-22.1.4.tgz#38c29eae18e735f85cd531a4c641ebabcf791839" - integrity sha512-uiYKuiCnkTEbs4eOU1QJlM5NPw6BR1qgpct8VlrhS4n861WCCjTy5cxiAWJ3Doc0R72d44j3uoBfAEp7KEVn2w== +"@schematics/angular@22.1.8": + version "22.1.8" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-22.1.8.tgz#c6a91a7c54e19a25ce762a473acbeaebed8b3151" + integrity sha512-V37T9uHOQVHyxxOqwcJ9xjSIW/mW9UuSfjOc7WJE4V8+3zj0abDJLHuoxDZKe0icYGapgOcvHyYjtNOjSeSivw== dependencies: - "@angular-devkit/core" "22.1.4" - "@angular-devkit/schematics" "22.1.4" + "@angular-devkit/core" "22.1.8" + "@angular-devkit/schematics" "22.1.8" jsonc-parser "3.3.1" typescript "6.0.3" @@ -1436,9 +1482,9 @@ integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== "@tybys/wasm-util@^0.10.3": - version "0.10.3" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz#015cba9e9dd47ce14d03d2a8c5d547bfb169665d" - integrity sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg== + version "0.10.4" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.4.tgz#3e85ecb266f9d1722250e89bc69528615720b154" + integrity sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A== dependencies: tslib "^2.4.0" @@ -1487,100 +1533,100 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@typescript-eslint/eslint-plugin@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz#52f9f0e47d5a7571c4336e69bfeea581509ef2cf" - integrity sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ== +"@typescript-eslint/eslint-plugin@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz#59f635c74dd1e2bffb6aecbda557b24dadffd3dc" + integrity sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.67.0" - "@typescript-eslint/type-utils" "8.67.0" - "@typescript-eslint/utils" "8.67.0" - "@typescript-eslint/visitor-keys" "8.67.0" + "@typescript-eslint/scope-manager" "8.70.0" + "@typescript-eslint/type-utils" "8.70.0" + "@typescript-eslint/utils" "8.70.0" + "@typescript-eslint/visitor-keys" "8.70.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.67.0.tgz#0158022ec9927e0afcd58a8cc2ad57e01d892f5c" - integrity sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w== +"@typescript-eslint/parser@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.70.0.tgz#96ce2de96c06c8442fea1a8f7b07855a56b3c5e3" + integrity sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q== dependencies: - "@typescript-eslint/scope-manager" "8.67.0" - "@typescript-eslint/types" "8.67.0" - "@typescript-eslint/typescript-estree" "8.67.0" - "@typescript-eslint/visitor-keys" "8.67.0" + "@typescript-eslint/scope-manager" "8.70.0" + "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/typescript-estree" "8.70.0" + "@typescript-eslint/visitor-keys" "8.70.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.67.0.tgz#1552db007ca9206a1c6c7acf49e210bd17a8c56f" - integrity sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw== +"@typescript-eslint/project-service@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.70.0.tgz#a62e837362f26c604ad15b20bacce1c3f4d6b552" + integrity sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.67.0" - "@typescript-eslint/types" "^8.67.0" + "@typescript-eslint/tsconfig-utils" "^8.70.0" + "@typescript-eslint/types" "^8.70.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz#4d4c2da09560d10dd7d947cba2d29d14d25af16d" - integrity sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg== +"@typescript-eslint/scope-manager@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz#b77628b03c9c56ef21fb5a6bc2f4a4335163b999" + integrity sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ== dependencies: - "@typescript-eslint/types" "8.67.0" - "@typescript-eslint/visitor-keys" "8.67.0" + "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/visitor-keys" "8.70.0" -"@typescript-eslint/tsconfig-utils@8.67.0", "@typescript-eslint/tsconfig-utils@^8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz#f45a3eba6b9132fb47141ec03ce2f275f1ea991d" - integrity sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg== +"@typescript-eslint/tsconfig-utils@8.70.0", "@typescript-eslint/tsconfig-utils@^8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz#f583ca72159c4fd8e775c153da3241de6b77974f" + integrity sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw== -"@typescript-eslint/type-utils@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz#96bed105275559df3bcf0449b73a6414d35c59ce" - integrity sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q== +"@typescript-eslint/type-utils@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz#7f9c01c24e56bfad2a089167926c7cdded9de5f6" + integrity sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw== dependencies: - "@typescript-eslint/types" "8.67.0" - "@typescript-eslint/typescript-estree" "8.67.0" - "@typescript-eslint/utils" "8.67.0" + "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/typescript-estree" "8.70.0" + "@typescript-eslint/utils" "8.70.0" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.67.0", "@typescript-eslint/types@^8.0.0", "@typescript-eslint/types@^8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.67.0.tgz#4a8d00cc1faba5c14feabc60f85b7a32652f34b6" - integrity sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww== +"@typescript-eslint/types@8.70.0", "@typescript-eslint/types@^8.0.0", "@typescript-eslint/types@^8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.70.0.tgz#9ee52888cdeca604fe9436935219b967fa7f6053" + integrity sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ== -"@typescript-eslint/typescript-estree@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz#116c3a47c06119c5a050e8851861d6497dd64bc2" - integrity sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw== +"@typescript-eslint/typescript-estree@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz#79cfcd9678ee28ea69cc13032c0f89bb1d298917" + integrity sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA== dependencies: - "@typescript-eslint/project-service" "8.67.0" - "@typescript-eslint/tsconfig-utils" "8.67.0" - "@typescript-eslint/types" "8.67.0" - "@typescript-eslint/visitor-keys" "8.67.0" + "@typescript-eslint/project-service" "8.70.0" + "@typescript-eslint/tsconfig-utils" "8.70.0" + "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/visitor-keys" "8.70.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.67.0", "@typescript-eslint/utils@^8.0.0", "@typescript-eslint/utils@^8.65.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.67.0.tgz#3e478a3d69d330a1fc50c12746cc2ee0732ccfcd" - integrity sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A== +"@typescript-eslint/utils@8.70.0", "@typescript-eslint/utils@^8.0.0", "@typescript-eslint/utils@^8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.70.0.tgz#78a78b4c52dd8523e5321993cb46ebe6d7934510" + integrity sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.67.0" - "@typescript-eslint/types" "8.67.0" - "@typescript-eslint/typescript-estree" "8.67.0" + "@typescript-eslint/scope-manager" "8.70.0" + "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/typescript-estree" "8.70.0" -"@typescript-eslint/visitor-keys@8.67.0": - version "8.67.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz#601d40af9acf82a28da2286f3edafc69bba9017f" - integrity sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA== +"@typescript-eslint/visitor-keys@8.70.0": + version "8.70.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz#b451c8aea76dc97fc768b8d9d7f61019bf789628" + integrity sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ== dependencies: - "@typescript-eslint/types" "8.67.0" + "@typescript-eslint/types" "8.70.0" eslint-visitor-keys "^5.0.0" "@vitejs/plugin-basic-ssl@2.3.0": @@ -1639,21 +1685,21 @@ ajv@^6.14.0: uri-js "^4.2.2" angular-eslint@^22.1.0: - version "22.1.0" - resolved "https://registry.yarnpkg.com/angular-eslint/-/angular-eslint-22.1.0.tgz#b8e91d79f994eb44ad294d3aff04367732af3dde" - integrity sha512-LU5a6MOSeQTETnc4xaHi10p9S1cstNczr93sChi3rx8IzgH4+risIsjjQHVLSnQDrSGH2O8jAGCaChker7f7TQ== + version "22.5.0" + resolved "https://registry.yarnpkg.com/angular-eslint/-/angular-eslint-22.5.0.tgz#f6ef093011e01866847b3af92fd0261c2936c9f7" + integrity sha512-qDJVh/YLYiZ7k2qZCBGsmfO+qSiHcdyQp1UFdm3eHHc3YRNlW/Xvj1wp1oDKxuHSE//aqmbVFHlkcqdwJyymdg== dependencies: "@angular-devkit/core" ">= 22.0.0 < 23.0.0" "@angular-devkit/schematics" ">= 22.0.0 < 23.0.0" - "@angular-eslint/builder" "22.1.0" - "@angular-eslint/eslint-plugin" "22.1.0" - "@angular-eslint/eslint-plugin-template" "22.1.0" - "@angular-eslint/schematics" "22.1.0" - "@angular-eslint/template-parser" "22.1.0" + "@angular-eslint/builder" "22.5.0" + "@angular-eslint/eslint-plugin" "22.5.0" + "@angular-eslint/eslint-plugin-template" "22.5.0" + "@angular-eslint/schematics" "22.5.0" + "@angular-eslint/template-parser" "22.5.0" "@typescript-eslint/types" "^8.0.0" "@typescript-eslint/utils" "^8.0.0" -ansi-escapes@^7.0.0: +ansi-escapes@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz#5395bb74b2150a4a1d6e3c2565f4aeca78d28627" integrity sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== @@ -1684,16 +1730,16 @@ array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: is-array-buffer "^3.0.5" array-includes@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" - integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.2.0.tgz#18771e6837351aa139c3b58873371d0afa64964a" + integrity sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg== dependencies: - call-bind "^1.0.8" + call-bind "^1.0.9" call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.24.0" - es-object-atoms "^1.1.1" - get-intrinsic "^1.3.0" + es-abstract "^1.24.2" + es-object-atoms "^1.1.2" + es-shim-unscopables "^1.1.0" is-string "^1.1.1" math-intrinsics "^1.1.0" @@ -1775,10 +1821,10 @@ base64-arraybuffer@^1.0.2: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== -baseline-browser-mapping@^2.11.12: - version "2.11.15" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.15.tgz#9c0cac93d7d304f3d61bb41088a102cd62e68676" - integrity sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA== +baseline-browser-mapping@^2.11.20: + version "2.11.23" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz#304c980a35de0f460cf12985359d6e11494c1ab4" + integrity sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ== beasties@0.4.3: version "0.4.3" @@ -1816,30 +1862,30 @@ boolbase@^1.0.0: integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== brace-expansion@^1.1.7: - version "1.1.18" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.18.tgz#3ce74d89885136be1535341f8c3d4425c29a5cab" - integrity sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw== + version "1.1.21" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.21.tgz#edf4fab5c64d051aea5a8def49aba1c7522279f3" + integrity sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^5.0.8: - version "5.0.9" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.9.tgz#7c72438809b5fa5babf54199a1f1c281a6984fcf" - integrity sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg== + version "5.0.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.12.tgz#995fbb4750a77c4d16a7dc942ff2ca6ef8e675ec" + integrity sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ== dependencies: balanced-match "^4.0.2" browserslist@^4.24.0, browserslist@^4.26.0: - version "4.28.8" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.8.tgz#a3c79ceb70028527e5da7dafc887f3200b5168c0" - integrity sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA== + version "4.28.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.9.tgz#07ce6b449b90af880eb9bfb7cd39372cc4f71c8c" + integrity sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg== dependencies: - baseline-browser-mapping "^2.11.12" - caniuse-lite "^1.0.30001809" - electron-to-chromium "^1.5.402" - node-releases "^2.0.53" - update-browserslist-db "^1.3.0" + baseline-browser-mapping "^2.11.20" + caniuse-lite "^1.0.30001810" + electron-to-chromium "^1.5.420" + node-releases "^2.0.54" + update-browserslist-db "^1.3.2" buffer-from@^1.0.0: version "1.1.2" @@ -1851,6 +1897,17 @@ bytes@^3.1.2, bytes@~3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacheable@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cacheable/-/cacheable-2.5.0.tgz#d142d41043e5a865f6053cc70ef4e3ad068bd5ce" + integrity sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g== + dependencies: + "@cacheable/memory" "^2.2.0" + "@cacheable/utils" "^2.5.0" + hookified "^1.15.0" + keyv "^5.6.0" + qified "^0.10.1" + call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -1877,10 +1934,10 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" -caniuse-lite@^1.0.30001809: - version "1.0.30001809" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz#e6cf71f14ddfe008f114dd2a846923be3c03a07b" - integrity sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ== +caniuse-lite@^1.0.30001810: + version "1.0.30001810" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz#4970b477dea3278374de9bc43aa8f5d39fc3cda2" + integrity sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg== canvg@^3.0.11: version "3.0.11" @@ -1925,12 +1982,12 @@ cli-spinners@^3.2.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-3.4.0.tgz#1f11f6d48c4e5bc6849fcb4efa0dc98f9e7299ea" integrity sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw== -cli-truncate@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.2.0.tgz#c8e72aaca8339c773d128c36e0a17c6315b694eb" - integrity sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw== +cli-truncate@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-6.1.1.tgz#35ae66a040eef357aedcfffd6149d847bf7a2eb1" + integrity sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw== dependencies: - slice-ansi "^8.0.0" + slice-ansi "^9.0.0" string-width "^8.2.0" cli-width@^4.1.0: @@ -1967,7 +2024,7 @@ content-type@^1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -content-type@^2.0.0: +content-type@^2.0.0, content-type@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/content-type/-/content-type-2.1.0.tgz#d9389c43c0a8cf6a355db464d21e07092a40493a" integrity sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag== @@ -2064,6 +2121,11 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +date-fns@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.4.0.tgz#806539edf45c616b2b76b5f78b88c56ed3c7e036" + integrity sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w== + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2145,9 +2207,9 @@ domhandler@^5.0.2, domhandler@^5.0.3: domelementtype "^2.3.0" dompurify@^3.3.1: - version "3.4.14" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.14.tgz#a789edb2c7bcdb69a93713a34edb7e0a5245d8c9" - integrity sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg== + version "3.4.15" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.15.tgz#30351b34513894f428c13bddb28bff0a027ae804" + integrity sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw== optionalDependencies: "@types/trusted-types" "^2.0.7" @@ -2174,10 +2236,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.5.402: - version "1.5.411" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.411.tgz#d56c7e43f91e2b3abb53b4eecd646e20d0b18b34" - integrity sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg== +electron-to-chromium@^1.5.420: + version "1.5.428" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz#36bdb39055d71ab08a54855508ef33940bc467e6" + integrity sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw== emoji-regex@^10.3.0: version "10.6.0" @@ -2205,9 +2267,9 @@ entities@^7.0.1: integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== entities@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-8.0.0.tgz#c1df5fe3602429747fa233d0dd26f142f0ce4743" - integrity sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA== + version "8.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-8.1.0.tgz#9632d69619ebdbe0cf0ef22f53088fe5a2163d86" + integrity sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA== environment@^1.0.0: version "1.1.0" @@ -2224,7 +2286,7 @@ es-abstract-get@^1.0.0: is-callable "^1.2.7" object-inspect "^1.13.4" -es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.2: +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.2: version "1.24.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.2.tgz#2dbd38c180735ee983f77585140a2706a963ed9a" integrity sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg== @@ -2429,11 +2491,11 @@ eslint-plugin-import@^2.31.0: tsconfig-paths "^3.15.0" eslint-plugin-perfectionist@^5.10.1: - version "5.10.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.10.1.tgz#3fb3401fe97eae57aa1194a1fd824ff95becb497" - integrity sha512-Kprsp9Us0GqAesYaAIzUViw57xYp5WBqzXrcE0Mtww++E5fexWXYBipMuuD7yvyH4vvpBH0+oJ+OMAmZ0oYXkw== + version "5.11.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.11.1.tgz#953768d5e3ca9de4e3d5124fe8767d43f2d8da71" + integrity sha512-xloHScnOpin9Jy3CegfzoBeY33ASk0spLyM2sJs0LyNHQjv9m0osUD2PYNT7NrJieZbUR2Ebn4A5Q0+0dOcv+A== dependencies: - "@typescript-eslint/utils" "^8.65.0" + "@typescript-eslint/utils" "^8.70.0" natural-orderby "^5.0.0" eslint-plugin-simple-import-sort@^14.0.0: @@ -2467,16 +2529,16 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== eslint@^10.8.1: - version "10.8.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.8.1.tgz#fb37d514c19b6dd5b2d6b70169fd26fddfa97967" - integrity sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ== + version "10.10.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.10.0.tgz#14b1d1849eedb2ee6805f3759050479ce2b23d71" + integrity sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" "@eslint/config-array" "^0.23.5" "@eslint/config-helpers" "^0.7.0" "@eslint/core" "^1.2.1" - "@eslint/plugin-kit" "^0.7.2" + "@eslint/plugin-kit" "^0.7.3" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" @@ -2491,7 +2553,7 @@ eslint@^10.8.1: esquery "^1.7.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^8.0.0" + file-entry-cache "11.1.5 || >11.1.6 <12" find-up "^5.0.0" glob-parent "^6.0.2" ignore "^5.2.0" @@ -2540,11 +2602,6 @@ etag@^1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -eventemitter3@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" - integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== - eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.1.1.tgz#b96cbb7dace4f3774f58a9e3b1ae9a4a524872e2" @@ -2558,9 +2615,9 @@ eventsource@^3.0.2: eventsource-parser "^3.0.1" express-rate-limit@^8.2.1: - version "8.6.2" - resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.6.2.tgz#1a6c7fb6c5f5a83883c14f1d198fcdff60087c5d" - integrity sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A== + version "8.7.0" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.7.0.tgz#87739a3535e7db3ca87b4a77b1ec5cc2c0a711e4" + integrity sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g== dependencies: debug "^4.4.3" ip-address "^10.2.0" @@ -2636,9 +2693,9 @@ fast-string-width@^3.0.2: fast-string-truncated-width "^3.0.2" fast-uri@^3.0.1: - version "3.1.5" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.5.tgz#610f37419a030270430cecd68d74e3d4d96725d0" - integrity sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw== + version "3.1.8" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.8.tgz#f7db8d942e20eead3cfe93b0beeb4eb0169e938b" + integrity sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg== fast-wrap-ansi@^0.2.0: version "0.2.2" @@ -2657,12 +2714,12 @@ fflate@^0.8.1: resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.3.tgz#bc27d8eb30343d4d512abb03480202ce65d825fc" integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA== -file-entry-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" - integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== +"file-entry-cache@11.1.5 || >11.1.6 <12": + version "11.1.5" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-11.1.5.tgz#c8210eb055de63e68685ccfb6a017e386d4577d0" + integrity sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q== dependencies: - flat-cache "^4.0.0" + flat-cache "^6.1.23" finalhandler@^2.1.0: version "2.1.1" @@ -2684,15 +2741,16 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" - integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== +flat-cache@^6.1.23: + version "6.1.23" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-6.1.23.tgz#735dc888c271868d7301b3bbb8edba5028afb61d" + integrity sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA== dependencies: - flatted "^3.2.9" - keyv "^4.5.4" + cacheable "^2.5.0" + flatted "^3.4.2" + hookified "^1.15.0" -flatted@^3.2.9: +flatted@^3.4.2: version "3.4.4" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6" integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== @@ -2858,6 +2916,13 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +hashery@^1.4.0, hashery@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/hashery/-/hashery-1.5.1.tgz#4ba82ad54911ac617467870845d57a9fe508a400" + integrity sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ== + dependencies: + hookified "^1.15.0" + hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" @@ -2866,9 +2931,19 @@ hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: function-bind "^1.1.2" hono@^4.11.4: - version "4.13.3" - resolved "https://registry.yarnpkg.com/hono/-/hono-4.13.3.tgz#9715a35f637115f7455287ce6fdddcab21653d6b" - integrity sha512-r8AO2mYHoLxSHkgafNeC/BXyb2vWRxD3jem4Ts+ptav8oTG5FIRifAjuJEmZI4bSvvc2ns0GxmIYiZnHqN3mMw== + version "4.13.8" + resolved "https://registry.yarnpkg.com/hono/-/hono-4.13.8.tgz#fdb2b060c31cafa559ebc8f8b04ab4970e659abe" + integrity sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ== + +hookified@^1.15.0, hookified@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/hookified/-/hookified-1.15.1.tgz#b1fafeaa5489cdc29cb85546a8f837ed4ffbbcb6" + integrity sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg== + +hookified@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hookified/-/hookified-2.2.0.tgz#1d024ac1668973dd5bcc4a96ab9ccdb7639ef8d4" + integrity sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA== hosted-git-info@^10.1.0: version "10.1.1" @@ -2927,10 +3002,10 @@ iconv-lite@^0.7.2, iconv-lite@~0.7.0: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ignore@7.0.5: - version "7.0.5" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" - integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== +ignore@7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.8.tgz#84d8466899958458ee30b4190c839ee1446cc88d" + integrity sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q== ignore@^5.2.0: version "5.3.2" @@ -2938,9 +3013,9 @@ ignore@^5.2.0: integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== ignore@^7.0.5: - version "7.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.6.tgz#6a57aaef4c90df27ac3590875d29e8f11988c88e" - integrity sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw== + version "7.0.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.9.tgz#475b2197ade916edab05ade35c691518e8543382" + integrity sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw== immutable@^5.1.5: version "5.1.9" @@ -2977,9 +3052,9 @@ iobuffer@^5.3.2: integrity sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA== ip-address@^10.2.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.5.0.tgz#fcd6d7dfe9e68416b7cb71afe9bc960b3e38c13b" - integrity sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g== + version "10.7.1" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.7.1.tgz#df8031c20df6b7f1dcc9f68c4f4fa5ee71c45d39" + integrity sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA== ipaddr.js@1.9.1: version "1.9.1" @@ -3069,7 +3144,7 @@ is-finalizationregistry@^1.1.0: dependencies: call-bound "^1.0.3" -is-fullwidth-code-point@^5.0.0, is-fullwidth-code-point@^5.1.0: +is-fullwidth-code-point@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== @@ -3209,9 +3284,9 @@ javascript-natural-sort@^0.7.1: integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== jose@^6.1.3: - version "6.2.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.9.tgz#344fa11af928f8000a4f81972c4d567d587170f0" - integrity sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA== + version "6.2.12" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.12.tgz#65663e146edd010b98ece83f81737d3aa95fd0d7" + integrity sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw== js-tokens@^10.0.0: version "10.0.0" @@ -3223,11 +3298,6 @@ jsesc@^3.0.2: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -3279,12 +3349,12 @@ jspdf@^4.2.0: dompurify "^3.3.1" html2canvas "^1.0.0-rc.5" -keyv@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== +keyv@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.6.0.tgz#03044074c6b4d072d0a62c7b9fa649537baf0105" + integrity sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw== dependencies: - json-buffer "3.0.1" + "@keyv/serialize" "^1.1.1" levn@^0.4.1: version "0.4.1" @@ -3369,25 +3439,23 @@ lightningcss@^1.32.0: lightningcss-win32-x64-msvc "1.33.0" lint-staged@^17.3.0: - version "17.3.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-17.3.0.tgz#f13483b12f11790746e0ec82808ca0614ba80f4b" - integrity sha512-woZS3vNe3UKqBaLPvbLOtKRY4tLANpWQhom12MGWqC8Mh1lCOO+WgSwmX2amjJAqTY9BkXYW87fCUH5H9Ph6xw== + version "17.5.1" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-17.5.1.tgz#a9222014fc2680934837b5b2214e98c050d2ea3e" + integrity sha512-7EDuco1xnBMeVpvAbeMq1U5KXwJGLmY8q6l+Ye78r36C4mPc+Vg1Z2SK8gHyRTyvPro90A0q5/FAZ+Au23d6QQ== dependencies: - picomatch "^4.0.5" + picomatch "^4.0.7" string-argv "^0.3.2" - tinyexec "^1.2.4" + tinyexec "^1.3.1" optionalDependencies: yaml "^2.9.0" -listr2@10.2.2: - version "10.2.2" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-10.2.2.tgz#01d987a6e3fb03b17612968617366f28fdeaf4d3" - integrity sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw== +listr2@11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-11.0.0.tgz#e3e792ae2a6155122abdd9e7c150d7af2fc58458" + integrity sha512-8K88S0aSrcSXdJfiZtEy5BQMnR+TyjrCGLcgAvQs6ta0NEnIm0RJ72/Pv67Jvg07cfBhDbuN74V81lSSVYEFEw== dependencies: - cli-truncate "^5.2.0" - eventemitter3 "^5.0.4" - log-update "^6.1.0" - rfdc "^1.4.1" + cli-truncate "^6.1.1" + log-update "^8.0.0" wrap-ansi "^10.0.0" lmdb@3.5.6: @@ -3425,16 +3493,17 @@ log-symbols@^7.0.1: is-unicode-supported "^2.0.0" yoctocolors "^2.1.1" -log-update@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" - integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== +log-update@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-8.0.0.tgz#07d7bde6320dda8cf6fb12619e9d30695525b141" + integrity sha512-lddSgOt3bPASrylL54ZSpy8nBHns+vBVSoILlVOx+dei300pnLRN958rj/EdlVLKuWlSESU3qdnDZdAI7FXYGg== dependencies: - ansi-escapes "^7.0.0" + ansi-escapes "^7.3.0" cli-cursor "^5.0.0" - slice-ansi "^7.1.0" - strip-ansi "^7.1.0" - wrap-ansi "^9.0.0" + slice-ansi "^9.0.0" + string-width "^8.2.0" + strip-ansi "^7.2.0" + wrap-ansi "^10.0.0" lru-cache@^11.0.0, lru-cache@^11.1.0: version "11.5.2" @@ -3514,11 +3583,6 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -moment@^2.30.1: - version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - mrmime@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" @@ -3555,10 +3619,10 @@ mute-stream@^3.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-3.0.0.tgz#cd8014dd2acb72e1e91bb67c74f0019e620ba2d1" integrity sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw== -nanoid@^3.3.17: - version "3.3.18" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.18.tgz#f66a2de1199ffde0fcf21c8a5f13106b1c081913" - integrity sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w== +nanoid@^3.3.18: + version "3.3.19" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.19.tgz#336d4aa4bcd4fb24d2cddede7ffeae40bec03f0a" + integrity sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug== natural-compare@^1.4.0: version "1.4.0" @@ -3571,9 +3635,11 @@ natural-orderby@^5.0.0: integrity sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg== negotiator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" - integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + version "1.1.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.1.0.tgz#16e003d0db4ac24fd9df168edf871625deeae3df" + integrity sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg== + dependencies: + content-type "^2.1.0" node-addon-api@^6.1.0: version "6.1.0" @@ -3602,10 +3668,10 @@ node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc "^2.0.1" -node-releases@^2.0.53: - version "2.0.53" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.53.tgz#0fe5ad8a7935e075172f574e56f0c20f07fa41af" - integrity sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ== +node-releases@^2.0.54: + version "2.0.55" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.55.tgz#c52faedb68001dcfe77495b5ed1ac5827a1788fc" + integrity sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ== npm-package-arg@14.0.0: version "14.0.0" @@ -3691,9 +3757,9 @@ object.values@^1.2.1: es-object-atoms "^1.0.0" obug@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/obug/-/obug-2.1.4.tgz#9090d8a548a522517915d2aa6aae907197ac6cf8" - integrity sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA== + version "2.2.1" + resolved "https://registry.yarnpkg.com/obug/-/obug-2.2.1.tgz#9c453efeeda822050eb9774242e969c784d64d78" + integrity sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q== on-finished@^2.4.1: version "2.4.1" @@ -3862,11 +3928,16 @@ picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@4.0.5, picomatch@^4.0.4, picomatch@^4.0.5: +picomatch@4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.5.tgz#51ea57a17d86f605f81039595fbc40ed06a55fab" integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== +picomatch@^4.0.4, picomatch@^4.0.5, picomatch@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.7.tgz#6313360034ccb36b3dc61ecbdff78121f90fe21f" + integrity sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA== + piscina@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/piscina/-/piscina-5.2.0.tgz#3a9a568ab9e3a0556b5c94522ba305962de13e3b" @@ -3890,16 +3961,16 @@ postcss-media-query-parser@^0.2.3: integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig== postcss-safe-parser@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz#36e4f7e608111a0ca940fd9712ce034718c40ec0" - integrity sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A== + version "7.1.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-7.1.0.tgz#87b1416f874c318f56b34463ef7f5d49c6347c9c" + integrity sha512-1WzZxRLaAFwEh6Do+zyGpjWV3nGJNxxhuh7Ubu/q1ICImMgZJnLwhoaEpRbG4pJppp2Y1ncL9ffA2f+LhrefQg== postcss@^8.4.49, postcss@^8.5.17: - version "8.5.26" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.26.tgz#6e75135780c7e10df3433bf2266c552d35c8c620" - integrity sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ== + version "8.5.28" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.28.tgz#da4563a99a06e62d6c1cd1acae363224bcaed6e9" + integrity sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A== dependencies: - nanoid "^3.3.17" + nanoid "^3.3.18" picocolors "^1.1.1" source-map-js "^1.2.1" @@ -3919,9 +3990,9 @@ proc-log@^7.0.0: integrity sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg== proxy-addr@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + version "2.0.8" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.8.tgz#624e760fd8ab06b1b8320c2f8506d4422cf8e932" + integrity sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ== dependencies: forwarded "0.2.0" ipaddr.js "1.9.1" @@ -3936,10 +4007,17 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qified@^0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/qified/-/qified-0.10.1.tgz#0640bf21bbe6ca540db290ad8f5fde4d870b8bda" + integrity sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA== + dependencies: + hookified "^2.1.1" + qs@^6.14.0, qs@^6.15.2: - version "6.15.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b" - integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A== + version "6.16.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.16.0.tgz#c22c723a28a920f3aacdce8289fabd43eccb79fd" + integrity sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA== dependencies: es-define-property "^1.0.1" side-channel "^1.1.1" @@ -4032,11 +4110,6 @@ restore-cursor@^5.0.0: onetime "^7.0.0" signal-exit "^4.1.0" -rfdc@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" - integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== - rgbcolor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d" @@ -4287,18 +4360,10 @@ signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -slice-ansi@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" - integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== - dependencies: - ansi-styles "^6.2.1" - is-fullwidth-code-point "^5.0.0" - -slice-ansi@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-8.0.0.tgz#22d0b66d18bc5c57f488bfcf36cbde3bef731537" - integrity sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg== +slice-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-9.0.0.tgz#8ed38fdc31f16b607a56cf86c3160d2684f1b61b" + integrity sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA== dependencies: ansi-styles "^6.2.3" is-fullwidth-code-point "^5.1.0" @@ -4404,7 +4469,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -strip-ansi@^7.1.0, strip-ansi@^7.1.2: +strip-ansi@^7.1.0, strip-ansi@^7.1.2, strip-ansi@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== @@ -4443,10 +4508,10 @@ tiny-emitter@^2.1.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -tinyexec@^1.2.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.3.0.tgz#aacc1dbb1d4e93e6ad8dd64944e09f9ad147a474" - integrity sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ== +tinyexec@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.3.1.tgz#16a2e3c6e23fafce72640e678e651db36145b9c6" + integrity sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA== tinyglobby@0.2.17, tinyglobby@^0.2.15, tinyglobby@^0.2.17: version "0.2.17" @@ -4548,14 +4613,14 @@ typed-function@^4.2.1: integrity sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A== typescript-eslint@^8.67.0: - version "8.67.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.67.0.tgz#1e92de09ee0ff2d96cc0848f5e9f345ea930d963" - integrity sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg== + version "8.70.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.70.0.tgz#6c90c532079132ba7d4a386bf0882c56d82fe5af" + integrity sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw== dependencies: - "@typescript-eslint/eslint-plugin" "8.67.0" - "@typescript-eslint/parser" "8.67.0" - "@typescript-eslint/typescript-estree" "8.67.0" - "@typescript-eslint/utils" "8.67.0" + "@typescript-eslint/eslint-plugin" "8.70.0" + "@typescript-eslint/parser" "8.70.0" + "@typescript-eslint/typescript-estree" "8.70.0" + "@typescript-eslint/utils" "8.70.0" typescript@6.0.3, typescript@~6.0.3: version "6.0.3" @@ -4577,10 +4642,10 @@ unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz#a71c28dd22f505481dbc4689087b18d933e90afd" - integrity sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ== +update-browserslist-db@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz#197e21fb2561fa89f8b94fad605a2b296a18993a" + integrity sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ== dependencies: escalade "^3.2.0" picocolors "^1.1.1" @@ -4609,6 +4674,11 @@ vary@^1, vary@^1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +verkit@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/verkit/-/verkit-0.3.2.tgz#0511ea63310551b62a20c010aa820350c5c350c6" + integrity sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg== + vite@8.1.5: version "8.1.5" resolved "https://registry.yarnpkg.com/vite/-/vite-8.1.5.tgz#ccffce3ee487b1886223b24ebb27b91674827d30" @@ -4727,9 +4797,9 @@ y18n@^5.0.5: integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yaml@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" - integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== + version "2.9.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.1.tgz#c16233fb31944e705cfefaff38795587f57588ce" + integrity sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw== yargs-parser@^22.0.0: version "22.0.0" @@ -4763,7 +4833,12 @@ zod-to-json-schema@^3.25.1: resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz#3fa799a7badd554541472fb65843fdc460b2e5aa" integrity sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA== -zod@4.4.3, "zod@^3.25 || ^4.0", zod@^4.0.10: +zod@4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== + +"zod@^3.25 || ^4.0", zod@^4.0.10: + version "4.6.5" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.6.5.tgz#e3af3edbdc260239c52f73caad05dbbffe6e82cd" + integrity sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==