and so it begins

This commit is contained in:
user
2025-10-20 18:59:38 +03:00
parent f5b99afc8f
commit e239b3bbf6
53 changed files with 4813 additions and 2887 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
export * from "@pkg/logic/domains/airport/data/entities";

View File

@@ -1,205 +0,0 @@
import { eq, type AirportsDatabase } from "@pkg/airports-db";
import { airportModel, type Airport } from "./entities";
import { ERROR_CODES, type Result } from "@pkg/result";
import { getError, Logger } from "@pkg/logger";
import { readFileSync } from "fs";
import { airport } from "@pkg/airports-db/schema";
import { chunk } from "$lib/core/array.utils";
import { COUNTRIES } from "$lib/core/countries";
export class AirportsRepository {
private db: AirportsDatabase;
constructor(db: AirportsDatabase) {
this.db = db;
}
async getAirport(query: string): Promise<Result<Airport>> {
try {
const qRes = await this.db.query.airport.findFirst({
where: eq(airport.gpsCode, query),
});
if (!qRes) {
return {
error: getError({
code: ERROR_CODES.DATABASE_ERROR,
message: "Could not find airport",
detail: "Could not find airport",
userHint:
"Please try again later, or contact us for more assistance",
}),
};
}
const parsed = airportModel.safeParse({
id: qRes.id,
type: qRes.type,
name: qRes.name,
latitudeDeg: qRes.latitudeDeg,
longitudeDeg: qRes.longitudeDeg,
elevationFt: qRes.elevationFt,
continent: qRes.continent,
isoCountry: qRes.isoCountry,
isoRegion: qRes.isoRegion,
municipality: qRes.municipality,
scheduledService: qRes.scheduledService,
gpsCode: qRes.gpsCode,
iataCode: qRes.iataCode,
localCode: qRes.localCode,
createdAt: qRes.createdAt,
updatedAt: qRes.updatedAt,
});
if (!parsed.success) {
Logger.info(parsed.error.errors);
return {
error: getError({
code: ERROR_CODES.DATABASE_ERROR,
message: "An error occcured while",
detail: "An error occured ",
userHint:
"Please try again later, or contact us for more assistance",
}),
};
}
return { data: parsed.data };
} catch (err) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "An error occcured while",
detail: "An error occured ",
userHint:
"Please try again later, or contact us for more assistance",
},
err,
),
};
}
}
async seedAirportsDb(filepath: string): Promise<Result<boolean>> {
try {
Logger.info(`Seeding airports from ${filepath}`);
const fileContent = readFileSync(filepath, {
flag: "r",
encoding: "utf-8",
});
const airportsData = JSON.parse(fileContent);
const payload = [] as any[];
const processedIataCodes = new Set<string>();
for (const [_, airportInfo] of Object.entries(airportsData)) {
const airport = airportInfo as {
icao: string;
iata: string;
name: string;
city: string;
state: string;
country: string;
elevation: number;
lat: number;
lon: number;
tz: string;
};
// Skip airports with empty IATA codes
if (!airport.iata || airport.iata.length < 1) {
continue;
}
// Skip if this IATA code has already been processed
if (processedIataCodes.has(airport.iata)) {
Logger.info(
`Skipping duplicate IATA code: ${airport.iata} for airport: ${airport.name}`,
);
continue;
}
processedIataCodes.add(airport.iata);
// Find matching country name from country code
const countryCode = airport.country;
const matchingCountry =
COUNTRIES.find((c) => c.code === countryCode)?.name ?? "";
const result = airportModel.safeParse({
id: -1,
type: "airport", // Assuming default type as "airport"
name: airport.name,
latitudeDeg: airport.lat,
longitudeDeg: airport.lon,
elevationFt: airport.elevation,
continent: "", // This field isn't in the JSON, might need a mapping function
isoCountry: countryCode,
country: matchingCountry,
isoRegion: `${countryCode}-${airport.state}`, // Constructing isoRegion from country and state
municipality: airport.city,
scheduledService: "yes", // Assuming default as "yes"
gpsCode: airport.icao,
iataCode: airport.iata,
localCode: airport.iata, // Using IATA as localCode as it's not provided
createdAt: new Date(),
updatedAt: new Date(),
});
if (!result.success) {
Logger.error(result.error.errors);
Logger.debug(airport);
continue;
}
const parsed = result.data;
payload.push({
ident: parsed.ident,
type: parsed.type,
name: parsed.name,
latitudeDeg: parsed.latitudeDeg,
longitudeDeg: parsed.longitudeDeg,
elevationFt: parsed.elevationFt,
continent: parsed.continent,
country: parsed.country,
isoCountry: parsed.isoCountry,
isoRegion: parsed.isoRegion,
municipality: parsed.municipality,
scheduledService: parsed.scheduledService,
gpsCode: parsed.gpsCode,
iataCode: parsed.iataCode,
localCode: parsed.localCode,
createdAt: parsed.createdAt,
updatedAt: parsed.updatedAt,
});
}
Logger.info(`Inserting ${payload.length} airports into the database`);
const chunkSize = 500;
for (const each of chunk(payload, chunkSize)) {
const out = await this.db
.insert(airport)
.values(each)
.returning({ insertedId: airport.id });
Logger.info(`Inserted ${out.length} airports into the database`);
}
return { data: true };
} catch (err) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Could not seed the database",
detail: "An error occurred while seeding the database",
userHint:
"Please try again later, or contact us for more assistance",
},
err,
),
};
}
}
}

