es-application-template 0.0.8 → 0.0.11
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/CHANGELOG.md +6 -0
- package/README.md +10 -0
- package/dist/components/sidebar/index.d.ts +1 -0
- package/dist/index.js +396 -109
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +397 -110
- package/dist/index.mjs.map +1 -1
- package/dist/management/setting-keys/config-tree/setting-key-convention.d.ts +56 -0
- package/dist/styles/index.css +78 -26
- package/package.json +1 -1
- package/dist/index.cjs +0 -6743
- package/dist/index.cjs.map +0 -1
- package/dist/styles.css +0 -438
- package/dist/styles.css.map +0 -1
- package/dist/views/es-application-template/management/setting-keys/config-tree/ConfigTreeGridEditor.d.ts +0 -14
- package/dist/views/es-application-template/management/setting-keys/config-tree/TreeSelect.d.ts +0 -10
package/dist/index.js
CHANGED
|
@@ -5403,19 +5403,26 @@ const Sidebar = (props) => {
|
|
|
5403
5403
|
toggleSidebar();
|
|
5404
5404
|
}, 400);
|
|
5405
5405
|
};
|
|
5406
|
-
const sidebar = (jsxRuntime.jsxs("div", { tabIndex: 0, className: classNames__default["default"](
|
|
5406
|
+
const sidebar = (jsxRuntime.jsxs("div", { tabIndex: 0, className: classNames__default["default"]('es-application-sidebar-root', {
|
|
5407
|
+
'is-hidden': !open
|
|
5408
|
+
}), onKeyDown: (e) => {
|
|
5407
5409
|
if ((!keyboard) && e.code === 'Escape' && flag) {
|
|
5408
5410
|
handClose();
|
|
5409
5411
|
e.stopPropagation();
|
|
5410
5412
|
e.preventDefault();
|
|
5411
5413
|
}
|
|
5412
|
-
}, children: [jsxRuntime.jsx("div", { className: classNames__default["default"]('
|
|
5413
|
-
'
|
|
5414
|
+
}, children: [jsxRuntime.jsx("div", { className: classNames__default["default"]('es-application-sidebar-backdrop', backdropClassName, {
|
|
5415
|
+
'is-hidden': !flag || hiddenBackground,
|
|
5414
5416
|
show: flag
|
|
5415
|
-
}), style: { zIndex: zIndex - 1 }, onClick: handClose }), jsxRuntime.jsxs("div", { className: classNames__default["default"]('
|
|
5417
|
+
}), style: { zIndex: zIndex - 1 }, onClick: handClose }), jsxRuntime.jsxs("div", { className: classNames__default["default"]('es-application-sidebar', panelClassName, {
|
|
5416
5418
|
open: flag,
|
|
5417
5419
|
fullscreen: isFullScreen
|
|
5418
|
-
}), style: {
|
|
5420
|
+
}), style: {
|
|
5421
|
+
'--es-application-sidebar-width': typeof width === 'number'
|
|
5422
|
+
? `${width}px`
|
|
5423
|
+
: width ?? '600px',
|
|
5424
|
+
zIndex
|
|
5425
|
+
}, children: [jsxRuntime.jsxs("div", { style: {
|
|
5419
5426
|
position: 'fixed',
|
|
5420
5427
|
top: '50%',
|
|
5421
5428
|
transform: 'translate(-50%, -50%)',
|
|
@@ -5577,83 +5584,156 @@ const ConfigTreeJsonDrawer = ({ open, onClose, rows, mode = 'VIEW', onApply, fil
|
|
|
5577
5584
|
return (jsxRuntime.jsx(Sidebar, { open: open, toggleSidebar: onClose, width: 760, keyboard: true, hiddenFullScreenButton: true, zIndex: 1061, children: jsxRuntime.jsxs("div", { className: 'h-100 bg-white d-flex flex-column', children: [jsxRuntime.jsx(ModalHeader, { typeModal: 'Detail', handleModal: onClose, title: mode === 'IMPORT' ? 'Nhập JSON thiết lập' : 'Xem / Xuất JSON thiết lập' }), mode === 'VIEW' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'd-flex justify-content-end gap-50 border-bottom p-1', children: [jsxRuntime.jsx(reactstrap.Button, { type: 'button', color: 'outline-primary', className: 'be-button', disabled: currentPreview.errors.length > 0, onClick: () => { void handleCopy(); }, children: "Sao ch\u00E9p" }), jsxRuntime.jsx(reactstrap.Button, { type: 'button', color: 'primary', className: 'be-button', disabled: currentPreview.errors.length > 0, onClick: () => downloadJson(currentJson, fileName), children: "T\u1EA3i file JSON" })] }), jsxRuntime.jsx("div", { className: 'flex-grow-1 overflow-auto', style: { minHeight: 0 }, children: jsxRuntime.jsx("div", { className: 'p-1', children: currentPreview.errors.length > 0 ? (jsxRuntime.jsxs("div", { className: 'alert alert-danger py-50 px-1 mb-0', children: [jsxRuntime.jsx("div", { className: 'fw-bolder mb-25', children: "D\u1EEF li\u1EC7u nh\u00E1p ch\u01B0a h\u1EE3p l\u1EC7:" }), currentPreview.errors.slice(0, 5).map((error, index) => (jsxRuntime.jsxs("div", { children: ["\u2022 ", error] }, `${index}-${error}`)))] })) : (jsxRuntime.jsx("pre", { className: 'bg-light border rounded p-1 mb-0', children: currentJson })) }) })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'border-bottom p-1', children: [jsxRuntime.jsxs("div", { className: 'd-flex align-items-end gap-1 flex-wrap', children: [jsxRuntime.jsxs("div", { style: { width: 190 }, children: [jsxRuntime.jsx(reactstrap.Label, { className: 'mb-25', children: "Ch\u1EBF \u0111\u1ED9 nh\u1EADp" }), jsxRuntime.jsxs(reactstrap.Input, { type: 'select', value: importMode, onChange: event => setImportMode(event.target.value), children: [jsxRuntime.jsx("option", { value: 'MERGE', children: "H\u1EE3p nh\u1EA5t (khuy\u1EBFn ngh\u1ECB)" }), jsxRuntime.jsx("option", { value: 'REPLACE', children: "Thay th\u1EBF to\u00E0n b\u1ED9" })] })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: 'file', accept: '.json,application/json', hidden: true, onChange: event => { void handleFileChange(event); } }), jsxRuntime.jsx(reactstrap.Button, { type: 'button', color: 'outline-primary', className: 'be-button', onClick: () => fileInputRef.current?.click(), children: "Ch\u1ECDn file .json" })] })] }), jsxRuntime.jsx("div", { className: 'small text-muted mt-50', children: "H\u1EE3p nh\u1EA5t: key m\u1EDBi \u0111\u01B0\u1EE3c th\u00EAm, key tr\u00F9ng \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt, key kh\u00E1c gi\u1EEF nguy\u00EAn. Thay th\u1EBF: d\u00F9ng JSON import l\u00E0m to\u00E0n b\u1ED9 d\u1EEF li\u1EC7u nh\u00E1p hi\u1EC7n t\u1EA1i." })] }), jsxRuntime.jsxs("div", { className: 'flex-grow-1 min-h-0 d-flex flex-column p-1', children: [jsxRuntime.jsx(reactstrap.Label, { for: 'config-json-import', className: 'mb-50', children: "D\u00E1n JSON ho\u1EB7c ch\u1ECDn file" }), jsxRuntime.jsx(reactstrap.Input, { id: 'config-json-import', type: 'textarea', className: 'font-monospace flex-grow-1', style: { minHeight: 280, resize: 'none' }, value: importText, placeholder: 'D\u00E1n JSON thi\u1EBFt l\u1EADp t\u1EA1i \u0111\u00E2y...', onChange: event => setImportText(event.target.value) }), !importText.trim() ? (jsxRuntime.jsx("div", { className: 'alert alert-info py-50 px-1 mt-1 mb-0', children: "Ch\u01B0a c\u00F3 d\u1EEF li\u1EC7u import. C\u00F3 th\u1EC3 d\u00F9ng ChatGPT t\u1EA1o file JSON r\u1ED3i d\u00E1n ho\u1EB7c t\u1EA3i file l\u00EAn." })) : importPreview.errors.length > 0 ? (jsxRuntime.jsxs("div", { className: 'alert alert-danger py-50 px-1 mt-1 mb-0', children: [jsxRuntime.jsx("div", { className: 'fw-bolder mb-25', children: "JSON ch\u01B0a h\u1EE3p l\u1EC7:" }), importPreview.errors.slice(0, 5).map((error, index) => (jsxRuntime.jsxs("div", { children: ["\u2022 ", error] }, `${index}-${error}`))), importPreview.errors.length > 5 ? (jsxRuntime.jsxs("div", { children: ["\u2022 ... v\u00E0 ", importPreview.errors.length - 5, " l\u1ED7i kh\u00E1c"] })) : null] })) : (jsxRuntime.jsxs("div", { className: 'alert alert-success py-50 px-1 mt-1 mb-0', children: ["JSON h\u1EE3p l\u1EC7. Sau khi \u00E1p d\u1EE5ng, Grid s\u1EBD c\u00F3 ", countConfigNodes(importPreview.rows), " node. D\u1EEF li\u1EC7u ch\u1EC9 l\u00E0 nh\u00E1p cho \u0111\u1EBFn khi b\u1EA5m L\u01B0u \u1EDF m\u00E0n ch\u00EDnh."] }))] }), importMode === 'REPLACE' ? (jsxRuntime.jsx("div", { className: 'alert alert-warning rounded-0 border-start-0 border-end-0 mb-0 py-50 px-1', children: "Ch\u1EBF \u0111\u1ED9 Thay th\u1EBF s\u1EBD b\u1ECF to\u00E0n b\u1ED9 key hi\u1EC7n c\u00F3 kh\u1ECFi d\u1EEF li\u1EC7u nh\u00E1p tr\u01B0\u1EDBc khi \u00E1p d\u1EE5ng JSON import." })) : null, jsxRuntime.jsxs("div", { className: 'd-flex justify-content-end gap-50 border-top p-1', children: [jsxRuntime.jsx(reactstrap.Button, { type: 'button', color: 'secondary', outline: true, className: 'be-button', onClick: onClose, children: "H\u1EE7y" }), jsxRuntime.jsx(reactstrap.Button, { type: 'button', color: 'primary', className: 'be-button', disabled: !importPreview.ready || !onApply, onClick: handleApply, children: "\u00C1p d\u1EE5ng v\u00E0o Grid" })] })] }))] }) }));
|
|
5578
5585
|
};
|
|
5579
5586
|
|
|
5580
|
-
const
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
+
const UI_RULES = [
|
|
5588
|
+
{
|
|
5589
|
+
id: 'list-grid-visible',
|
|
5590
|
+
section: 'list-grid',
|
|
5591
|
+
category: 'ui-list',
|
|
5592
|
+
pattern: 'ui.list.grid.<field>.visible',
|
|
5593
|
+
type: 'boolean',
|
|
5594
|
+
defaultValue: true,
|
|
5595
|
+
examples: ['ui.list.grid.email.visible'],
|
|
5596
|
+
description: 'Ẩn hoặc hiện một cột trên Grid của danh sách.'
|
|
5597
|
+
},
|
|
5587
5598
|
{
|
|
5599
|
+
id: 'list-toolbar-visible',
|
|
5600
|
+
section: 'list-grid',
|
|
5601
|
+
category: 'ui-list',
|
|
5588
5602
|
pattern: 'ui.list.toolbar.<keyCode>.visible',
|
|
5589
5603
|
type: 'boolean',
|
|
5590
|
-
|
|
5604
|
+
defaultValue: true,
|
|
5605
|
+
examples: ['ui.list.toolbar.status.visible'],
|
|
5606
|
+
description: 'Ẩn hoặc hiện một điều khiển trên toolbar của danh sách.'
|
|
5591
5607
|
},
|
|
5592
5608
|
{
|
|
5609
|
+
id: 'list-toolbar-disabled',
|
|
5610
|
+
section: 'list-grid',
|
|
5611
|
+
category: 'ui-list',
|
|
5593
5612
|
pattern: 'ui.list.toolbar.<keyCode>.disabled',
|
|
5594
5613
|
type: 'boolean',
|
|
5595
|
-
|
|
5614
|
+
defaultValue: false,
|
|
5615
|
+
examples: ['ui.list.toolbar.status.disabled'],
|
|
5616
|
+
description: 'Bật hoặc khóa một điều khiển trên toolbar của danh sách.'
|
|
5596
5617
|
},
|
|
5597
5618
|
{
|
|
5619
|
+
id: 'list-action-visible',
|
|
5620
|
+
section: 'list-grid',
|
|
5621
|
+
category: 'ui-list',
|
|
5598
5622
|
pattern: 'ui.list.action.<id>.visible',
|
|
5599
5623
|
type: 'boolean',
|
|
5600
|
-
|
|
5624
|
+
defaultValue: true,
|
|
5625
|
+
examples: ['ui.list.action.DELETE.visible'],
|
|
5626
|
+
description: 'Ẩn hoặc hiện một action của dòng hay danh sách.'
|
|
5601
5627
|
},
|
|
5602
5628
|
{
|
|
5629
|
+
id: 'list-action-disabled',
|
|
5630
|
+
section: 'list-grid',
|
|
5631
|
+
category: 'ui-list',
|
|
5603
5632
|
pattern: 'ui.list.action.<id>.disabled',
|
|
5604
5633
|
type: 'boolean',
|
|
5605
|
-
|
|
5634
|
+
defaultValue: false,
|
|
5635
|
+
examples: ['ui.list.action.DELETE.disabled'],
|
|
5636
|
+
description: 'Bật hoặc khóa một action của dòng hay danh sách.'
|
|
5606
5637
|
},
|
|
5607
5638
|
{
|
|
5639
|
+
id: 'form-field-visible',
|
|
5640
|
+
section: 'form-field',
|
|
5641
|
+
category: 'ui-form',
|
|
5608
5642
|
pattern: 'ui.form.field.<name>.visible',
|
|
5609
5643
|
type: 'boolean',
|
|
5610
|
-
|
|
5644
|
+
defaultValue: true,
|
|
5645
|
+
examples: ['ui.form.field.email.visible'],
|
|
5646
|
+
description: 'Ẩn hoặc hiện field trên form.'
|
|
5611
5647
|
},
|
|
5612
5648
|
{
|
|
5649
|
+
id: 'form-field-required',
|
|
5650
|
+
section: 'form-field',
|
|
5651
|
+
category: 'ui-form',
|
|
5613
5652
|
pattern: 'ui.form.field.<name>.required',
|
|
5614
5653
|
type: 'boolean',
|
|
5615
|
-
|
|
5654
|
+
defaultValue: false,
|
|
5655
|
+
examples: ['ui.form.field.email.required'],
|
|
5656
|
+
description: 'Quy định field có bắt buộc nhập hay không.'
|
|
5616
5657
|
},
|
|
5617
5658
|
{
|
|
5659
|
+
id: 'form-field-disabled',
|
|
5660
|
+
section: 'form-field',
|
|
5661
|
+
category: 'ui-form',
|
|
5618
5662
|
pattern: 'ui.form.field.<name>.disabled',
|
|
5619
5663
|
type: 'boolean',
|
|
5620
|
-
|
|
5664
|
+
defaultValue: false,
|
|
5665
|
+
examples: ['ui.form.field.email.disabled'],
|
|
5666
|
+
description: 'Bật hoặc khóa tương tác với field.'
|
|
5621
5667
|
},
|
|
5622
5668
|
{
|
|
5669
|
+
id: 'form-field-readonly',
|
|
5670
|
+
section: 'form-field',
|
|
5671
|
+
category: 'ui-form',
|
|
5623
5672
|
pattern: 'ui.form.field.<name>.readOnly',
|
|
5624
5673
|
type: 'boolean',
|
|
5625
|
-
|
|
5674
|
+
defaultValue: false,
|
|
5675
|
+
examples: ['ui.form.field.email.readOnly'],
|
|
5676
|
+
description: 'Quy định field chỉ đọc hay có thể chỉnh sửa.'
|
|
5626
5677
|
},
|
|
5627
5678
|
{
|
|
5679
|
+
id: 'form-action-visible',
|
|
5680
|
+
section: 'form-action',
|
|
5681
|
+
category: 'ui-form',
|
|
5628
5682
|
pattern: 'ui.form.action.<id>.visible',
|
|
5629
5683
|
type: 'boolean',
|
|
5630
|
-
|
|
5684
|
+
defaultValue: true,
|
|
5685
|
+
examples: ['ui.form.action.SAVE.visible'],
|
|
5686
|
+
description: 'Ẩn hoặc hiện một action trên form.'
|
|
5631
5687
|
},
|
|
5632
5688
|
{
|
|
5689
|
+
id: 'form-action-disabled',
|
|
5690
|
+
section: 'form-action',
|
|
5691
|
+
category: 'ui-form',
|
|
5633
5692
|
pattern: 'ui.form.action.<id>.disabled',
|
|
5634
5693
|
type: 'boolean',
|
|
5635
|
-
|
|
5694
|
+
defaultValue: false,
|
|
5695
|
+
examples: ['ui.form.action.SAVE.disabled'],
|
|
5696
|
+
description: 'Bật hoặc khóa một action trên form.'
|
|
5636
5697
|
},
|
|
5637
5698
|
{
|
|
5699
|
+
id: 'form-tab-visible',
|
|
5700
|
+
section: 'form-structure',
|
|
5701
|
+
category: 'ui-form',
|
|
5638
5702
|
pattern: 'ui.form.tab.<key>.visible',
|
|
5639
5703
|
type: 'boolean',
|
|
5640
|
-
|
|
5704
|
+
defaultValue: true,
|
|
5705
|
+
examples: ['ui.form.tab.permission.visible'],
|
|
5706
|
+
description: 'Ẩn hoặc hiện tab trên form.'
|
|
5641
5707
|
},
|
|
5642
5708
|
{
|
|
5709
|
+
id: 'form-step-visible',
|
|
5710
|
+
section: 'form-structure',
|
|
5711
|
+
category: 'ui-form',
|
|
5643
5712
|
pattern: 'ui.form.step.<key>.visible',
|
|
5644
5713
|
type: 'boolean',
|
|
5645
|
-
|
|
5714
|
+
defaultValue: true,
|
|
5715
|
+
examples: ['ui.form.step.confirm.visible'],
|
|
5716
|
+
description: 'Ẩn hoặc hiện một bước trong quy trình form.'
|
|
5646
5717
|
},
|
|
5647
5718
|
{
|
|
5719
|
+
id: 'form-section-visible',
|
|
5720
|
+
section: 'form-structure',
|
|
5721
|
+
category: 'ui-form',
|
|
5648
5722
|
pattern: 'ui.form.section.<key>.visible',
|
|
5649
5723
|
type: 'boolean',
|
|
5650
|
-
|
|
5724
|
+
defaultValue: true,
|
|
5725
|
+
examples: ['ui.form.section.security.visible'],
|
|
5726
|
+
description: 'Ẩn hoặc hiện một khu vực trên form.'
|
|
5651
5727
|
}
|
|
5652
5728
|
];
|
|
5653
|
-
const
|
|
5729
|
+
const NON_UI_RULES = [
|
|
5654
5730
|
{
|
|
5731
|
+
id: 'behavior-boolean',
|
|
5732
|
+
section: 'other-settings',
|
|
5733
|
+
category: 'behavior',
|
|
5655
5734
|
pattern: 'behavior.<meaning>',
|
|
5656
5735
|
type: 'boolean',
|
|
5736
|
+
sampleValue: true,
|
|
5657
5737
|
examples: [
|
|
5658
5738
|
'behavior.allowDelete',
|
|
5659
5739
|
'behavior.allowEdit',
|
|
@@ -5663,182 +5743,389 @@ const EXPLICIT_KEY_SAMPLES = [
|
|
|
5663
5743
|
'behavior.allowChangeStatus',
|
|
5664
5744
|
'behavior.allowSort',
|
|
5665
5745
|
'behavior.autoSubmit'
|
|
5666
|
-
]
|
|
5746
|
+
],
|
|
5747
|
+
description: 'Điều khiển một hành vi nghiệp vụ có ý nghĩa ổn định.'
|
|
5667
5748
|
},
|
|
5668
5749
|
{
|
|
5750
|
+
id: 'default-number',
|
|
5751
|
+
section: 'other-settings',
|
|
5752
|
+
category: 'default',
|
|
5669
5753
|
pattern: 'default.<fieldOrMeaning>',
|
|
5670
5754
|
type: 'number',
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
]
|
|
5755
|
+
sampleValue: 1,
|
|
5756
|
+
examples: ['default.status', 'default.pageSize'],
|
|
5757
|
+
description: 'Giá trị số mặc định cho field hoặc hành vi.'
|
|
5675
5758
|
},
|
|
5676
5759
|
{
|
|
5760
|
+
id: 'default-string',
|
|
5761
|
+
section: 'other-settings',
|
|
5762
|
+
category: 'default',
|
|
5677
5763
|
pattern: 'default.<fieldOrMeaning>',
|
|
5678
5764
|
type: 'string',
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
]
|
|
5765
|
+
sampleValue: 'VND',
|
|
5766
|
+
examples: ['default.note', 'default.currencyCode'],
|
|
5767
|
+
description: 'Giá trị chuỗi mặc định cho field hoặc hành vi.'
|
|
5683
5768
|
},
|
|
5684
5769
|
{
|
|
5770
|
+
id: 'default-date',
|
|
5771
|
+
section: 'other-settings',
|
|
5772
|
+
category: 'default',
|
|
5685
5773
|
pattern: 'default.<fieldOrMeaning>',
|
|
5686
5774
|
type: 'date',
|
|
5687
|
-
|
|
5775
|
+
sampleValue: '2026-01-01',
|
|
5776
|
+
examples: ['default.startDate'],
|
|
5777
|
+
description: 'Giá trị ngày mặc định.'
|
|
5688
5778
|
},
|
|
5689
5779
|
{
|
|
5780
|
+
id: 'default-datetime',
|
|
5781
|
+
section: 'other-settings',
|
|
5782
|
+
category: 'default',
|
|
5690
5783
|
pattern: 'default.<fieldOrMeaning>',
|
|
5691
5784
|
type: 'datetime',
|
|
5692
|
-
|
|
5785
|
+
sampleValue: '2026-01-01T08:00:00+07:00',
|
|
5786
|
+
examples: ['default.executeAt'],
|
|
5787
|
+
description: 'Giá trị ngày giờ mặc định.'
|
|
5693
5788
|
},
|
|
5694
5789
|
{
|
|
5790
|
+
id: 'default-guid',
|
|
5791
|
+
section: 'other-settings',
|
|
5792
|
+
category: 'default',
|
|
5695
5793
|
pattern: 'default.<fieldOrMeaning>',
|
|
5696
5794
|
type: 'guid',
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
]
|
|
5795
|
+
sampleValue: '00000000-0000-0000-0000-000000000001',
|
|
5796
|
+
examples: ['default.departmentId', 'default.warehouseId'],
|
|
5797
|
+
description: 'Giá trị định danh mặc định.'
|
|
5701
5798
|
},
|
|
5702
5799
|
{
|
|
5800
|
+
id: 'option-json',
|
|
5801
|
+
section: 'other-settings',
|
|
5802
|
+
category: 'option',
|
|
5703
5803
|
pattern: 'option.<meaning>',
|
|
5704
5804
|
type: 'json',
|
|
5805
|
+
sampleValue: [0, 1],
|
|
5705
5806
|
examples: [
|
|
5706
5807
|
'option.allowedStatuses',
|
|
5707
5808
|
'option.allowedTypes',
|
|
5708
5809
|
'option.exportFormats'
|
|
5709
|
-
]
|
|
5810
|
+
],
|
|
5811
|
+
description: 'Danh sách hoặc cấu trúc tùy chọn có schema rõ ràng.'
|
|
5710
5812
|
},
|
|
5711
5813
|
{
|
|
5814
|
+
id: 'custom-domain-boolean',
|
|
5815
|
+
section: 'other-settings',
|
|
5816
|
+
category: 'custom',
|
|
5712
5817
|
pattern: '<domain>.<meaning>',
|
|
5713
5818
|
type: 'boolean',
|
|
5819
|
+
sampleValue: true,
|
|
5714
5820
|
examples: [
|
|
5715
5821
|
'approval.autoSubmit',
|
|
5716
5822
|
'approval.requireComment',
|
|
5717
5823
|
'password.requireChangeOnFirstLogin',
|
|
5718
5824
|
'confirmation.requireRedeployConfirmation'
|
|
5719
|
-
]
|
|
5825
|
+
],
|
|
5826
|
+
description: 'Cờ tùy chỉnh cho một miền nghiệp vụ chưa thuộc pattern chung.'
|
|
5720
5827
|
},
|
|
5721
5828
|
{
|
|
5829
|
+
id: 'custom-domain-number',
|
|
5830
|
+
section: 'other-settings',
|
|
5831
|
+
category: 'custom',
|
|
5722
5832
|
pattern: '<domain>.<meaning>',
|
|
5723
5833
|
type: 'number',
|
|
5834
|
+
sampleValue: 30,
|
|
5724
5835
|
examples: [
|
|
5725
5836
|
'integration.timeoutSeconds',
|
|
5726
5837
|
'integration.retryCount',
|
|
5727
5838
|
'calculation.decimalPlaces'
|
|
5728
|
-
]
|
|
5839
|
+
],
|
|
5840
|
+
description: 'Giá trị số tùy chỉnh cho một miền nghiệp vụ.'
|
|
5729
5841
|
},
|
|
5730
5842
|
{
|
|
5843
|
+
id: 'custom-domain-string',
|
|
5844
|
+
section: 'other-settings',
|
|
5845
|
+
category: 'custom',
|
|
5731
5846
|
pattern: '<domain>.<meaning>',
|
|
5732
5847
|
type: 'string',
|
|
5733
|
-
|
|
5848
|
+
sampleValue: 'HALF_UP',
|
|
5849
|
+
examples: ['calculation.roundingMode'],
|
|
5850
|
+
description: 'Giá trị chuỗi tùy chỉnh cho một miền nghiệp vụ.'
|
|
5734
5851
|
},
|
|
5735
5852
|
{
|
|
5853
|
+
id: 'custom-root-number',
|
|
5854
|
+
section: 'other-settings',
|
|
5855
|
+
category: 'custom',
|
|
5736
5856
|
pattern: '<key>',
|
|
5737
5857
|
type: 'number',
|
|
5738
|
-
|
|
5858
|
+
sampleValue: 100,
|
|
5859
|
+
examples: ['maxDetailRows'],
|
|
5860
|
+
description: 'Key gốc chỉ dùng khi không có namespace phù hợp và ý nghĩa vẫn rõ ràng.'
|
|
5739
5861
|
}
|
|
5740
5862
|
];
|
|
5741
|
-
const
|
|
5742
|
-
...
|
|
5743
|
-
...
|
|
5863
|
+
const SETTING_KEY_RULES = [
|
|
5864
|
+
...UI_RULES,
|
|
5865
|
+
...NON_UI_RULES
|
|
5744
5866
|
];
|
|
5745
|
-
const
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5867
|
+
const SETTING_KEY_CONVENTION = {
|
|
5868
|
+
id: 'becoxy-setting-key-convention',
|
|
5869
|
+
version: 1,
|
|
5870
|
+
title: 'BECoxy Setting Key Convention',
|
|
5871
|
+
objective: [
|
|
5872
|
+
'Các thiết lập UI và behavior phải được đọc qua setting.get(key, fallback).',
|
|
5873
|
+
'Không hard-code cấu hình nếu giá trị cần được quản trị từ Setting.'
|
|
5874
|
+
],
|
|
5875
|
+
keyStructure: {
|
|
5876
|
+
pattern: '<Resource>.<setting-key>',
|
|
5877
|
+
explanation: [
|
|
5878
|
+
'Resource là mã ổn định của feature hoặc tài nguyên đang được cấu hình.',
|
|
5879
|
+
'Phần setting key phải tuân thủ các pattern bên dưới.',
|
|
5880
|
+
'Không nhúng productCode, userId hoặc scope vào key.'
|
|
5881
|
+
],
|
|
5882
|
+
examples: [
|
|
5883
|
+
'RESOURCE_CODE.ui.list.toolbar.status.visible',
|
|
5884
|
+
'RESOURCE_CODE.ui.form.field.email.required',
|
|
5885
|
+
'RESOURCE_CODE.ui.form.action.SAVE.disabled'
|
|
5886
|
+
]
|
|
5765
5887
|
},
|
|
5766
|
-
|
|
5767
|
-
value: '
|
|
5768
|
-
label: '
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5888
|
+
categories: [
|
|
5889
|
+
{ value: 'ui-list', label: 'UI danh sách' },
|
|
5890
|
+
{ value: 'ui-form', label: 'UI biểu mẫu' },
|
|
5891
|
+
{ value: 'behavior', label: 'Hành vi' },
|
|
5892
|
+
{ value: 'default', label: 'Mặc định' },
|
|
5893
|
+
{ value: 'option', label: 'Tùy chọn' },
|
|
5894
|
+
{ value: 'custom', label: 'Tùy chỉnh' }
|
|
5895
|
+
],
|
|
5896
|
+
ruleSections: [
|
|
5897
|
+
{
|
|
5898
|
+
id: 'list-grid',
|
|
5899
|
+
title: 'LIST / GRID',
|
|
5900
|
+
description: 'Cấu hình cột Grid, toolbar và action trên màn danh sách.'
|
|
5901
|
+
},
|
|
5902
|
+
{
|
|
5903
|
+
id: 'form-field',
|
|
5904
|
+
title: 'FORM FIELD',
|
|
5905
|
+
description: 'Cấu hình trạng thái hiển thị và tương tác của field.'
|
|
5906
|
+
},
|
|
5907
|
+
{
|
|
5908
|
+
id: 'form-action',
|
|
5909
|
+
title: 'FORM ACTION',
|
|
5910
|
+
description: 'Cấu hình action trên form.'
|
|
5911
|
+
},
|
|
5912
|
+
{
|
|
5913
|
+
id: 'form-structure',
|
|
5914
|
+
title: 'FORM STRUCTURE',
|
|
5915
|
+
description: 'Cấu hình tab, step và section trên form.'
|
|
5916
|
+
},
|
|
5917
|
+
{
|
|
5918
|
+
id: 'other-settings',
|
|
5919
|
+
title: 'BEHAVIOR / DEFAULT / OPTION / CUSTOM',
|
|
5920
|
+
description: 'Các pattern ngoài UI chỉ dùng khi đúng ý nghĩa nghiệp vụ.'
|
|
5921
|
+
}
|
|
5922
|
+
],
|
|
5923
|
+
rules: SETTING_KEY_RULES,
|
|
5924
|
+
guidance: [
|
|
5925
|
+
{
|
|
5926
|
+
title: 'CÁCH ĐỌC SETTING',
|
|
5927
|
+
paragraphs: ['Chỉ dùng public API của package:'],
|
|
5928
|
+
examples: [
|
|
5929
|
+
'setting.get<T>(key)',
|
|
5930
|
+
'setting.get<T>(key, fallback)',
|
|
5931
|
+
"setting.get('RESOURCE_CODE.ui.form.field.Code.visible', true)",
|
|
5932
|
+
"setting.get('RESOURCE_CODE.ui.form.action.SAVE.disabled', false)"
|
|
5933
|
+
],
|
|
5934
|
+
items: [
|
|
5935
|
+
'Không tạo useSetting(resource).',
|
|
5936
|
+
'Không tạo useFeatureSetting hoặc useFieldSetting.',
|
|
5937
|
+
'Không tạo getBoolean, getNumber hoặc getJson.',
|
|
5938
|
+
'Không tạo SettingResourceScope.'
|
|
5939
|
+
]
|
|
5940
|
+
},
|
|
5941
|
+
{
|
|
5942
|
+
title: 'FALLBACK',
|
|
5943
|
+
paragraphs: ['Boolean UI setting phải có fallback rõ ràng khi phù hợp.'],
|
|
5944
|
+
items: [
|
|
5945
|
+
'visible: fallback thường là true.',
|
|
5946
|
+
'disabled: fallback thường là false.',
|
|
5947
|
+
'required: fallback thường là false.',
|
|
5948
|
+
'readOnly: fallback thường là false.'
|
|
5949
|
+
]
|
|
5950
|
+
},
|
|
5951
|
+
{
|
|
5952
|
+
title: 'NAMING',
|
|
5953
|
+
items: [
|
|
5954
|
+
'Không dùng label hiển thị hoặc nội dung đã dịch làm key.',
|
|
5955
|
+
'ID, action và key phải ổn định khi thay đổi ngôn ngữ.',
|
|
5956
|
+
'Không nhúng productCode, userId hoặc scope vào key.',
|
|
5957
|
+
'Sai: ui.form.field.Tên người dùng.visible.',
|
|
5958
|
+
'Đúng: ui.form.field.username.visible.',
|
|
5959
|
+
'Sai: PRODUCT.RESOURCE_CODE.ui.form.field.Code.visible.',
|
|
5960
|
+
'Đúng: RESOURCE_CODE.ui.form.field.Code.visible.'
|
|
5961
|
+
]
|
|
5962
|
+
},
|
|
5963
|
+
{
|
|
5964
|
+
title: 'SCOPE / PRECEDENCE',
|
|
5965
|
+
paragraphs: ['Effective Setting được giải quyết theo thứ tự SYSTEM → PRODUCT → USER → setting.get(...).'],
|
|
5966
|
+
items: [
|
|
5967
|
+
'Feature code không tự xử lý precedence.',
|
|
5968
|
+
'Feature code không tự request riêng SYSTEM, PRODUCT và USER.',
|
|
5969
|
+
'Scope, Product và User được runtime quản lý ngoài key.'
|
|
5970
|
+
]
|
|
5971
|
+
},
|
|
5972
|
+
{
|
|
5973
|
+
title: 'KHI THÊM SETTING MỚI',
|
|
5974
|
+
items: [
|
|
5975
|
+
'Kiểm tra pattern hiện có và ưu tiên reuse convention.',
|
|
5976
|
+
'Chỉ tạo namespace mới khi behavior không thuộc pattern hiện có.',
|
|
5977
|
+
'Xác định type, fallback và description rõ nghĩa.',
|
|
5978
|
+
'Thêm key vào Setting catalog khi cần quản trị.',
|
|
5979
|
+
'Không tạo API wrapper riêng chỉ cho một key.'
|
|
5980
|
+
]
|
|
5981
|
+
},
|
|
5982
|
+
{
|
|
5983
|
+
title: 'VÍ DỤ ÁP DỤNG',
|
|
5984
|
+
examples: [
|
|
5985
|
+
"const canShowCode = setting.get('RESOURCE_CODE.ui.form.field.Code.visible', true)",
|
|
5986
|
+
"const codeRequired = setting.get('RESOURCE_CODE.ui.form.field.Code.required', false)",
|
|
5987
|
+
"const saveDisabled = setting.get('RESOURCE_CODE.ui.form.action.SAVE.disabled', false)"
|
|
5988
|
+
]
|
|
5989
|
+
},
|
|
5990
|
+
{
|
|
5991
|
+
title: 'NGUYÊN TẮC CHO AI/CODEX',
|
|
5992
|
+
items: [
|
|
5993
|
+
'Đọc source hiện tại trước khi thay đổi.',
|
|
5994
|
+
'Xác định resource hiện tại.',
|
|
5995
|
+
'Liệt kê component, action và field cần cấu hình.',
|
|
5996
|
+
'Ánh xạ vào convention có sẵn; không tự phát minh key khi pattern đã bao phủ.',
|
|
5997
|
+
'Dùng setting.get(..., fallback).',
|
|
5998
|
+
'Không thêm hook hoặc public Setting API mới.',
|
|
5999
|
+
'Không thay đổi Setting Runtime.',
|
|
6000
|
+
'Không thêm selector Scope, Product hoặc User vào feature.',
|
|
6001
|
+
'Chỉ sửa các file thực sự sử dụng setting.'
|
|
6002
|
+
]
|
|
6003
|
+
}
|
|
6004
|
+
]
|
|
6005
|
+
};
|
|
6006
|
+
const getRuleSampleValue = (rule) => {
|
|
6007
|
+
if (rule.sampleValue !== undefined) {
|
|
6008
|
+
return rule.sampleValue;
|
|
5787
6009
|
}
|
|
5788
|
-
if (
|
|
5789
|
-
return
|
|
6010
|
+
if (rule.defaultValue !== undefined) {
|
|
6011
|
+
return rule.defaultValue;
|
|
5790
6012
|
}
|
|
5791
|
-
return
|
|
6013
|
+
return null;
|
|
6014
|
+
};
|
|
6015
|
+
const setNestedLeaf = (target, path, leaf) => {
|
|
6016
|
+
const segments = path.split('.').filter(Boolean);
|
|
6017
|
+
let current = target;
|
|
6018
|
+
segments.forEach((segment, index) => {
|
|
6019
|
+
if (index === segments.length - 1) {
|
|
6020
|
+
current[segment] = leaf;
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
const next = current[segment];
|
|
6024
|
+
if (!next || typeof next !== 'object' || Array.isArray(next)) {
|
|
6025
|
+
current[segment] = {};
|
|
6026
|
+
}
|
|
6027
|
+
current = current[segment];
|
|
6028
|
+
});
|
|
6029
|
+
};
|
|
6030
|
+
const buildSettingImportExample = (resourceCode = 'RESOURCE_CODE', convention = SETTING_KEY_CONVENTION) => {
|
|
6031
|
+
const resource = resourceCode.trim() || 'RESOURCE_CODE';
|
|
6032
|
+
const settings = {};
|
|
6033
|
+
convention.rules.forEach(rule => {
|
|
6034
|
+
const example = rule.examples[0];
|
|
6035
|
+
if (!example) {
|
|
6036
|
+
return;
|
|
6037
|
+
}
|
|
6038
|
+
setNestedLeaf(settings, example, {
|
|
6039
|
+
type: rule.type,
|
|
6040
|
+
value: getRuleSampleValue(rule),
|
|
6041
|
+
description: rule.description
|
|
6042
|
+
});
|
|
6043
|
+
});
|
|
6044
|
+
return {
|
|
6045
|
+
[resource]: settings
|
|
6046
|
+
};
|
|
5792
6047
|
};
|
|
5793
|
-
const
|
|
6048
|
+
const serializeSettingImportExample = (resourceCode = 'RESOURCE_CODE', convention = SETTING_KEY_CONVENTION) => JSON.stringify(buildSettingImportExample(resourceCode, convention), null, 2);
|
|
6049
|
+
const filterSettingKeyRules = (rows, searchTerm, category, type) => {
|
|
5794
6050
|
const keyword = searchTerm.trim().toLowerCase();
|
|
5795
6051
|
return rows.filter(row => {
|
|
5796
6052
|
const matchesKeyword = !keyword || [
|
|
5797
6053
|
row.pattern,
|
|
5798
6054
|
row.type,
|
|
6055
|
+
row.description,
|
|
5799
6056
|
...row.examples
|
|
5800
6057
|
].some(value => value.toLowerCase().includes(keyword));
|
|
5801
|
-
const matchesCategory = !category ||
|
|
6058
|
+
const matchesCategory = !category || row.category === category;
|
|
5802
6059
|
const matchesType = !type || row.type === type;
|
|
5803
|
-
return matchesKeyword &&
|
|
5804
|
-
matchesCategory &&
|
|
5805
|
-
matchesType;
|
|
6060
|
+
return matchesKeyword && matchesCategory && matchesType;
|
|
5806
6061
|
});
|
|
5807
6062
|
};
|
|
6063
|
+
|
|
6064
|
+
const RESOURCE_CODE_PLACEHOLDER = 'RESOURCE_CODE';
|
|
6065
|
+
const SELECT_MENU_PORTAL_STYLES = {
|
|
6066
|
+
menuPortal: (base) => ({
|
|
6067
|
+
...base,
|
|
6068
|
+
zIndex: 1100
|
|
6069
|
+
})
|
|
6070
|
+
};
|
|
6071
|
+
const TYPE_OPTIONS = Array.from(new Set(SETTING_KEY_CONVENTION.rules.map(row => row.type))).map(value => ({
|
|
6072
|
+
value,
|
|
6073
|
+
label: value
|
|
6074
|
+
}));
|
|
5808
6075
|
const TechnicalText = ({ children }) => (jsxRuntime.jsx("span", { className: 'config-key-sample-text text-break', children: children }));
|
|
5809
6076
|
const KeySampleTable = ({ rows, onCopy }) => (jsxRuntime.jsx("div", { className: 'table-responsive', children: jsxRuntime.jsxs("table", { className: 'table table-sm table-bordered align-middle mb-0', style: {
|
|
5810
6077
|
tableLayout: 'fixed',
|
|
5811
6078
|
minWidth: 930
|
|
5812
|
-
}, children: [jsxRuntime.jsx("caption", { className: 'visually-hidden', children: "Danh s\u00E1ch m\u1EABu key c\u1EA5u h\u00ECnh" }), jsxRuntime.jsx("thead", { children: jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsx("th", { scope: 'col', style: { width: 360 }, children: "M\u1EABu key \u0111\u1EA7y \u0111\u1EE7" }), jsxRuntime.jsx("th", { scope: 'col', style: { width: 110 }, children: "Type" }), jsxRuntime.jsx("th", { scope: 'col', children: "V\u00ED d\u1EE5" })] }) }), jsxRuntime.jsx("tbody", { children: rows.map(row => (jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsx("td", { children: jsxRuntime.jsx(TechnicalText, { children: row.pattern }) }), jsxRuntime.jsx("td", { children: row.type }), jsxRuntime.jsx("td", { children: row.examples.map(example => (jsxRuntime.jsxs("div", { className: 'config-key-sample-copy-row', children: [jsxRuntime.jsx(TechnicalText, { children: example }), jsxRuntime.jsx("button", { type: 'button', className: 'config-key-sample-copy-button', onClick: () => onCopy(example), "aria-label": `Sao chép key ${example}`, "data-tooltip-id": 'tooltip', "data-tooltip-content": 'Sao ch\u00E9p key', children: jsxRuntime.jsx(becoxyIcons.Copy, { fontSize: 15 }) })] }, example))) })] },
|
|
6079
|
+
}, children: [jsxRuntime.jsx("caption", { className: 'visually-hidden', children: "Danh s\u00E1ch m\u1EABu key c\u1EA5u h\u00ECnh" }), jsxRuntime.jsx("thead", { children: jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsx("th", { scope: 'col', style: { width: 360 }, children: "M\u1EABu key \u0111\u1EA7y \u0111\u1EE7" }), jsxRuntime.jsx("th", { scope: 'col', style: { width: 110 }, children: "Type" }), jsxRuntime.jsx("th", { scope: 'col', children: "V\u00ED d\u1EE5" })] }) }), jsxRuntime.jsx("tbody", { children: rows.map(row => (jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsx("td", { children: jsxRuntime.jsx(TechnicalText, { children: `${RESOURCE_CODE_PLACEHOLDER}.${row.pattern}` }) }), jsxRuntime.jsx("td", { children: row.type }), jsxRuntime.jsx("td", { children: row.examples.map(example => (jsxRuntime.jsxs("div", { className: 'config-key-sample-copy-row', children: [jsxRuntime.jsx(TechnicalText, { children: example }), jsxRuntime.jsx("button", { type: 'button', className: 'config-key-sample-copy-button', onClick: () => onCopy(`${RESOURCE_CODE_PLACEHOLDER}.${example}`), "aria-label": `Sao chép key ${RESOURCE_CODE_PLACEHOLDER}.${example}`, "data-tooltip-id": 'tooltip', "data-tooltip-content": 'Sao ch\u00E9p key', children: jsxRuntime.jsx(becoxyIcons.Copy, { fontSize: 15 }) })] }, example))) })] }, row.id))) })] }) }));
|
|
5813
6080
|
const ConfigTreeRulesDrawer = ({ open, onClose }) => {
|
|
6081
|
+
const [activeTab, setActiveTab] = react.useState('samples');
|
|
5814
6082
|
const [searchTerm, setSearchTerm] = react.useState('');
|
|
5815
6083
|
const [categoryFilter, setCategoryFilter] = react.useState(null);
|
|
5816
6084
|
const [typeFilter, setTypeFilter] = react.useState(null);
|
|
5817
6085
|
react.useEffect(() => {
|
|
5818
6086
|
if (!open) {
|
|
6087
|
+
setActiveTab('samples');
|
|
5819
6088
|
setSearchTerm('');
|
|
5820
6089
|
setCategoryFilter(null);
|
|
5821
6090
|
setTypeFilter(null);
|
|
5822
6091
|
}
|
|
5823
6092
|
}, [open]);
|
|
5824
|
-
const filteredSamples = react.useMemo(() =>
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
typeFilter
|
|
5828
|
-
]);
|
|
5829
|
-
const copyKey = async (value) => {
|
|
6093
|
+
const filteredSamples = react.useMemo(() => filterSettingKeyRules(SETTING_KEY_CONVENTION.rules, searchTerm, categoryFilter?.value, typeFilter?.value), [categoryFilter, searchTerm, typeFilter]);
|
|
6094
|
+
const importExampleText = react.useMemo(() => serializeSettingImportExample(RESOURCE_CODE_PLACEHOLDER), []);
|
|
6095
|
+
const copyText = async (value, successMessage) => {
|
|
5830
6096
|
try {
|
|
5831
6097
|
await navigator.clipboard.writeText(value);
|
|
5832
|
-
notificationSuccess(
|
|
6098
|
+
notificationSuccess(successMessage);
|
|
6099
|
+
}
|
|
6100
|
+
catch {
|
|
6101
|
+
notificationError('Không thể sao chép nội dung');
|
|
6102
|
+
}
|
|
6103
|
+
};
|
|
6104
|
+
const downloadImportExample = () => {
|
|
6105
|
+
try {
|
|
6106
|
+
const blob = new Blob([importExampleText], { type: 'application/json;charset=utf-8' });
|
|
6107
|
+
const url = URL.createObjectURL(blob);
|
|
6108
|
+
const link = document.createElement('a');
|
|
6109
|
+
link.href = url;
|
|
6110
|
+
link.download = 'becoxy-setting-resource-example.json';
|
|
6111
|
+
document.body.appendChild(link);
|
|
6112
|
+
link.click();
|
|
6113
|
+
link.remove();
|
|
6114
|
+
URL.revokeObjectURL(url);
|
|
6115
|
+
notificationSuccess('Đã tải JSON mẫu để import');
|
|
5833
6116
|
}
|
|
5834
6117
|
catch {
|
|
5835
|
-
notificationError('Không thể
|
|
6118
|
+
notificationError('Không thể tải JSON mẫu');
|
|
5836
6119
|
}
|
|
5837
6120
|
};
|
|
5838
|
-
return (jsxRuntime.jsx(Sidebar, { open: open, toggleSidebar: onClose, width: 1000, keyboard: true, hiddenFullScreenButton: true, zIndex: 1061, panelClassName: 'config-tree-rules-drawer', children: jsxRuntime.jsxs("div", { className: 'config-key-sample-layout h-100 bg-white d-flex flex-column', children: [jsxRuntime.jsx(ModalHeader, { typeModal: 'Setting', handleModal: onClose, title: 'M\u1EABu key c\u1EA5u h\u00ECnh' }), jsxRuntime.jsxs("div", { className: 'config-key-sample-toolbar border-bottom p-1 flex-shrink-0 d-flex align-items-center justify-content-start flex-wrap gap-1', children: [jsxRuntime.jsx(reactstrap.Input, { type: 'search', bsSize: 'sm', value: searchTerm, onChange: event => setSearchTerm(event.target.value), placeholder: 'T\u00ECm m\u1EABu key...', "aria-label": 'T\u00ECm m\u1EABu key', style: {
|
|
6121
|
+
return (jsxRuntime.jsx(Sidebar, { open: open, toggleSidebar: onClose, width: 1000, keyboard: true, hiddenFullScreenButton: true, zIndex: 1061, panelClassName: 'config-tree-rules-drawer', children: jsxRuntime.jsxs("div", { className: 'config-key-sample-layout h-100 bg-white d-flex flex-column', children: [jsxRuntime.jsx(ModalHeader, { typeModal: 'Setting', handleModal: onClose, title: 'M\u1EABu key c\u1EA5u h\u00ECnh' }), activeTab === 'samples' ? (jsxRuntime.jsxs("div", { className: 'config-key-sample-toolbar border-bottom p-1 flex-shrink-0 d-flex align-items-center justify-content-start flex-wrap gap-1', children: [jsxRuntime.jsx(reactstrap.Input, { type: 'search', bsSize: 'sm', value: searchTerm, onChange: event => setSearchTerm(event.target.value), placeholder: 'T\u00ECm m\u1EABu key...', "aria-label": 'T\u00ECm m\u1EABu key', style: {
|
|
5839
6122
|
width: 300,
|
|
5840
6123
|
maxWidth: '100%'
|
|
5841
|
-
} }), jsxRuntime.jsx("div", { style: { width: 200 }, children: jsxRuntime.jsx(Select__default["default"], { options:
|
|
6124
|
+
} }), jsxRuntime.jsx("div", { style: { width: 200 }, children: jsxRuntime.jsx(Select__default["default"], { options: SETTING_KEY_CONVENTION.categories, value: categoryFilter, onChange: (option) => setCategoryFilter(option), isClearable: true, menuPortalTarget: document.body, menuPosition: 'fixed', styles: SELECT_MENU_PORTAL_STYLES, placeholder: 'T\u1EA5t c\u1EA3 nh\u00F3m m\u1EABu', className: 'react-select w-100', classNamePrefix: 'select' }) }), jsxRuntime.jsx("div", { style: { width: 180 }, children: jsxRuntime.jsx(Select__default["default"], { options: TYPE_OPTIONS, value: typeFilter, onChange: (option) => setTypeFilter(option), isClearable: true, menuPortalTarget: document.body, menuPosition: 'fixed', styles: SELECT_MENU_PORTAL_STYLES, placeholder: 'T\u1EA5t c\u1EA3 Type', className: 'react-select w-100', classNamePrefix: 'select' }) })] })) : null, jsxRuntime.jsxs("div", { className: 'config-key-sample-tabs border-bottom p-1 flex-shrink-0 d-flex align-items-center justify-content-between flex-wrap gap-1', children: [jsxRuntime.jsxs("div", { className: 'd-flex align-items-center gap-50', role: 'tablist', "aria-label": 'N\u1ED9i dung quy t\u1EAFc Setting', children: [jsxRuntime.jsx(reactstrap.Button, { type: 'button', size: 'sm', color: 'primary', outline: activeTab !== 'samples', role: 'tab', "aria-selected": activeTab === 'samples', onClick: () => setActiveTab('samples'), children: "M\u1EABu d\u1EA1ng key" }), jsxRuntime.jsx(reactstrap.Button, { type: 'button', size: 'sm', color: 'primary', outline: activeTab !== 'import-example', role: 'tab', "aria-selected": activeTab === 'import-example', onClick: () => setActiveTab('import-example'), children: "JSON m\u1EABu ho\u00E0n ch\u1EC9nh" })] }), jsxRuntime.jsx("div", { className: 'd-flex align-items-center gap-50', children: activeTab === 'import-example' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs(reactstrap.Button, { type: 'button', size: 'sm', color: 'secondary', outline: true, onClick: () => {
|
|
6125
|
+
void copyText(importExampleText, 'Đã sao chép JSON mẫu');
|
|
6126
|
+
}, children: [jsxRuntime.jsx(becoxyIcons.Copy, { fontSize: 15, className: 'me-50' }), "Copy JSON m\u1EABu"] }), jsxRuntime.jsxs(reactstrap.Button, { type: 'button', size: 'sm', color: 'secondary', outline: true, onClick: downloadImportExample, children: [jsxRuntime.jsx(becoxyIcons.Download, { fontSize: 15, className: 'me-50' }), "T\u1EA3i JSON m\u1EABu"] })] })) : null })] }), jsxRuntime.jsx("div", { className: 'config-key-sample-content p-1 overflow-auto flex-grow-1', role: 'tabpanel', children: activeTab === 'samples' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [filteredSamples.length > 0 ? (jsxRuntime.jsx(KeySampleTable, { rows: filteredSamples, onCopy: value => {
|
|
6127
|
+
void copyText(value, 'Đã sao chép key');
|
|
6128
|
+
} })) : null, filteredSamples.length === 0 ? (jsxRuntime.jsx("div", { className: 'config-key-sample-empty text-center py-2', children: "Kh\u00F4ng t\u00ECm th\u1EA5y m\u1EABu key." })) : null] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: 'alert alert-warning py-50 px-1 mb-1', children: ["Thay ", jsxRuntime.jsx("strong", { children: "RESOURCE_CODE" }), " b\u1EB1ng m\u00E3 Resource th\u1EF1c t\u1EBF tr\u01B0\u1EDBc khi import."] }), jsxRuntime.jsx("pre", { className: 'config-key-convention-text mb-0', children: importExampleText })] })) })] }) }));
|
|
5842
6129
|
};
|
|
5843
6130
|
|
|
5844
6131
|
const ConfigTreeGridFooter = ({ rows }) => {
|