@nice2dev/ui-printing 1.1.7 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/index.cjs +2 -2
  2. package/dist/index.mjs +1900 -1734
  3. package/dist/ui-printing.css +1 -1
  4. package/package.json +3 -7
  5. package/src/print-button/NicePrintButton.tsx +39 -17
  6. package/src/print-button/PrintButton.module.css +9 -9
  7. package/src/print-preview/NicePrintPreview.tsx +10 -9
  8. package/src/print-preview/PrintPreview.module.css +7 -7
  9. package/src/print-queue/NicePrintQueue.tsx +76 -42
  10. package/src/print-queue/PrintQueue.module.css +9 -9
  11. package/src/template-browser/NiceTemplateBrowser.tsx +39 -18
  12. package/src/template-browser/TemplateBrowser.module.css +17 -17
  13. package/src/template-editor/NiceTemplateEditor.tsx +306 -179
  14. package/src/template-editor/TemplateEditor.module.css +90 -83
  15. package/src/types/printingTypes.ts +1 -1
  16. package/dist/index.d.ts.map +0 -1
  17. package/dist/print-button/NicePrintButton.d.ts.map +0 -1
  18. package/dist/print-button/index.d.ts.map +0 -1
  19. package/dist/print-preview/NicePrintPreview.d.ts.map +0 -1
  20. package/dist/print-preview/index.d.ts.map +0 -1
  21. package/dist/print-queue/NicePrintQueue.d.ts.map +0 -1
  22. package/dist/print-queue/index.d.ts.map +0 -1
  23. package/dist/style.css +0 -1
  24. package/dist/template-browser/NiceTemplateBrowser.d.ts.map +0 -1
  25. package/dist/template-browser/index.d.ts.map +0 -1
  26. package/dist/template-editor/NiceTemplateEditor.d.ts.map +0 -1
  27. package/dist/template-editor/index.d.ts.map +0 -1
  28. package/dist/types/printingTypes.d.ts.map +0 -1
  29. package/src/template-editor/NiceTemplateEditor.tsx.first +0 -1011
  30. package/src/template-editor/NiceTemplateEditor.tsx.tmp +0 -392
@@ -1,3 +1,5 @@
1
+ import { NiceIcon, useNiceTranslation } from '@nice2dev/ui';
2
+ import type { NiceTranslateFn } from '@nice2dev/ui';
1
3
  import React, { useState, useCallback } from 'react';
2
4
 
3
5
  import type { PrintJob, PrinterInfo, NicePrintQueueProps } from '../types/printingTypes';
@@ -5,32 +7,46 @@ import type { PrintJob, PrinterInfo, NicePrintQueueProps } from '../types/printi
5
7
  import styles from './PrintQueue.module.css';
6
8
 
7
9
  // ── Helpers ───────────────────────────────────────────────────────────────────
