317 lines
12 KiB
JavaScript
317 lines
12 KiB
JavaScript
$(function () {
|
||
var accountModalEl = document.getElementById('account-modal');
|
||
var creditModalEl = document.getElementById('credit-modal');
|
||
|
||
var $form = $('#account-form');
|
||
var $error = $('#account-form-error');
|
||
var $rate = $('#account-rate-field');
|
||
var $goalFields = $('#account-goal-fields');
|
||
var $colorHidden = $('#acc-color-hidden');
|
||
var hasColorOverride = false;
|
||
|
||
if (!accountModalEl) {
|
||
return;
|
||
}
|
||
|
||
var accountModal = new bootstrap.Modal(accountModalEl);
|
||
var creditModal = creditModalEl ? new bootstrap.Modal(creditModalEl) : null;
|
||
var archiveEl = document.getElementById('archive-modal');
|
||
var archiveModal = archiveEl ? new bootstrap.Modal(archiveEl) : null;
|
||
|
||
// Плитка «Кредит» в модалке счёта переключает на модалку кредита (см. design-хендофф) —
|
||
// Bootstrap не любит два открытых .modal одновременно (общий backdrop/scroll-lock), поэтому
|
||
// ждём полного закрытия account-modal (событие hidden.bs.modal) и только потом открываем credit-modal.
|
||
var openCreditAfterHide = false;
|
||
|
||
$(accountModalEl).on('hidden.bs.modal', function () {
|
||
$('#acc-color-picker').hide();
|
||
|
||
if (openCreditAfterHide) {
|
||
openCreditAfterHide = false;
|
||
creditModal.show();
|
||
}
|
||
});
|
||
|
||
function applyPreview(color, icon) {
|
||
$('#acc-color-swatch').css('background', color);
|
||
$('#acc-icon-preview').css('background', 'color-mix(in srgb,' + color + ' 20%, transparent)').css('color', color);
|
||
$('#acc-icon-preview-use').attr('href', '#' + icon);
|
||
}
|
||
|
||
function setType(type, isEdit) {
|
||
var $tile = $('#acc-type-tiles .pm-tile[data-type="' + type + '"]');
|
||
|
||
$('#acc-type-tiles .pm-tile').removeClass('selected');
|
||
$tile.addClass('selected');
|
||
$('#acc-type-hidden').val(type);
|
||
|
||
applyPreview(hasColorOverride ? $colorHidden.val() : $tile.data('color'), $tile.data('icon'));
|
||
|
||
var isSavings = type === 'savings';
|
||
$rate.toggle(isSavings && !isEdit);
|
||
$rate.find('[name=rate_percent]').prop('required', isSavings && !isEdit);
|
||
$goalFields.toggle(isSavings);
|
||
|
||
if (!isEdit) {
|
||
var includeVal = isSavings ? '0' : '1';
|
||
$form.find('[name=include_in_total][value="' + includeVal + '"]').prop('checked', true);
|
||
}
|
||
}
|
||
|
||
function resetForm() {
|
||
$form[0].reset();
|
||
$form.find('[name=id]').val('');
|
||
$error.hide().text('');
|
||
hasColorOverride = false;
|
||
$colorHidden.val('');
|
||
setType('bank', false);
|
||
$form.find('[name=include_in_total][value="1"]').prop('checked', true);
|
||
$('#acc-type-tiles').removeClass('locked');
|
||
$('#acc-type-lock-hint').hide();
|
||
}
|
||
|
||
$(document).on('click', '.js-account-add', function () {
|
||
resetForm();
|
||
$form.data('mode', 'create').attr('action', '/accounts/create');
|
||
$('#account-modal-title').text('Новый счёт');
|
||
accountModal.show();
|
||
});
|
||
|
||
$(document).on('click', '.js-account-archive-open', function () {
|
||
if (archiveModal) {
|
||
archiveModal.show();
|
||
}
|
||
});
|
||
|
||
$(document).on('click', '.js-account-restore', function () {
|
||
var $row = $(this).closest('.archive-row');
|
||
var csrf = $form.find('[name=csrf_token]').val();
|
||
|
||
$.post('/accounts/restore/' + $row.data('id'), {csrf_token: csrf})
|
||
.done(function () { window.location.reload(); })
|
||
.fail(function () { window.alert('Не удалось восстановить счёт'); });
|
||
});
|
||
|
||
// Общая логика заполнения модалки счёта на редактирование — используется и для room-card
|
||
// (обычный счёт), и для goal-card (та же карточка счёта, только с иным набором data-*
|
||
// не нужна — goal-card несёт ровно те же атрибуты, см. index.html), $accountId берётся
|
||
// отдельно, т.к. имя атрибута с id разное (data-id / data-account-id).
|
||
function populateEditForm($card, accountId) {
|
||
resetForm();
|
||
$form.data('mode', 'edit').attr('action', '/accounts/edit/' + accountId);
|
||
$form.find('[name=title]').val($card.data('title'));
|
||
$form.find('[name=summa]').val($card.data('summa'));
|
||
$form.find('[name=currency]').val($card.data('currency'));
|
||
$form.find('[name=description]').val($card.data('description'));
|
||
$form.find('[name=goal_target_summa]').val($card.data('goal-target'));
|
||
$form.find('[name=goal_deadline]').val($card.data('goal-deadline'));
|
||
|
||
var color = $card.data('color');
|
||
if (color) {
|
||
hasColorOverride = true;
|
||
$colorHidden.val(color);
|
||
}
|
||
|
||
var includeVal = $card.data('include') ? '1' : '0';
|
||
$form.find('[name=include_in_total][value="' + includeVal + '"]').prop('checked', true);
|
||
|
||
setType($card.data('type'), true);
|
||
$('#acc-type-tiles').addClass('locked');
|
||
$('#acc-type-lock-hint').show();
|
||
|
||
$('#account-modal-title').text('Редактировать счёт');
|
||
accountModal.show();
|
||
}
|
||
|
||
$(document).on('click', '.js-account-edit', function (e) {
|
||
e.preventDefault();
|
||
|
||
var $card = $(this).closest('.room-card');
|
||
populateEditForm($card, $card.data('id'));
|
||
});
|
||
|
||
$(document).on('click', '.js-account-delete', function (e) {
|
||
e.preventDefault();
|
||
|
||
var $card = $(this).closest('.room-card');
|
||
var title = $card.data('title');
|
||
var csrf = $form.find('[name=csrf_token]').val();
|
||
|
||
if (!window.confirm('Архивировать счёт «' + title + '»?')) {
|
||
return;
|
||
}
|
||
|
||
$.post('/accounts/delete/' + $card.data('id'), {csrf_token: csrf})
|
||
.done(function () { window.location.reload(); })
|
||
.fail(function () { window.alert('Не удалось архивировать счёт'); });
|
||
});
|
||
|
||
// «Редактировать» на карточке копилки — просто .js-account-edit (goal-card несёт класс
|
||
// .room-card и тот же набор data-*, см. index.html), отдельного обработчика не нужно.
|
||
// «Удалить» на карточке копилки с целью — убирает только цель, счёт остаётся (для копилки
|
||
// без цели там же используется обычный .js-account-delete, архивирующий сам счёт).
|
||
$(document).on('click', '.js-goal-delete', function (e) {
|
||
e.preventDefault();
|
||
|
||
var $card = $(this).closest('.goal-card');
|
||
var csrf = $form.find('[name=csrf_token]').val();
|
||
|
||
if (!window.confirm('Удалить цель? Сам счёт останется.')) {
|
||
return;
|
||
}
|
||
|
||
$.post('/goals/delete/' + $card.data('goal-id'), {csrf_token: csrf})
|
||
.done(function () { window.location.reload(); })
|
||
.fail(function () { window.alert('Не удалось удалить цель'); });
|
||
});
|
||
|
||
$(document).on('click', '#acc-type-tiles .pm-tile', function () {
|
||
if ($form.data('mode') === 'edit') {
|
||
return;
|
||
}
|
||
|
||
var type = $(this).data('type');
|
||
|
||
if (type === 'credit') {
|
||
openCreditAfterHide = true;
|
||
accountModal.hide();
|
||
return;
|
||
}
|
||
|
||
setType(type, $form.data('mode') === 'edit');
|
||
});
|
||
|
||
$(document).on('click', '#acc-color-swatch', function (e) {
|
||
e.stopPropagation();
|
||
$('#acc-color-picker').toggle();
|
||
});
|
||
|
||
$(document).on('click', '.acc-color-swatch-opt', function () {
|
||
var color = $(this).data('color');
|
||
|
||
hasColorOverride = true;
|
||
$colorHidden.val(color);
|
||
applyPreview(color, $('#acc-type-tiles .pm-tile.selected').data('icon'));
|
||
$('#acc-color-picker').hide();
|
||
});
|
||
|
||
$(document).on('click', function (e) {
|
||
if (!$(e.target).closest('#acc-color-swatch, #acc-color-picker').length) {
|
||
$('#acc-color-picker').hide();
|
||
}
|
||
});
|
||
|
||
// ---------- модалка «Новый кредит» (вёрстка-заглушка, см. App/design_handoff_personal_finance) ----------
|
||
|
||
$(document).on('click', '#credit-mode-tiles .pm-tile', function () {
|
||
$('#credit-mode-tiles .pm-tile').removeClass('selected');
|
||
$(this).addClass('selected');
|
||
|
||
var isExisting = $(this).data('mode') === 'existing';
|
||
$('#credit-existing-hint').toggle(isExisting);
|
||
$('#credit-existing-fields').css('display', isExisting ? 'grid' : 'none');
|
||
});
|
||
|
||
$(document).on('click', '#credit-type-tiles .pm-tile', function () {
|
||
$('#credit-type-tiles .pm-tile').removeClass('selected');
|
||
$(this).addClass('selected');
|
||
|
||
var isCard = $(this).data('credit-type') === 'card';
|
||
$('#credit-card-fields').toggle(isCard);
|
||
$('#credit-noncard-fields').toggle(!isCard);
|
||
$('#credit-min-hint').html(
|
||
'Минимум нужно: <strong style="color:var(--ink)">' +
|
||
(isCard ? 'название, лимит, ставка' : 'название, сумма, ставка и срок или платёж') +
|
||
'</strong>'
|
||
);
|
||
});
|
||
|
||
$(document).on('submit', '#account-form', function (e) {
|
||
e.preventDefault();
|
||
$error.hide().text('');
|
||
|
||
$.post($form.attr('action'), $form.serialize())
|
||
.done(function () { window.location.reload(); })
|
||
.fail(function (xhr) {
|
||
var errors = xhr.responseJSON && xhr.responseJSON.errors;
|
||
var message = errors ? Object.values(errors).join(' ') : 'Не удалось сохранить счёт';
|
||
$error.text(message).show();
|
||
});
|
||
});
|
||
|
||
$(document).on('click', '.room-menu-btn', function () {
|
||
$(this).closest('.room-menu').toggleClass('open');
|
||
});
|
||
|
||
$(document).on('click', function (e) {
|
||
if (!$(e.target).closest('.room-menu').length) {
|
||
$('.room-menu.open').removeClass('open');
|
||
}
|
||
});
|
||
|
||
// ---------- ручная сортировка счетов (drag & drop) ----------
|
||
// Одна и та же кнопка включает режим (карточки становятся draggable, ⋮-меню временно
|
||
// отключено — см. CSS .reorder-mode .room-menu) и выключает его же, сохраняя итоговый
|
||
// порядок одним запросом на /accounts/reorder.
|
||
|
||
var $accountsGrid = $('.grid-cards').first();
|
||
var reorderMode = false;
|
||
var $dragging = null;
|
||
|
||
function saveAccountOrder() {
|
||
var ids = $accountsGrid.find('.room-card').map(function () { return $(this).data('id'); }).get();
|
||
var csrf = $form.find('[name=csrf_token]').val();
|
||
|
||
$.post('/accounts/reorder', {csrf_token: csrf, ids: ids});
|
||
}
|
||
|
||
$(document).on('click', '.js-account-reorder-toggle', function () {
|
||
reorderMode = !reorderMode;
|
||
|
||
$accountsGrid.toggleClass('reorder-mode', reorderMode);
|
||
$accountsGrid.find('.room-card').attr('draggable', reorderMode);
|
||
$(this).toggleClass('active', reorderMode).attr(
|
||
'title', reorderMode ? 'Готово' : 'Изменить порядок счетов'
|
||
);
|
||
|
||
if (!reorderMode) {
|
||
saveAccountOrder();
|
||
}
|
||
});
|
||
|
||
$(document).on('dragstart', '.room-card', function (e) {
|
||
if (!reorderMode) {
|
||
return;
|
||
}
|
||
|
||
$dragging = $(this).addClass('dragging');
|
||
e.originalEvent.dataTransfer.effectAllowed = 'move';
|
||
e.originalEvent.dataTransfer.setData('text/plain', String($(this).data('id')));
|
||
});
|
||
|
||
$(document).on('dragover', '.room-card', function (e) {
|
||
if (!reorderMode || !$dragging) {
|
||
return;
|
||
}
|
||
|
||
e.preventDefault();
|
||
|
||
var $target = $(this);
|
||
|
||
if ($target.is($dragging)) {
|
||
return;
|
||
}
|
||
|
||
if ($dragging.index() < $target.index()) {
|
||
$target.after($dragging);
|
||
} else {
|
||
$target.before($dragging);
|
||
}
|
||
});
|
||
|
||
$(document).on('dragend', '.room-card', function () {
|
||
$(this).removeClass('dragging');
|
||
$dragging = null;
|
||
});
|
||
});
|