From 3f0a911c80ca2d174a7e1d23ff2726a8232c2459 Mon Sep 17 00:00:00 2001
From: "Shishkevich D." <135337715+shishkevichd@users.noreply.github.com>
Date: Sat, 8 Mar 2025 14:39:25 +0000
Subject: [PATCH] refactor: switching to the use of typed props

---
 web/html/login.html                           |  2 +-
 web/html/xui/client_bulk_modal.html           |  4 +-
 web/html/xui/common_sider.html                |  4 +-
 web/html/xui/component/password.html          | 50 +++++++++++++------
 web/html/xui/component/persianDatepicker.html | 28 ++++++++---
 web/html/xui/component/setting.html           | 21 +++++++-
 web/html/xui/component/sortableTable.html     | 38 ++++++++++----
 web/html/xui/component/themeSwitch.html       | 25 ++++++----
 web/html/xui/form/client.html                 |  4 +-
 web/html/xui/form/inbound.html                |  4 +-
 web/html/xui/xray.html                        |  2 +-
 11 files changed, 126 insertions(+), 56 deletions(-)

diff --git a/web/html/login.html b/web/html/login.html
index 82c9afd5..0e5e5ef1 100644
--- a/web/html/login.html
+++ b/web/html/login.html
@@ -461,7 +461,7 @@
                   </a-form-item>
                   <a-form-item>
                     <a-row justify="center" class="centered">
-                      <theme-switch-login></theme-switch-login>
+                      <a-theme-switch-login></a-theme-switch-login>
                     </a-row>
                   </a-form-item>
                 </a-form>
diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html
index 74e49225..82e68c74 100644
--- a/web/html/xui/client_bulk_modal.html
+++ b/web/html/xui/client_bulk_modal.html
@@ -106,9 +106,9 @@
             <a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
                 format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
                 v-model="clientsBulkModal.expiryTime"></a-date-picker>
-            <persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
+            <a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
                 value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime">
-            </persian-datepicker>
+            </a-persian-datepicker>
         </a-form-item>
         <a-form-item v-if="clientsBulkModal.expiryTime != 0">
             <template slot="label">
diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html
index df659489..1d5fe075 100644
--- a/web/html/xui/common_sider.html
+++ b/web/html/xui/common_sider.html
@@ -34,7 +34,7 @@
 
 {{define "commonSider"}}
 <a-layout-sider :theme="themeSwitcher.currentTheme" id="sider" collapsible breakpoint="md">
-  <theme-switch></theme-switch>
+  <a-theme-switch></a-theme-switch>
   <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']" @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
     {{template "menuItems" .}}
   </a-menu>
@@ -43,7 +43,7 @@
   <div class="drawer-handle" @click="siderDrawer.change()" slot="handle">
     <a-icon :type="siderDrawer.visible ? 'close' : 'menu-fold'"></a-icon>
   </div>
-  <theme-switch></theme-switch>
+  <a-theme-switch></a-theme-switch>
   <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']" @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
     {{template "menuItems" .}}
   </a-menu>
diff --git a/web/html/xui/component/password.html b/web/html/xui/component/password.html
index 37e4c793..70b218ef 100644
--- a/web/html/xui/component/password.html
+++ b/web/html/xui/component/password.html
@@ -1,26 +1,46 @@
 {{define "component/passwordInput"}}
 <template>
