feat(06-task-title-ai): complete OpenAI task title suggestions

Phase 6 complete:

- add AI title generator service using gpt-5-nano

- add task popup button for biuro@project-pro.pl

- add AJAX endpoint returning title suggestions without auto-save
This commit is contained in:
Codex
2026-05-04 23:45:54 +02:00
parent 254cf70d35
commit 3b1ba1b5ed
10 changed files with 692 additions and 12 deletions

View File

@@ -1,3 +1,6 @@
<?
$can_generate_ai_title = isset( $this -> user['email'] ) && strtolower( trim( (string)$this -> user['email'] ) ) === 'biuro@project-pro.pl';
?>
<div class="task_details" task_id="<?= $this -> task['id'];?>">
<a href="#" class="close"><i class="fa fa-times"></i></a>
<div class="title">
@@ -12,8 +15,11 @@
<span class="task-title-wrapper">
<span class="task-id">#<?= $this -> task['id'];?></span>
<span class="task-title-view">
<span class="task-title-text"><?= $this -> task['name'];?></span>
<span class="task-title-text"><?= htmlspecialchars( (string)$this -> task['name'] );?></span>
<a href="#" class="task-title-edit-btn" title="Edytuj tytuł"><i class="fa fa-pencil"></i></a>
<? if ( $can_generate_ai_title ):?>
<a href="#" class="task-title-ai-btn" title="Generuj tytul AI"><i class="fa fa-magic"></i></a>
<? endif;?>
</span>
<span class="task-title-edit-box" style="display: none;">
<input type="text" class="task-title-input form-control" value="<?= htmlspecialchars($this -> task['name']);?>">
@@ -602,6 +608,7 @@
font-weight: 600;
}
.task_popup .task_details .title .task-title-edit-btn,
.task_popup .task_details .title .task-title-ai-btn,
.task_popup .task_details .title .task-title-save,
.task_popup .task_details .title .task-title-cancel {
border: 1px solid #099885;
@@ -622,6 +629,18 @@
background: #099885;
color: #fff;
}
.task_popup .task_details .title .task-title-ai-btn {
border-color: #299cdb;
color: #299cdb;
}
.task_popup .task_details .title .task-title-ai-btn:hover {
background: #299cdb;
color: #fff;
}
.task_popup .task_details .title .task-title-ai-btn.disabled {
pointer-events: none;
opacity: .65;
}
.task_popup .task_details .title .task-title-cancel {
border-color: #cc563d;
color: #cc563d;
@@ -1025,6 +1044,48 @@
popup.find( '.task-title-input' ).focus();
});
popup.on( 'click', '.task-title-ai-btn', function( e ) {
e.preventDefault();
var btn = $( this );
var task_id = popup.attr( 'task_id' );
var original_html = btn.html();
if ( btn.hasClass( 'disabled' ) )
return;
btn
.addClass( 'disabled' )
.html( '<i class="fa fa-spinner fa-spin"></i>' );
$.ajax({
type: 'POST',
url: '/tasks/task_generate_title/',
data: { task_id: task_id },
success: function( response ) {
var res = typeof response === 'string' ? JSON.parse( response ) : response;
if ( res.status === 'success' && res.title )
{
popup.find( '.task-title-view' ).hide();
popup.find( '.task-title-edit-box' ).css( 'display', 'inline-flex' );
popup.find( '.task-title-input' ).val( res.title ).focus().select();
}
else
{
alert( res.msg ? res.msg : 'Nie udalo sie wygenerowac tytulu.' );
}
},
error: function() {
alert( 'Nie udalo sie wygenerowac tytulu.' );
},
complete: function() {
btn
.removeClass( 'disabled' )
.html( original_html );
}
});
});
popup.on( 'click', '.task-title-cancel', function( e ) {
e.preventDefault();
popup.find( '.task-title-edit-box' ).hide();