feat: Implement email task import functionality

- Added `import_tasks_from_email` method in `Cron` class to handle task imports from email.
- Created `MailToTaskImporter` class for processing emails and creating tasks based on their content.
- Updated configuration to include IMAP settings for email import.
- Enhanced `Tasks` class with a method to change the client associated with a task.
- Modified task details view to include a dropdown for selecting clients.
- Improved task timer functionality with separate start and stop functions.
- Added logic to handle task work duration validation.
- Updated JavaScript to manage task timers and client changes more effectively.
This commit is contained in:
2026-02-07 00:53:48 +01:00
parent 47ffc19a23
commit ba84cdab3d
10 changed files with 983 additions and 90 deletions

View File

@@ -241,7 +241,78 @@
return $( '.task_popup .task_details' ).hasClass( 'open_works_time' );
}
function task_popup( task_id, open_works_time = false ) {
function start_task_timer( task_id ) {
if ( !task_id )
return;
if ( task_refresh ) {
clearInterval( task_refresh );
}
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/task_start/',
data: {
task_id: task_id
},
beforeSend: function() {},
success: function( response ) {
data = jQuery.parseJSON( response );
if ( data.status == 'success' )
{
$( 'li.task .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( 'li.task .task_start' ).removeClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_start' ).addClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_end' ).removeClass( 'hidden' ).addClass( 'animate' );
$( '.task_popup .task_end' ).removeClass( 'hidden' ).addClass( 'animate' );
$( '.task_popup .task_start' ).addClass( 'hidden' );
}
}
});
}
function stop_task_timer( task_id ) {
if ( !task_id )
return;
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/task_end/',
data: {
task_id: task_id
},
beforeSend: function() {},
success: function( response ) {
data = jQuery.parseJSON( response );
if ( data.status == 'success' )
{
$( 'li[task_id="' + task_id + '"] .task_start' ).removeClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( '.task_popup .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( '.task_popup .task_start' ).removeClass( 'hidden' );
}
}
});
}
function close_task_popup() {
var popup = $( '.task_popup' );
var details = popup.find( '.task_details' );
var task_id = details.attr( 'task_id' );
var is_timer_running = details.find( '.task_end' ).length && !details.find( '.task_end' ).hasClass( 'hidden' );
popup.empty().hide();
if ( task_id && is_timer_running )
stop_task_timer( task_id );
}
function task_popup( task_id, open_works_time = false, auto_start_timer = true ) {
var current_popup_task_id = $( '.task_popup .task_details' ).attr( 'task_id' );
var popup_already_open = $( '.task_popup' ).is( ':visible' ) && $( '.task_popup .task_details' ).length;
var should_auto_start_timer = auto_start_timer && !( popup_already_open && String( current_popup_task_id ) === String( task_id ) );
$.ajax({
url: '/tasks/task_popup/',
@@ -277,6 +348,9 @@
if ( open_works_time )
$( '.task_popup .task_details' ).addClass( 'open_works_time' );
if ( should_auto_start_timer )
start_task_timer( task_id );
}
});
}
@@ -359,7 +433,7 @@
$( 'body' ).on( 'click', '.task_popup .close', function(e) {
e.preventDefault();
$( '.task_popup' ).empty().hide();
close_task_popup();
return false;
});
@@ -405,7 +479,7 @@
return this.value;
}).get();
// close popup
$( '.task_popup' ).empty().hide();
close_task_popup();
reload_tasks( projects, users );
}
}
@@ -437,7 +511,37 @@
return this.value;
}).get();
// close popup
$( '.task_popup' ).empty().hide();
close_task_popup();
reload_tasks( projects, users );
}
}
});
});
// change task client
$( 'body' ).on( 'change', 'select[name="task_client"]', function() {
var task_id = $( this ).attr( 'task_id' );
var client_id = $( this ).val();
$.ajax({
url: '/tasks/task_change_client/',
type: 'POST',
data: {
task_id: task_id,
client_id: client_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();
reload_tasks( projects, users );
}
}
@@ -468,7 +572,7 @@
return this.value;
}).get();
// close popup
$( '.task_popup' ).empty().hide();
close_task_popup();
reload_tasks( projects, users );
}
}
@@ -616,57 +720,13 @@
$( 'body' ).on( 'click', '.task_start', function(e) {
e.preventDefault();
var task_id = $( this ).attr( 'task_id' );
if ( task_refresh ) {
clearInterval( task_refresh );
}
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/task_start/',
data: {
task_id: task_id
},
beforeSend: function() {},
success: function( response ) {
data = jQuery.parseJSON( response );
if ( data.status == 'success' )
{
$( 'li.task .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( 'li.task .task_start' ).removeClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_start' ).addClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_end' ).removeClass( 'hidden' ).addClass( 'animate' );
$( '.task_popup .task_end' ).removeClass( 'hidden' ).addClass( 'animate' );
$( '.task_popup .task_start' ).addClass( 'hidden' );
}
}
});
start_task_timer( task_id );
});
$( 'body' ).on( 'click', '.task_end', function(e) {
e.preventDefault();
var task_id = $( this ).attr( 'task_id' );
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/task_end/',
data: {
task_id: task_id
},
beforeSend: function() {},
success: function( response ) {
data = jQuery.parseJSON( response );
if ( data.status == 'success' )
{
$( 'li[task_id="' + task_id + '"] .task_start' ).removeClass( 'hidden' );
$( 'li[task_id="' + task_id + '"] .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( '.task_popup .task_end' ).addClass( 'hidden' ).removeClass( 'animate' );
$( '.task_popup .task_start' ).removeClass( 'hidden' );
}
}
});
stop_task_timer( task_id );
});
$( 'body' ).on( 'click', '.task_popup .task_end', function(e) {
@@ -1097,7 +1157,7 @@
return this.value;
}).get();
reload_tasks( checkedVals );
$( '.task_popup' ).empty().hide();
close_task_popup();
}
}
});