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