From 1722f171bc7ab8e4fd6ae7e83094f73e2bb50a62 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Fri, 6 Feb 2026 23:11:14 +0100 Subject: [PATCH] tasks: improve attachments UX and update refactoring docs --- REFACTORING_PLAN.md | 10 +- layout/style.scss | 123 ++++++++++++++++++- templates/tasks/main_view.php | 209 +++++++++++++++++++++++++++++---- templates/tasks/task_popup.php | 75 +++++++++++- 4 files changed, 387 insertions(+), 30 deletions(-) diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md index 4d9fca4..364aac7 100644 --- a/REFACTORING_PLAN.md +++ b/REFACTORING_PLAN.md @@ -11,7 +11,7 @@ - Obszar Czas pracy: zmigrowany i ustabilizowany ## Etap 1: Tasks / Czas pracy (ZROBIONE) -- Dodano `autoload/Domain/Tasks/class.WorkTimeRepository.php`. +- Dodano `autoload/Domain/Tasks/WorkTimeRepository.php`. - `factory\Tasks::work_time_clients()` deleguje teraz do repozytorium Domain. - Usunięto limit 3 miesięcy: repozytorium zwraca wszystkie istotne miesiące. - Uwzględniono statusy zadań: @@ -23,7 +23,7 @@ - `tests/run.php` ## Etap 2: Standaryzacja kontrolerów (W TOKU) -- [x] Dodano `autoload/Controllers/class.TasksController.php`. +- [x] Dodano `autoload/Controllers/TasksController.php`. - [x] Przeniesiono akcję czasu pracy do nowego kontrolera: `TasksController::workTime()`. - [x] Zostawiono adapter kompatybilności w `autoload/controls/class.Tasks.php`. - [x] Oznaczono starą metodę `controls\Tasks::work_time()` jako deprecated. @@ -63,3 +63,9 @@ - Bez zmian typu big-bang. - Jeden ograniczony obszar funkcjonalny na commit. - Najpierw kompatybilność, adaptery usuwać dopiero po pełnej migracji. +## Ostatnie wdrozenia (2026-02-06) +- Popup zadania: dodano zarzadzanie zalacznikami (dodawanie, edycja nazwy, usuwanie). +- Upload zalacznikow: obsluga wielu plikow w jednym wyslaniu (attachments[] + multiple). +- UX uploadu: dodano loader na przycisku ("Wysylanie..."), blokade wielokliku i odblokowanie po zakonczeniu requestu. +- Poprawiono krytyczny blad JS: dodano brakujaca funkcje is_task_popup_works_time_open(). +- Ujednolicono napisy UI dla popupu i listy zadan (usuniecie "krzaczkow" przez encje HTML tam, gdzie to potrzebne). \ No newline at end of file diff --git a/layout/style.scss b/layout/style.scss index bca5ef4..fc68760 100644 --- a/layout/style.scss +++ b/layout/style.scss @@ -939,6 +939,127 @@ body>.top { } } + .attachments { + margin-top: 10px; + border: 1px solid #e6e9ed; + border-radius: 8px; + padding: 12px; + background: #f9fbfd; + + h3 { + margin-bottom: 10px; + font-size: 16px; + font-weight: 600; + } + + .attachments_upload { + display: flex; + gap: 10px; + align-items: center; + margin-bottom: 10px; + + .attachment_file_input { + flex: 1; + margin-bottom: 0; + border-radius: 6px; + background: #fff; + } + + .attachment-upload-btn { + white-space: nowrap; + border-radius: 6px; + min-width: 150px; + + &.is-loading { + pointer-events: none; + opacity: 0.85; + + i { + margin-right: 6px; + } + } + } + } + + .attachments_list { + margin: 0; + padding: 0; + list-style: none; + display: grid; + gap: 8px; + max-height: 180px; + overflow-y: auto; + + li { + display: flex; + align-items: center; + gap: 8px; + background: #fff; + border: 1px solid #e4e8ee; + border-radius: 6px; + padding: 8px 10px; + + .attachment-link { + color: #1f3d72; + text-decoration: none; + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: calc(100% - 140px); + + &:hover { + text-decoration: underline; + } + } + + small { + color: #6b7280; + margin-right: auto; + } + + .attachment-rename, + .attachment-delete { + display: inline-flex; + align-items: center; + justify-content: center; + height: 28px; + width: 28px; + border-radius: 4px; + text-decoration: none; + transition: all 0.2s ease; + } + + .attachment-rename { + border: 1px solid #d8e2f6; + color: $cBlue; + background: #f4f8ff; + + &:hover { + background: #e7f0ff; + } + } + + .attachment-delete { + border: 1px solid #f1d3d1; + color: $cRed; + background: #fff6f6; + + &:hover { + background: #ffeaea; + } + } + } + + .attachments-empty { + color: #6b7280; + border-style: dashed; + justify-content: center; + font-size: 13px; + } + } + } + .description { padding: 15px; border-radius: 0.25rem; @@ -1579,4 +1700,4 @@ body>.top { .billing-details-wrap { background: #f8fbff; } -} \ No newline at end of file +} diff --git a/templates/tasks/main_view.php b/templates/tasks/main_view.php index 4b2ac18..b4c8d2d 100644 --- a/templates/tasks/main_view.php +++ b/templates/tasks/main_view.php @@ -10,7 +10,7 @@ zapisz aktualizuj - domyślny + domyślny

