Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | 47x 5x 3x 5x 4x 9x 9x 12x 6x 6x 7x | /**
* Room Types Strategy
*
* Handles the roomTypes taxonomy table which uses code/displayName instead
* of the standard id/name fields.
*
* @module taxonomy/room-types-strategy
*/
import { eq } from "drizzle-orm";
import type { SQL } from "drizzle-orm";
import type {
SQLiteColumn,
SQLiteTableWithColumns,
} from "drizzle-orm/sqlite-core";
import type { TaxonomyStrategy } from "./taxonomy-strategy.types";
/**
* Special taxonomy strategy for roomTypes with code/displayName fields
*
* This strategy handles the roomTypes table which has a unique structure:
* - Uses 'code' as the primary key (e.g., 'LR' for Living Room) instead of 'id'
* - Uses 'displayName' as the main text field instead of 'name'
* - Has additional fields: displayNameHindi, icon
*
* The strategy provides frontend compatibility by transforming items to include
* both the original fields (code, displayName) and aliased fields (id, name)
* so the frontend can work with a consistent interface.
*
* Database structure:
* ```ts
* {
* code: string, // Primary key (e.g., 'LR', 'BR', 'KIT')
* displayName: string, // English name (e.g., 'Living Room')
* displayNameHindi?: string, // Hindi name (optional)
* icon?: string, // Icon identifier (optional)
* sortOrder: number,
* isActive: boolean
* }
* ```
*
* API response (after transformation):
* ```ts
* {
* code: 'LR',
* displayName: 'Living Room',
* displayNameHindi: 'लिविंग रूम',
* icon: 'living-room',
* sortOrder: 0,
* isActive: true,
* id: 'LR', // Alias for code
* name: 'Living Room' // Alias for displayName
* }
* ```
*
* @template TTable - The SQLite table type (typeof schema.roomTypes)
*
* @example
* ```ts
* const strategy = new RoomTypesStrategy(schema.roomTypes);
* const data = strategy.prepareInsertData({
* code: 'LR',
* displayName: 'Living Room',
* displayNameHindi: 'लिविंग रूम'
* });
* // Returns: { code: 'LR', displayName: 'Living Room', displayNameHindi: '...', sortOrder: 0, isActive: true }
* ```
*/
// biome-ignore lint/suspicious/noExplicitAny: Drizzle ORM generic table type requires any for dynamic column access
export class RoomTypesStrategy<TTable extends SQLiteTableWithColumns<any>>
implements TaxonomyStrategy<TTable>
{
/**
* Creates a new RoomTypesStrategy instance
* @param table - The Drizzle ORM table reference (schema.roomTypes)
*/
constructor(private table: TTable) {}
/**
* Returns the database table reference
* @returns The roomTypes table
*/
getTable(): TTable {
return this.table;
}
/**
* Returns the primary key column (always 'code' for roomTypes)
* @returns The table.code column
*/
getPrimaryKeyField(): SQLiteColumn {
return this.table.code;
}
/**
* Returns the name field column (always 'displayName' for roomTypes)
* @returns The table.displayName column
*/
getNameField(): SQLiteColumn {
return this.table.displayName;
}
/**
* Builds a SQL WHERE condition to find a room type by its code
*
* Unlike standard types that query by 'id', this queries by 'code'
*
* @param id - The code value to search for (e.g., 'LR', 'BR')
* @returns SQL condition: WHERE code = 'value'
*
* @example
* ```ts
* const condition = strategy.buildFindByIdQuery('LR');
* const item = await db.select().from(roomTypes).where(condition);
* // Executes: SELECT * FROM roomTypes WHERE code = 'LR'
* ```
*/
buildFindByIdQuery(id: string): SQL {
return eq(this.table.code, id);
}
/**
* Transforms a database item for API response
*
* Adds 'id' and 'name' fields as aliases for 'code' and 'displayName'
* to maintain frontend compatibility. The frontend expects all taxonomy
* items to have id/name fields for consistent rendering.
*
* @param item - The raw database item with code/displayName
* @returns Enhanced item with both original and aliased fields
*
* @example
* ```ts
* const dbItem = {
* code: 'LR',
* displayName: 'Living Room',
* displayNameHindi: 'लिविंग रूम',
* icon: 'living-room',
* sortOrder: 0,
* isActive: true
* };
* const apiItem = strategy.transformItem(dbItem);
* // Returns: { ...dbItem, id: 'LR', name: 'Living Room' }
* ```
*/
transformItem<T>(item: T): T & { id: string; name: string } {
// Add id/name fields for frontend compatibility
// Frontend expects id/name, but roomTypes uses code/displayName
const roomTypeItem = item as unknown as {
code: string;
displayName: string;
};
return {
...item,
id: roomTypeItem.code,
name: roomTypeItem.displayName,
};
}
/**
* Prepares data for INSERT operation
*
* Unlike standard types that auto-generate IDs, roomTypes uses the
* provided 'code' value as the primary key. This method explicitly
* maps roomTypes-specific fields.
*
* Required fields: code, displayName
* Optional fields: displayNameHindi, icon, sortOrder, isActive
*
* @param body - The request body data from the API client
* @returns Data ready for database insertion with defaults applied
*
* @example
* ```ts
* const data = strategy.prepareInsertData({
* code: 'LR',
* displayName: 'Living Room',
* displayNameHindi: 'लिविंग रूम',
* icon: 'living-room'
* });
* // Returns: {
* // code: 'LR',
* // displayName: 'Living Room',
* // displayNameHindi: 'लिविंग रूम',
* // icon: 'living-room',
* // sortOrder: 0,
* // isActive: true
* // }
* ```
*/
prepareInsertData(body: Record<string, unknown>): Record<string, unknown> {
// Use code from body (no auto-generated ID)
// Map displayName and other roomTypes-specific fields
return {
code: body.code as string,
displayName: body.displayName as string,
displayNameHindi: body.displayNameHindi as string | undefined,
icon: body.icon as string | undefined,
sortOrder: body.sortOrder ?? 0,
isActive: body.isActive ?? true,
};
}
/**
* Prepares data for UPDATE operation
*
* Removes both 'code' and 'id' fields from the request body since:
* - 'code' is the primary key and cannot be updated
* - 'id' is an alias sent by frontend (mapped to 'code')
*
* All other fields (displayName, displayNameHindi, icon, sortOrder, isActive)
* can be updated.
*
* @param body - The request body data from the API client
* @returns Data ready for database update (without code/id fields)
*
* @example
* ```ts
* const data = strategy.prepareUpdateData({
* code: 'LR', // Removed (primary key)
* id: 'LR', // Removed (alias)
* displayName: 'Updated Living Room',
* icon: 'new-icon'
* });
* // Returns: { displayName: 'Updated Living Room', icon: 'new-icon' }
* ```
*/
prepareUpdateData(body: Record<string, unknown>): Record<string, unknown> {
// Remove both code and id from body (can't update primary key)
const { code: _code, id: _id, ...updateData } = body;
return updateData;
}
/**
* Builds an update query for a single item in a reorder operation
*
* Used when reordering room types to update only the sortOrder field.
* Returns both the WHERE condition (using code) and the update data.
*
* @param itemId - The code of the room type to update (e.g., 'LR')
* @param sortOrder - The new sort order value
* @returns Object containing the SQL condition and update data
*
* @example
* ```ts
* const { condition, data } = strategy.buildReorderUpdate('LR', 5);
* await db.update(roomTypes).set(data).where(condition);
* // Executes: UPDATE roomTypes SET sortOrder = 5 WHERE code = 'LR'
* ```
*/
buildReorderUpdate(
itemId: string,
sortOrder: number,
): {
condition: SQL;
data: Record<string, unknown>;
} {
return {
condition: eq(this.table.code, itemId),
data: { sortOrder },
};
}
}
|