and so it begins
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
export * from "@pkg/logic/domains/airport/data/entities";
|
||||
@@ -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,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
0
apps/admin/src/lib/domains/product/controller.ts
Normal file
0
apps/admin/src/lib/domains/product/controller.ts
Normal file
4
apps/admin/src/lib/domains/product/product-list.svelte
Normal file
4
apps/admin/src/lib/domains/product/product-list.svelte
Normal file
@@ -0,0 +1,4 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<span>Show the product list here</span>
|
||||
1
apps/admin/src/lib/domains/product/product-modals.svelte
Normal file
1
apps/admin/src/lib/domains/product/product-modals.svelte
Normal file
@@ -0,0 +1 @@
|
||||
<span>Show the product create and details modals here</span>
|
||||
9
apps/admin/src/lib/domains/product/router.ts
Normal file
9
apps/admin/src/lib/domains/product/router.ts
Normal 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
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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 });
|
||||
};
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user