Projekty

@@ -25,7 +25,7 @@
user['id'] == 1 ):?>
-

Użytkownicy

+

Użytkownicy

users as $user ):?>
-

Zadania do sprawdzenia show_tasks_to_review == 'hide' ):?>

+

Zadania do sprawdzenia show_tasks_to_review == 'hide' ):?>

    tasks_to_review as $task ):?>
-

Do rozliczenia show_tasks_bulk == 'hide' ):?>

+

Do rozliczenia show_tasks_bulk == 'hide' ):?>

    tasks_bulk as $task ):?>
-

Zamknięte zadania show_tasks_closed == 'hide' ):?>

+

Zamknięte zadania show_tasks_closed == 'hide' ):?>

    tasks_closed as $task ):?> Wysyłanie...' ); + input.prop( 'disabled', true ); + + var formData = new FormData(); + formData.append( 'task_id', task_id ); + for ( var i = 0; i < files.length; i++ ) { + formData.append( 'attachments[]', files[i] ); + } + + $.ajax({ + type: 'POST', + cache: false, + url: '/tasks/task_attachment_upload/', + data: formData, + processData: false, + contentType: false, + success: function( response ) { + var data = jQuery.parseJSON( response ); + if ( data.status == 'success' || data.status == 'partial' ) { + if ( data.status == 'partial' && data.msg ) { + $.alert( data.msg ); + } + task_popup( task_id, open_works_time ); + } else { + $.alert( data.msg ? data.msg : 'Nie udało się dodać załączników.' ); + } + }, + complete: function() { + button + .removeClass( 'is-loading disabled' ) + .removeAttr( 'aria-disabled' ) + .html( original_button_html ); + input.prop( 'disabled', false ); + } + }); + }); + + $( 'body' ).on( 'click', '.task_popup .attachment-delete', function(e){ + e.preventDefault(); + + var attachment_id = $( this ).attr( 'attachment_id' ); + var task_id = $( '.task_popup .task_details' ).attr( 'task_id' ); + var open_works_time = is_task_popup_works_time_open(); $.confirm({ - title: 'Potwierdź', - content: 'Na pewno chcesz usunąć wybrany punkt?', + title: 'Potwierdź', + content: 'Na pewno chcesz usunac zalacznik?', type: 'orange', closeIcon: true, closeIconClass: 'fa fa-close', @@ -772,7 +838,106 @@ theme: 'material', buttons: { confirm: { - text: 'Usuń', + text: 'Usuń', + btnClass: 'btn-red', + action: function() { + $.ajax({ + type: 'POST', + cache: false, + url: '/tasks/task_attachment_delete/', + data: { + attachment_id: attachment_id + }, + success: function( response ) { + var data = jQuery.parseJSON( response ); + if ( data.status == 'success' ) { + task_popup( task_id, open_works_time ); + } + } + }); + } + }, + cancel: { + text: 'Anuluj', + btnClass: 'btn-default', + action: function() {} + } + } + }); + }); + + $( 'body' ).on( 'click', '.task_popup .attachment-rename', function(e){ + e.preventDefault(); + + var attachment_id = $( this ).attr( 'attachment_id' ); + var current_title = $( this ).attr( 'title_current' ); + var task_id = $( '.task_popup .task_details' ).attr( 'task_id' ); + var open_works_time = is_task_popup_works_time_open(); + + $.confirm({ + title: 'Zmien nazwe zalacznika', + content: '' + + '
    ' + + '
    ' + + '').text(current_title).html() + '\" />' + + '
    ' + + '
    ', + buttons: { + formSubmit: { + text: 'Zapisz', + btnClass: 'btn-blue', + action: function () { + var new_title = this.$content.find( '.attachment-new-title' ).val(); + $.ajax({ + type: 'POST', + cache: false, + url: '/tasks/task_attachment_rename/', + data: { + attachment_id: attachment_id, + title: new_title + }, + success: function( response ) { + var data = jQuery.parseJSON( response ); + if ( data.status == 'success' ) { + task_popup( task_id, open_works_time ); + } + } + }); + } + }, + cancel: { + text: 'Anuluj', + action: function () {} + } + }, + onContentReady: function () { + var jc = this; + this.$content.find( 'form' ).on( 'submit', function (e) { + e.preventDefault(); + jc.$$formSubmit.trigger( 'click' ); + }); + } + }); + }); + + $( 'body' ).on( 'click', '.task_popup .checklist li .point-delete', function(e){ + e.preventDefault(); + var action_id = $( this ).attr( 'action_id' ); + + $.confirm({ + title: 'Potwierdź', + content: 'Na pewno chcesz usunąć wybrany punkt?', + type: 'orange', + closeIcon: true, + closeIconClass: 'fa fa-close', + typeAnimated: true, + animation: 'opacity', + boxWidth: '500px', + useBootstrap: false, + theme: 'material', + buttons: { + confirm: { + text: 'Usuń', btnClass: 'btn-red', action: function(){ $.ajax({ @@ -901,8 +1066,8 @@ var task_id = $( this ).attr( 'task_id' ); $.confirm({ - title: 'Potwierdź', - content: 'Na pewno chcesz usunąć wybrane zadanie?', + title: 'Potwierdź', + content: 'Na pewno chcesz usunąć wybrane zadanie?', type: 'orange', closeIcon: true, closeIconClass: 'fa fa-close', @@ -913,7 +1078,7 @@ theme: 'material', buttons: { confirm: { - text: 'Usuń', + text: 'Usuń', btnClass: 'btn-red', action: function(){ $.ajax({ @@ -1060,8 +1225,8 @@ var task_id = $( this ).attr( 'task_id' ); $.confirm({ - title: 'Potwierdź', - content: 'Na pewno chcesz usunąć wybrane zadanie?', + title: 'Potwierdź', + content: 'Na pewno chcesz usunąć wybrane zadanie?', type: 'orange', closeIcon: true, closeIconClass: 'fa fa-close', @@ -1072,7 +1237,7 @@ theme: 'material', buttons: { confirm: { - text: 'Usuń', + text: 'Usuń', btnClass: 'btn-red', action: function(){ $.ajax({ @@ -1127,4 +1292,4 @@ } }); }) - \ No newline at end of file + diff --git a/templates/tasks/task_popup.php b/templates/tasks/task_popup.php index 56c63df..f68e665 100644 --- a/templates/tasks/task_popup.php +++ b/templates/tasks/task_popup.php @@ -63,6 +63,29 @@
+
+

Załączniki

+ +
    + task_attachments ) and count( $this -> task_attachments ) ):?> + task_attachments as $attachment ):?> +
  • + + + + () + + +
  • + + +
  • Brak załączników.
  • + +
+
@@ -113,16 +136,16 @@
@@ -138,4 +161,46 @@
- \ No newline at end of file + + +