Refactor task management UI and styles

- Updated button styles to have reduced padding and removed border-radius for a flatter design.
- Adjusted the layout of the tasks container to display 5 columns instead of 7.
- Introduced a new main view layout for tasks with separate left and right columns.
- Removed the suspended tasks tab from the in-progress tasks section.
- Enhanced the task filtering functionality with new buttons for saving, updating, and setting default filters.
- Added project selection dropdown in the task popup for better task management.
- Improved JavaScript handling for task filtering and checkbox interactions.
This commit is contained in:
2025-11-26 00:44:38 +01:00
parent c03f4eded2
commit b4e86d506e
9 changed files with 256 additions and 121 deletions

View File

@@ -165,11 +165,6 @@ class Projects
'user_tmp' => 'Grzegorz Pyziak',
'user' => $user
] ),
'inprogress_tasks_suspended' => \Tpl::view( 'projects/inprogress-tasks', [
'inprogress_tasks' => \factory\Projects::get_inprogress_tasks( 8, \S::get( 'project_id' ) ),
'user_tmp' => 'Projekty',
'user' => $user
] ),
'inprogress_tasks_fees' => \Tpl::view( 'projects/inprogress-tasks', [
'inprogress_tasks' => \factory\Projects::get_inprogress_tasks( 10, \S::get( 'project_id' ) ),
'user_tmp' => 'Opłaty',

View File

@@ -46,12 +46,16 @@ class Tasks
if ( $projects )
\S::set_session( 'selected_projects', $projects );
else
\S::del_session( 'selected_projects' );
\S::set_session( 'selected_projects', null );
if ( $users )
\S::set_session( 'selected_users', $users );
else
\S::del_session( 'selected_users' );
\S::set_session( 'selected_users', null );
$show_tasks_closed = \S::get_session( 'tasks_closed' ) ? \S::get_session( 'tasks_closed' ) : 'hide';
$show_tasks_bulk = \S::get_session( 'tasks_bulk' );
$show_tasks_to_review = \S::get_session( 'tasks_to_review' );
echo json_encode( [
'tasks_new' => \Tpl::view( 'tasks/main_view_by_ajax', [
@@ -78,24 +82,12 @@ class Tasks
'statuses' => \factory\Tasks::get_statuses(),
'open_task_id' => $open_task_id
] ),
'tasks_suspended' => \Tpl::view( 'tasks/main_view_by_ajax', [
'tasks' => \factory\Tasks::get_tasks( 4, $user['id'], $projects, $users ),
'user' => $user,
'statuses' => \factory\Tasks::get_statuses(),
'open_task_id' => $open_task_id
] ),
'tasks_to_do' => \Tpl::view( 'tasks/main_view_by_ajax', [
'tasks' => \factory\Tasks::get_tasks( 5, $user['id'], $projects, $users ),
'user' => $user,
'statuses' => \factory\Tasks::get_statuses(),
'open_task_id' => $open_task_id
] ),
'tasks_fvat' => \Tpl::view( 'tasks/main_view_by_ajax', [
'tasks' => \factory\Tasks::get_tasks( 6, $user['id'], $projects, $users ),
'user' => $user,
'statuses' => \factory\Tasks::get_statuses(),
'open_task_id' => $open_task_id
] ),
'tasks_gantt' => \factory\Tasks::get_tasks_gantt( $user['id'], $projects, $users ),
] );
exit;
@@ -114,7 +106,7 @@ class Tasks
if ( \S::get( 'tasks_closed' ) == 'show' )
{
\S::del_session( 'tasks_closed' );
\S::set_session( 'tasks_closed', 'show' );
header( 'Location: /tasks/main_view/' );
exit;
}
@@ -128,7 +120,7 @@ class Tasks
if ( \S::get( 'tasks_bulk' ) == 'show' )
{
\S::del_session( 'tasks_bulk' );
\S::set_session( 'tasks_bulk', 'show' );
header( 'Location: /tasks/main_view/' );
exit;
}
@@ -143,23 +135,35 @@ class Tasks
if ( \S::get( 'tasks_to_review' ) == 'show' )
{
\S::del_session( 'tasks_to_review' );
\S::set_session( 'tasks_to_review', 'show' );
header( 'Location: /tasks/main_view/' );
exit;
}
$show_tasks_closed = \S::get_session( 'tasks_closed' );
$show_tasks_closed = \S::get_session( 'tasks_closed' ) ? \S::get_session( 'tasks_closed' ) : 'hide';
$show_tasks_bulk = \S::get_session( 'tasks_bulk' );
$show_tasks_to_review = \S::get_session( 'tasks_to_review' );
// get default filtr
if ( !\S::get_session( 'fresh_load' ) ) {
$default_filtr = \factory\Tasks::get_default_filtr( $user['id'] );
if ( $default_filtr )
{
if ( !\S::get_session( 'selected_projects' ) and $default_filtr['projects'] )
\S::set_session( 'selected_projects', explode( ',', $default_filtr['projects'] ) );
if ( !\S::get_session( 'selected_users' ) and $default_filtr['users'] )
\S::set_session( 'selected_users', explode( ',', $default_filtr['users'] ) );
}
\S::set_session( 'fresh_load', true );
}
return \Tpl::view( 'tasks/main_view', [
'tasks_new' => \factory\Tasks::get_tasks( 0, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ),
'tasks_to_review' => $show_tasks_to_review == 'hide' ? null : \factory\Tasks::get_tasks( 1, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ),
'tasks_closed' => $show_tasks_closed == 'hide' ? null : \factory\Tasks::get_tasks( 2, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ), // zakończone
'tasks_bulk' => $show_tasks_bulk == 'hide' ? null : \factory\Tasks::get_tasks( 3, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ), // opłaty
'tasks_suspended' => \factory\Tasks::get_tasks( 4, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ), // projektowe
'tasks_to_do' => \factory\Tasks::get_tasks( 5, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ), // do zrobienia
'tasks_fvat' => \factory\Tasks::get_tasks( 6, $user['id'], \S::get_session( 'selected_projects' ), \S::get_session( 'selected_users' ) ), // faktury
'user' => $user,
'statuses' => \factory\Tasks::get_statuses(),
'open_task_id' => \factory\Tasks::get_open_task_id( $user['id'] ),
@@ -295,11 +299,24 @@ class Tasks
\S::send_email(
'biuro@project-pro.pl',
'crmPRO - zmieniono status zadania',
'<p>Witaj<br/>zmieniono status zadania <b>' . $task['name'] . ' - ' . \factory\Crm::get_client_name( (int)$task['client_id'] ) . '</b> na <b>' . $statuses[ $task['status'] ] . '</b>.</p>'
'<p>Witaj<br/>zmieniono status zadania <b>' . $task['name'] . ' - ' . \factory\Crm::get_client_name( (int)$task['client_id'] ) . '</b> na <b>' . $statuses[ $task['status'] ] . '</b>.</p>' .
'<p>' . $task['text'] . '</p>' .
'<p>Pozdrawiamy<br/>Zespół crmPRO.pl</p>'
);
}
}
static public function task_change_project() {
global $mdb;
if ( $mdb -> update( 'tasks', [ 'project_id' => \S::get( 'project_id' ) ], [ 'id' => \S::get( 'task_id' ) ] ) ) {
echo json_encode( [ 'status' => 'success' ] );
} else {
echo json_encode( [ 'status' => 'error' ] );
}
exit;
}
static public function task_change_priority() {
global $mdb;
@@ -420,7 +437,8 @@ class Tasks
'task' => \factory\Tasks::task_details( \S::get( 'task_id' ), $user['id'] ),
'task_works' => \factory\Tasks::task_works( \S::get( 'task_id' ) ),
'user' => $user,
'statuses' => \factory\Tasks::get_statuses()
'statuses' => \factory\Tasks::get_statuses(),
'projects' => \factory\Projects::user_projects( $user['id'] )
] );
exit;
}
@@ -498,4 +516,21 @@ class Tasks
echo json_encode( [ 'status' => 'error' ] );
exit;
}
static public function filtr_set_default() {
global $user;
if ( !$user ) {
header( 'Location: /logowanie' );
exit;
}
$response = [ 'status' => 'error', 'msg' => 'Podczas ustawiania domyślnego filtru wystąpił błąd. Proszę spróbować ponownie.' ];
if ( \factory\Tasks::filtr_set_default( $user['id'], \S::get( 'filtr_id' ) ) )
$response = [ 'status' => 'success', 'msg' => 'Filtr został ustawiony jako domyślny.' ];
echo json_encode( $response );
exit;
}
}

