gov-layout 1.3.4 → 1.3.6
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.
- package/README.md +588 -585
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +29 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/styles.css +24 -0
package/README.md
CHANGED
|
@@ -1,585 +1,588 @@
|
|
|
1
|
-
# gov-layout
|
|
2
|
-
|
|
3
|
-
Government Layout Components สำหรับเว็บแอปพลิเคชันภาครัฐ
|
|
4
|
-
|
|
5
|
-
> ใช้คู่กับ `gov-token-css` เพื่อให้สีตรงตาม Design System
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 📥 Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install gov-layout gov-token-css
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
```css
|
|
16
|
-
/* ใน globals.css */
|
|
17
|
-
@import "gov-token-css";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<Icons.
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{ id: '
|
|
134
|
-
{ id: '
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
> **💡
|
|
156
|
-
>
|
|
157
|
-
|
|
158
|
-
>
|
|
159
|
-
>
|
|
160
|
-
>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
|
201
|
-
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
- ✅
|
|
221
|
-
- ✅
|
|
222
|
-
- ✅
|
|
223
|
-
- ✅
|
|
224
|
-
- ✅
|
|
225
|
-
- ✅
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
|
276
|
-
|
|
277
|
-
| `user.
|
|
278
|
-
| `
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
| `
|
|
283
|
-
| `
|
|
284
|
-
| `
|
|
285
|
-
| `
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
- ✅
|
|
305
|
-
- ✅
|
|
306
|
-
- ✅
|
|
307
|
-
- ✅ **
|
|
308
|
-
- ✅
|
|
309
|
-
- ✅
|
|
310
|
-
- ✅
|
|
311
|
-
- ✅
|
|
312
|
-
- ✅
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
//
|
|
383
|
-
<SettingsPanel
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
<
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
|
416
|
-
|
|
417
|
-
| `
|
|
418
|
-
| `
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
html.dark
|
|
434
|
-
html.dark
|
|
435
|
-
html.dark
|
|
436
|
-
html.dark
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
//
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
{
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
-
|
|
1
|
+
# gov-layout
|
|
2
|
+
|
|
3
|
+
Government Layout Components สำหรับเว็บแอปพลิเคชันภาครัฐ
|
|
4
|
+
|
|
5
|
+
> ใช้คู่กับ `gov-token-css` เพื่อให้สีตรงตาม Design System
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📥 Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install gov-layout gov-token-css
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
/* ใน globals.css */
|
|
17
|
+
@import "gov-token-css";
|
|
18
|
+
@import "gov-layout/styles.css";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> ใช้ `gov-layout/styles.css` เพื่อให้ class สีเช่น `text-text-primary` ทำงาน แม้ไม่ได้ import component จาก `gov-layout`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Components ทั้งหมด
|
|
26
|
+
|
|
27
|
+
| Component | ใช้สำหรับ | import |
|
|
28
|
+
|-----------|----------|--------|
|
|
29
|
+
| `StaffSidebar` | Sidebar เจ้าหน้าที่ | `import { StaffSidebar } from 'gov-layout'` |
|
|
30
|
+
| `UserHeader` | Header ผู้ใช้ทั่วไป | `import { UserHeader } from 'gov-layout'` |
|
|
31
|
+
| `UserSidebar` | Sidebar ผู้ใช้ (slide-in) | `import { UserSidebar } from 'gov-layout'` |
|
|
32
|
+
| `SettingsPanel` | หน้าตั้งค่า (font + theme) | `import { SettingsPanel } from 'gov-layout'` |
|
|
33
|
+
| `SettingsProvider` | Context wrapper | `import { SettingsProvider } from 'gov-layout'` |
|
|
34
|
+
| `useSettings` | Hook อ่าน/เปลี่ยนค่า | `import { useSettings } from 'gov-layout'` |
|
|
35
|
+
| `Icons` | ไอคอน built-in 33 ตัว | `import { Icons } from 'gov-layout'` |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🎯 Icons (ไอคอน built-in)
|
|
40
|
+
|
|
41
|
+
import ครั้งเดียว ใช้ได้ทุกไอคอน — ไม่ต้องสร้าง SVG เอง
|
|
42
|
+
|
|
43
|
+
### วิธีใช้งาน
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { Icons } from 'gov-layout';
|
|
47
|
+
|
|
48
|
+
// ใช้ตรงๆ
|
|
49
|
+
<Icons.Folder />
|
|
50
|
+
<Icons.User />
|
|
51
|
+
<Icons.Gear />
|
|
52
|
+
|
|
53
|
+
// ปรับขนาด (default = 20)
|
|
54
|
+
<Icons.Home size={24} />
|
|
55
|
+
|
|
56
|
+
// ใส่ใน MenuItem
|
|
57
|
+
const menuItems: MenuItem[] = [
|
|
58
|
+
{ id: 'services', title: 'งานบริการ', icon: <Icons.Folder />, path: '/services' },
|
|
59
|
+
{ id: 'users', title: 'จัดการผู้ใช้', icon: <Icons.User />, path: '/users' },
|
|
60
|
+
{ id: 'reports', title: 'รายงาน', icon: <Icons.BarChart />, path: '/reports' },
|
|
61
|
+
];
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### ไอคอนทั้งหมด
|
|
65
|
+
|
|
66
|
+
| ชื่อ | Component | ใช้สำหรับ |
|
|
67
|
+
|------|-----------|----------|
|
|
68
|
+
| 🏠 | `Icons.Home` | หน้าแรก / Dashboard |
|
|
69
|
+
| 🔍 | `Icons.Search` | ค้นหา |
|
|
70
|
+
| 🔔 | `Icons.Bell` | แจ้งเตือน |
|
|
71
|
+
| 📁 | `Icons.Folder` | งานบริการ / หมวดหมู่ |
|
|
72
|
+
| 📋 | `Icons.Clipboard` | แบบฟอร์ม / คำร้อง |
|
|
73
|
+
| 📄 | `Icons.FileText` | รายงาน / เอกสาร |
|
|
74
|
+
| 📅 | `Icons.Calendar` | ตารางงาน / นัดหมาย |
|
|
75
|
+
| 👤 | `Icons.User` | ข้อมูลผู้ใช้ |
|
|
76
|
+
| 👥 | `Icons.Users` | จัดการสมาชิก |
|
|
77
|
+
| ⚙️ | `Icons.Gear` | ตั้งค่าระบบ |
|
|
78
|
+
| 🔧 | `Icons.Wrench` | ซ่อมบำรุง |
|
|
79
|
+
| 🛡️ | `Icons.Shield` | สิทธิ์การใช้งาน |
|
|
80
|
+
| ❓ | `Icons.HelpCircle` | ช่วยเหลือ |
|
|
81
|
+
| 📊 | `Icons.BarChart` | สถิติ / รายงาน |
|
|
82
|
+
| 🕐 | `Icons.History` | ประวัติการใช้งาน |
|
|
83
|
+
| 💾 | `Icons.Database` | สำรองข้อมูล |
|
|
84
|
+
| | `Icons.Building` | หน่วยงาน / องค์กร |
|
|
85
|
+
| 📍 | `Icons.MapPin` | สถานที่ |
|
|
86
|
+
| 📞 | `Icons.Phone` | ติดต่อ |
|
|
87
|
+
| ✉️ | `Icons.Mail` | อีเมล / ข้อความ |
|
|
88
|
+
| ✅ | `Icons.CheckCircle` | สำเร็จ / อนุมัติ |
|
|
89
|
+
| ⚠️ | `Icons.AlertTriangle` | คำเตือน |
|
|
90
|
+
| ❌ | `Icons.XCircle` | ปฏิเสธ / ข้อผิดพลาด |
|
|
91
|
+
| ➕ | `Icons.PlusCircle` | เพิ่มรายการ |
|
|
92
|
+
| 🚪 | `Icons.LogOut` | ออกจากระบบ |
|
|
93
|
+
| ⬇️ | `Icons.Download` | ดาวน์โหลด |
|
|
94
|
+
| ⬆️ | `Icons.Upload` | อัปโหลด |
|
|
95
|
+
| 🖨️ | `Icons.Printer` | พิมพ์เอกสาร |
|
|
96
|
+
| ⭐ | `Icons.Star` | รายการโปรด |
|
|
97
|
+
| ❤️ | `Icons.Heart` | ถูกใจ |
|
|
98
|
+
| 👁️ | `Icons.Eye` | ดูรายละเอียด |
|
|
99
|
+
| ✏️ | `Icons.Edit` | แก้ไข |
|
|
100
|
+
| 🗑️ | `Icons.Trash` | ลบ |
|
|
101
|
+
|
|
102
|
+
### IconProps
|
|
103
|
+
|
|
104
|
+
| Prop | Type | Default | คำอธิบาย |
|
|
105
|
+
|------|------|---------|----------|
|
|
106
|
+
| `size` | `number?` | `20` | ขนาด (width & height) |
|
|
107
|
+
| `className` | `string?` | - | CSS class |
|
|
108
|
+
| `style` | `CSSProperties?` | - | inline style |
|
|
109
|
+
|
|
110
|
+
> 💡 ถ้าต้องการ import ทีละตัวก็ได้: `import { FolderIcon, UserIcon } from 'gov-layout'`
|
|
111
|
+
|
|
112
|
+
## 1. StaffSidebar (เจ้าหน้าที่)
|
|
113
|
+
|
|
114
|
+
Sidebar ฝั่งซ้ายแบบ fixed — รองรับพับ/กาง (collapsible)
|
|
115
|
+
|
|
116
|
+
### ตัวอย่างใช้งาน
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { StaffSidebar } from 'gov-layout';
|
|
120
|
+
import type { MenuItem } from 'gov-layout';
|
|
121
|
+
|
|
122
|
+
// ไม่ต้องระบุ icon — sidebar จับคู่ไอคอนให้อัตโนมัติจาก id
|
|
123
|
+
const menuItems: MenuItem[] = [
|
|
124
|
+
{
|
|
125
|
+
id: 'services', // → 📁 FolderIcon
|
|
126
|
+
title: 'งานบริการ',
|
|
127
|
+
children: [
|
|
128
|
+
{ id: 'water', title: 'ประปา', path: '/services/water' },
|
|
129
|
+
{ id: 'tax', title: 'ภาษี', path: '/services/tax' },
|
|
130
|
+
],
|
|
131
|
+
dividerAfter: true,
|
|
132
|
+
},
|
|
133
|
+
{ id: 'users', title: 'จัดการผู้ใช้', path: '/users' }, // → 👥 UsersIcon
|
|
134
|
+
{ id: 'reports', title: 'รายงาน', path: '/reports' }, // → 📊 BarChartIcon
|
|
135
|
+
{ id: 'roles', title: 'สิทธิ์การใช้งาน', path: '/roles' }, // → 🛡️ ShieldIcon
|
|
136
|
+
{ id: 'logs', title: 'ประวัติ', path: '/logs' }, // → 🕐 HistoryIcon
|
|
137
|
+
{ id: 'backup', title: 'สำรองข้อมูล', path: '/backup' }, // → 💾 DatabaseIcon
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
<StaffSidebar
|
|
141
|
+
orgLogo="/logo.png"
|
|
142
|
+
orgName="เทศบาลตำบล Biza"
|
|
143
|
+
orgSubtitle="จังหวัดราชบุรี"
|
|
144
|
+
menuItems={menuItems}
|
|
145
|
+
user={{ firstName: 'สมชาย', lastName: 'ใจดี' }}
|
|
146
|
+
roleLabel="เจ้าหน้าที่"
|
|
147
|
+
currentPath="/services/water"
|
|
148
|
+
onNavigate={(path) => router.push(path)}
|
|
149
|
+
onLogout={() => signOut()}
|
|
150
|
+
onProfile={() => router.push('/profile')}
|
|
151
|
+
collapsible
|
|
152
|
+
/>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> **💡 Auto-Icon:** ไม่ต้อง import ไอคอน — sidebar จับคู่จาก `id` อัตโนมัติ
|
|
156
|
+
> ถ้าอยากกำหนดเอง ก็ส่ง `icon` prop ได้ตามปกติ: `icon: <Icons.Folder />`
|
|
157
|
+
|
|
158
|
+
> **💡 ข้อมูลองค์กร (`orgLogo`, `orgName`, `orgSubtitle`) มาจากไหนก็ได้:**
|
|
159
|
+
> - ดึงจาก **ตัวกลาง SSO** หลังล็อกอิน (แนะนำ) เช่น `useSSOAuth().organization`
|
|
160
|
+
> - ดึงจาก **API** เช่น `fetchOrgInfo()`
|
|
161
|
+
> - **Fix ค่า** ตรงๆ ก็ได้ ถ้าใช้ระบบเดียว
|
|
162
|
+
>
|
|
163
|
+
> Library ไม่ผูกกับแหล่งข้อมูลใดๆ — แค่รับ props แล้วแสดงผล
|
|
164
|
+
|
|
165
|
+
### Default Bottom Menu
|
|
166
|
+
|
|
167
|
+
ไม่ต้องตั้งค่าเอง — มี **ตั้งค่าระบบ** + **ช่วยเหลือ** อยู่ด้านล่างอัตโนมัติ
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
งานบริการ ▽
|
|
171
|
+
ประปา / ภาษี / ทะเบียน
|
|
172
|
+
──────────────
|
|
173
|
+
จัดการผู้ใช้
|
|
174
|
+
รายงาน
|
|
175
|
+
ตั้งค่าระบบ ← default (ไม่ต้องส่ง)
|
|
176
|
+
ช่วยเหลือ ← default (ไม่ต้องส่ง)
|
|
177
|
+
↕ spacer
|
|
178
|
+
โปรไฟล์ + ออกจากระบบ
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
ถ้าอยากเปลี่ยน bottom menu เอง:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<StaffSidebar
|
|
185
|
+
bottomMenuItems={[
|
|
186
|
+
{ id: 'settings', title: 'ตั้งค่า', icon: <Icons.Gear />, path: '/settings' },
|
|
187
|
+
]}
|
|
188
|
+
...
|
|
189
|
+
/>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
ถ้าไม่อยากมี bottom menu:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<StaffSidebar bottomMenuItems={[]} ... />
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Props ทั้งหมด
|
|
199
|
+
|
|
200
|
+
| Prop | Type | Default | คำอธิบาย |
|
|
201
|
+
|------|------|---------|----------|
|
|
202
|
+
| `orgLogo` | `string?` | - | URL รูปตราองค์กร |
|
|
203
|
+
| `orgName` | `string` | **required** | ชื่อองค์กร |
|
|
204
|
+
| `orgSubtitle` | `string?` | - | ชื่อรอง เช่น จังหวัด |
|
|
205
|
+
| `menuItems` | `MenuItem[]` | **required** | เมนูหลัก |
|
|
206
|
+
| `bottomMenuItems` | `MenuItem[]?` | ตั้งค่าระบบ + ช่วยเหลือ | เมนูด้านล่าง |
|
|
207
|
+
| `user` | `User \| null` | **required** | ข้อมูลผู้ใช้ |
|
|
208
|
+
| `roleLabel` | `string` | **required** | ป้ายตำแหน่ง เช่น "เจ้าหน้าที่" |
|
|
209
|
+
| `onNavigate` | `(path) => void` | **required** | callback เมื่อคลิกเมนู |
|
|
210
|
+
| `onLogout` | `() => void` | **required** | callback ออกจากระบบ |
|
|
211
|
+
| `onProfile` | `() => void?` | - | callback เมื่อกดโปรไฟล์ผู้ใช้ |
|
|
212
|
+
| `currentPath` | `string?` | - | path ปัจจุบัน (highlight active) |
|
|
213
|
+
| `width` | `string?` | `'280px'` | ความกว้าง sidebar |
|
|
214
|
+
| `collapsible` | `boolean?` | `false` | เปิดโหมดพับ/กาง |
|
|
215
|
+
| `isOpen` | `boolean?` | - | controlled open/close |
|
|
216
|
+
| `onToggle` | `() => void?` | - | callback เมื่อกดพับ/กาง |
|
|
217
|
+
|
|
218
|
+
### Features
|
|
219
|
+
|
|
220
|
+
- ✅ Dropdown submenu (พับ/กางอัตโนมัติ)
|
|
221
|
+
- ✅ Auto-expand เมื่อ child active
|
|
222
|
+
- ✅ Active item highlight
|
|
223
|
+
- ✅ Collapsible — พับเป็น icon-only 64px, กางเป็น 280px
|
|
224
|
+
- ✅ Tooltip เมื่อพับ
|
|
225
|
+
- ✅ Default ตั้งค่าระบบ + ช่วยเหลือ (override ได้)
|
|
226
|
+
- ✅ โปรไฟล์ + ออกจากระบบ ล่างสุดเสมอ
|
|
227
|
+
- ✅ `dividerAfter` เส้นคั่นระหว่างกลุ่ม
|
|
228
|
+
- ✅ ใช้ Standard Avatar Placeholder กรณีที่ไม่มีรูปโปรไฟล์หรือโหลดรูปไม่สำเร็จ (v1.3.2+) 🆕
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 2. UserHeader (ผู้ใช้ทั่วไป)
|
|
233
|
+
|
|
234
|
+
Header ด้านบนพร้อม notification bell
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { UserHeader } from 'gov-layout';
|
|
238
|
+
|
|
239
|
+
<UserHeader
|
|
240
|
+
user={{
|
|
241
|
+
firstName: 'ชนธัญ',
|
|
242
|
+
pictureUrl: '/avatar.jpg',
|
|
243
|
+
subtitle: 'ผู้สูงอายุ', // แสดงใต้ข้อความทักทาย (optional)
|
|
244
|
+
}}
|
|
245
|
+
notifications={[
|
|
246
|
+
{
|
|
247
|
+
id: 1,
|
|
248
|
+
title: 'คำร้องใหม่รอตรวจสอบ',
|
|
249
|
+
description: 'มีคำร้องใหม่เข้ามา กรุณาตรวจสอบ',
|
|
250
|
+
date: '2 ชม. ที่แล้ว',
|
|
251
|
+
type: 'warning',
|
|
252
|
+
isRead: false,
|
|
253
|
+
category: 'action', // ← แสดงในแท็บ "ต้องดำเนินการ"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: 2,
|
|
257
|
+
title: 'คำร้องได้รับการอนุมัติ',
|
|
258
|
+
description: 'คำร้องหมายเลข #1234 อนุมัติสำเร็จ',
|
|
259
|
+
date: '5 ชม. ที่แล้ว',
|
|
260
|
+
type: 'success',
|
|
261
|
+
isRead: true,
|
|
262
|
+
category: 'general', // ← แสดงในแท็บ "แจ้งเตือนทั่วไป"
|
|
263
|
+
},
|
|
264
|
+
]}
|
|
265
|
+
onToggleSidebar={() => setOpen(true)}
|
|
266
|
+
onMarkAllRead={() => markAllRead()}
|
|
267
|
+
onViewAll={() => router.push('/notifications')}
|
|
268
|
+
onNotificationClick={(notif) => router.push(`/notifications/${notif.id}`)}
|
|
269
|
+
onProfile={() => router.push('/profile')}
|
|
270
|
+
/>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Props
|
|
274
|
+
|
|
275
|
+
| Prop | Type | Default | คำอธิบาย |
|
|
276
|
+
|------|------|---------|----------|
|
|
277
|
+
| `user.firstName` | `string?` | - | ชื่อ |
|
|
278
|
+
| `user.lastName` | `string?` | - | นามสกุล |
|
|
279
|
+
| `user.pictureUrl` | `string?` | - | URL รูปโปรไฟล์ |
|
|
280
|
+
| `user.subtitle` | `string?` | - | ข้อความใต้ชื่อ เช่น "ผู้สูงอายุ" |
|
|
281
|
+
| `notifications` | `NotificationItem[]?` | `[]` | รายการแจ้งเตือน |
|
|
282
|
+
| `onToggleSidebar` | `() => void?` | - | callback เปิด sidebar |
|
|
283
|
+
| `onMarkAllRead` | `() => void?` | - | callback อ่านทั้งหมดแล้ว |
|
|
284
|
+
| `onViewAll` | `() => void?` | - | callback ดูทั้งหมด |
|
|
285
|
+
| `onNotificationClick` | `(notification) => void?` | - | callback เมื่อกดแจ้งเตือนแต่ละรายการ |
|
|
286
|
+
| `onProfile` | `() => void?` | - | callback เมื่อกดโปรไฟล์ |
|
|
287
|
+
| `notificationBell` | `ReactNode?` | - | custom bell icon |
|
|
288
|
+
| `className` | `string?` | - | className เพิ่มเติม |
|
|
289
|
+
|
|
290
|
+
### 🔔 Notification Filter Tabs (v1.2.25+)
|
|
291
|
+
|
|
292
|
+
Dropdown แจ้งเตือนมีแท็บกรอง 3 หมวด พร้อม badge ตัวเลข:
|
|
293
|
+
|
|
294
|
+
| แท็บ | กรองจาก `category` | ตัวอย่างใช้งาน |
|
|
295
|
+
|------|-------------------|----------------|
|
|
296
|
+
| **ทั้งหมด** | แสดงทุกรายการ | ดูภาพรวม |
|
|
297
|
+
| **ต้องดำเนินการ** | `'action'` | คำร้องใหม่รอตรวจสอบ, ต้องอัปโหลดเอกสาร, มีคิวใหม่ |
|
|
298
|
+
| **แจ้งเตือนทั่วไป** | `'general'` หรือไม่ระบุ | อนุมัติสำเร็จ, จองสำเร็จ, ยกเลิกแล้ว |
|
|
299
|
+
|
|
300
|
+
> 💡 ถ้าไม่ส่ง `category` จะถูกจัดเป็น **แจ้งเตือนทั่วไป** อัตโนมัติ (backward compatible)
|
|
301
|
+
|
|
302
|
+
### Features
|
|
303
|
+
|
|
304
|
+
- ✅ Notification bell พร้อม badge (99+ เมื่อเกิน)
|
|
305
|
+
- ✅ ไม่มีแจ้งเตือน → ไม่แสดง badge
|
|
306
|
+
- ✅ Notification dropdown แบบ scroll
|
|
307
|
+
- ✅ **Filter tabs**: ทั้งหมด / ต้องดำเนินการ / แจ้งเตือนทั่วไป 🆕
|
|
308
|
+
- ✅ **Badge ตัวเลข** แต่ละแท็บ 🆕
|
|
309
|
+
- ✅ **Category badge** "ต้องดำเนินการ" ติดแต่ละรายการ 🆕
|
|
310
|
+
- ✅ **Empty state แยกตาม filter** 🆕
|
|
311
|
+
- ✅ กดแจ้งเตือนแต่ละรายการ → `onNotificationClick`
|
|
312
|
+
- ✅ Subtitle ใต้ข้อความทักทาย (เช่น "ผู้สูงอายุ")
|
|
313
|
+
- ✅ ปุ่มเปิด sidebar (☰)
|
|
314
|
+
- ✅ กดโปรไฟล์ผู้ใช้ (avatar + ชื่อ) → `onProfile`
|
|
315
|
+
- ✅ รองรับ Dark Mode
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 3. UserSidebar (ผู้ใช้ทั่วไป)
|
|
320
|
+
|
|
321
|
+
Sidebar ฝั่งขวาแบบ slide-in + overlay
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { UserSidebar } from 'gov-layout';
|
|
325
|
+
|
|
326
|
+
<UserSidebar
|
|
327
|
+
isOpen={isSidebarOpen}
|
|
328
|
+
onClose={() => setIsSidebarOpen(false)}
|
|
329
|
+
user={{ firstName: 'สมหญิง', lastName: 'ใจดี', pictureUrl: '/avatar.jpg' }}
|
|
330
|
+
roleLabel="ผู้สูงอายุ"
|
|
331
|
+
menuItems={[
|
|
332
|
+
{ id: 'profile', title: 'ข้อมูลส่วนตัว', path: '/profile' },
|
|
333
|
+
{ id: 'services', title: 'บริการหลัก', path: '/services' },
|
|
334
|
+
{ id: 'settings', title: 'ตั้งค่าระบบ', path: '/settings' },
|
|
335
|
+
]}
|
|
336
|
+
onNavigate={(path) => router.push(path)}
|
|
337
|
+
onLogout={() => signOut()}
|
|
338
|
+
onProfile={() => router.push('/profile')}
|
|
339
|
+
/>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
> **หมายเหตุ:** `roleLabel` แต่ละระบบส่งค่าเองได้ เช่น "ผู้สูงอายุ", "ผู้ใช้ปกติ", "อาสาสมัคร"
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 4. SettingsPanel (ตั้งค่าระบบ) 🆕
|
|
347
|
+
|
|
348
|
+
ปรับขนาดตัวอักษร (5 ระดับ) + โหมดสว่าง/มืด — ค่าจำใน localStorage
|
|
349
|
+
|
|
350
|
+
### ขั้นตอนที่ 1: ครอบ SettingsProvider
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
// app/providers.tsx ← ต้องเป็น 'use client'
|
|
354
|
+
'use client';
|
|
355
|
+
import { SettingsProvider } from 'gov-layout';
|
|
356
|
+
|
|
357
|
+
export default function Providers({ children }: { children: React.ReactNode }) {
|
|
358
|
+
return <SettingsProvider>{children}</SettingsProvider>;
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
// app/layout.tsx
|
|
364
|
+
import Providers from './providers';
|
|
365
|
+
|
|
366
|
+
export default function RootLayout({ children }) {
|
|
367
|
+
return (
|
|
368
|
+
<html lang="th">
|
|
369
|
+
<body>
|
|
370
|
+
<Providers>{children}</Providers>
|
|
371
|
+
</body>
|
|
372
|
+
</html>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### ขั้นตอนที่ 2: วาง SettingsPanel
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
import { SettingsPanel } from 'gov-layout';
|
|
381
|
+
|
|
382
|
+
// ผู้ใช้ทั่วไป → ทั้ง font + theme
|
|
383
|
+
<SettingsPanel />
|
|
384
|
+
|
|
385
|
+
// เจ้าหน้าที่ → แค่ปรับขนาดฟอนต์
|
|
386
|
+
<SettingsPanel showTheme={false} />
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
| Prop | Type | Default | คำอธิบาย |
|
|
390
|
+
|------|------|---------|----------|
|
|
391
|
+
| `showTheme` | `boolean?` | `true` | แสดงตัวเลือกโหมดสว่าง/มืด |
|
|
392
|
+
| `className` | `string?` | - | className เพิ่มเติม |
|
|
393
|
+
|
|
394
|
+
### ขั้นตอนที่ 3: ใช้ useSettings() hook (ถ้าต้องการ)
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
import { useSettings } from 'gov-layout';
|
|
398
|
+
|
|
399
|
+
function MyComponent() {
|
|
400
|
+
const { theme, toggleTheme, fontSize, setFontSize, fontSizeOption } = useSettings();
|
|
401
|
+
|
|
402
|
+
return (
|
|
403
|
+
<div>
|
|
404
|
+
<p>ธีม: {theme}</p>
|
|
405
|
+
<p>ฟอนต์: {fontSize} (×{fontSizeOption.scale})</p>
|
|
406
|
+
<button onClick={toggleTheme}>สลับธีม</button>
|
|
407
|
+
<button onClick={() => setFontSize('large')}>ฟอนต์ใหญ่</button>
|
|
408
|
+
</div>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### ขนาดตัวอักษร (5 ระดับ)
|
|
414
|
+
|
|
415
|
+
| ค่า | Label | Scale | ผลลัพธ์ |
|
|
416
|
+
|-----|-------|-------|---------|
|
|
417
|
+
| `xsmall` | เล็กมาก | ×0.8 | ย่อทุกอย่าง 80% |
|
|
418
|
+
| `small` | เล็ก | ×0.9 | ย่อเล็กน้อย |
|
|
419
|
+
| `medium` | กลาง | ×1.0 | ค่าเริ่มต้น |
|
|
420
|
+
| `large` | ใหญ่ | ×1.2 | ขยาย 120% |
|
|
421
|
+
| `xlarge` | ใหญ่มาก | ×1.4 | ขยาย 140% |
|
|
422
|
+
|
|
423
|
+
### หลักการทำงาน
|
|
424
|
+
|
|
425
|
+
- **Theme** — เพิ่ม/ลบ class `dark` บน `<html>` ➜ ใช้ CSS `html.dark` จัดสี
|
|
426
|
+
- **Font size** — ปรับ `body.style.zoom` + CSS variables ตาม scale
|
|
427
|
+
- **Persistence** — เก็บใน localStorage (`app-theme`, `app-font-size`)
|
|
428
|
+
|
|
429
|
+
### Dark mode CSS ที่ต้องเพิ่ม
|
|
430
|
+
|
|
431
|
+
```css
|
|
432
|
+
/* globals.css — เพิ่มสำหรับ dark mode */
|
|
433
|
+
html.dark body { background-color: #0f172a; color: #f1f5f9; }
|
|
434
|
+
html.dark aside { background-color: #1e293b !important; }
|
|
435
|
+
html.dark header { background-color: #1e293b !important; }
|
|
436
|
+
html.dark h1,
|
|
437
|
+
html.dark h2,
|
|
438
|
+
html.dark h3 { color: #f1f5f9 !important; }
|
|
439
|
+
html.dark p { color: #94a3b8 !important; }
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 📐 Types
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
// เมนู
|
|
448
|
+
interface MenuItem {
|
|
449
|
+
id: string;
|
|
450
|
+
title: string;
|
|
451
|
+
path?: string; // path สำหรับ navigate
|
|
452
|
+
icon?: React.ReactNode; // icon component
|
|
453
|
+
children?: MenuItem[]; // submenu → แสดงเป็น dropdown
|
|
454
|
+
dividerAfter?: boolean; // เส้นคั่นด้านล่าง
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ผู้ใช้
|
|
458
|
+
interface User {
|
|
459
|
+
id?: string | number;
|
|
460
|
+
firstName?: string;
|
|
461
|
+
lastName?: string;
|
|
462
|
+
pictureUrl?: string;
|
|
463
|
+
role?: string;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// การแจ้งเตือน
|
|
467
|
+
type NotificationCategory = 'action' | 'general';
|
|
468
|
+
|
|
469
|
+
interface NotificationItem {
|
|
470
|
+
id: string | number;
|
|
471
|
+
title: string;
|
|
472
|
+
description: string;
|
|
473
|
+
date: string;
|
|
474
|
+
type: 'info' | 'success' | 'warning' | 'error' | 'reminder';
|
|
475
|
+
isRead: boolean;
|
|
476
|
+
category?: NotificationCategory; // 🆕 หมวดหมู่: 'action' | 'general'
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ตั้งค่า
|
|
480
|
+
type Theme = 'light' | 'dark';
|
|
481
|
+
type FontSizeKey = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 📁 Layout Examples
|
|
487
|
+
|
|
488
|
+
### Staff Layout (เจ้าหน้าที่)
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
'use client';
|
|
492
|
+
import { StaffSidebar, SettingsPanel } from 'gov-layout';
|
|
493
|
+
|
|
494
|
+
export default function AdminLayout({ children }) {
|
|
495
|
+
const [currentPath, setCurrentPath] = useState('/');
|
|
496
|
+
|
|
497
|
+
return (
|
|
498
|
+
<div style={{ display: 'flex' }}>
|
|
499
|
+
<StaffSidebar
|
|
500
|
+
orgLogo="/logo.png"
|
|
501
|
+
orgName="เทศบาลตำบล Biza"
|
|
502
|
+
orgSubtitle="จังหวัดราชบุรี"
|
|
503
|
+
menuItems={menuItems}
|
|
504
|
+
user={user}
|
|
505
|
+
roleLabel="เจ้าหน้าที่"
|
|
506
|
+
currentPath={currentPath}
|
|
507
|
+
onNavigate={(path) => setCurrentPath(path)}
|
|
508
|
+
onLogout={() => signOut()}
|
|
509
|
+
onProfile={() => setCurrentPath('/profile')}
|
|
510
|
+
collapsible
|
|
511
|
+
/>
|
|
512
|
+
<main style={{ marginLeft: 280, flex: 1, padding: 32 }}>
|
|
513
|
+
{currentPath === '/settings' ? (
|
|
514
|
+
<SettingsPanel showTheme={false} />
|
|
515
|
+
) : (
|
|
516
|
+
children
|
|
517
|
+
)}
|
|
518
|
+
</main>
|
|
519
|
+
</div>
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### User Layout (ผู้ใช้ทั่วไป)
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
'use client';
|
|
528
|
+
import { UserHeader, UserSidebar, SettingsPanel } from 'gov-layout';
|
|
529
|
+
|
|
530
|
+
export default function UserLayout({ children }) {
|
|
531
|
+
const [open, setOpen] = useState(false);
|
|
532
|
+
const [currentPath, setCurrentPath] = useState('/');
|
|
533
|
+
|
|
534
|
+
return (
|
|
535
|
+
<>
|
|
536
|
+
<UserHeader
|
|
537
|
+
user={{ ...user, subtitle: 'ผู้สูงอายุ' }}
|
|
538
|
+
notifications={notifications}
|
|
539
|
+
onToggleSidebar={() => setOpen(true)}
|
|
540
|
+
onNotificationClick={(notif) => setCurrentPath(`/notifications/${notif.id}`)}
|
|
541
|
+
onProfile={() => setCurrentPath('/profile')}
|
|
542
|
+
/>
|
|
543
|
+
<UserSidebar
|
|
544
|
+
isOpen={open}
|
|
545
|
+
onClose={() => setOpen(false)}
|
|
546
|
+
user={user}
|
|
547
|
+
roleLabel="ผู้ใช้ทั่วไป"
|
|
548
|
+
menuItems={menuItems}
|
|
549
|
+
onNavigate={(path) => setCurrentPath(path)}
|
|
550
|
+
onLogout={() => signOut()}
|
|
551
|
+
onProfile={() => setCurrentPath('/profile')}
|
|
552
|
+
/>
|
|
553
|
+
<main style={{ padding: 32 }}>
|
|
554
|
+
{currentPath === '/settings' ? (
|
|
555
|
+
<SettingsPanel showTheme={true} />
|
|
556
|
+
) : (
|
|
557
|
+
children
|
|
558
|
+
)}
|
|
559
|
+
</main>
|
|
560
|
+
</>
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## 🔧 Sub-Components
|
|
568
|
+
|
|
569
|
+
ใช้แยกกันได้ถ้าต้องการ customize เฉพาะส่วน
|
|
570
|
+
|
|
571
|
+
```tsx
|
|
572
|
+
import {
|
|
573
|
+
SidebarHeader, // logo + ชื่อองค์กร
|
|
574
|
+
SidebarMenu, // เมนู dropdown
|
|
575
|
+
SidebarUserProfile, // avatar + logout
|
|
576
|
+
ThemeSettings, // UI เลือก theme อย่างเดียว
|
|
577
|
+
FontSizeSettings, // UI เลือก font size อย่างเดียว
|
|
578
|
+
} from 'gov-layout';
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## 🎨 Styling
|
|
584
|
+
|
|
585
|
+
- Components ใช้ **inline styles** + CSS variables จาก `gov-token-css`
|
|
586
|
+
- ถ้าไม่ได้ import `gov-token-css` จะใช้สี **fallback** อัตโนมัติ
|
|
587
|
+
- Dark mode ต้องเพิ่ม CSS เอง (ดู section Dark mode CSS ด้านบน)
|
|
588
|
+
- Font size ใช้ `body.style.zoom` → scale ทุกอย่างรวมถึง inline `px`
|