91 lines
3.0 KiB
JavaScript
91 lines
3.0 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { render, Box, Text, useApp, useInput } from 'ink';
|
|
import chalk from 'chalk';
|
|
import { getCategories } from '../src/services/misc/getCategories.js';
|
|
import { closePool } from '../src/db/mssqlClient.js';
|
|
|
|
import '../src/config/env.js';
|
|
|
|
const e = React.createElement;
|
|
|
|
const VISIBLE_ROWS = 20;
|
|
|
|
function flattenTree(nodes, depth = 0, out = []) {
|
|
for (const node of nodes) {
|
|
out.push({ ...node, depth });
|
|
flattenTree(node.children, depth + 1, out);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function formatRow(row) {
|
|
const indent = ' '.repeat(row.depth);
|
|
const count = chalk.gray(` (${row.count})`);
|
|
return `${indent}${row.cName ?? '—'}${count} ${chalk.dim(String(row.kKategorie))}`;
|
|
}
|
|
|
|
const CategoryBrowser = ({ rows }) => {
|
|
const { exit } = useApp();
|
|
const [selected, setSelected] = useState(0);
|
|
const windowStart = Math.max(0, Math.min(selected - Math.floor(VISIBLE_ROWS / 2), rows.length - VISIBLE_ROWS));
|
|
const visible = rows.slice(windowStart, windowStart + VISIBLE_ROWS);
|
|
const current = rows[selected];
|
|
|
|
const quit = () => {
|
|
closePool()
|
|
.catch(() => {})
|
|
.finally(() => {
|
|
exit();
|
|
process.exit(0);
|
|
});
|
|
};
|
|
|
|
useInput((input, key) => {
|
|
if (input === 'q' || (key.ctrl && input === 'c')) {
|
|
quit();
|
|
return;
|
|
}
|
|
if (key.upArrow) {
|
|
setSelected((s) => Math.max(0, s - 1));
|
|
}
|
|
if (key.downArrow) {
|
|
setSelected((s) => Math.min(rows.length - 1, s + 1));
|
|
}
|
|
});
|
|
|
|
return e(Box, { flexDirection: 'column' },
|
|
e(Text, null, chalk.cyan.bold(`=== Categories (${rows.length}) ===`)),
|
|
e(Box, { marginTop: 1, flexDirection: 'column' },
|
|
visible.map((row, index) => {
|
|
const absoluteIndex = windowStart + index;
|
|
const prefix = absoluteIndex === selected ? chalk.cyan('> ') : ' ';
|
|
const label = absoluteIndex === selected
|
|
? chalk.cyan.bold(formatRow(row))
|
|
: formatRow(row);
|
|
return e(Text, { key: row.kKategorie }, prefix, label);
|
|
})
|
|
),
|
|
rows.length > VISIBLE_ROWS
|
|
? e(Text, { dimColor: true }, `Showing ${windowStart + 1}-${windowStart + visible.length} of ${rows.length}`)
|
|
: null,
|
|
current
|
|
? e(Box, { marginTop: 1, flexDirection: 'column' },
|
|
e(Text, { dimColor: true },
|
|
`kKategorie: ${current.kKategorie} parent: ${current.kOberKategorie} sort: ${current.nSort ?? '—'} articles: ${current.count}`
|
|
)
|
|
)
|
|
: null,
|
|
e(Box, { marginTop: 1 }, e(Text, null, chalk.gray('↑↓ navigate, q exit')))
|
|
);
|
|
};
|
|
|
|
try {
|
|
const { tree } = await getCategories();
|
|
const rows = flattenTree(tree);
|
|
render(e(CategoryBrowser, { rows }));
|
|
} catch (error) {
|
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
await closePool().catch(() => {});
|
|
process.exit(1);
|
|
}
|