View File

@@ -3,7 +3,7 @@ namespace factory;
class Tasks
{
public static $statuses = [ 0 => 'nowe', 3 => 'do rozliczenia', 5 => 'do zrobienia', 4 => 'zaplanowane', 1 => 'do sprawdzenia', 6 => 'faktury', 2 => 'zamknięte' ];
public static $statuses = [ 0 => 'nowe', 3 => 'do rozliczenia', 5 => 'do zrobienia', 1 => 'do sprawdzenia', 2 => 'zamknięte' ];
public static $priorities = [ 0 => 'niski', 1 => 'normalny', 2 => 'wysoki', 3 => 'pilny' ];
@@ -167,7 +167,7 @@ class Tasks
static public function get_filtrs( $user_id ) {
global $mdb;
return $mdb -> select( 'tasks_filtrs', '*', [ 'user_id' => $user_id ] );
return $mdb -> select( 'tasks_filtrs', '*', [ 'user_id' => $user_id, 'ORDER' => [ 'name' => 'ASC' ] ] );
}
static public function filtr_update( $filtr_id, $projects, $users ) {
@@ -557,5 +557,18 @@ class Tasks
return $children;
}
static public function filtr_set_default( $user_id, $filtr_id ) {
global $mdb;
$mdb -> update( 'tasks_filtrs', [ 'is_default' => 0 ], [ 'user_id' => $user_id ] );
return $mdb -> update( 'tasks_filtrs', [ 'is_default' => 1 ], [ 'AND' => [ 'id' => $filtr_id, 'user_id' => $user_id ] ] );
}
static public function get_default_filtr( $user_id ) {
global $mdb;
return $mdb -> get( 'tasks_filtrs', '*', [ 'AND' => [ 'user_id' => $user_id, 'is_default' => 1 ] ] );
}
}
?>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -97,11 +97,11 @@ body {
}
.btn {
padding: 12px 25px;
padding: 9px 25px;
transition: all 0.3s ease;
color: #FFF;
border: 0;
border-radius: 6px;
border-radius: 0;
cursor: pointer;
display: inline-flex;
text-decoration: none;
@@ -164,7 +164,7 @@ input[type="checkbox"] {
.form-control {
border: 1px solid #cdcdcd;
border-radius: 6px;
border-radius: 0;
height: 35px;
width: 100%;
padding: 5px;
@@ -316,7 +316,7 @@ body>.top {
.tasks_container {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-template-columns: repeat(5, 1fr);
gap: 20px;
.column {
@@ -577,7 +577,6 @@ body>.top {
display: inline-flex;
padding: 7px 15px;
color: #FFF;
border-radius: 6px;
text-decoration: none;
align-items: center;
justify-content: center;
@@ -1148,7 +1147,7 @@ body>.top {
transform: translate(-50%, -50%);
background: #FFF;
padding: 25px;
border-radius: 6px;
border-radius: 0;
max-width: 1140px;
width: 100%;
@@ -1502,4 +1501,35 @@ body>.top {
background: $cGreen;
border: 0;
color: #FFF;
}
.tasks_main_view {
display: flex;
gap: 20px;
._left_column {
padding: 25px;
background: #fcfcfc;
border-right: 1px solid #e8e8e8;
width: 350px;
select[name="filtr"] {
margin-bottom: 10px;
}
._buttons {
display: flex;
gap: 10px;
justify-content: space-between;
.btn {
flex: 1;
}
}
}
._right_column {
flex: 1;
max-width: calc(100% - 350px);
}
}

View File

@@ -103,9 +103,6 @@
<div id="inprogress-tasks-grzegorz-container" class="tab-pane">
<?= $this -> inprogress_tasks_grzegorz;?>
</div>
<div id="inprogress-tasks-projects-container" class="tab-pane">
<?= $this -> inprogress_tasks_suspended;?>
</div>
<div id="inprogress-tasks-fees" class="tab-pane">
<?= $this -> inprogress_tasks_fees;?>
</div>

View File

@@ -1,22 +1,31 @@
<div class="action_menu">
<a href="/tasks/task_edit/" class="btn btn_add" title="Dodaj zadanie">
<i class="fa fa-plus"></i>Dodaj zadanie
</a>
</div>
<div class="projects_container" style="margin-bottom: 25px;">
<div class="left">
<div class="tasks_main_view">
<div class="_left_column">
<select name="filtr" class="form-control">
<option value="">--- wybierz filtr ---</option>
<? foreach ( $this -> tasks_filtrs as $filtr ):?>
<option value="<?= $filtr[ 'id' ];?>" projects="<?= $filtr['projects'];?>" users="<?= $filtr['users'];?>"><?= $filtr[ 'name' ];?></option>
<? endforeach;?>
</select>
<div class="_buttons">
<a href="#" class="btn btn-success btn_small" id="_new_filtr">zapisz</a>
<a href="#" class="btn btn-primary btn_small" id="_update_filtr">aktualizuj</a>
<!-- set default -->
<a href="#" class="btn btn-dark btn_small" id="_set_default_filtr">domyślny</a>
</div>
<div class="_projects">
<h4>Projekty</h4>
<? foreach ( $this -> projects as $project ):?>
<div class="_project">
<label for="project_<?= $project[ 'id' ];?>">
<input type="checkbox" class="g-checkbox" name="projects" value="<?= $project[ 'id' ];?>" <? if ( is_array( $this -> selected_projects ) and in_array( $project['id'], $this -> selected_projects ) ):?>checked<? endif;?>>
<?= $project[ 'name' ];?> <? if ( $project[ 'client_id' ] ):?>- <small><?= \factory\Crm::get_client_name( (int)$project[ 'client_id' ] );?><? endif;?></small>
<?= $project[ 'name' ];?> (<?= $project['total_tasks'];?>)
</label>
</div>
<? endforeach;?>
</div>
<? if ( $this -> user['id'] == 1 ):?>
<div class="_users">
<h4>Użytkownicy</h4>
<? foreach ( $this -> users as $user ):?>
<div class="_user">
<label for="user_<?= $user[ 'id' ];?>">
@@ -28,20 +37,15 @@
</div>
<? endif;?>
</div>
<div class="right">
<select name="filtr" class="form-control">
<option value="">--- wybierz filtr ---</option>
<? foreach ( $this -> tasks_filtrs as $filtr ):?>
<option value="<?= $filtr[ 'id' ];?>" projects="<?= $filtr['projects'];?>" users="<?= $filtr['users'];?>"><?= $filtr[ 'name' ];?></option>
<? endforeach;?>
</select>
<div class="buttons">
<a href="#" class="btn btn-success btn_small">zapisz nowy filtr</a>
<a href="#" class="btn btn-primary btn_small">aktualizuj filtr</a>
<div class="_right_column">
<div class="action_menu">
<a href="/tasks/task_edit/" class="btn btn_add" title="Dodaj zadanie">
<i class="fa fa-plus"></i>Dodaj zadanie
</a>
</div>
<div class="gantt-target"></div>
</div>
</div>
<div class="gantt-target" style="margin-bottom: 25px;"></div>
<div class="tasks_container">
<div class="column tasks_new">
<h2>Nowe zadania</h2>
@@ -58,39 +62,6 @@
<? endforeach;?>
</ul>
</div>
<? if ( $this -> user['id'] == 1 ):?>
<div class="column tasks_fvat">
<h2>Faktury</h2>
<ul>
<? foreach ( $this -> tasks_fvat as $task ):?>
<?
echo \Tpl::view( 'tasks/task_single', [
'task' => $task,
'user' => $this -> user,
'statuses' => $this -> statuses,
'task_open' => $this -> open_task_id == $task['id'] ? true : false
] );
?>
<? endforeach;?>
</ul>
</div>
<? endif;?>
<!-- tasks_suspended -->
<div class="column tasks_suspended">
<h2>Zadania zaplanowane</h2>
<ul>
<? foreach ( $this -> tasks_suspended as $task ):?>
<?
echo \Tpl::view( 'tasks/task_single', [
'task' => $task,
'user' => $this -> user,
'statuses' => $this -> statuses,
'task_open' => $this -> open_task_id == $task['id'] ? true : false
] );
?>
<? endforeach;?>
</ul>
</div>
<!-- tasks_to_do -->
<div class="column tasks_to_do">
<h2>Zadania do zrobienia</h2>
@@ -157,6 +128,8 @@
</div>
<script type="text/javascript">
let isProgrammaticUpdate = false;
var tasks = [
<?
foreach ( $this -> tasks_gantt as $task ) {
@@ -179,22 +152,20 @@
echo ',';
}
?>
/*{
start: '2018-10-01',
end: '2018-10-08',
name: 'Redesign website',
id: "Task 0",
progress: 91
},
{
start: '2018-10-03',
end: '2018-10-06',
name: 'Write new content',
id: "Task 1",
progress: 55,
dependencies: 'Task 0'
},*/
]
];
if ( tasks.length <= 0 ) {
tasks = [
{
start: new Date().toISOString().split('T')[0],
end: new Date().toISOString().split('T')[0],
name: "Brak zadań do wyświetlenia",
id: "0",
progress: 100,
custom_class: "gantt-task-empty",
dependencies: ""
}
];
}
var gantt_chart = new Gantt(".gantt-target", tasks, {
on_click: function (task) {
console.log(task);
@@ -260,7 +231,8 @@
$( '.tasks_container .tasks_suspended ul' ).empty().append( data.tasks_suspended );
$( '.tasks_container .tasks_to_do ul' ).empty().append( data.tasks_to_do );
$( '.tasks_container .tasks_fvat ul' ).empty().append( data.tasks_fvat );
gantt_chart.refresh( data.tasks_gantt );
if ( data.tasks_gantt )
gantt_chart.refresh( data.tasks_gantt );
}
});
}
@@ -305,8 +277,30 @@
});
}
$( 'body' ).on( 'click', '.projects_container .right .btn-primary', function(e) {
var filtr_id = $( '.projects_container .right select[name="filtr"]' ).val();
// set default filtr
$( 'body' ).on( 'click', '.tasks_main_view #_set_default_filtr', function(e) {
var filtr_id = $( '.tasks_main_view select[name="filtr"]' ).val();
$.ajax({
url: '/tasks/filtr_set_default/',
type: 'POST',
data: {
filtr_id: filtr_id
},
success: function( response ) {
var data = jQuery.parseJSON( response );
if ( data.status == 'success' ) {
show_default_popup( 'Filtr został ustawiony jako domyślny' );
// autoclose popup after 2 seconds
setTimeout( function() {
$( '.default_popup .close' ).click();
}, 2000 );
}
}
});
});
$( 'body' ).on( 'click', '.tasks_main_view #_update_filtr', function(e) {
var filtr_id = $( '.tasks_main_view select[name="filtr"]' ).val();
$.ajax({
url: '/tasks/filtr_update/',
type: 'POST',
@@ -325,7 +319,7 @@
});
});
$( 'body' ).on( 'click', '.projects_container .right .btn-success', function(e) {
$( 'body' ).on( 'click', '.tasks_main_view #_new_filtr', function(e) {
$.ajax({
url: '/tasks/filtr_save_form/',
type: 'POST',
@@ -343,6 +337,10 @@
if ( data.status == 'success' )
{
show_default_popup( data.popup_content );
$( '#filtr_save_form input[type="checkbox"]' ).iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
});
}
}
});
@@ -378,6 +376,38 @@
}
});
// change task project
$( 'body' ).on( 'change', 'select[name="task_project"]', function() {
var task_id = $( this ).attr( 'task_id' );
var project_id = $( this ).val();
$.ajax({
url: '/tasks/task_change_project/',
type: 'POST',
data: {
task_id: task_id,
project_id: project_id
},
success: function( response )
{
var data = jQuery.parseJSON( response );
if ( data.status == 'success' )
{
var projects = jQuery( 'input[name="projects"]:checked' ).map(function() {
return this.value;
}).get();
projects.join( "," );
var users = jQuery( 'input[name="users"]:checked' ).map(function() {
return this.value;
}).get();
// close popup
$( '.task_popup' ).empty().hide();
reload_tasks( projects, users );
}
}
});
});
// change task priority
$( 'body' ).on( 'change', 'select[name="task_priority"]', function() {
var task_id = $( this ).attr( 'task_id' );
@@ -800,6 +830,8 @@
$( 'body' ).on( 'change', 'select[name="filtr"]', function(e) {
e.preventDefault();
isProgrammaticUpdate = true;
var projects = $( this ).find( 'option:selected' ).attr( 'projects' );
var users = $( this ).find( 'option:selected' ).attr( 'users' );
// parsowanie wejść (string "1,2,3" -> ['1','2','3'])
@@ -813,25 +845,34 @@
.map(s => s.trim())
.filter(Boolean);
// UPEWNIJ SIĘ, jak brzmi nazwa pól w HTML: projects czy projects[]
const $projectInputs = $('.projects_container input[name="projects[]"].g-checkbox, .projects_container input[name="projects"].g-checkbox');
const $userInputs = $('.projects_container input[name="users[]"].g-checkbox, .projects_container input[name="users"].g-checkbox');
// Selektor dla checkboxów w filtrze
const $projectInputs = $('.tasks_main_view input[name="projects"].g-checkbox');
const $userInputs = $('.tasks_main_view input[name="users"].g-checkbox');
// najpierw czyścimy
$projectInputs.prop('checked', false);
$userInputs.prop('checked', false);
// najpierw czyścimy wszystkie
$projectInputs.each(function() {
$(this).iCheck('uncheck');
});
$userInputs.each(function() {
$(this).iCheck('uncheck');
});
// ustawiamy zaznaczenia
$projectInputs.each(function () {
const val = String($(this).val()).trim();
if (projectsArr.includes(val)) $(this).prop('checked', true);
if (projectsArr.includes(val)) {
$(this).iCheck('check');
}
});
$userInputs.each(function () {
const val = String($(this).val()).trim();
if (usersArr.includes(val)) $(this).prop('checked', true);
if (usersArr.includes(val)) {
$(this).iCheck('check');
}
});
isProgrammaticUpdate = false;
// wywołuj z tablicami, nie ze stringami
reload_tasks(projectsArr, usersArr);
@@ -970,7 +1011,20 @@
$( function()
{
$( '.projects_container input.g-checkbox').on( 'change', function (e) {
$( 'input[type="checkbox"]' ).iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
});
$(".tasks_main_view input.g-checkbox").on('ifChanged', function (e) {
$(this).trigger("change", e);
});
$( '.tasks_main_view input.g-checkbox').on( 'change', function (e) {
if (isProgrammaticUpdate) {
return;
}
var projects = jQuery( 'input[name="projects"].g-checkbox:checked' ).map(function() {
return this.value;
}).get();

View File

@@ -85,6 +85,17 @@
</select>
</div>
</div>
<div class="project box">
<h3>Projekt</h3>
<div class="current_project">
<select name="task_project" class="form-control" task_id="<?= $this -> task['id'];?>">
<option value="0">-- brak --</option>
<? foreach ( $this -> projects as $project ):?>
<option value="<?= $project['id'];?>" <? if ( $this -> task['project_id'] == $project['id'] ):?>selected="selected"<? endif;?>><?= $project['name'];?></option>
<? endforeach;?>
</select>
</div>
</div>
<div class="dates box">
<h3>Termin</h3>
<? if ( $this -> task['date_start'] ):?>