|
227 | 227 | outputTable.append(outTableHead, outTableBody); |
228 | 228 | crmOutputContainer.append(outputTitleWrap, outputTable); |
229 | 229 |
|
230 | | - // ** организация кнопки для добавления "нового" клиента (последующее открытие модального окна, ряд сопутствующих действий) |
231 | | - const addBtnWrap = document.createElement('div'); |
232 | | - const addBtn = document.createElement('button'); |
233 | | - const addBtnIcon = document.createElement('i'); |
234 | | - |
235 | | - addBtnWrap.classList.add('crm__add-btn-wrap'); |
236 | | - addBtn.classList.add('crm__add-btn'); |
237 | | - addBtnIcon.classList.add('crm__add-btn-icon', 'bi', 'bi-person-plus-fill'); |
238 | | - |
239 | | - addBtn.setAttribute('id', 'add-btn'); |
240 | | - addBtn.setAttribute('type', 'button'); |
241 | | - |
242 | | - addBtn.textContent = 'Добавить клиента'; |
243 | | - |
244 | | - addBtn.append(addBtnIcon); |
245 | | - addBtnWrap.append(addBtn); |
246 | | - crmAddContainer.append(addBtnWrap); |
247 | | - |
248 | | - // обработка клика по кнопке, создание/отображение модального окна |
249 | | - addBtn.addEventListener('click', () => { |
250 | | - const modalWrap = createModalWindowByType('add'); // создание по типу "add" |
251 | | - |
252 | | - crmAddContainer.append(modalWrap); // добавление в DOM |
253 | | - |
254 | | - // инициализация через Bootstrap API |
255 | | - const bootstrapModal = new bootstrap.Modal(modalWrap); |
256 | | - bootstrapModal.show(); // отображение |
257 | | - |
258 | | - // добавление валидации для вводимых данных/в модальном окне (для "основных" инпутов, ФИО) |
259 | | - const allModalBodyFormInputs = |
260 | | - modalWrap.querySelectorAll('.modal__body-input'); |
261 | | - mainInputsValidation(allModalBodyFormInputs, { |
262 | | - allowOnlyRussian: true, |
263 | | - singleHyphen: true, |
264 | | - noExtraSpaces: true, |
265 | | - }); |
266 | | - |
267 | | - // принудительное удаление атрибута aria-hidden="true" с модального окна (исключение ошибки с ARIA) |
268 | | - deleteAriaHiddenTrue(modalWrap); |
269 | | - }); |
270 | | - |
271 | 230 | // ** появление/скрытие поля для ввода данных/фильтрационного инпута (по нажатию на logo, на 320px) |
272 | 231 | searchLogoImg.addEventListener('click', () => { |
273 | 232 | document |
|
292 | 251 |
|
293 | 252 | // ! ТЕСТИРОВАНИЕ |
294 | 253 | // addClientsToTable(clientsDataArrWithIds); // отрисовка данных, наполнение таблицы клиентов |
295 | | - console.log(clientsDataArrWithIds); // ? ВЫВОД МАССИВА ОБЪЕКТОВ |
| 254 | + // console.log(clientsDataArrWithIds); // ? ВЫВОД МАССИВА ОБЪЕКТОВ |
296 | 255 | } catch (error) { |
297 | 256 | console.error('Не удалось загрузить список клиентов..', error); |
298 | 257 | alert('Ошибка при загрузке данных с сервера!?'); |
|
309 | 268 |
|
310 | 269 | getClientsServerListData(); // получение данных о клиентах (с сервера) |
311 | 270 |
|
312 | | - // TODO: |
313 | | - // ** наполнение таблицы данных о клиентах (согласно откорректированного исходного, далее формирующегося массива) |
| 271 | + // ** наполнение таблицы данных о клиентах (согласно откорректированного/формирующегося массива) |
314 | 272 | let updateClientsDataArr = []; |
315 | 273 |
|
| 274 | + function addClientsToTable(clientsServerData = []) { |
| 275 | + // ?? |
| 276 | + // const selectedBodyRows = getSelectedBodyRows(); // сохранение выделенных body-строк (если такие есть) |
| 277 | + |
| 278 | + outTableBody.innerHTML = ''; // предварительная очистка таблицы |
| 279 | + updateClientsDataArr = correctInitArr(clientsServerData); |
| 280 | + // ! ТЕСТИРОВАНИЕ |
| 281 | + // console.log(updateClientsDataArr); // ? ВЫВОД МАССИВА ОБЪЕКТОВ |
| 282 | + |
| 283 | + if (updateClientsDataArr.length === 0) { |
| 284 | + // TODO: |
| 285 | + const emptyTableRow = createEmptyTableMessageRow(); // если массив клиентов/таблица данных пуста, вывод сообщения |
| 286 | + outTableBody.append(emptyTableRow); |
| 287 | + } else { |
| 288 | + for (const [index, client] of updateClientsDataArr.entries()) { |
| 289 | + // TODO: |
| 290 | + const clientTableTrRow = createClientTableTrRow(index, client); |
| 291 | + outTableBody.append(clientTableTrRow); |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + // ?? |
| 296 | + // addClickListenersToBodyRows(); // добавление прослушки для всех строк (кроме заглавной), при компоновке, после пере-компоновки (новой отрисовки), для возможности выделения по клику |
| 297 | + // restoreSelectedBodyRows(selectedBodyRows); // восстановление выделенных body-строк (если такие были) |
| 298 | + } |
| 299 | + |
| 300 | + // ** корректировка исходного/серверного массива клиентов (обработка ID, добавление свойства fullName и временных свойств) |
| 301 | + function correctInitArr(clientsServerData = []) { |
| 302 | + const newClientsDataArr = structuredClone(clientsServerData); // клонирование входящего массива (серверного) |
| 303 | + |
| 304 | + for (const client of newClientsDataArr) { |
| 305 | + client.shortId = client.id.slice(-6); // сокращение серверного ID (до 6 цифр) |
| 306 | + client.fullName = `${client.surname} ${client.name} ${client.patronymic}`; // получение "общего" fullName |
| 307 | + |
| 308 | + // преобразование серверных "createdAt" и "updatedAt" в отдельные поля, как даты и времени |
| 309 | + if (client.createdAt) { |
| 310 | + const createdAtDate = conversionStringDate(client.createdAt); |
| 311 | + client.createdAtDate = createdAtDate.toLocaleDateString('ru-RU'); // дата создания |
| 312 | + client.createdAtTime = createdAtDate.toLocaleTimeString('ru-RU', { |
| 313 | + hour: '2-digit', |
| 314 | + minute: '2-digit', |
| 315 | + }); // время создания |
| 316 | + } |
| 317 | + |
| 318 | + if (client.updatedAt) { |
| 319 | + const updatedAtDate = conversionStringDate(client.updatedAt); |
| 320 | + client.updatedAtDate = updatedAtDate.toLocaleDateString('ru-RU'); // дата изменений |
| 321 | + client.updatedAtTime = updatedAtDate.toLocaleTimeString('ru-RU', { |
| 322 | + hour: '2-digit', |
| 323 | + minute: '2-digit', |
| 324 | + }); // время изменений |
| 325 | + } |
| 326 | + } |
| 327 | + |
| 328 | + return newClientsDataArr; // передача откорректированного/дополненного массива |
| 329 | + } |
| 330 | + |
| 331 | + // ** преобразование строковой даты в объект Date |
| 332 | + function conversionStringDate(dateString) { |
| 333 | + return new Date(dateString); // возврат "полноценного" объекта Date |
| 334 | + } |
| 335 | + |
| 336 | + // ** изменение направления стрелки/svg-icon, согласно прожатия по заглавной ячейке (при сортировке данных) |
| 337 | + const allHeaderRowCells = document.querySelectorAll( |
| 338 | + '.crm__output-table-head-cell' |
| 339 | + ); |
| 340 | + |
| 341 | + function changeIconDirection(event) { |
| 342 | + const headerRowCell = event.currentTarget; // фиксация всей/целиком "th" заглавной ячейки |
| 343 | + const cellIcon = headerRowCell.querySelector('.head-cell__icon'); // определение иконки внутри ячейки |
| 344 | + const cellSort = headerRowCell.querySelector('.head-cell__sort'); // определение доп. текста, типа "А-Я" |
| 345 | + |
| 346 | + // проверка/подтверждение наличия иконки (переключение) |
| 347 | + if (cellIcon) { |
| 348 | + cellIcon.classList.toggle('head-cell__icon-up'); |
| 349 | + cellIcon.classList.toggle('head-cell__icon-down'); |
| 350 | + } |
| 351 | + |
| 352 | + // проверка/подтверждение наличия доп. текста (замена) |
| 353 | + if (cellSort) { |
| 354 | + cellSort.textContent = cellSort.textContent === 'А-Я' ? 'Я-А' : 'А-Я'; |
| 355 | + } |
| 356 | + } |
| 357 | + |
| 358 | + // организация прослушек "для каждой" заглавной ячейки |
| 359 | + allHeaderRowCells.forEach((cell) => { |
| 360 | + cell.addEventListener('click', (event) => changeIconDirection(event)); // передача события |
| 361 | + |
| 362 | + // отработка сортировки/сброса сортировки через TAB/Enter (изменение направления стелок) |
| 363 | + cell.addEventListener('keydown', (event) => { |
| 364 | + if (event.key === 'Enter') { |
| 365 | + event.preventDefault(); |
| 366 | + changeIconDirection(event); // передача события |
| 367 | + } |
| 368 | + }); |
| 369 | + }); |
| 370 | + |
316 | 371 | // ** организация "общей/универсальной" логики для валидации полей ввода/инпутов (согласно передаваемых параметров) |
317 | 372 | function mainInputsValidation(inputs, options) { |
318 | 373 | inputs.forEach((input) => |
|
932 | 987 | noExtraSpaces: true, |
933 | 988 | }); |
934 | 989 |
|
935 | | - // ** изменение направления стрелки/svg-icon, согласно прожатия по заглавной ячейке (при сортировке данных) |
936 | | - const allHeaderRowCells = document.querySelectorAll( |
937 | | - '.crm__output-table-head-cell' |
938 | | - ); |
| 990 | + // ** организация кнопки для добавления "нового" клиента (последующее открытие модального окна, ряд сопутствующих действий) |
| 991 | + const addBtnWrap = document.createElement('div'); |
| 992 | + const addBtn = document.createElement('button'); |
| 993 | + const addBtnIcon = document.createElement('i'); |
939 | 994 |
|
940 | | - function changeIconDirection(event) { |
941 | | - const headerRowCell = event.currentTarget; // фиксация всей/целиком "th" заглавной ячейки |
942 | | - const cellIcon = headerRowCell.querySelector('.head-cell__icon'); // определение иконки внутри ячейки |
943 | | - const cellSort = headerRowCell.querySelector('.head-cell__sort'); // определение доп. текста, типа "А-Я" |
| 995 | + addBtnWrap.classList.add('crm__add-btn-wrap'); |
| 996 | + addBtn.classList.add('crm__add-btn'); |
| 997 | + addBtnIcon.classList.add('crm__add-btn-icon', 'bi', 'bi-person-plus-fill'); |
944 | 998 |
|
945 | | - // проверка/подтверждение наличия иконки (переключение) |
946 | | - if (cellIcon) { |
947 | | - cellIcon.classList.toggle('head-cell__icon-up'); |
948 | | - cellIcon.classList.toggle('head-cell__icon-down'); |
949 | | - } |
| 999 | + addBtn.setAttribute('id', 'add-btn'); |
| 1000 | + addBtn.setAttribute('type', 'button'); |
950 | 1001 |
|
951 | | - // проверка/подтверждение наличия доп. текста (замена) |
952 | | - if (cellSort) { |
953 | | - cellSort.textContent = cellSort.textContent === 'А-Я' ? 'Я-А' : 'А-Я'; |
954 | | - } |
955 | | - } |
| 1002 | + addBtn.textContent = 'Добавить клиента'; |
956 | 1003 |
|
957 | | - // организация прослушек "для каждой" заглавной ячейки |
958 | | - allHeaderRowCells.forEach((cell) => { |
959 | | - cell.addEventListener('click', (event) => changeIconDirection(event)); // передача события |
| 1004 | + addBtn.append(addBtnIcon); |
| 1005 | + addBtnWrap.append(addBtn); |
| 1006 | + crmAddContainer.append(addBtnWrap); |
960 | 1007 |
|
961 | | - // отработка сортировки/сброса сортировки через TAB/Enter (изменение направления стелок) |
962 | | - cell.addEventListener('keydown', (event) => { |
963 | | - if (event.key === 'Enter') { |
964 | | - event.preventDefault(); |
965 | | - changeIconDirection(event); // передача события |
966 | | - } |
| 1008 | + // обработка клика по кнопке, создание/отображение модального окна |
| 1009 | + addBtn.addEventListener('click', () => { |
| 1010 | + const modalWrap = createModalWindowByType('add'); // создание по типу "add" |
| 1011 | + |
| 1012 | + crmAddContainer.append(modalWrap); // добавление в DOM |
| 1013 | + |
| 1014 | + // инициализация через Bootstrap API |
| 1015 | + const bootstrapModal = new bootstrap.Modal(modalWrap); |
| 1016 | + bootstrapModal.show(); // отображение |
| 1017 | + |
| 1018 | + // добавление валидации для вводимых данных/в модальном окне (для "основных" инпутов, ФИО) |
| 1019 | + const allModalBodyFormInputs = |
| 1020 | + modalWrap.querySelectorAll('.modal__body-input'); |
| 1021 | + mainInputsValidation(allModalBodyFormInputs, { |
| 1022 | + allowOnlyRussian: true, |
| 1023 | + singleHyphen: true, |
| 1024 | + noExtraSpaces: true, |
967 | 1025 | }); |
| 1026 | + |
| 1027 | + // принудительное удаление атрибута aria-hidden="true" с модального окна (исключение ошибки с ARIA) |
| 1028 | + deleteAriaHiddenTrue(modalWrap); |
968 | 1029 | }); |
969 | 1030 |
|
970 | 1031 | // ** создание "универсального" модального окна, для добавления или изменения данных клиента (согласно передаваемого типа) |
|
0 commit comments