View File

@@ -1,16 +1,15 @@
<script lang="ts">
import Title from "$lib/components/atoms/title.svelte";
import Icon from "$lib/components/atoms/icon.svelte";
import UsersIcon from "~icons/solar/users-group-rounded-broken";
import PackageIcon from "~icons/solar/box-broken";
import BackpackIcon from "~icons/solar/backpack-linear";
import BagIcon from "~icons/lucide/briefcase";
import SuitcaseIcon from "~icons/bi/suitcase2";
import SeatIcon from "~icons/solar/armchair-2-linear";
import type { FullOrderModel } from "$lib/domains/order/data/entities";
import { capitalize } from "$lib/core/string.utils";
import PinfoCard from "$lib/domains/passengerinfo/view/pinfo-card.svelte";
import Title from "$lib/components/atoms/title.svelte";
import { adminSiteNavMap } from "$lib/core/constants";
import { capitalize } from "$lib/core/string.utils";
import type { FullOrderModel } from "$lib/domains/order/data/entities";
import PinfoCard from "$lib/domains/passengerinfo/view/pinfo-card.svelte";
import SuitcaseIcon from "~icons/bi/suitcase2";
import BagIcon from "~icons/lucide/briefcase";
import BackpackIcon from "~icons/solar/backpack-linear";
import PackageIcon from "~icons/solar/box-broken";
import UsersIcon from "~icons/solar/users-group-rounded-broken";
let { order }: { order: FullOrderModel } = $props();

View File

@@ -0,0 +1,4 @@
<script lang="ts">
</script>
<span>Show the product list here</span>

View File

@@ -0,0 +1 @@
<span>Show the product create and details modals here</span>

View File

@@ -0,0 +1,9 @@
import { protectedProcedure } from "$lib/server/trpc/t";
import { createTRPCRouter } from "$lib/trpc/t";
export const productRouter = createTRPCRouter({
getAllProducts: protectedProcedure.query(async ({}) => {
return {};
}),
// TODO: complete the implementation of this
});

View File

