feat: add outlet direction (horizontal/vertical), wall light styles, floor textures, and stretch ceiling

- Add configurable outlet direction (horizontal/vertical) stored in metadata
- Add wall light style variants (classic, pendant-globe, sconce-up, sconce-down)
- Add PBR floor textures including natural oak
- Add stretch ceiling offset support with DB migration
- Add furniture surface texture selection
- Add canvas theme colors utility for dark mode support
- Update projection views with improved rendering
- Add EN and RU translations for all new properties
This commit is contained in:
2026-04-12 20:52:49 +03:00
parent d8a914bf2a
commit 521ea5e85b
34 changed files with 1278 additions and 162 deletions
+7
View File
@@ -41,6 +41,9 @@ export type {
VerticalAnchor,
PositionAnchor,
ElectricalType,
OutletDirection,
WallLightStyle,
FurnitureTexture,
ElectricalItem,
CreateElectricalItemDto,
UpdateElectricalItemDto,
@@ -62,6 +65,10 @@ export {
OPENING_TYPES,
DOOR_OPEN_DIRECTIONS,
ELECTRICAL_TYPES,
OUTLET_DIRECTIONS,
WALL_LIGHT_STYLES,
FURNITURE_TEXTURES,
TEXTURABLE_FURNITURE,
FURNITURE_TYPES,
HORIZONTAL_ANCHORS,
VERTICAL_ANCHORS,
@@ -17,6 +17,10 @@ export const createRoomSchema = z.object({
wallHeight: z.number().positive('Wall height must be positive').optional(),
plinthHeight: z.number().min(0, 'Plinth height must be non-negative').optional(),
plinthThickness: z.number().min(0, 'Plinth thickness must be non-negative').optional(),
stretchCeilingOffset: z
.number()
.min(0, 'Stretch ceiling offset must be non-negative')
.optional(),
order: z.number().int().min(0).optional(),
posX: z.number().optional(),
posY: z.number().optional(),
@@ -35,6 +39,10 @@ export const updateRoomSchema = z.object({
wallHeight: z.number().positive('Wall height must be positive').optional(),
plinthHeight: z.number().min(0, 'Plinth height must be non-negative').optional(),
plinthThickness: z.number().min(0, 'Plinth thickness must be non-negative').optional(),
stretchCeilingOffset: z
.number()
.min(0, 'Stretch ceiling offset must be non-negative')
.optional(),
order: z.number().int().min(0).optional(),
posX: z.number().optional(),
posY: z.number().optional(),
+40
View File
@@ -224,6 +224,17 @@ export const ELECTRICAL_TYPES = [
] as const;
export type ElectricalType = (typeof ELECTRICAL_TYPES)[number];
export const OUTLET_DIRECTIONS = ['horizontal', 'vertical'] as const;
export type OutletDirection = (typeof OUTLET_DIRECTIONS)[number];
export const WALL_LIGHT_STYLES = [
'classic',
'pendant-globe',
'sconce-up',
'sconce-down',
] as const;
export type WallLightStyle = (typeof WALL_LIGHT_STYLES)[number];
export interface ElectricalItem {
readonly id: string;
readonly roomId: string;
@@ -277,6 +288,35 @@ export interface UpdateElectricalItemDto {
readonly metadata?: Record<string, unknown> | null;
}
// ── FurnitureTexture ──
export const FURNITURE_TEXTURES = [
'NONE',
'WOOD_LIGHT',
'WOOD_MEDIUM',
'WOOD_DARK',
'WOOD_HERRINGBONE',
'OAK_NATURAL',
'LAMINATE',
'CONCRETE',
] as const;
export type FurnitureTexture = (typeof FURNITURE_TEXTURES)[number];
/** Furniture types that support surface texture selection. */
export const TEXTURABLE_FURNITURE: readonly FurnitureType[] = [
'DESK',
'TABLE',
'SHELF',
'NIGHTSTAND',
'DRESSER',
'DRESSING_TABLE',
'BOOKCASE',
'WARDROBE',
'CHAIR',
'BED',
'CRIB',
];
// ── FurnitureItem ──
export const FURNITURE_TYPES = [
+10
View File
@@ -11,6 +11,7 @@ export const FLOOR_TYPES = [
'WOOD_MEDIUM',
'WOOD_DARK',
'WOOD_HERRINGBONE',
'OAK_NATURAL',
'TILE_WHITE',
'TILE_GRAY',
'LAMINATE',
@@ -50,6 +51,13 @@ export interface Room {
readonly wallHeight: number;
readonly plinthHeight: number;
readonly plinthThickness: number;
/**
* Distance (meters) the stretch ceiling hangs below the structural ceiling.
* 0 means no stretch ceiling. The effective ceiling plane is at
* `wallHeight - stretchCeilingOffset`. Visualization only — does not
* affect wall geometry, opening positions, or electrical elevations.
*/
readonly stretchCeilingOffset: number;
readonly order: number;
readonly posX: number;
readonly posY: number;
@@ -80,6 +88,7 @@ export interface CreateRoomDto {
readonly wallHeight?: number;
readonly plinthHeight?: number;
readonly plinthThickness?: number;
readonly stretchCeilingOffset?: number;
readonly order?: number;
readonly posX?: number;
readonly posY?: number;
@@ -98,6 +107,7 @@ export interface UpdateRoomDto {
readonly wallHeight?: number;
readonly plinthHeight?: number;
readonly plinthThickness?: number;
readonly stretchCeilingOffset?: number;
readonly order?: number;
readonly posX?: number;
readonly posY?: number;