format utils for printing
This commit is contained in:
parent
5fde9f667b
commit
b949d4a6c6
125
server/src/printer/__tests__/format-utils.test.ts
Normal file
125
server/src/printer/__tests__/format-utils.test.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { formatUtils } from '../format-utils';
|
||||
|
||||
describe('formatUtils', () => {
|
||||
describe('createBanner', () => {
|
||||
it('should create a banner with default length', () => {
|
||||
const banner = formatUtils.createBanner('=');
|
||||
expect(banner).toBe('='.repeat(40));
|
||||
});
|
||||
|
||||
it('should create a banner with custom length', () => {
|
||||
const banner = formatUtils.createBanner('-', 32);
|
||||
expect(banner).toBe('-'.repeat(32));
|
||||
});
|
||||
|
||||
it('should handle empty character', () => {
|
||||
const banner = formatUtils.createBanner('', 10);
|
||||
expect(banner).toBe('');
|
||||
});
|
||||
|
||||
it('should handle zero length', () => {
|
||||
const banner = formatUtils.createBanner('*', 0);
|
||||
expect(banner).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatCheckbox', () => {
|
||||
it('should format text with checkbox', () => {
|
||||
const formatted = formatUtils.formatCheckbox('Test Item');
|
||||
expect(formatted).toBe('[ ] Test Item');
|
||||
});
|
||||
|
||||
it('should handle empty text', () => {
|
||||
const formatted = formatUtils.formatCheckbox('');
|
||||
expect(formatted).toBe('[ ] ');
|
||||
});
|
||||
|
||||
it('should handle text with special characters', () => {
|
||||
const formatted = formatUtils.formatCheckbox('Test: Item (123)');
|
||||
expect(formatted).toBe('[ ] Test: Item (123)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatList', () => {
|
||||
it('should format list without numbering', () => {
|
||||
const items = ['Item 1', 'Item 2', 'Item 3'];
|
||||
const formatted = formatUtils.formatList(items);
|
||||
expect(formatted).toEqual([
|
||||
'[ ] Item 1',
|
||||
'[ ] Item 2',
|
||||
'[ ] Item 3'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should format list with numbering', () => {
|
||||
const items = ['Item 1', 'Item 2', 'Item 3'];
|
||||
const formatted = formatUtils.formatList(items, 1);
|
||||
expect(formatted).toEqual([
|
||||
'[ ] 1: Item 1',
|
||||
'[ ] 2: Item 2',
|
||||
'[ ] 3: Item 3'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty list', () => {
|
||||
const formatted = formatUtils.formatList([]);
|
||||
expect(formatted).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle list with empty items', () => {
|
||||
const items = ['', 'Item 2', ''];
|
||||
const formatted = formatUtils.formatList(items);
|
||||
expect(formatted).toEqual([
|
||||
'[ ] ',
|
||||
'[ ] Item 2',
|
||||
'[ ] '
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatSection', () => {
|
||||
it('should format section with default banner', () => {
|
||||
const section = formatUtils.formatSection('Header', 'Content');
|
||||
expect(section).toEqual([
|
||||
'[ ] Header',
|
||||
'='.repeat(40),
|
||||
'',
|
||||
'Content',
|
||||
''
|
||||
]);
|
||||
});
|
||||
|
||||
it('should format section with custom banner', () => {
|
||||
const section = formatUtils.formatSection('Header', 'Content', '-');
|
||||
expect(section).toEqual([
|
||||
'[ ] Header',
|
||||
'-'.repeat(40),
|
||||
'',
|
||||
'Content',
|
||||
''
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty content', () => {
|
||||
const section = formatUtils.formatSection('Header', '');
|
||||
expect(section).toEqual([
|
||||
'[ ] Header',
|
||||
'='.repeat(40),
|
||||
'',
|
||||
'',
|
||||
''
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty header', () => {
|
||||
const section = formatUtils.formatSection('', 'Content');
|
||||
expect(section).toEqual([
|
||||
'[ ] ',
|
||||
'='.repeat(40),
|
||||
'',
|
||||
'Content',
|
||||
''
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
50
server/src/printer/format-utils.ts
Normal file
50
server/src/printer/format-utils.ts
Normal file
@ -0,0 +1,50 @@
|
||||
export const formatUtils = {
|
||||
/**
|
||||
* Creates a banner line with the specified character
|
||||
* @param char Character to repeat
|
||||
* @param length Length of the banner
|
||||
* @returns Formatted banner string
|
||||
*/
|
||||
createBanner(char: string, length: number = 40): string {
|
||||
return char.repeat(length);
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats a checkbox item with the given text
|
||||
* @param text Text to display after the checkbox
|
||||
* @returns Formatted checkbox string
|
||||
*/
|
||||
formatCheckbox(text: string): string {
|
||||
return `[ ] ${text}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats a list of items into lines with optional numbering
|
||||
* @param items List of items to format
|
||||
* @param startIndex Starting index for numbering (0 for no numbers)
|
||||
* @returns Array of formatted lines
|
||||
*/
|
||||
formatList(items: string[], startIndex: number = 0): string[] {
|
||||
return items.map((item, index) => {
|
||||
const num = startIndex > 0 ? `${index + startIndex}: ` : '';
|
||||
return this.formatCheckbox(`${num}${item}`);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats a section with a header and content
|
||||
* @param header Section header
|
||||
* @param content Section content
|
||||
* @param bannerChar Character to use for the banner
|
||||
* @returns Array of formatted lines
|
||||
*/
|
||||
formatSection(header: string, content: string, bannerChar: string = '='): string[] {
|
||||
return [
|
||||
this.formatCheckbox(header),
|
||||
this.createBanner(bannerChar),
|
||||
'',
|
||||
content,
|
||||
'',
|
||||
];
|
||||
}
|
||||
};
|
@ -4,6 +4,7 @@ import { Task, Step, Printer as PrinterInterface } from '@shared/index';
|
||||
import { StepRepository, PrintHistoryRepository } from '../db/repositories';
|
||||
import { Knex } from 'knex';
|
||||
import logger from '../logger';
|
||||
import { formatUtils } from './format-utils';
|
||||
|
||||
export class SerialPrinter implements PrinterInterface {
|
||||
private device: USB | null = null;
|
||||
@ -57,8 +58,8 @@ export class SerialPrinter implements PrinterInterface {
|
||||
.align('ct')
|
||||
.style('b')
|
||||
.size(1, 1) // Normal size (0.08 x 2.13 mm)
|
||||
.text(`[ ] Task: ${task.name}`)
|
||||
.text('='.repeat(32))
|
||||
.text(formatUtils.formatCheckbox(`Task: ${task.name}`))
|
||||
.text(formatUtils.createBanner('=', 32))
|
||||
.text('')
|
||||
.align('lt');
|
||||
|
||||
@ -71,12 +72,14 @@ export class SerialPrinter implements PrinterInterface {
|
||||
// Print steps
|
||||
for (let i = 0; i < taskSteps.length; i++) {
|
||||
const step = taskSteps[i];
|
||||
const stepSection = formatUtils.formatSection(`Step ${i + 1}: ${step.name}`, step.instructions, '-');
|
||||
|
||||
await this.printer
|
||||
.size(1, 1) // Normal size for step header
|
||||
.text(`[ ] Step ${i + 1}: ${step.name}`)
|
||||
.text('-'.repeat(32))
|
||||
.size(0, 0) // Smaller size for instructions (0.08 x 2.13 mm)
|
||||
.text(step.instructions)
|
||||
.text(stepSection[0]) // Header with checkbox
|
||||
.text(stepSection[1]) // Banner
|
||||
.size(0, 0) // Smaller size for instructions
|
||||
.text(stepSection[3]) // Instructions
|
||||
.text('');
|
||||
|
||||
// Print step ID as barcode
|
||||
@ -110,17 +113,19 @@ export class SerialPrinter implements PrinterInterface {
|
||||
}
|
||||
|
||||
try {
|
||||
const stepSection = formatUtils.formatSection(`Step: ${step.name}`, step.instructions);
|
||||
|
||||
await this.printer
|
||||
.font('a')
|
||||
.align('ct')
|
||||
.style('b')
|
||||
.size(1, 1) // Normal size (0.08 x 2.13 mm)
|
||||
.text(`[ ] Step: ${step.name}`)
|
||||
.text('='.repeat(32))
|
||||
.text(stepSection[0]) // Header with checkbox
|
||||
.text(stepSection[1]) // Banner
|
||||
.text('')
|
||||
.align('lt')
|
||||
.size(0, 0) // Smaller size for instructions
|
||||
.text(step.instructions)
|
||||
.text(stepSection[3]) // Instructions
|
||||
.text('')
|
||||
.text('');
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { Task, Step, Printer } from '@shared/index';
|
||||
import { StepRepository, PrintHistoryRepository } from '../db/repositories';
|
||||
import { Knex } from 'knex';
|
||||
import logger from '../logger';
|
||||
import { formatUtils } from './format-utils';
|
||||
|
||||
export class TestPrinter implements Printer {
|
||||
private readonly outputDir: string;
|
||||
@ -35,15 +36,10 @@ export class TestPrinter implements Printer {
|
||||
const filename = path.join(this.outputDir, `task-${task.id}-${timestamp}.txt`);
|
||||
|
||||
const content = [
|
||||
`[ ] Task: ${task.name}`,
|
||||
'='.repeat(40),
|
||||
'',
|
||||
...taskSteps.map((step, index) => [
|
||||
`[ ] Step ${index + 1}: ${step.name}`,
|
||||
'-'.repeat(40),
|
||||
step.instructions,
|
||||
'',
|
||||
]).flat(),
|
||||
...formatUtils.formatSection(`Task: ${task.name}`, ''),
|
||||
...taskSteps.map((step, index) =>
|
||||
formatUtils.formatSection(`Step ${index + 1}: ${step.name}`, step.instructions, '-')
|
||||
).flat(),
|
||||
].join('\n');
|
||||
|
||||
await fs.writeFile(filename, content);
|
||||
@ -61,13 +57,7 @@ export class TestPrinter implements Printer {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const filename = path.join(this.outputDir, `step-${step.id}-${timestamp}.txt`);
|
||||
|
||||
const content = [
|
||||
`[ ] Step: ${step.name}`,
|
||||
'='.repeat(40),
|
||||
'',
|
||||
step.instructions,
|
||||
'',
|
||||
].join('\n');
|
||||
const content = formatUtils.formatSection(`Step: ${step.name}`, step.instructions).join('\n');
|
||||
|
||||
await fs.writeFile(filename, content);
|
||||
logger.info(`Printed step ${step.id} to ${filename}`);
|
||||
|
Loading…
Reference in New Issue
Block a user