adminforth 1.2.34 → 1.2.36

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.
@@ -190,7 +190,9 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
190
190
  let field = f.field;
191
191
  let operator = this.OperatorsMap[f.operator];
192
192
  if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
193
- placeholder = `(${f.value.map((_, j) => `p${i}_${j}`).join(', ')})`;
193
+ placeholder = `(${f.value.map((_, j) => `{p${i}_${j}:${
194
+ column._underlineType
195
+ }}`).join(', ')})`;
194
196
  }
195
197
 
196
198
  return `${field} ${operator} ${placeholder}`
@@ -194,7 +194,7 @@ class ClickhouseConnector extends AdminForthBaseConnector {
194
194
  let field = f.field;
195
195
  let operator = this.OperatorsMap[f.operator];
196
196
  if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
197
- placeholder = `(${f.value.map((_, j) => `p${i}_${j}`).join(', ')})`;
197
+ placeholder = `(${f.value.map((_, j) => `{p${i}_${j}:${column._underlineType}}`).join(', ')})`;
198
198
  }
199
199
  return `${field} ${operator} ${placeholder}`;
200
200
  }).join(' AND ')}` : '';
@@ -20,7 +20,7 @@ export const styles = () => ({
20
20
  lightSidebarHeading: "alias:lightSidebarText opacity:0.3", // sidebar heading
21
21
  lightList: "#FFFFFF", // list view background
22
22
  lightListTable: "#FFFFFF", // list view table background
23
- lightListTableHeading: "rgb(249 250 251)", // list view table heading
23
+ lightListTableHeading: "alias:lightListTableHeadingText opacity:0.04", // list view table heading
24
24
  lightListTableHeadingText: "#333333", // list view table heading text
25
25
  lightListTableText: "#333333", // list view table text
26
26
  lightListTableRowHover: "rgb(249 250 251)", // list view row hover
@@ -36,7 +36,7 @@ export const styles = () => ({
36
36
  // lightListButtonIcon: "#333333", // list view button icon
37
37
  lightForm: "#FFFFFF", // show view background
38
38
  lightFormBorder: "#F5F5F5", // show view rows border
39
- lightFormHeading: "rgb(249 250 251)", // show view heading
39
+ lightFormHeading: "alias:lightListTableHeading", // show view heading
40
40
  lightButtons: "#FFFFFF", // button background
41
41
  lightButtonsBorder: "#DDDDDD", // button border
42
42
  lightButtonsText: "#111827", // button text
@@ -75,7 +75,7 @@ export const styles = () => ({
75
75
  darkListBorder: "#444444",
76
76
  darkForm: "#111111",
77
77
  darkFormBorder: "#222222",
78
- darkFormHeading: "#334155"
78
+ darkFormHeading: "alias:darkListTableHeading"
79
79
  },
80
80
  boxShadow: {
81
81
  customLight: "0 4px 8px rgba(0, 0, 0, 0.1)", // Lighter shadow
@@ -84,7 +84,7 @@
84
84
  :aria-controls="`dropdown-example${i}`"
85
85
  :data-collapse-toggle="`dropdown-example${i}`"
86
86
  >
87
- <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
87
+ <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
88
88
 
89
89
  <span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
90
90
  <svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
@@ -262,13 +262,11 @@ async function loadMenu() {
262
262
  }
263
263
 
264
264
  function handleCustomLayout() {
265
- console.log(route)
266
265
  if (route.meta?.customLayout) {
267
266
  defaultLayout.value = false;
268
267
  } else {
269
268
  defaultLayout.value = true;
270
269
  }
271
- console.log(defaultLayout.value,route);
272
270
  }
273
271
 
274
272
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2">
3
+ <div class="grid w-40 gap-4 mb-2">
4
4
  <div>
5
5
  <label for="start-time" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ label }}</label>
6
6
 
@@ -11,13 +11,13 @@
11
11
 
12
12
  <input ref="datepickerStartEl" type="text"
13
13
  class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
14
- placeholder="Start date">
14
+ placeholder="Select date">
15
15
  </div>
16
16
  </div>
17
17
  </div>
18
18
 
19
19
  <div>
20
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
20
+ <div class="grid w-40 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
21
21
  <div>
22
22
  <div class="relative">
23
23
  <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="flex flex-wrap gap-2">
3
3
  <input
4
+ :min="minFormatted"
5
+ :max="maxFormatted"
4
6
  type="number" aria-describedby="helper-text-explanation"
5
7
  class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
6
8
  placeholder="From"
@@ -8,6 +10,8 @@
8
10
  >
9
11
 
10
12
  <input
13
+ :min="minFormatted"
14
+ :max="maxFormatted"
11
15
  type="number" aria-describedby="helper-text-explanation"
12
16
  class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
13
17
  placeholder="To"
@@ -21,7 +25,7 @@
21
25
  @click="clear">Clear
22
26
  </button>
23
27
 
24
- <div class="w-full">
28
+ <div v-if="min && max" class="w-full px-2.5">
25
29
  <vue-slider
26
30
  class="custom-slider"
27
31
  :dot-size="20"
@@ -47,12 +51,8 @@ const props = defineProps({
47
51
  valueEnd: {
48
52
  default: '',
49
53
  },
50
- min: {
51
- default: 0,
52
- },
53
- max: {
54
- default: 10,
55
- },
54
+ min: {},
55
+ max: {},
56
56
  });
57
57
 
58
58
  const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
@@ -108,6 +108,10 @@ watch(end, () => {
108
108
  emit('update:valueEnd', end.value);
109
109
  })
110
110
 
111
+ watch([minFormatted,maxFormatted], () => {
112
+ setSliderValues(minFormatted.value, maxFormatted.value)
113
+ })
114
+
111
115
  const clear = () => {
112
116
  start.value = ''
113
117
  end.value = ''
@@ -70,8 +70,8 @@
70
70
 
71
71
  <CustomRangePicker
72
72
  v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
73
- :min="c.min"
74
- :max="c.max"
73
+ :min="getFilterMinValue(c.name)"
74
+ :max="getFilterMaxValue(c.name)"
75
75
  :valueStart="getFilterItem({ column: c, operator: 'gte' })"
76
76
  @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
77
77
  :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
@@ -207,4 +207,16 @@ async function clear() {
207
207
  filtersStore.clearFilters();
208
208
  emits('update:filters', [...filtersStore.filters]);
209
209
  }
210
+
211
+ function getFilterMinValue(columnName) {
212
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
213
+ return props.columnsMinMax[columnName]?.min
214
+ }
215
+ }
216
+
217
+ function getFilterMaxValue(columnName) {
218
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
219
+ return props.columnsMinMax[columnName]?.max
220
+ }
221
+ }
210
222
  </script>
@@ -5,7 +5,7 @@
5
5
  :class="{
6
6
  'px-4': isChild,
7
7
  'px-2': !isChild,
8
- 'bg-sidebarActive dark:bg-gray-700': item.resourceId ?
8
+ 'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item.resourceId ?
9
9
  ($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
10
10
  ($route.name === item.path)
11
11
  }"
@@ -5,7 +5,7 @@
5
5
  >
6
6
  <form autocomplete="off" @submit.prevent>
7
7
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
8
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
9
9
  <tr>
10
10
  <th scope="col" class="px-6 py-3">
11
11
  Field
@@ -41,7 +41,7 @@
41
41
  </div>
42
42
  <span
43
43
  class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
44
- v-if="sort.findIndex((s) => s.field === c.name) !== -1">
44
+ v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
45
45
  {{ sort.findIndex((s) => s.field === c.name) + 1 }}
46
46
  </span>
47
47
 
@@ -321,9 +321,6 @@ async function selectAll(value) {
321
321
 
322
322
  const totalPages = computed(() => Math.ceil(props.totalRows / props.pageSize));
323
323
 
324
-
325
-
326
-
327
324
  const allFromThisPageChecked = computed(() => {
328
325
  if (!props.rows) return false;
329
326
  return props.rows.every((r) => checkboxesInternal.value.includes(r.id));
@@ -361,14 +358,6 @@ async function onClick(e,row) {
361
358
  },
362
359
  })}
363
360
  }
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
361
 
373
362
  async function deleteRecord(row) {
374
363
  const data = await window.adminforth.confirm({
@@ -396,9 +385,5 @@ async function deleteRecord(row) {
396
385
  console.error(e);
397
386
  };
398
387
  }
399
-
400
-
401
388
  }
402
-
403
-
404
389
  </script>
@@ -23,4 +23,5 @@
23
23
  // ::-webkit-scrollbar-thumb:hover {
24
24
  // @apply bg-gray-700
25
25
  // }
26
- // }
26
+ // }
27
+
@@ -47,7 +47,7 @@
47
47
  class="relative overflow-x-auto shadow-resourseFormShadow "
48
48
  >
49
49
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
50
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
51
51
  <tr>
52
52
  <th scope="col" class="px-6 py-3">
53
53
  Field
package/modules/styles.ts CHANGED
@@ -25,7 +25,7 @@ export const styles = () => ({
25
25
 
26
26
  lightList: "#FFFFFF", // list view background
27
27
  lightListTable: "#FFFFFF", // list view table background
28
- lightListTableHeading: "rgb(249 250 251)", // list view table heading
28
+ lightListTableHeading: "alias:lightListTableHeadingText opacity:0.04", // list view table heading
29
29
  lightListTableHeadingText: "#333333", // list view table heading text
30
30
  lightListTableText: "#333333", // list view table text
31
31
  lightListTableRowHover: "rgb(249 250 251)", // list view row hover
@@ -42,7 +42,7 @@ export const styles = () => ({
42
42
 
43
43
  lightForm: "#FFFFFF", // show view background
44
44
  lightFormBorder: "#F5F5F5", // show view rows border
45
- lightFormHeading: "rgb(249 250 251)", // show view heading
45
+ lightFormHeading: "alias:lightListTableHeading", // show view heading
46
46
 
47
47
  lightButtons: "#FFFFFF", // button background
48
48
  lightButtonsBorder: "#DDDDDD", // button border
@@ -90,7 +90,7 @@ export const styles = () => ({
90
90
 
91
91
  darkForm: "#111111",
92
92
  darkFormBorder: "#222222",
93
- darkFormHeading: "#334155"
93
+ darkFormHeading: "alias:darkListTableHeading"
94
94
 
95
95
  },
96
96
  boxShadow: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.34",
3
+ "version": "1.2.36",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/spa/src/App.vue CHANGED
@@ -84,7 +84,7 @@
84
84
  :aria-controls="`dropdown-example${i}`"
85
85
  :data-collapse-toggle="`dropdown-example${i}`"
86
86
  >
87
- <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
87
+ <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
88
88
 
89
89
  <span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
90
90
  <svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
@@ -262,13 +262,11 @@ async function loadMenu() {
262
262
  }
263
263
 
264
264
  function handleCustomLayout() {
265
- console.log(route)
266
265
  if (route.meta?.customLayout) {
267
266
  defaultLayout.value = false;
268
267
  } else {
269
268
  defaultLayout.value = true;
270
269
  }
271
- console.log(defaultLayout.value,route);
272
270
  }
273
271
 
274
272
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2">
3
+ <div class="grid w-40 gap-4 mb-2">
4
4
  <div>
5
5
  <label for="start-time" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ label }}</label>
6
6
 
@@ -11,13 +11,13 @@
11
11
 
12
12
  <input ref="datepickerStartEl" type="text"
13
13
  class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
14
- placeholder="Start date">
14
+ placeholder="Select date">
15
15
  </div>
16
16
  </div>
17
17
  </div>
18
18
 
19
19
  <div>
20
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
20
+ <div class="grid w-40 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
21
21
  <div>
22
22
  <div class="relative">
23
23
  <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="flex flex-wrap gap-2">
3
3
  <input
4
+ :min="minFormatted"
5
+ :max="maxFormatted"
4
6
  type="number" aria-describedby="helper-text-explanation"
5
7
  class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
6
8
  placeholder="From"
@@ -8,6 +10,8 @@
8
10
  >
9
11
 
10
12
  <input
13
+ :min="minFormatted"
14
+ :max="maxFormatted"
11
15
  type="number" aria-describedby="helper-text-explanation"
12
16
  class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
13
17
  placeholder="To"
@@ -21,7 +25,7 @@
21
25
  @click="clear">Clear
22
26
  </button>
23
27
 
24
- <div class="w-full">
28
+ <div v-if="min && max" class="w-full px-2.5">
25
29
  <vue-slider
26
30
  class="custom-slider"
27
31
  :dot-size="20"
@@ -47,12 +51,8 @@ const props = defineProps({
47
51
  valueEnd: {
48
52
  default: '',
49
53
  },
50
- min: {
51
- default: 0,
52
- },
53
- max: {
54
- default: 10,
55
- },
54
+ min: {},
55
+ max: {},
56
56
  });
57
57
 
58
58
  const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
@@ -108,6 +108,10 @@ watch(end, () => {
108
108
  emit('update:valueEnd', end.value);
109
109
  })
110
110
 
111
+ watch([minFormatted,maxFormatted], () => {
112
+ setSliderValues(minFormatted.value, maxFormatted.value)
113
+ })
114
+
111
115
  const clear = () => {
112
116
  start.value = ''
113
117
  end.value = ''
@@ -70,8 +70,8 @@
70
70
 
71
71
  <CustomRangePicker
72
72
  v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
73
- :min="c.min"
74
- :max="c.max"
73
+ :min="getFilterMinValue(c.name)"
74
+ :max="getFilterMaxValue(c.name)"
75
75
  :valueStart="getFilterItem({ column: c, operator: 'gte' })"
76
76
  @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
77
77
  :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
@@ -207,4 +207,16 @@ async function clear() {
207
207
  filtersStore.clearFilters();
208
208
  emits('update:filters', [...filtersStore.filters]);
209
209
  }
210
+
211
+ function getFilterMinValue(columnName) {
212
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
213
+ return props.columnsMinMax[columnName]?.min
214
+ }
215
+ }
216
+
217
+ function getFilterMaxValue(columnName) {
218
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
219
+ return props.columnsMinMax[columnName]?.max
220
+ }
221
+ }
210
222
  </script>
@@ -5,7 +5,7 @@
5
5
  :class="{
6
6
  'px-4': isChild,
7
7
  'px-2': !isChild,
8
- 'bg-sidebarActive dark:bg-gray-700': item.resourceId ?
8
+ 'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item.resourceId ?
9
9
  ($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
10
10
  ($route.name === item.path)
11
11
  }"
@@ -5,7 +5,7 @@
5
5
  >
6
6
  <form autocomplete="off" @submit.prevent>
7
7
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
8
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
9
9
  <tr>
10
10
  <th scope="col" class="px-6 py-3">
11
11
  Field
@@ -41,7 +41,7 @@
41
41
  </div>
42
42
  <span
43
43
  class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
44
- v-if="sort.findIndex((s) => s.field === c.name) !== -1">
44
+ v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
45
45
  {{ sort.findIndex((s) => s.field === c.name) + 1 }}
46
46
  </span>
47
47
 
@@ -321,9 +321,6 @@ async function selectAll(value) {
321
321
 
322
322
  const totalPages = computed(() => Math.ceil(props.totalRows / props.pageSize));
323
323
 
324
-
325
-
326
-
327
324
  const allFromThisPageChecked = computed(() => {
328
325
  if (!props.rows) return false;
329
326
  return props.rows.every((r) => checkboxesInternal.value.includes(r.id));
@@ -361,14 +358,6 @@ async function onClick(e,row) {
361
358
  },
362
359
  })}
363
360
  }
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
361
 
373
362
  async function deleteRecord(row) {
374
363
  const data = await window.adminforth.confirm({
@@ -396,9 +385,5 @@ async function deleteRecord(row) {
396
385
  console.error(e);
397
386
  };
398
387
  }
399
-
400
-
401
388
  }
402
-
403
-
404
389
  </script>
@@ -23,4 +23,5 @@
23
23
  // ::-webkit-scrollbar-thumb:hover {
24
24
  // @apply bg-gray-700
25
25
  // }
26
- // }
26
+ // }
27
+
@@ -47,7 +47,7 @@
47
47
  class="relative overflow-x-auto shadow-resourseFormShadow "
48
48
  >
49
49
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
50
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
51
51
  <tr>
52
52
  <th scope="col" class="px-6 py-3">
53
53
  Field