import Elysia, { status, t } from "elysia"; import { unitsTable } from "../db/schema"; import { createSelectSchema } from "drizzle-typebox"; import { db } from "../db"; import { and, asc, between, count, desc, eq, inArray, max, min, isNull, not, } from "drizzle-orm"; export const getUnitSchema = createSelectSchema(unitsTable); export const unitsController = new Elysia({ prefix: "/units" }) .get( "/", async ({ query: { project, unitTypes, cost, floor, area, view, order, offset, limit, }, }) => { try { return await db.query.unitsTable.findMany({ where: and( project ? eq(unitsTable.project, project) : undefined, unitTypes ? inArray(unitsTable.unitType, unitTypes) : undefined, cost ? between(unitsTable.salesPrice, cost[0], cost[1]) : undefined, floor ? between(unitsTable.floor, floor[0], floor[1]) : undefined, area ? between(unitsTable.squareFt, area[0], area[1]) : undefined, view ? eq(unitsTable.unitView, view) : undefined, not(isNull(unitsTable.unitType)) ), orderBy: order ? [ order[1] === "asc" ? asc( unitsTable[ order[0] === "cost" ? "salesPrice" : "squareFt" ] ) : desc( unitsTable[ order[0] === "cost" ? "salesPrice" : "squareFt" ] ), ] : undefined, offset, limit, }); } catch (err) { console.log((err as Error).message); return status(500, "Internal server error"); } }, { query: t.Partial( t.Object({ project: t.String(), unitTypes: t.Array( t.Union([ t.Literal("Studio Squared"), t.Literal("1 BR Squared"), t.Literal("Studio Flex"), t.Literal("2 BR Squared"), t.Literal("Studio2"), t.Literal("One Bedroom2"), t.Literal("One Bedroom Loft"), t.Literal("Two Bedroom Loft"), ]) ), cost: t.Tuple([t.Number(), t.Number()]), floor: t.Tuple([t.Number(), t.Number()]), area: t.Tuple([t.Number(), t.Number()]), view: t.Union([ t.Literal("Canal / Amenities"), t.Literal("Corner-Canal / Amenities"), t.Literal("Corner-Canal View"), t.Literal("Business Bay"), t.Literal("Park Facing"), t.Literal("Corner-Park Facing"), t.Literal("Partial Park"), t.Literal("BK/DT / Amenities"), t.Literal("Corner-BK/DT / Amenities"), t.Literal("Corner-Canal / BK/DT View"), t.Literal("Marina View"), t.Literal("Partial Marina View"), t.Literal("Partial Marina, Partial City View"), t.Literal("City view"), t.Literal("Marina View, Sea View"), t.Literal("Partial Marina View, Partial Sea View"), ]), order: t.Tuple([ t.Union([t.Literal("cost"), t.Literal("sqft")]), t.Union([t.Literal("asc"), t.Literal("desc")]), ]), offset: t.Number({ default: 0 }), limit: t.Number({ default: 16 }), }) ), } ) .get( "/count", async ({ query: { project, unitTypes, cost, floor, area, view } }) => { try { return ( await db .select({ count: count() }) .from(unitsTable) .where( and( project ? eq(unitsTable.project, project) : undefined, unitTypes ? inArray(unitsTable.unitType, unitTypes) : undefined, cost ? between(unitsTable.salesPrice, cost[0], cost[1]) : undefined, floor ? between(unitsTable.floor, floor[0], floor[1]) : undefined, area ? between(unitsTable.squareFt, area[0], area[1]) : undefined, view ? eq(unitsTable.unitView, view) : undefined, not(isNull(unitsTable.unitType)) ) ) )[0].count; } catch (err) { console.log((err as Error).message); return status(500, "Internal server error"); } }, { query: t.Partial( t.Object({ project: t.String(), unitTypes: t.Array( t.Union([ t.Literal("Studio Squared"), t.Literal("1 BR Squared"), t.Literal("Studio Flex"), t.Literal("2 BR Squared"), t.Literal("Studio2"), t.Literal("One Bedroom2"), t.Literal("One Bedroom Loft"), t.Literal("Two Bedroom Loft"), ]) ), cost: t.Tuple([t.Number(), t.Number()]), floor: t.Tuple([t.Number(), t.Number()]), area: t.Tuple([t.Number(), t.Number()]), view: t.Union([ t.Literal("Canal / Amenities"), t.Literal("Corner-Canal / Amenities"), t.Literal("Corner-Canal View"), t.Literal("Business Bay"), t.Literal("Park Facing"), t.Literal("Corner-Park Facing"), t.Literal("Partial Park"), t.Literal("BK/DT / Amenities"), t.Literal("Corner-BK/DT / Amenities"), t.Literal("Corner-Canal / BK/DT View"), t.Literal("Marina View"), t.Literal("Partial Marina View"), t.Literal("Partial Marina, Partial City View"), t.Literal("City view"), t.Literal("Marina View, Sea View"), t.Literal("Partial Marina View, Partial Sea View"), ]), }) ), } ) .get( "/filters/:filterName", async ({ query: { project, view, unitTypes, cost, floor, area }, params: { filterName }, }) => { try { const filters = and( project ? eq(unitsTable.project, project) : undefined, unitTypes && filterName !== "unitTypes" ? inArray(unitsTable.unitType, unitTypes) : undefined, view && filterName !== "views" ? eq(unitsTable.unitView, view) : undefined, cost && filterName !== "cost" ? between(unitsTable.salesPrice, cost[0], cost[1]) : undefined, floor && filterName !== "floor" ? between(unitsTable.floor, floor[0], floor[1]) : undefined, area && filterName !== "area" ? between(unitsTable.squareFt, area[0], area[1]) : undefined ); if (filterName === "unitTypes") return ( await db .selectDistinctOn([unitsTable.unitType], { unitType: unitsTable.unitType, }) .from(unitsTable) .where(filters) ) .map((unit) => unit.unitType) .filter(Boolean); else if (filterName === "views") return ( await db .selectDistinctOn([unitsTable.unitView], { unitView: unitsTable.unitView, }) .from(unitsTable) .where(filters) ) .map((unit) => unit.unitView) .filter(Boolean); else if (filterName === "cost") return ( await db .select({ min: min(unitsTable.salesPrice), max: max(unitsTable.salesPrice), }) .from(unitsTable) .where(filters) )[0]; else if (filterName === "area") return ( await db .select({ min: min(unitsTable.squareFt), max: max(unitsTable.squareFt), }) .from(unitsTable) .where(filters) )[0]; return ( await db .select({ min: min(unitsTable.floor), max: max(unitsTable.floor), }) .from(unitsTable) .where(filters) )[0]; } catch (err) { console.log((err as Error).message); return status(500, "Internal server error"); } }, { response: { 200: t.Union([ t.Array(t.String()), t.Object({ min: t.Number(), max: t.Number(), }), ]), 500: t.ObjectString({}), }, params: t.Object({ filterName: t.Union([ t.Literal("unitTypes"), t.Literal("views"), t.Literal("cost"), t.Literal("area"), t.Literal("floor"), ]), }), query: t.Partial( t.Object({ project: t.String(), unitTypes: t.Array( t.Union([ t.Literal("Studio Squared"), t.Literal("1 BR Squared"), t.Literal("Studio Flex"), t.Literal("2 BR Squared"), t.Literal("Studio2"), t.Literal("One Bedroom2"), t.Literal("One Bedroom Loft"), t.Literal("Two Bedroom Loft"), ]) ), cost: t.Tuple([t.Number(), t.Number()]), floor: t.Tuple([t.Number(), t.Number()]), area: t.Tuple([t.Number(), t.Number()]), view: t.Union([ t.Literal("Canal / Amenities"), t.Literal("Corner-Canal / Amenities"), t.Literal("Corner-Canal View"), t.Literal("Business Bay"), t.Literal("Park Facing"), t.Literal("Corner-Park Facing"), t.Literal("Partial Park"), t.Literal("BK/DT / Amenities"), t.Literal("Corner-BK/DT / Amenities"), t.Literal("Corner-Canal / BK/DT View"), t.Literal("Marina View"), t.Literal("Partial Marina View"), t.Literal("Partial Marina, Partial City View"), t.Literal("City view"), t.Literal("Marina View, Sea View"), t.Literal("Partial Marina View, Partial Sea View"), ]), }) ), } ) .get( "/get-floors-data/:project", async ({ params: { project } }) => { try { // Получаем данные о номерах квартир, этажах и типах const unitsData = await db .select() .from(unitsTable) .where(eq(unitsTable.project, decodeURIComponent(project))); // Создаем структуру для хранения данных по этажам const floorMap = new Map< string | number, { floor: number; East: { types: Record; totalUnits: number; }; West: { types: Record; totalUnits: number; }; others: { types: Record; totalUnits: number; }; } >(); // Обрабатываем данные квартир for (const unit of unitsData) { if (unit.unitNo && unit.floor && unit.unitType) { // Инициализируем данные для этажа, если их еще нет // Инициализируем данные для этажа, если их еще нет if (!floorMap.has(unit.floor)) { floorMap.set(unit.floor, { floor: unit.floor, East: { types: {}, totalUnits: 0, }, West: { types: {}, totalUnits: 0, }, others: { types: {}, totalUnits: 0, }, }); } const floorData = floorMap.get(unit.floor)!; // Увеличиваем счетчик для типа квартиры на соответствующей стороне if (unit.wing === "East") { floorData.East.types[unit.unitType] = (floorData.East.types[unit.unitType] || 0) + 1; floorData.East.totalUnits++; } else if (unit.wing === "West") { floorData.West.types[unit.unitType] = (floorData.West.types[unit.unitType] || 0) + 1; floorData.West.totalUnits++; } else { floorData.others.types[unit.unitType] = (floorData.others.types[unit.unitType] || 0) + 1; floorData.others.totalUnits++; } } } // Преобразуем Map в массив и сортируем по этажам return Array.from(floorMap.values()).sort((a, b) => a.floor - b.floor); } catch (err) { console.log((err as Error).message); return status(500, "Internal server error"); } }, { params: t.Object({ project: t.String(), }), } );