feat(display): add layout manager with element CRUD (Task 8)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2990f9aa4d
commit
aaac2bedff
211
display/middleware/src/modules/display/layout-manager.ts
Normal file
211
display/middleware/src/modules/display/layout-manager.ts
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import { getDb } from '../../db/index';
|
||||||
|
|
||||||
|
export interface Layout {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
grid_cols: number | null;
|
||||||
|
grid_rows: number | null;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutWithElements extends Layout {
|
||||||
|
elements: LayoutElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutElement {
|
||||||
|
id: number;
|
||||||
|
layout_id: number;
|
||||||
|
datapoint_id: number | null;
|
||||||
|
type: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number | null;
|
||||||
|
height: number | null;
|
||||||
|
font_size: number;
|
||||||
|
font_weight: string;
|
||||||
|
text_align: string;
|
||||||
|
icon: string | null;
|
||||||
|
style: string;
|
||||||
|
static_text: string | null;
|
||||||
|
grid_col: number | null;
|
||||||
|
grid_row: number | null;
|
||||||
|
sort_order: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateLayoutData {
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
grid_cols?: number;
|
||||||
|
grid_rows?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateLayoutData {
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
grid_cols?: number | null;
|
||||||
|
grid_rows?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateElementData {
|
||||||
|
datapoint_id?: number;
|
||||||
|
type: string;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
font_size?: number;
|
||||||
|
font_weight?: string;
|
||||||
|
text_align?: string;
|
||||||
|
icon?: string;
|
||||||
|
style?: string;
|
||||||
|
static_text?: string;
|
||||||
|
grid_col?: number;
|
||||||
|
grid_row?: number;
|
||||||
|
sort_order?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateElementData = Partial<CreateElementData>;
|
||||||
|
|
||||||
|
export function getAllLayouts(): Layout[] {
|
||||||
|
const db = getDb();
|
||||||
|
return db.prepare('SELECT * FROM layouts ORDER BY id ASC').all() as Layout[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLayoutById(id: number): LayoutWithElements | undefined {
|
||||||
|
const db = getDb();
|
||||||
|
const layout = db.prepare('SELECT * FROM layouts WHERE id = ?').get(id) as Layout | undefined;
|
||||||
|
if (!layout) return undefined;
|
||||||
|
|
||||||
|
const elements = db
|
||||||
|
.prepare('SELECT * FROM layout_elements WHERE layout_id = ? ORDER BY sort_order ASC, id ASC')
|
||||||
|
.all(id) as LayoutElement[];
|
||||||
|
|
||||||
|
return { ...layout, elements };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createLayout(data: CreateLayoutData): Layout {
|
||||||
|
const db = getDb();
|
||||||
|
const result = db.run(
|
||||||
|
`INSERT INTO layouts (name, type, width, height, grid_cols, grid_rows)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
|
[
|
||||||
|
data.name,
|
||||||
|
data.type ?? 'free',
|
||||||
|
data.width ?? 800,
|
||||||
|
data.height ?? 480,
|
||||||
|
data.grid_cols ?? null,
|
||||||
|
data.grid_rows ?? null,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
const id = result.lastInsertRowid as number;
|
||||||
|
return db.prepare('SELECT * FROM layouts WHERE id = ?').get(id) as Layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateLayout(id: number, data: UpdateLayoutData): Layout | undefined {
|
||||||
|
const db = getDb();
|
||||||
|
const fields: string[] = [];
|
||||||
|
const values: unknown[] = [];
|
||||||
|
|
||||||
|
if (data.name !== undefined) { fields.push('name = ?'); values.push(data.name); }
|
||||||
|
if (data.type !== undefined) { fields.push('type = ?'); values.push(data.type); }
|
||||||
|
if (data.width !== undefined) { fields.push('width = ?'); values.push(data.width); }
|
||||||
|
if (data.height !== undefined) { fields.push('height = ?'); values.push(data.height); }
|
||||||
|
if (data.grid_cols !== undefined) { fields.push('grid_cols = ?'); values.push(data.grid_cols); }
|
||||||
|
if (data.grid_rows !== undefined) { fields.push('grid_rows = ?'); values.push(data.grid_rows); }
|
||||||
|
|
||||||
|
if (fields.length === 0) {
|
||||||
|
return db.prepare('SELECT * FROM layouts WHERE id = ?').get(id) as Layout | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.push(id);
|
||||||
|
db.run(`UPDATE layouts SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||||
|
return db.prepare('SELECT * FROM layouts WHERE id = ?').get(id) as Layout | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteLayout(id: number): boolean {
|
||||||
|
const db = getDb();
|
||||||
|
const result = db.run('DELETE FROM layouts WHERE id = ?', [id]);
|
||||||
|
return (result.changes as number) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addElement(layoutId: number, data: CreateElementData): LayoutElement {
|
||||||
|
const db = getDb();
|
||||||
|
const result = db.run(
|
||||||
|
`INSERT INTO layout_elements
|
||||||
|
(layout_id, datapoint_id, type, x, y, width, height, font_size, font_weight,
|
||||||
|
text_align, icon, style, static_text, grid_col, grid_row, sort_order)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[
|
||||||
|
layoutId,
|
||||||
|
data.datapoint_id ?? null,
|
||||||
|
data.type,
|
||||||
|
data.x ?? 0,
|
||||||
|
data.y ?? 0,
|
||||||
|
data.width ?? null,
|
||||||
|
data.height ?? null,
|
||||||
|
data.font_size ?? 24,
|
||||||
|
data.font_weight ?? 'normal',
|
||||||
|
data.text_align ?? 'left',
|
||||||
|
data.icon ?? null,
|
||||||
|
data.style ?? 'plain',
|
||||||
|
data.static_text ?? null,
|
||||||
|
data.grid_col ?? null,
|
||||||
|
data.grid_row ?? null,
|
||||||
|
data.sort_order ?? 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
const id = result.lastInsertRowid as number;
|
||||||
|
return db.prepare('SELECT * FROM layout_elements WHERE id = ?').get(id) as LayoutElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateElement(id: number, data: UpdateElementData): LayoutElement | undefined {
|
||||||
|
const db = getDb();
|
||||||
|
const fields: string[] = [];
|
||||||
|
const values: unknown[] = [];
|
||||||
|
|
||||||
|
const fieldMap: Record<string, string> = {
|
||||||
|
datapoint_id: 'datapoint_id',
|
||||||
|
type: 'type',
|
||||||
|
x: 'x',
|
||||||
|
y: 'y',
|
||||||
|
width: 'width',
|
||||||
|
height: 'height',
|
||||||
|
font_size: 'font_size',
|
||||||
|
font_weight: 'font_weight',
|
||||||
|
text_align: 'text_align',
|
||||||
|
icon: 'icon',
|
||||||
|
style: 'style',
|
||||||
|
static_text: 'static_text',
|
||||||
|
grid_col: 'grid_col',
|
||||||
|
grid_row: 'grid_row',
|
||||||
|
sort_order: 'sort_order',
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [key, col] of Object.entries(fieldMap)) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
||||||
|
fields.push(`${col} = ?`);
|
||||||
|
values.push((data as Record<string, unknown>)[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields.length === 0) {
|
||||||
|
return db.prepare('SELECT * FROM layout_elements WHERE id = ?').get(id) as LayoutElement | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.push(id);
|
||||||
|
db.run(`UPDATE layout_elements SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||||
|
return db.prepare('SELECT * FROM layout_elements WHERE id = ?').get(id) as LayoutElement | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteElement(id: number): boolean {
|
||||||
|
const db = getDb();
|
||||||
|
const result = db.run('DELETE FROM layout_elements WHERE id = ?', [id]);
|
||||||
|
return (result.changes as number) > 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user