Skip to content

Commit 7b9a947

Browse files
committed
template styles
1 parent 434cf91 commit 7b9a947

4 files changed

Lines changed: 211 additions & 18 deletions

File tree

src/app/components/settings/settings.component.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,15 @@ export class SettingsComponent implements OnInit {
391391
let optimizedBadgeHtml = badgeHtml;
392392

393393
try {
394-
// Use the first available printer
395-
const printerName = printers[0];
394+
// Use the selected default printer or fallback to first available
395+
const savedDefaultPrinter = localStorage.getItem("printer.default");
396+
const printerName =
397+
savedDefaultPrinter && savedDefaultPrinter !== "default"
398+
? savedDefaultPrinter
399+
: printers[0];
400+
401+
console.log(`Using printer for QR badge: ${printerName}`);
402+
396403
const paperWidth =
397404
await this.qzTrayService.getOptimalPaperWidth(printerName);
398405
const dpi = await this.qzTrayService.getOptimalDpi(printerName);

src/app/components/templates/templates.component.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,12 @@ <h3>{{ "TEMPLATES.PREVIEW_TITLE" | translate }}</h3>
684684
</div>
685685
</div>
686686
<div class="preview-container">
687-
<div [innerHTML]="getSafePreview()"></div>
687+
<iframe
688+
#previewFrame
689+
class="preview-iframe"
690+
[srcdoc]="previewHtml()"
691+
sandbox="allow-same-origin"
692+
></iframe>
688693
</div>
689694
</div>
690695
</div>

src/app/components/templates/templates.component.scss

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
padding: 1.5rem;
55
background: var(--bg-primary);
66
min-height: 100%;
7+
max-width: 100%;
8+
overflow-x: hidden;
79

810
.header {
911
margin-bottom: 2rem;
@@ -26,6 +28,8 @@
2628
border-radius: 12px;
2729
padding: 2rem;
2830
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
31+
max-width: 100%;
32+
overflow: hidden;
2933

3034
.btn-primary {
3135
background: $primary;
@@ -463,37 +467,42 @@
463467

464468
.form-with-preview {
465469
display: grid;
466-
grid-template-columns: 1fr 1fr;
470+
grid-template-columns: 1fr 400px;
467471
gap: 2rem;
468472
align-items: start;
473+
max-width: 100%;
469474

470475
@media (max-width: 1200px) {
471476
grid-template-columns: 1fr;
472477
gap: 1.5rem;
473478
}
474479

475480
.form-section-wrapper {
476-
flex: 1;
481+
min-width: 0;
477482
}
478483

479484
.preview-section {
480-
flex: 1;
481-
position: sticky;
482-
top: 100px;
485+
position: fixed;
486+
top: 173px;
487+
max-height: calc(100vh - 120px);
488+
align-self: start;
489+
right: 24px;
483490

484491
.preview-header {
485492
display: flex;
486493
justify-content: space-between;
487494
align-items: center;
488495
margin-bottom: 1rem;
489496
gap: 1rem;
497+
flex-wrap: wrap;
490498

491499
h3 {
492500
font-size: 1.1rem;
493501
font-weight: 600;
494502
color: var(--text-primary);
495503
margin: 0;
496504
flex: 1;
505+
min-width: 150px;
497506
}
498507

499508
.preview-mode-toggle {
@@ -524,12 +533,17 @@
524533
padding: 1rem;
525534
background: #fafafa;
526535
overflow-y: auto;
527-
max-height: 600px;
536+
overflow-x: hidden;
537+
max-height: calc(100vh - 220px);
528538
display: flex;
529539
justify-content: center;
530540

531-
div {
541+
.preview-iframe {
532542
width: 100%;
543+
min-height: 500px;
544+
border: none;
545+
background: white;
546+
border-radius: 4px;
533547
}
534548
}
535549
}

src/app/components/templates/templates.component.ts

Lines changed: 175 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit, signal, inject } from "@angular/core";
1+
import { Component, OnInit, signal, inject, effect } from "@angular/core";
22

33
import {
44
FormsModule,
@@ -8,9 +8,11 @@ import {
88
Validators,
99
} from "@angular/forms";
1010
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
11+
import { debounceTime } from "rxjs/operators";
1112
import { PrintTemplateService } from "../../services/print-template.service";
1213
import { ToastService } from "../../services/toast.service";
13-
import { PrintTemplate } from "../../models";
14+
import { ReceiptGeneratorService } from "../../services/receipt-generator.service";
15+
import { PrintTemplate, Sale, SaleItem } from "../../models";
1416
import { ToggleSwitchComponent } from "../toggle-switch/toggle-switch.component";
1517
import { TranslatePipe } from "../../pipes/translate.pipe";
1618
import { TranslationService } from "../../services/translation.service";
@@ -29,6 +31,7 @@ import { TranslationService } from "../../services/translation.service";
2931
})
3032
export class TemplatesComponent implements OnInit {
3133
private printTemplateService = inject(PrintTemplateService);
34+
private receiptGeneratorService = inject(ReceiptGeneratorService);
3235
private fb = inject(FormBuilder);
3336
sanitizer = inject(DomSanitizer);
3437
private translationService = inject(TranslationService);
@@ -41,6 +44,7 @@ export class TemplatesComponent implements OnInit {
4144
showForm = signal<boolean>(false);
4245
previewMode = signal<"text" | "styled">("styled");
4346
templateForm: FormGroup;
47+
previewHtml = signal<string>("");
4448

4549
/** Inserted by Angular inject() migration for backwards compatibility */
4650
constructor(...args: unknown[]);
@@ -98,13 +102,24 @@ export class TemplatesComponent implements OnInit {
98102
textAlign: ["center"],
99103
isDefault: [false],
100104
});
105+
106+
// Auto-regenerate preview when preview mode changes
107+
effect(() => {
108+
this.previewMode(); // Track signal
109+
if (this.showForm()) {
110+
this.generatePreview();
111+
}
112+
});
101113
}
102114

103115
ngOnInit(): void {
104116
this.loadTemplates();
105-
// Subscribe to form changes for live preview
106-
this.templateForm.valueChanges.subscribe(() => {
107-
// This will trigger change detection for the preview
117+
// Generate initial preview
118+
this.generatePreview();
119+
120+
// Subscribe to form changes for live preview with debounce
121+
this.templateForm.valueChanges.pipe(debounceTime(300)).subscribe(() => {
122+
this.generatePreview();
108123
});
109124
}
110125

@@ -386,13 +401,39 @@ export class TemplatesComponent implements OnInit {
386401
}
387402

388403
getSafePreview(): SafeHtml {
389-
return this.sanitizer.bypassSecurityTrustHtml(this.generatePreview());
404+
return this.sanitizer.bypassSecurityTrustHtml(this.previewHtml());
390405
}
391406

392-
generatePreview(): string {
407+
async generatePreview(): Promise<void> {
393408
const formValue = this.templateForm.value;
394409
const mode = this.previewMode();
395410

411+
// Create sample sale data for preview
412+
const sampleSale = this.createSampleSale();
413+
414+
// Convert form values to PrintTemplate format
415+
const template = this.formValueToTemplate(formValue);
416+
417+
try {
418+
// Use the real receipt generator service
419+
const html = await this.receiptGeneratorService.generateReceipt(
420+
sampleSale,
421+
template,
422+
{
423+
plainTextMode: mode === "text",
424+
}
425+
);
426+
427+
this.previewHtml.set(html);
428+
} catch (error) {
429+
console.error("Error generating preview:", error);
430+
// Fallback to simple preview if generation fails
431+
this.previewHtml.set(this.generateFallbackPreview(formValue, mode));
432+
}
433+
}
434+
435+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
436+
private generateFallbackPreview(formValue: any, mode: string): string {
396437
// If text mode, generate text-only preview
397438
if (mode === "text") {
398439
return this.generatePlainTextPreview(formValue);
@@ -402,9 +443,134 @@ export class TemplatesComponent implements OnInit {
402443
return this.generateStyledPreview(formValue);
403444
}
404445

446+
private createSampleSale(): Sale {
447+
// Create sample sale data for preview
448+
const sampleItems: SaleItem[] = [
449+
{
450+
product: {
451+
_id: "1",
452+
product_id: "PROD-001",
453+
name: "Sample Product A",
454+
sku: "PROD-001",
455+
price: 15.99,
456+
stock: 100,
457+
category: "Sample",
458+
active: true,
459+
available: true,
460+
},
461+
quantity: 2,
462+
unitPrice: 15.99,
463+
discount: 0,
464+
subtotal: 31.98,
465+
total: 0,
466+
},
467+
{
468+
product: {
469+
_id: "2",
470+
product_id: "PROD-002",
471+
name: "Sample Product B",
472+
sku: "PROD-002",
473+
price: 24.5,
474+
stock: 50,
475+
category: "Sample",
476+
active: true,
477+
available: true,
478+
},
479+
quantity: 1,
480+
unitPrice: 24.5,
481+
discount: 0,
482+
subtotal: 24.5,
483+
total: 0,
484+
},
485+
];
486+
487+
return {
488+
_id: "PREVIEW-001",
489+
saleNumber: "SALE-" + new Date().getTime(),
490+
items: sampleItems,
491+
subtotal: 56.48,
492+
discount: 0,
493+
tax: 4.52,
494+
total: 61.0,
495+
paymentMethod: "cash",
496+
cashier: "Demo Cashier",
497+
register: "Register 1",
498+
status: "completed",
499+
createdAt: new Date(),
500+
updatedAt: new Date(),
501+
} as Sale;
502+
}
503+
504+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
505+
private formValueToTemplate(formValue: any): PrintTemplate {
506+
return {
507+
_id: "preview",
508+
name: formValue.name || "Preview",
509+
description: formValue.description || "",
510+
templateType: formValue.templateType,
511+
paperSize: formValue.paperSize,
512+
isDefault: false,
513+
active: true,
514+
header: {
515+
showLogo: formValue.showLogo,
516+
showStoreName: formValue.showStoreName,
517+
showStoreAddress: formValue.showStoreAddress,
518+
showStorePhone: formValue.showStorePhone,
519+
showStoreEmail: formValue.showStoreEmail,
520+
storeName: formValue.storeName,
521+
storeAddress: formValue.storeAddress,
522+
storePhone: formValue.storePhone,
523+
storeEmail: formValue.storeEmail,
524+
storeNameSize: formValue.storeNameSize,
525+
storeNameFont: formValue.storeNameFont,
526+
storeNameBold: formValue.storeNameBold,
527+
storeAddressSize: formValue.storeAddressSize,
528+
storeAddressFont: formValue.storeAddressFont,
529+
storeAddressBold: formValue.storeAddressBold,
530+
storePhoneSize: formValue.storePhoneSize,
531+
storePhoneFont: formValue.storePhoneFont,
532+
storePhoneBold: formValue.storePhoneBold,
533+
storeEmailSize: formValue.storeEmailSize,
534+
storeEmailFont: formValue.storeEmailFont,
535+
storeEmailBold: formValue.storeEmailBold,
536+
},
537+
body: {
538+
showProductCode: formValue.showProductCode,
539+
showBarcode: formValue.showBarcode,
540+
showQuantity: formValue.showQuantity,
541+
showUnitPrice: formValue.showUnitPrice,
542+
showDiscount: formValue.showDiscount,
543+
showTax: formValue.showTax,
544+
showSubtotal: formValue.showSubtotal,
545+
productSize: formValue.productSize,
546+
productFont: formValue.productFont,
547+
productBold: formValue.productBold,
548+
fontSize: formValue.fontSize,
549+
},
550+
footer: {
551+
showTotals: formValue.showTotals,
552+
showPaymentMethod: formValue.showPaymentMethod,
553+
showCashier: formValue.showCashier,
554+
showDateTime: formValue.showDateTime,
555+
showThankYou: formValue.showThankYou,
556+
customMessage: formValue.customMessage,
557+
totalSize: formValue.totalSize,
558+
totalFont: formValue.totalFont,
559+
totalBold: formValue.totalBold,
560+
footerSize: formValue.footerSize,
561+
footerFont: formValue.footerFont,
562+
footerBold: formValue.footerBold,
563+
},
564+
styles: {
565+
textAlign: formValue.textAlign,
566+
},
567+
} as PrintTemplate;
568+
}
569+
570+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
405571
private generatePlainTextPreview(formValue: any): string {
406572
const width = formValue.paperSize === "58mm" ? 28 : 40;
407-
let lines: string[] = [];
573+
const lines: string[] = [];
408574

409575
// Header
410576
if (formValue.showLogo) {
@@ -465,6 +631,7 @@ export class TemplatesComponent implements OnInit {
465631
return `<div style="font-family: 'Courier New', monospace; width: 280px; margin: 0 auto; padding: 8px; box-sizing: border-box; border: 1px solid #ddd; font-size: 12px; line-height: 1.4; background: white; white-space: pre-wrap; word-wrap: break-word;"><code>${this.escapeHtml(textContent)}</code></div>`;
466632
}
467633

634+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
468635
private generateStyledPreview(formValue: any): string {
469636
const fontSize =
470637
formValue.fontSize === "small"

0 commit comments

Comments
 (0)