-    <a-input :value="value" :type="showPassword ? 'text' : 'password'"
-             :placeholder="placeholder"
-             :autocomplete="autocomplete"
-             :name="name"
-             @input="$emit('input', $event.target.value)">
-        <template v-if="icon" #prefix>
-            <a-icon :type="icon" style="font-size: 16px;" />
-        </template>
-        <template #addonAfter>
-            <a-icon :type="showPassword ? 'eye-invisible' : 'eye'"
-                    @click="toggleShowPassword"
-                    style="font-size: 16px;" />
-        </template>
-    </a-input>
+  <a-input :value="value" :type="showPassword ? 'text' : 'password'" :placeholder="placeholder"
+    :autocomplete="autocomplete" :name="name" @input="$emit('input', $event.target.value)">
+    <template v-if="icon" #prefix>
+      <a-icon :type="icon" style="font-size: 16px;" />
+    </template>
+    <template #addonAfter>
+      <a-icon :type="showPassword ? 'eye-invisible' : 'eye'" @click="toggleShowPassword" style="font-size: 16px;" />
+    </template>
+  </a-input>
 </template>
 {{end}}
 
 {{define "component/password"}}
 <script>
   Vue.component('password-input', {
-    props: ["title", "value", "placeholder", "icon", "autocomplete", "name"],
+    props: {
+      'title': {
+        type: String,
+        required: false,
+      },
+      'value': {
+        type: String,
+        required: false,
+      },
+      'placeholder': {
+        type: String,
+        required: false,
+      },
+      'autocomplete': {
+        type: String,
+        required: false,
+      },
+      'name': {
+        type: String,
+        required: false,
+      },
+      'icon': {
+        type: undefined,
+        required: false
+      }
+    },
     template: `{{template "component/passwordInput"}}`,
     data() {
       return {
diff --git a/web/html/xui/component/persianDatepicker.html b/web/html/xui/component/persianDatepicker.html
index df47c4f3..2df79a06 100644
--- a/web/html/xui/component/persianDatepicker.html
+++ b/web/html/xui/component/persianDatepicker.html
@@ -2,10 +2,10 @@
 <template>
     <div>
         <a-input :value="value" type="text" v-model="date" data-jdp class="persian-datepicker"
-                 @input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
-                 :placeholder="placeholder">
+            @input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
+            :placeholder="placeholder">
             <template #addonAfter>
-                <a-icon type="calendar" style="font-size: 14px; opacity: 0.5;"/>
+                <a-icon type="calendar" style="font-size: 14px; opacity: 0.5;" />
             </template>
         </a-input>
     </div>
@@ -13,15 +13,27 @@
 {{end}}
 
 {{define "component/persianDatepicker"}}
-<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css?{{ .cur_ver }}"/>
+<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css?{{ .cur_ver }}" />
 <script src="{{ .base_path }}assets/moment/moment-jalali.min.js?{{ .cur_ver }}"></script>
 <script src="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.js?{{ .cur_ver }}"></script>
 <script>
-
     const persianDatepicker = {};
 
-    Vue.component('persian-datepicker', {
-        props: ['placeholder', 'format', 'value'],
+    Vue.component('a-persian-datepicker', {
+        props: {
+            'format': {
+                type: undefined,
+                required: false,
+            },
+            'value': {
+                type: String,
+                required: false,
+            },
+            'placeholder': {
+                type: String,
+                required: false,
+            },
+        },
         template: `{{template "component/persianDatepickerTemplate"}}`,
         data() {
             return {
@@ -57,4 +69,4 @@
         }
     });
 </script>
-{{end}}
+{{end}}
\ No newline at end of file
diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html
index bc2c6e42..85220d25 100644
--- a/web/html/xui/component/setting.html
+++ b/web/html/xui/component/setting.html
@@ -21,7 +21,24 @@
 {{define "component/setting"}}
 <script>
     Vue.component('a-setting-list-item', {
-        props: ["title", "description", "paddings"],
+        props: {
+            'title': {
+                type: String,
+                required: true,
+            },
+            'description': {
+                type: String,
+                required: false,
+            },
+            'paddings': {
+                type: String,
+                required: false,
+                defaultValue: "default",
+                validator: function (value) {
+                    return ['small', 'default'].includes(value)
+                }
+            }
+        },
         template: `{{ template "component/settingListItem" }}`,
         computed: {
             padding() {
@@ -29,7 +46,7 @@
                     case "small":
                         return "10px 20px !important"
                         break;
-                    default:
+                    case "default":
                         return "20px !important"
                         break;
                 }
diff --git a/web/html/xui/component/sortableTable.html b/web/html/xui/component/sortableTable.html
index f62eba44..67bbcee2 100644
--- a/web/html/xui/component/sortableTable.html
+++ b/web/html/xui/component/sortableTable.html
@@ -1,9 +1,5 @@
 {{define "component/sortableTableTrigger"}}
-<a-icon type="drag"
-  class="sortable-icon"
-  style="cursor: move;"
-  @mouseup="mouseUpHandler"
-  @mousedown="mouseDownHandler"
+<a-icon type="drag" class="sortable-icon" style="cursor: move;" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
   @click="clickHandler" />
 {{end}}
 
@@ -28,7 +24,16 @@
         newElementIndex: null,
       };
     },
-    props: ['data-source', 'customRow'],
+    props: {
+      'data-source': {
+        type: undefined,
+        required: false,
+      },
+      'customRow': {
+        type: undefined,
+        required: false,
+      }
+    },
     inheritAttrs: false,
     provide() {
       const sortable = {}
@@ -44,7 +49,7 @@
         sortable,
       }
     },
-    render: function(createElement) {
+    render: function (createElement) {
       return createElement('a-table', {
         class: {
           'ant-table-is-sorting': this.isDragging(),
@@ -59,7 +64,7 @@
           drop: (e) => this.dropHandler(e),
         },
         scopedSlots: this.$scopedSlots,
-      }, this.$slots.default, )
+      }, this.$slots.default,)
     },
     created() {
       this.$memoSort = {};
@@ -163,9 +168,14 @@
       }
     }
   });
-  Vue.component('table-sort-trigger', {
+  Vue.component('a-table-sort-trigger', {
     template: `{{template "component/sortableTableTrigger"}}`,
-    props: ['item-index'],
+    props: {
+      'item-index': {
+        type: undefined,
+        required: false
+      }
+    },
     inject: ['sortable'],
     methods: {
       mouseDownHandler(e) {
@@ -190,27 +200,33 @@
       display: none;
     }
   }
+
   .ant-table-is-sorting .draggable-row td {
     background-color: #ffffff !important;
   }
+
   .dark .ant-table-is-sorting .draggable-row td {
     background-color: var(--dark-color-surface-100) !important;
   }
+
   .ant-table-is-sorting .dragging td {
     background-color: rgb(232 244 242) !important;
     color: rgba(0, 0, 0, 0.3);
   }
+
   .dark .ant-table-is-sorting .dragging td {
     background-color: var(--dark-color-table-hover) !important;
     color: rgba(255, 255, 255, 0.3);
   }
+
   .ant-table-is-sorting .dragging {
     opacity: 1;
     box-shadow: 1px -2px 2px #008771;
     transition: all 0.2s;
   }
+
   .ant-table-is-sorting .dragging .ant-table-row-index {
     opacity: 0.3;
   }
 </style>
-{{end}}
+{{end}}
\ No newline at end of file
diff --git a/web/html/xui/component/themeSwitch.html b/web/html/xui/component/themeSwitch.html
index 0de64a84..28cb79b1 100644
--- a/web/html/xui/component/themeSwitch.html
+++ b/web/html/xui/component/themeSwitch.html
@@ -6,9 +6,13 @@
         <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
         <span>Theme</span>
       </span>
-      <a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()"> Dark <a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
+      <a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()"> Dark
+        <a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme"
+          @change="themeSwitcher.toggleTheme()"></a-switch>
       </a-menu-item>
-      <a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOffUltra()"> Ultra <a-checkbox style="margin-left: 2px;" :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
+      <a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
+        @mousedown="themeSwitcher.animationsOffUltra()"> Ultra <a-checkbox style="margin-left: 2px;"
+          :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
       </a-menu-item>
     </a-sub-menu>
   </a-menu>
@@ -17,12 +21,15 @@
 
 {{define "component/themeSwitchTemplateLogin"}}
 <template>
-  <a-menu @mousedown="themeSwitcher.animationsOff()" id="change-theme" :theme="themeSwitcher.currentTheme" mode="inline" selected-keys="">
+  <a-menu @mousedown="themeSwitcher.animationsOff()" id="change-theme" :theme="themeSwitcher.currentTheme" mode="inline"
+    selected-keys="">
     <a-menu-item mode="inline" class="ant-menu-theme-switch">
       <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
-      <a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
+      <a-switch size="small" :default-checked="themeSwitcher.isDarkTheme"
+        @change="themeSwitcher.toggleTheme()"></a-switch>
       <template v-if="themeSwitcher.isDarkTheme">
-        <a-checkbox style="margin-left: 1rem; vertical-align: middle;" :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()">Ultra</a-checkbox>
+        <a-checkbox style="margin-left: 1rem; vertical-align: middle;" :checked="themeSwitcher.isUltra"
+          @click="themeSwitcher.toggleUltra()">Ultra</a-checkbox>
       </template>
     </a-menu-item>
   </a-menu>
@@ -83,8 +90,7 @@
     };
   }
   const themeSwitcher = createThemeSwitcher();
-  Vue.component('theme-switch', {
-    props: [],
+  Vue.component('a-theme-switch', {
     template: `{{template "component/themeSwitchTemplate"}}`,
     data: () => ({
       themeSwitcher
@@ -96,8 +102,7 @@
       document.getElementById('message').className = themeSwitcher.currentTheme;
     }
   });
-  Vue.component('theme-switch-login', {
-    props: [],
+  Vue.component('a-theme-switch-login', {
     template: `{{template "component/themeSwitchTemplateLogin"}}`,
     data: () => ({
       themeSwitcher
@@ -110,4 +115,4 @@
     }
   });
 </script>
-{{end}}
+{{end}}
\ No newline at end of file
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index c2bcace1..3aff67e0 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -154,8 +154,8 @@
         </template>
         <a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
             :dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker>
-        <persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
-                            value="client._expiryTime" v-model="client._expiryTime"></persian-datepicker>
+        <a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
+                            value="client._expiryTime" v-model="client._expiryTime"></a-persian-datepicker>
         <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
     </a-form-item>
     <a-form-item v-if="client.expiryTime != 0">
diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html
index 091393ff..e0521c69 100644
--- a/web/html/xui/form/inbound.html
+++ b/web/html/xui/form/inbound.html
@@ -57,9 +57,9 @@
         <a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
             format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
             v-model="dbInbound._expiryTime"></a-date-picker>
-        <persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
+        <a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
             value="dbInbound._expiryTime" v-model="dbInbound._expiryTime">
-        </persian-datepicker>
+        </a-persian-datepicker>
     </a-form-item>
 </a-form>
 
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index d101bdcd..41bbd214 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -348,7 +348,7 @@
                         :indent-size="0" 
                         v-on:onSort="replaceRule">
                     <template slot="action" slot-scope="text, rule, index">
-                        <table-sort-trigger :item-index="index"></table-sort-trigger>
+                        <a-table-sort-trigger :item-index="index"></a-table-sort-trigger>
                         <span class="ant-table-row-index"> [[ index+1 ]] </span>
                         <a-dropdown :trigger="['click']">
                         <a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>