8
- const STATUS_LABELS: Record<string, string> = {
9
- queued: 'Kolejka',
10
- printing: 'Drukowanie',
11
- done: 'Wydrukowano',
12
- error: 'Błąd',
13
- paused: 'Wstrzymano',
14
- cancelled: 'Anulowano',
10
+ const STATUS_DEFAULTS: Record<string, string> = {
11
+ queued: 'Queued',
12
+ printing: 'Printing',
13
+ done: 'Printed',
14
+ error: 'Error',
15
+ paused: 'Paused',
16
+ cancelled: 'Cancelled',
15
17
  };
16
18
 
17
- function formatTime(iso: string): string {
19
+ const statusLabel = (t: NiceTranslateFn, status: string): string =>
20
+ STATUS_DEFAULTS[status] ? t(`printing.status.${status}`, STATUS_DEFAULTS[status]) : status;
21
+
22
+ function formatTime(lang: string, iso: string): string {
18
23
  try {
19
24
  const d = new Date(iso);
20
- return d.toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' });
25
+ return d.toLocaleTimeString(lang, { hour: '2-digit', minute: '2-digit' });
21
26
  } catch {
22
27
  return iso;
23
28
  }
24
29
  }
25
30
 
31
+ /**
32
+ * How long a job has been running. Rolls over into hours and days — without
33
+ * that, a job stuck since yesterday rendered as `1680m 12s`, which reads as a
34
+ * bug rather than as a stuck job.
35
+ */
26
36
  function formatDuration(startIso: string): string {
27
- const diff = Date.now() - new Date(startIso).getTime();
28
- const s = Math.floor(diff / 1000);
37
+ const s = Math.floor((Date.now() - new Date(startIso).getTime()) / 1000);
29
38
  if (s < 60) {
30
39
  return `${s}s`;
31
40
  }
32
41
  const m = Math.floor(s / 60);
33
- return `${m}m ${s % 60}s`;
42
+ if (m < 60) {
43
+ return `${m}m ${s % 60}s`;
44
+ }
45
+ const h = Math.floor(m / 60);
46
+ if (h < 24) {
47
+ return `${h}h ${m % 60}m`;
48
+ }
49
+ return `${Math.floor(h / 24)}d ${h % 24}h`;
34
50
  }
35
51
 
36
52
  // ── Stats bar ─────────────────────────────────────────────────────────────────
@@ -38,6 +54,7 @@ interface StatsBarProps {
38
54
  jobs: PrintJob[];
39
55
  }
40
56
  const StatsBar: React.FC<StatsBarProps> = ({ jobs }) => {
57
+ const { t } = useNiceTranslation();
41
58
  const printing = jobs.filter((j) => j.status === 'printing').length;
42
59
  const queued = jobs.filter((j) => j.status === 'queued').length;
43
60
  const done = jobs.filter((j) => j.status === 'done').length;
@@ -46,19 +63,20 @@ const StatsBar: React.FC<StatsBarProps> = ({ jobs }) => {
46
63
  <div className={styles.statsBar}>
47
64
  <span className={`${styles.stat} ${styles.statPrinting}`}>
48
65
  <span className={styles.dot} />
49
- Drukuje: <strong>{printing}</strong>
66
+ {t('printing.stats.active', 'Printing')}: <strong>{printing}</strong>
50
67
  </span>
51
68
  <span className={`${styles.stat} ${styles.statQueued}`}>
52
- <span className={styles.dot} />W kolejce: <strong>{queued}</strong>
69
+ <span className={styles.dot} />
70
+ {t('printing.stats.queued', 'In queue')}: <strong>{queued}</strong>
53
71
  </span>
54
72
  <span className={`${styles.stat} ${styles.statDone}`}>
55
73
  <span className={styles.dot} />
56
- Gotowe: <strong>{done}</strong>
74
+ {t('printing.stats.done', 'Done')}: <strong>{done}</strong>
57
75
  </span>
58
76
  {errored > 0 && (
59
77
  <span className={`${styles.stat} ${styles.statError}`}>
60
78
  <span className={styles.dot} />
61
- Błędy: <strong>{errored}</strong>
79
+ {t('printing.stats.errors', 'Errors')}: <strong>{errored}</strong>
62
80
  </span>
63
81
  )}
64
82
  </div>
@@ -90,6 +108,7 @@ const JobRow: React.FC<JobRowProps> = ({
90
108
  onPrint,
91
109
  onReorder,
92
110
  }) => {
111
+ const { t, lang } = useNiceTranslation();
93
112
  const printer = printers.find((p) => p.id === job.options.printerId);
94
113
  const canMoveUp = index > 0 && job.status === 'queued';
95
114
  const canMoveDown = index < total - 1 && job.status === 'queued';
@@ -108,7 +127,7 @@ const JobRow: React.FC<JobRowProps> = ({
108
127
 
109
128
  {/* Status badge */}
110
129
  <span className={`${styles.statusBadge} ${styles[`badge-${job.status}`]}`}>
111
- {STATUS_LABELS[job.status] ?? job.status}
130
+ {statusLabel(t, job.status)}
112
131
  </span>
113
132
 
114
133
  {/* Job info */}
@@ -116,9 +135,15 @@ const JobRow: React.FC<JobRowProps> = ({
116
135
  <div className={styles.jobName}>{job.templateName ?? job.templateId}</div>
117
136
  <div className={styles.jobMeta}>
118
137
  {job.entityLabel && <span>{job.entityLabel}</span>}
119
- {printer && <span>🖨 {printer.name}</span>}
138
+ {printer && (
139
+ <span>
140
+ <NiceIcon name="printer" size={12} /> {printer.name}
141
+ </span>
142
+ )}
120
143
  <span>×{job.options.copies}</span>
121
- {job.createdAt && <span className={styles.jobTime}>{formatTime(job.createdAt)}</span>}
144
+ {job.createdAt && (
145
+ <span className={styles.jobTime}>{formatTime(lang, job.createdAt)}</span>
146
+ )}
122
147
  {job.startedAt && job.status !== 'done' && (
123
148
  <span className={styles.jobDuration}>{formatDuration(job.startedAt)}</span>
124
149
  )}
@@ -140,20 +165,22 @@ const JobRow: React.FC<JobRowProps> = ({
140
165
  <button
141
166
  className={styles.copiesBtn}
142
167
  disabled={job.options.copies <= 1}
168
+ aria-label={t('printing.copies.decrease', 'One copy fewer')}
143
169
  onClick={() =>
144
170
  onUpdate(job.id, { options: { ...job.options, copies: job.options.copies - 1 } })
145
171
  }
146
172
  >
147
- −
173
+ <NiceIcon name="minus" size={12} />
148
174
  </button>
149
175
  <span className={styles.copiesValue}>{job.options.copies}</span>
150
176
  <button
151
177
  className={styles.copiesBtn}
178
+ aria-label={t('printing.copies.increase', 'One copy more')}
152
179
  onClick={() =>
153
180
  onUpdate(job.id, { options: { ...job.options, copies: job.options.copies + 1 } })
154
181
  }
155
182
  >
156
- +
183
+ <NiceIcon name="plus" size={12} />
157
184
  </button>
158
185
  </div>
159
186
 
@@ -162,50 +189,54 @@ const JobRow: React.FC<JobRowProps> = ({
162
189
  {canMoveUp && (
163
190
  <button
164
191
  className={styles.actionBtn}
165
- title="Przesuń wyżej"
192
+ title={t('printing.action.moveUp', 'Move up')}
166
193
  onClick={() => onReorder(index, index - 1)}
167
194
  >
168
- ↑
195
+ <NiceIcon name="arrow-up" size={14} />
169
196
  </button>
170
197
  )}
171
198
  {canMoveDown && (
172
199
  <button
173
200
  className={styles.actionBtn}
174
- title="Przesuń niżej"
201
+ title={t('printing.action.moveDown', 'Move down')}
175
202
  onClick={() => onReorder(index, index + 1)}
176
203
  >
177
- ↓
204
+ <NiceIcon name="arrow-down" size={14} />
178
205
  </button>
179
206
  )}
180
207
  {job.status === 'queued' && (
181
- <button className={styles.actionBtn} title="Drukuj teraz" onClick={() => onPrint(job.id)}>
182
- ▶
208
+ <button
209
+ className={styles.actionBtn}
210
+ title={t('printing.action.printNow', 'Print now')}
211
+ onClick={() => onPrint(job.id)}
212
+ >
213
+ <NiceIcon name="play" size={14} />
183
214
  </button>
184
215
  )}
185
216
  {job.status === 'printing' && (
186
217
  <button
187
218
  className={styles.actionBtn}
188
- title="Wstrzymaj"
219
+ title={t('printing.action.pause', 'Pause')}
189
220
  onClick={() => onUpdate(job.id, { status: 'paused' })}
190
221
  >
191
- ⏸
222
+ <NiceIcon name="pause" size={14} />
192
223
  </button>
193
224
  )}
194
225
  {job.status === 'paused' && (
195
226
  <button
196
227
  className={styles.actionBtn}
197
- title="Wznów"
228
+ title={t('printing.action.resume', 'Resume')}
198
229
  onClick={() => onUpdate(job.id, { status: 'queued' })}
199
230
  >
200
- ▶
231
+ <NiceIcon name="play" size={14} />
201
232
  </button>
202
233
  )}
203
234
  <button
204
235
  className={`${styles.actionBtn} ${styles.removeBtn}`}
205
- title="Usuń"
236
+ title={t('printing.action.remove', 'Remove')}
206
237
  onClick={() => onRemove(job.id)}
207
238
  >
208
- ✕
239
+ <NiceIcon name="close" size={14} />
209
240
  </button>
210
241
  </div>
211
242
  </div>
@@ -227,6 +258,7 @@ export const NicePrintQueue: React.FC<NicePrintQueueProps> = ({
227
258
  style,
228
259
  theme = 'light',
229
260
  }) => {
261
+ const { t } = useNiceTranslation();
230
262
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
231
263
  const [filter, setFilter] = useState<'all' | 'queued' | 'printing' | 'done' | 'error'>('all');
232
264
 
@@ -280,7 +312,7 @@ export const NicePrintQueue: React.FC<NicePrintQueueProps> = ({
280
312
  className={`${styles.filterTab} ${filter === f ? styles.filterTabActive : ''}`}
281
313
  onClick={() => setFilter(f)}
282
314
  >
283
- {f === 'all' ? 'Wszystkie' : STATUS_LABELS[f]}
315
+ {f === 'all' ? t('printing.filter.all', 'All') : statusLabel(t, f)}
284
316
  <span className={styles.filterCount}>
285
317
  {f === 'all' ? jobs.length : jobs.filter((j) => j.status === f).length}
286
318
  </span>
@@ -291,15 +323,17 @@ export const NicePrintQueue: React.FC<NicePrintQueueProps> = ({
291
323
  {/* Bulk actions */}
292
324
  {selectedIds.size > 0 && (
293
325
  <div className={styles.bulkActions}>
294
- <span className={styles.bulkCount}>{selectedIds.size} zaznaczonych</span>
326
+ <span className={styles.bulkCount}>
327
+ {selectedIds.size} {t('printing.bulk.selected', 'selected')}
328
+ </span>
295
329
  {onBulkPrint && (
296
330
  <button className={styles.bulkBtn} onClick={bulkPrint}>
297
- ▶ Drukuj wybrane
331
+ <NiceIcon name="printer" size={14} /> {t('printing.bulk.print', 'Print selected')}
298
332
  </button>
299
333
  )}
300
334
  {onBulkRemove && (
301
335
  <button className={`${styles.bulkBtn} ${styles.bulkBtnRemove}`} onClick={bulkRemove}>
302
- ✕ Usuń wybrane
336
+ <NiceIcon name="trash" size={14} /> {t('printing.bulk.remove', 'Remove selected')}
303
337
  </button>
304
338
  )}
305
339
  </div>
@@ -311,8 +345,8 @@ export const NicePrintQueue: React.FC<NicePrintQueueProps> = ({
311
345
  {filteredJobs.length === 0 ? (
312
346
  <div className={styles.emptyState}>
313
347
  {filter === 'all'
314
- ? 'Brak zadań w kolejce'
315
- : `Brak zadań ze statusem "${STATUS_LABELS[filter]}"`}
348
+ ? t('printing.empty.queue', 'No jobs in the queue')
349
+ : `${t('printing.empty.filtered', 'No jobs with status')} "${statusLabel(t, filter)}"`}
316
350
  </div>
317
351
  ) : (
318
352
  <>
@@ -323,9 +357,9 @@ export const NicePrintQueue: React.FC<NicePrintQueueProps> = ({
323
357
  onChange={selectAll}
324
358
  checked={selectedIds.size === filteredJobs.length && filteredJobs.length > 0}
325
359
  />
326
- <span className={styles.headerLabel}>Zadanie</span>
327
- <span className={styles.headerLabel}>Kopie</span>
328
- <span className={styles.headerLabel}>Akcje</span>
360
+ <span className={styles.headerLabel}>{t('printing.col.job', 'Job')}</span>
361
+ <span className={styles.headerLabel}>{t('printing.col.copies', 'Copies')}</span>
362
+ <span className={styles.headerLabel}>{t('printing.col.actions', 'Actions')}</span>
329
363
  </div>
330
364
  {filteredJobs.map((job, _index) => (
331
365
  <JobRow
@@ -10,9 +10,9 @@
10
10
  color: var(--nice-text, #1e293b);
11
11
  }
12
12
  .dark {
13
- background: var(--nice-text, #1e293b);
14
- border-color: var(--nice-text, #334155);
15
- color: var(--nice-bg-secondary, #f1f5f9);
13
+ background: var(--nice-bg-secondary, #1e293b);
14
+ border-color: var(--nice-border-color, #334155);
15
+ color: var(--nice-text-primary, #f1f5f9);
16
16
  }
17
17
 
18
18
  /* Stats bar */
@@ -25,8 +25,8 @@
25
25
  border-bottom: 1px solid var(--nice-border, #e2e8f0);
26
26
  }
27
27
  .dark .statsBar {
28
- background: var(--nice-text, #0f172a);
29
- border-color: var(--nice-text, #334155);
28
+ background: var(--nice-bg-primary, #0f172a);
29
+ border-color: var(--nice-border-color, #334155);
30
30
  }
31
31
 
32
32
  .stat {
@@ -64,7 +64,7 @@
64
64
  flex-wrap: wrap;
65
65
  }
66
66
  .dark .toolbar {
67
- border-color: var(--nice-text, #334155);
67
+ border-color: var(--nice-border-color, #334155);
68
68
  }
69
69
 
70
70
  .filterTabs {
@@ -152,7 +152,7 @@
152
152
  border-bottom: 1px solid var(--nice-bg-secondary, #f1f5f9);
153
153
  }
154
154
  .dark .listHeader {
155
- border-color: var(--nice-text, #1e293b);
155
+ border-color: var(--nice-border-color, #1e293b);
156
156
  }
157
157
  .headerLabel {
158
158
  white-space: nowrap;
@@ -169,13 +169,13 @@
169
169
  transition: background 0.1s;
170
170
  }
171
171
  .dark .jobRow {
172
- border-color: var(--nice-text, #334155);
172
+ border-color: var(--nice-border-color, #334155);
173
173
  }
174
174
  .jobRow:hover {
175
175
  background: var(--nice-bg-secondary, #f8fafc);
176
176
  }
177
177
  .dark .jobRow:hover {
178
- background: var(--nice-text, #0f172a);
178
+ background: var(--nice-bg-primary, #0f172a);
179
179
  }
180
180
  .jobRowSelected {
181
181
  background: var(--nice-primary-bg, #eff6ff) !important;
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable jsx-a11y/click-events-have-key-events -- modal overlay backdrops (Esc-on-parent provides keyboard close), selection rows in .map() accompanied by visible cursor:pointer styling and sibling buttons; full keyboard migration via useAccessibleClick + extracted subcomponents tracked in TODO_1.0.10 A2.3 */
2
+ import { NiceIcon, useNiceTranslation } from '@nice2dev/ui';
2
3
  import React, { useState } from 'react';
3
4
 
4
5
  import type {
@@ -9,7 +10,15 @@ import type {
9
10
 
10
11
  import styles from './TemplateBrowser.module.css';
11
12
 
12
- const CATEGORIES = ['Faktury', 'Raporty', 'Etykiety', 'Listy', 'Umowy', 'Inne'];
13
+ /** Built-in category chips; `key` is what a template's `category` field holds. */
14
+ const CATEGORIES: { key: string; fallback: string }[] = [
15
+ { key: 'printing.category.invoices', fallback: 'Invoices' },
16
+ { key: 'printing.category.reports', fallback: 'Reports' },
17
+ { key: 'printing.category.labels', fallback: 'Labels' },
18
+ { key: 'printing.category.letters', fallback: 'Letters' },
19
+ { key: 'printing.category.contracts', fallback: 'Contracts' },
20
+ { key: 'printing.category.other', fallback: 'Other' },
21
+ ];
13
22
 
14
23
  function importTemplateFromJson(file: File): Promise<PrintTemplate> {
15
24
  return new Promise((resolve, reject) => {
@@ -47,6 +56,7 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
47
56
  style,
48
57
  theme = 'light',
49
58
  }) => {
59
+ const { t } = useNiceTranslation();
50
60
  const [search, setSearch] = useState('');
51
61
  const [categoryFilter, setCategoryFilter] = useState<string | null>(null);
52
62
  const [hoveredId, setHoveredId] = useState<string | null>(null);
@@ -86,19 +96,23 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
86
96
  <input
87
97
  type="search"
88
98
  className={styles.searchInput}
89
- placeholder="Szukaj szablonów…"
99
+ placeholder={t('printing.searchTemplates', 'Search templates…')}
90
100
  value={search}
91
101
  onChange={(e) => setSearch(e.target.value)}
92
102
  />
93
- <button className={styles.actionBtn} onClick={onNew} title="Nowy szablon">
94
- + Nowy
103
+ <button
104
+ className={styles.actionBtn}
105
+ onClick={onNew}
106
+ title={t('printing.newTemplate', 'New template')}
107
+ >
108
+ <NiceIcon name="plus" size={14} /> {t('printing.new', 'New')}
95
109
  </button>
96
110
  <button
97
111
  className={styles.actionBtn}
98
112
  onClick={() => fileInputRef.current?.click()}
99
- title="Importuj JSON"
113
+ title={t('printing.importJson', 'Import JSON')}
100
114
  >
101
- ⬆ Import
115
+ <NiceIcon name="upload" size={14} /> {t('printing.import', 'Import')}
102
116
  </button>
103
117
  <input ref={fileInputRef} type="file" accept=".json" hidden onChange={handleFileImport} />
104
118
  </div>
@@ -109,15 +123,15 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
109
123
  className={`${styles.catChip} ${!categoryFilter ? styles.catChipActive : ''}`}
110
124
  onClick={() => setCategoryFilter(null)}
111
125
  >
112
- Wszystkie
126
+ {t('printing.filter.all', 'All')}
113
127
  </button>
114
128
  {CATEGORIES.map((cat) => (
115
129
  <button
116
- key={cat}
117
- className={`${styles.catChip} ${categoryFilter === cat ? styles.catChipActive : ''}`}
118
- onClick={() => setCategoryFilter((c) => (c === cat ? null : cat))}
130
+ key={cat.key}
131
+ className={`${styles.catChip} ${categoryFilter === cat.fallback ? styles.catChipActive : ''}`}
132
+ onClick={() => setCategoryFilter((c) => (c === cat.fallback ? null : cat.fallback))}
119
133
  >
120
- {cat}
134
+ {t(cat.key, cat.fallback)}
121
135
  </button>
122
136
  ))}
123
137
  </div>
@@ -168,9 +182,9 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
168
182
  onExport?.(tpl);
169
183
  exportTemplate(tpl);
170
184
  }}
171
- title="Eksportuj"
185
+ title={t('printing.export', 'Export')}
172
186
  >
173
- ⬇
187
+ <NiceIcon name="download" size={14} />
174
188
  </button>
175
189
  {onDelete && (
176
190
  <button
@@ -179,9 +193,9 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
179
193
  e.stopPropagation();
180
194
  onDelete(tpl.id);
181
195
  }}
182
- title="Usuń"
196
+ title={t('printing.action.remove', 'Remove')}
183
197
  >
184
- ✕
198
+ <NiceIcon name="trash" size={14} />
185
199
  </button>
186
200
  )}
187
201
  </div>
@@ -194,7 +208,10 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
194
208
  {tpl.description && <div className={styles.cardDesc}>{tpl.description}</div>}
195
209
  <div className={styles.cardMeta}>
196
210
  <span className={styles.cardSize}>
197
- {tpl.paperSize} {tpl.orientation === 'landscape' ? '↔' : '↕'}
211
+ {tpl.paperSize}{' '}
212
+ {tpl.orientation === 'landscape'
213
+ ? t('printing.orientation.landscape', 'landscape')
214
+ : t('printing.orientation.portrait', 'portrait')}
198
215
  </span>
199
216
  {tpl.category && <span className={styles.cardCat}>{tpl.category}</span>}
200
217
  </div>
@@ -213,9 +230,13 @@ export const NiceTemplateBrowser: React.FC<NiceTemplateBrowserProps> = ({
213
230
 
214
231
  {filtered.length === 0 && (
215
232
  <div className={styles.emptyState}>
216
- <span className={styles.emptyIcon}>📄</span>
233
+ <span className={styles.emptyIcon}>
234
+ <NiceIcon name="file" size={32} />
235
+ </span>
217
236
  <span>
218
- {search ? `Brak wyników dla "${search}"` : 'Brak szablonów. Utwórz pierwszy!'}
237
+ {search
238
+ ? `${t('printing.noMatches', 'No matches for')} "${search}"`
239
+ : t('printing.noTemplates', 'No templates yet.')}
219
240
  </span>
220
241
  </div>
221
242
  )}
@@ -11,9 +11,9 @@
11
11
  }
12
12
 
13
13
  .dark {
14
- background: var(--nice-text, #0f172a);
15
- color: var(--nice-border, #e2e8f0);
16
- border-color: var(--nice-text, #1e293b);
14
+ background: var(--nice-bg-primary, #0f172a);
15
+ color: var(--nice-text-secondary, #e2e8f0);
16
+ border-color: var(--nice-border-color, #1e293b);
17
17
  }
18
18
 
19
19
  /* ── Top bar ── */
@@ -28,8 +28,8 @@
28
28
  }
29
29
 
30
30
  .dark .topBar {
31
- background: var(--nice-text, #1e293b);
32
- border-bottom-color: var(--nice-text, #334155);
31
+ background: var(--nice-bg-secondary, #1e293b);
32
+ border-bottom-color: var(--nice-border-color, #334155);
33
33
  }
34
34
 
35
35
  .searchInput {
@@ -48,9 +48,9 @@
48
48
  background: var(--nice-bg, #fff);
49
49
  }
50
50
  .dark .searchInput {
51
- background: var(--nice-text, #334155);
52
- border-color: var(--nice-text-secondary, #475569);
53
- color: var(--nice-border, #e2e8f0);
51
+ background: var(--nice-bg-tertiary, #334155);
52
+ border-color: var(--nice-border-color, #475569);
53
+ color: var(--nice-text-secondary, #e2e8f0);
54
54
  }
55
55
 
56
56
  .actionBtn {
@@ -70,9 +70,9 @@
70
70
  background: var(--nice-bg-secondary, #f1f5f9);
71
71
  }
72
72
  .dark .actionBtn {
73
- background: var(--nice-text, #334155);
74
- border-color: var(--nice-text-secondary, #475569);
75
- color: var(--nice-border, #e2e8f0);
73
+ background: var(--nice-bg-tertiary, #334155);
74
+ border-color: var(--nice-border-color, #475569);
75
+ color: var(--nice-text-secondary, #e2e8f0);
76
76
  }
77
77
 
78
78
  /* ── Categories ── */
@@ -87,8 +87,8 @@
87
87
  }
88
88
 
89
89
  .dark .categoryRow {
90
- background: var(--nice-text, #1e293b);
91
- border-bottom-color: var(--nice-text, #334155);
90
+ background: var(--nice-bg-secondary, #1e293b);
91
+ border-bottom-color: var(--nice-border-color, #334155);
92
92
  }
93
93
 
94
94
  .catChip {
@@ -109,7 +109,7 @@
109
109
  .catChipActive {
110
110
  background: var(--nice-primary-hover, #2563eb);
111
111
  border-color: var(--nice-primary-hover, #2563eb);
112
- color: var(--nice-bg, #fff);
112
+ color: var(--nice-text-on-primary, #fff);
113
113
  }
114
114
 
115
115
  /* ── Template grid ── */
@@ -146,8 +146,8 @@
146
146
  }
147
147
 
148
148
  .dark .card {
149
- background: var(--nice-text, #1e293b);
150
- border-color: var(--nice-text, #334155);
149
+ background: var(--nice-bg-secondary, #1e293b);
150
+ border-color: var(--nice-border-color, #334155);
151
151
  }
152
152
 
153
153
  /* Thumbnail */
@@ -253,7 +253,7 @@
253
253
  }
254
254
 
255
255
  .dark .cardName {
256
- color: var(--nice-bg-secondary, #f1f5f9);
256
+ color: var(--nice-text-primary, #f1f5f9);
257
257
  }
258
258
 
259
259
  .cardDesc {