@@ -1,12 +1,9 @@
import type { UserModel } from "@pkg/logic/domains/user/data/entities";
import { AirportsRepository } from "$lib/domains/airport/data/repository";
import type { FlightTicket, TicketSearchPayload } from "./data/entities";
import { TicketRepository } from "$lib/domains/ticket/data/repository";
import { Logger } from "@pkg/logger";
import { env } from "$env/dynamic/private";
import { ScrapedTicketsDataSource } from "./data/scrape.data.source";
import { TicketRepository } from "$lib/domains/ticket/data/repository";
import { db } from "@pkg/db";
import { airportsDb } from "@pkg/airports-db";
import { Logger } from "@pkg/logger";
import type { FlightTicket } from "./data/entities";
import { ScrapedTicketsDataSource } from "./data/scrape.data.source";
// if the discount is lower than 1 or more than 100, then we don't apply the discount
function applyDiscount(price: number, discountPercent: number) {
@@ -20,15 +17,9 @@ function applyDiscount(price: number, discountPercent: number) {
export class TicketController {
private repo: TicketRepository;
private airportsRepo: AirportsRepository;
constructor(repo: TicketRepository, airportsRepo: AirportsRepository) {
constructor(repo: TicketRepository) {
this.repo = repo;
this.airportsRepo = airportsRepo;
}
async getAirport(query: string) {
return this.airportsRepo.getAirport(query);
}
async createTicket(payload: FlightTicket) {
@@ -56,10 +47,6 @@ export class TicketController {
async getTicketById(id: number) {
return this.repo.getTicketById(id);
}
async seedAirportsDb(filepath: string) {
return this.airportsRepo.seedAirportsDb(filepath);
}
}
export function getTC() {
@@ -67,8 +54,5 @@ export function getTC() {
env.TICKET_SCRAPER_API_URL,
env.API_KEY,
);
return new TicketController(
new TicketRepository(db, ds),
new AirportsRepository(airportsDb),
);
return new TicketController(new TicketRepository(db, ds));
}

View File

@@ -1,10 +1,11 @@
import { userRouter } from "$lib/domains/user/domain/router";
import { createTRPCRouter } from "../t";
import { authRouter } from "$lib/domains/auth/domain/router";
import { emailAccountsRouter } from "$lib/domains/account/domain/router";
import { authRouter } from "$lib/domains/auth/domain/router";
import { ckflowRouter } from "$lib/domains/ckflow/router";
import { couponRouter } from "$lib/domains/coupon/router";
import { orderRouter } from "$lib/domains/order/domain/router";
import { productRouter } from "$lib/domains/product/router";
import { userRouter } from "$lib/domains/user/domain/router";
import { createTRPCRouter } from "../t";
export const router = createTRPCRouter({
auth: authRouter,
@@ -13,6 +14,7 @@ export const router = createTRPCRouter({
emailAccounts: emailAccountsRouter,
ckflow: ckflowRouter,
coupon: couponRouter,
product: productRouter,
});
export type Router = typeof router;

View File

@@ -1,28 +1,27 @@
<script lang="ts">
import Title from "$lib/components/atoms/title.svelte";
import { pageTitle } from "$lib/hooks/page-title.svelte";
import type { PageData } from "./$types";
import { users } from "$lib/domains/user/data/store";
import { userInfo } from "$lib/stores/session.info";
import { trpcApiStore } from "$lib/stores/api";
import { get } from "svelte/store";
import { onMount } from "svelte";
import { Button } from "$lib/components/ui/button";
import Icon from "$lib/components/atoms/icon.svelte";
import Title from "$lib/components/atoms/title.svelte";
import { Button } from "$lib/components/ui/button";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "$lib/components/ui/card";
import { users } from "$lib/domains/user/data/store";
import { pageTitle } from "$lib/hooks/page-title.svelte";
import { trpcApiStore } from "$lib/stores/api";
import { userInfo } from "$lib/stores/session.info";
import { onMount } from "svelte";
import { get } from "svelte/store";
import type { PageData } from "./$types";
// Icons
import RefreshIcon from "~icons/solar/refresh-linear";
import OrderIcon from "~icons/solar/box-minimalistic-broken";
import CheckoutIcon from "~icons/solar/cart-large-minimalistic-broken";
import AlertIcon from "~icons/solar/shield-warning-linear";
import CheckIcon from "~icons/solar/check-circle-broken";
import EyeIcon from "~icons/solar/eye-broken";
import RefreshIcon from "~icons/solar/refresh-linear";
import AlertIcon from "~icons/solar/shield-warning-linear";
pageTitle.set("Dashboard");

View File

@@ -1,21 +0,0 @@
import type { RequestHandler } from "./$types";
import { Logger } from "$lib/core/logger";
import { getTC } from "$lib/domains/ticket/controller";
import path from "path";
export const POST: RequestHandler = async () => {
Logger.debug("Seeding airports");
const tc = getTC();
const filepath = path.join(
process.cwd(),
"src/lib/domains/airport/data/airports.json",
);
const res = await tc.seedAirportsDb(filepath);
Logger.info("Done seeding airports");
return new Response(JSON.stringify(res), { status: 200 });
};

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { UserRoleMap } from "$lib/core/enums";
import { authClient } from "$lib/domains/auth/config/client";
import { onMount } from "svelte";
import { UserRoleMap } from "$lib/core/enums";
onMount(async () => {
authClient.signUp
@@ -12,6 +12,7 @@
username: "admin",
// @ts-ignore
role: UserRoleMap.ADMIN,
discountPercent: 0,
})
.then((res) => console.log(res));
});