417 lines
13 KiB
TypeScript
417 lines
13 KiB
TypeScript
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<string, number>;
|
|
totalUnits: number;
|
|
};
|
|
West: {
|
|
types: Record<string, number>;
|
|
totalUnits: number;
|
|
};
|
|
others: {
|
|
types: Record<string, number>;
|
|
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(),
|
|
}),
|
|
}
|
|
);
|