raid_new.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <!-- This will be shown in a ts-modal in the raid.html-->
  2. <style>
  3. .new-raid-modal-content{
  4. max-height: 70vh;
  5. overflow-y: auto;
  6. }
  7. @media screen and (max-width: 767px) {
  8. .new-raid-modal-content {
  9. max-height: none;
  10. }
  11. }
  12. .new-raid-disk-info{
  13. cursor: pointer;
  14. }
  15. .new-raid-disk-info:hover{
  16. opacity: 0.5;
  17. }
  18. </style>
  19. <div class="content">
  20. <div class="ts-content">
  21. <div class="ts-header" i18n>Create New RAID Array
  22. // 建立 RAID 陣列
  23. </div>
  24. </div>
  25. <div class="ts-divider"></div>
  26. <div class="ts-content new-raid-modal-content">
  27. <div class="ts-grid mobile:is-stacked" >
  28. <div class="column is-3-wide">
  29. <div class="ts-procedure is-vertical has-top-spaced-large" style="position: sticky; top: 0;">
  30. <a step="1" class="item is-active">
  31. <div class="content">
  32. <div class="indicator"></div>
  33. <div class="label" i18n>RAID Name
  34. // 陣列名稱
  35. </div>
  36. </div>
  37. </a>
  38. <a step="2" class="item">
  39. <div class="content">
  40. <div class="indicator"></div>
  41. <div class="label" i18n>Select Disks
  42. // 選擇磁碟
  43. </div>
  44. </div>
  45. </a>
  46. <a step="3" class="item">
  47. <div class="content">
  48. <div class="indicator"></div>
  49. <div class="label" i18n>Raid Type
  50. // 陣列類型
  51. </div>
  52. </div>
  53. </a>
  54. <a step="4" class="item">
  55. <div class="content">
  56. <div class="indicator"></div>
  57. <div class="label" i18n>Confirm
  58. // 確認設定
  59. </div>
  60. </div>
  61. </a>
  62. </div>
  63. </div>
  64. <div class="column is-13-wide" style="overflow-y: auto;">
  65. <div class="ts-content">
  66. <!-- Array Name -->
  67. <div class="ts-text" i18n>RAID Name
  68. // 陣列名稱
  69. </div>
  70. <div class="ts-input has-top-spaced-small">
  71. <input type="text" placeholder="my-raid" id="raid_name" i18n>
  72. <span class="ts-icon is-circle-check-icon raid_name_valid_icon" style="color: var(--ts-positive-400); display:none;"></span>
  73. </div>
  74. <div class="ts-text is-description" i18n>Only alphabet, digits, _ (underscore) and - (hyphen) are allowed
  75. // 只允許字母、數字、_ (底線) 和 - (連字符)
  76. </div>
  77. <!-- Select Disks -->
  78. <div class="ts-divider has-top-spaced-small has-bottom-spaced-large"></div>
  79. <div id="new_raid_disk_select">
  80. <div class="ts-content">
  81. <div class="ts-text is-description" i18n>Loading disks, please wait...
  82. // 正在載入磁碟,請稍候...
  83. </div>
  84. </div>
  85. </div>
  86. <div class="ts-text is-description has-top-spaced-small" i18n> Tips: For any extra disks selected, it will be used as spare disks.
  87. // 提示:選擇的任何額外磁碟將用作備用磁碟。
  88. </div>
  89. <div class="ts-wrap is-end-aligned">
  90. <button class="ts-button is-outlined" i18n>Refresh
  91. // 重新整理
  92. </button>
  93. </div>
  94. <!-- Array Type -->
  95. <div class="ts-divider has-top-spaced-small has-bottom-spaced-small"></div>
  96. <div class="ts-text" i18n>RAID Type
  97. // 陣列類型
  98. </div>
  99. <button class="ts-button is-start-icon has-top-spaced-small" data-dropdown="new_raid_type_select">
  100. <span class="ts-icon is-chevron-down-icon"></span>
  101. <span class="raid_type_selected" i18n>Select RAID Type
  102. // 選擇陣列類型
  103. </span>
  104. </button>
  105. <div class="ts-dropdown is-end-icon" id="new_raid_type_select">
  106. <button class="item raid_type" value="raid1">RAID 1 <span class="ts-icon is-star-icon" style="color: var(--ts-warning-400);"></span></button>
  107. <button class="item raid_type" value="raid6">RAID 6 <span class="ts-icon is-star-icon" style="color: var(--ts-warning-400);"></span></button>
  108. <button class="item raid_type" value="raid5">RAID 5 </button>
  109. <button class="item raid_type" value="raid0">RAID 0</button>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. </div>
  115. <div class="ts-divider"></div>
  116. <div class="ts-content is-tertiary">
  117. <div class="ts-wrap is-end-aligned">
  118. <button class="ts-button" i18n>Confirm
  119. // 確認
  120. </button>
  121. <button class="ts-button is-outlined" i18n>Cancel
  122. // 取消
  123. </button>
  124. </div>
  125. </div>
  126. <script>
  127. /* RAID name validation */
  128. $("#raid_name").on("keydown", function() {
  129. validateRaidName();
  130. });
  131. $("#raid_name").on("change", function() {
  132. validateRaidName();
  133. });
  134. function validateRaidName(){
  135. var raidName = $("#raid_name").val().trim();
  136. var isValid = /^[a-zA-Z0-9_-]+$/.test(raidName) && raidName.trim() !== "";
  137. if (raidName == ""){
  138. $("#raid_name").parent().removeClass("is-end-icon");
  139. $("a[step='1']").removeClass("is-completed").removeClass("is-active");
  140. $("a[step='2']").removeClass("is-active");
  141. $(".ts-icon.raid_name_valid_icon").hide();
  142. } else if (isValid) {
  143. $("#raid_name").parent().removeClass("is-negative");
  144. $("a[step='1']").removeClass("is-active").addClass("is-completed");
  145. $("a[step='2']").addClass("is-active");
  146. $("#raid_name").parent().addClass("is-end-icon");
  147. $(".ts-icon.raid_name_valid_icon").show();
  148. } else {
  149. $("#raid_name").parent().addClass("is-negative");
  150. $("a[step='1']").removeClass("is-completed").addClass("is-active");
  151. $("a[step='2']").removeClass("is-active");
  152. $("#raid_name").parent().removeClass("is-end-icon");
  153. $(".ts-icon.raid_name_valid_icon").hide();
  154. }
  155. }
  156. /* Disk selection */
  157. function initNewRAIDDiskList(){
  158. $.get("./api/info/list", function(data) {
  159. if (data) {
  160. var disks = data;
  161. var diskList = $("#new_raid_disk_select");
  162. diskList.empty();
  163. for (var i = 0; i < disks.length; i++) {
  164. let disk = disks[i];
  165. let partitionTable = "";
  166. let diskIsMounted = false;
  167. let diskIsAlreadyRAID = false; //The disk already belongs to another RAID array
  168. // Render the partition table
  169. if (disk.partitions.length > 0) {
  170. partitionTable += `<table class="ts-table is-bordered is-striped">
  171. <thead>
  172. <tr>
  173. <th i18n>Partition Name
  174. // 分割區
  175. </th>
  176. <th i18n>Size
  177. // 大小
  178. </th>
  179. <th i18n>Type
  180. // 類型
  181. </th>
  182. <th i18n>Mount Point
  183. // 掛載點
  184. </th>
  185. </tr>
  186. </thead>
  187. <tbody>`;
  188. for (var j = 0; j < disk.partitions.length; j++) {
  189. let partition = disk.partitions[j];
  190. partitionTable += `<tr>
  191. <td>${partition.name}</td>
  192. <td>${humanFileSize(partition.size)}</td>
  193. <td>${partition.fstype || partition.blocktype}</td>
  194. <td>${partition.mountpoint || ""}</td>
  195. </tr>`;
  196. if (partition.mountpoint) {
  197. diskIsMounted = true;
  198. }
  199. if (partition.blocktype.includes("raid")) {
  200. diskIsAlreadyRAID = true;
  201. }
  202. }
  203. partitionTable += `</tbody></table>`;
  204. } else {
  205. partitionTable = `<div class="ts-text is-description" i18n>No Partitions
  206. // 無分割區
  207. </div>`;
  208. }
  209. let warningClass = "";
  210. let warningMessage = `<div class="ts-text is-negative" i18n>
  211. This disk is mounted and might be in use.
  212. // 此磁碟已被掛載,可能正在使用中。
  213. </div>`;
  214. if (diskIsMounted) {
  215. warningClass = "is-negative is-start-indicated";
  216. }
  217. let disabledClass = "";
  218. if (diskIsAlreadyRAID) {
  219. disabledClass = "is-disabled";
  220. }
  221. // Append disk information with partition table
  222. diskList.append(`<div class="ts-box ts-content has-top-spaced-small new-raid-disk-info ${warningClass} ${disabledClass}">
  223. ${diskIsMounted ? warningMessage : ""}
  224. <div class="ts-item">
  225. <div class="ts-header">${disk.model} <span class="ts-badge has-start-spaced-small">${humanFileSize(disk.size)}</span></div>
  226. <div class="ts-text is-description">
  227. /dev/${disk.name}
  228. </div>
  229. </div>
  230. <div class="has-top-spaced-small">
  231. ${partitionTable}
  232. </div>
  233. <div class="new_raid_disk_selected">
  234. <span class="ts-icon is-circle-check-icon" style="color: var(--ts-positive-400); font-size: 1.5rem;"></span>
  235. </div>
  236. </div>`);
  237. }
  238. } else {
  239. console.error("Failed to load disk info: " + data.message);
  240. }
  241. //Bind click event to each disk info
  242. $(".new-raid-disk-info").off("click").on("click", function() {
  243. var selectedDisk = $(this);
  244. var selectedIcon = selectedDisk.find(".new_raid_disk_selected");
  245. var isSelected = $(this).hasClass("selected");
  246. if (isSelected) {
  247. selectedDisk.removeClass("selected");
  248. selectedIcon.hide();
  249. } else {
  250. selectedDisk.addClass("selected");
  251. selectedIcon.show();
  252. }
  253. // Check if any disk is selected
  254. if ($(".new-raid-disk-info.selected").length > 0) {
  255. // Mark step 2 as completed and step 3 as active
  256. $("a[step='2']").removeClass("is-active").addClass("is-completed");
  257. $("a[step='3']").addClass("is-active");
  258. } else {
  259. // Restore the not complete state of the steps
  260. $("a[step='2']").removeClass("is-completed").addClass("is-active");
  261. $("a[step='3']").removeClass("is-active");
  262. }
  263. });
  264. relocale();
  265. });
  266. }
  267. initNewRAIDDiskList();
  268. /* RAID type selection */
  269. $(".raid_type").click(function() {
  270. var raid_type = $(this).text();
  271. var raid_type_value = $(this).val();
  272. $(".raid_type_selected").text(raid_type);
  273. $(".raid_type_selected").val(raid_type_value);
  274. $("#new_raid_type_select").removeClass("is-active");
  275. $("#new_raid_type_select").removeAttr("data-dropdown");
  276. $("#new_raid_type_select").attr("data-dropdown", "new_raid_type_select");
  277. $(".raid_type").removeClass("is-active");
  278. $(this).addClass("is-active");
  279. $(".raid_type").removeAttr("aria-pressed");
  280. $(this).attr("aria-pressed", "true");
  281. $(".raid_type").removeAttr("aria-selected");
  282. $(this).attr("aria-selected", "true");
  283. $(".raid_type").removeAttr("aria-expanded");
  284. $(this).attr("aria-expanded", "true");
  285. $(".raid_type").removeAttr("aria-hidden");
  286. $(this).attr("aria-hidden", "false");
  287. // Mark step 3 as completed and step 2 as active
  288. $("a[step='3']").removeClass("is-active").addClass("is-completed");
  289. $("a[step='2']").addClass("is-active");
  290. });
  291. </script>