This commit is contained in:
2026-03-22 23:55:14 +01:00
parent 33504f6013
commit 890f3a32c5
6 changed files with 245 additions and 35 deletions

3
.claude/memory/MEMORY.md Normal file
View File

@@ -0,0 +1,3 @@
# Memory Index
- [feedback_polish.md](feedback_polish.md) — Komunikacja po polsku

View File

@@ -0,0 +1,10 @@
---
name: Komunikacja po polsku
description: Użytkownik chce, żeby Claude pisał do niego po polsku
type: feedback
---
Zawsze komunikuj się z użytkownikiem po polsku.
**Why:** Użytkownik wyraźnie poprosił o komunikację w języku polskim.
**How to apply:** Wszystkie odpowiedzi, pytania i podsumowania pisz po polsku.

View File

@@ -593,6 +593,29 @@ class Tasks
exit;
}
static public function task_work_logs_popup()
{
global $user;
if ( !$user )
{
header( 'Location: /logowanie' );
exit;
}
$task_id = (int)\S::get( 'task_id' );
$task = \factory\Tasks::task_details( $task_id, $user['id'] );
if ( !is_array( $task ) )
$task = [ 'id' => $task_id, 'name' => '' ];
echo \Tpl::view( 'tasks/task_work_logs_popup', [
'task' => $task,
'task_works' => \factory\Tasks::task_works( $task_id )
] );
exit;
}
static public function task_attachment_upload()
{
global $user;

View File

@@ -0,0 +1,81 @@
<?
$to_input_datetime = function( $datetime ) {
if ( !$datetime )
return '';
$timestamp = strtotime( (string)$datetime );
if ( $timestamp === false )
return '';
return date( 'Y-m-d\TH:i', $timestamp );
};
$resolve_user_name = function( $user_id ) {
static $cache = [];
$user_id = (int)$user_id;
if ( !isset( $cache[ $user_id ] ) )
{
$details = \factory\Users::user_details( $user_id );
$name = 'Nieznany użytkownik';
if ( is_array( $details ) )
{
$full_name = trim( trim( (string)( $details['name'] ?? '' ) ) . ' ' . trim( (string)( $details['surname'] ?? '' ) ) );
if ( $full_name !== '' )
$name = $full_name;
}
$cache[ $user_id ] = $name;
}
return $cache[ $user_id ];
};
?>
<div class="work-time-logs-popup">
<div class="mb10">
<div><b>Zadanie:</b> <?= htmlspecialchars( (string)( $this -> task['name'] ?? '' ), ENT_QUOTES, 'UTF-8' );?></div>
<div><b>ID:</b> <?= (int)( $this -> task['id'] ?? 0 );?></div>
</div>
<? if ( is_array( $this -> task_works ) and count( $this -> task_works ) ):?>
<div class="table-responsive">
<table class="table table-sm table-striped mb0">
<thead>
<tr>
<th>Użytkownik</th>
<th style="width: 220px;">Start</th>
<th style="width: 220px;">Koniec</th>
<th class="text-center" style="width: 120px;">Akcja</th>
</tr>
</thead>
<tbody>
<? foreach ( $this -> task_works as $work ):?>
<tr class="js-work-log-row" data-work-id="<?= (int)$work['id'];?>">
<td><?= htmlspecialchars( $resolve_user_name( $work['user_id'] ), ENT_QUOTES, 'UTF-8' );?></td>
<td>
<input
type="datetime-local"
class="form-control js-work-log-start"
value="<?= htmlspecialchars( $to_input_datetime( $work['date_start'] ), ENT_QUOTES, 'UTF-8' );?>"
>
</td>
<td>
<input
type="datetime-local"
class="form-control js-work-log-end"
value="<?= htmlspecialchars( $to_input_datetime( $work['date_end'] ), ENT_QUOTES, 'UTF-8' );?>"
>
</td>
<td class="text-center">
<a href="#" class="js-save-work-log btn btn-xs btn-primary" task_work_id="<?= (int)$work['id'];?>">zapisz</a>
</td>
</tr>
<? endforeach;?>
</tbody>
</table>
</div>
<? else:?>
<div class="alert alert-info mb0">Brak wpisów time trackingu dla tego zadania.</div>
<? endif;?>
</div>

View File

@@ -157,7 +157,11 @@ usort( $billing_clients, function( $a, $b ) {
<tr class="billing-task-row" data-task-time="<?= (int)$row['time'];?>" data-task-amount="<?= number_format( (float)$row['amount'], 0, '.', '' );?>">
<td><?= $row['month'];?></td>
<td><?= $row['name'];?></td>
<td class="text-center"><?= $format_time( $row['time'] );?></td>
<td class="text-center">
<a href="#" class="open-work-logs-popup" task-id="<?= (int)$row['id'];?>" task-name="<?= htmlspecialchars( (string)$row['name'], ENT_QUOTES, 'UTF-8' );?>">
<?= $format_time( $row['time'] );?>
</a>
</td>
<td class="text-right"><?= $format_amount( $row['amount'] );?></td>
<td class="text-center">
<a href="#" class="close-task" task-id="<?= $row['id'];?>" client="<?= $summary['firm'];?>">zamknij zadanie</a>
@@ -250,6 +254,54 @@ usort( $billing_clients, function( $a, $b ) {
onConfirm();
}
function showPopupMessage( message ) {
if ( typeof $.alert === 'function' )
{
$.alert({
title: 'Informacja',
content: message
});
return;
}
alert( message );
}
function openWorkLogsPopup( task_id, task_name ) {
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/task_work_logs_popup/',
data: {
task_id: task_id
},
success: function( response ) {
if ( typeof $.confirm !== 'function' )
{
showPopupMessage( 'Brak obsługi popupu w tej konfiguracji.' );
return;
}
$.confirm({
title: 'Time tracking: ' + task_name,
content: response,
type: 'blue',
boxWidth: '980px',
useBootstrap: false,
backgroundDismiss: true,
buttons: {
close: {
text: 'Zamknij'
}
}
});
},
error: function() {
showPopupMessage( 'Nie udało się pobrać wpisów time trackingu.' );
}
});
}
$( 'body' ).on( 'click', '.toggle-billing-details', function(e){
e.preventDefault();
@@ -279,6 +331,70 @@ usort( $billing_clients, function( $a, $b ) {
});
});
$( 'body' ).on( 'click', '.open-work-logs-popup', function(e){
e.preventDefault();
var task_id = parseInt( $( this ).attr( 'task-id' ), 10 ) || 0;
var task_name = $( this ).attr( 'task-name' ) || ( 'Zadanie #' + task_id );
if ( !task_id )
{
showPopupMessage( 'Brak identyfikatora zadania.' );
return;
}
openWorkLogsPopup( task_id, task_name );
});
$( 'body' ).on( 'click', '.js-save-work-log', function(e){
e.preventDefault();
var button = $( this );
var row = button.closest( '.js-work-log-row' );
var task_work_id = parseInt( button.attr( 'task_work_id' ), 10 ) || 0;
var date_start = row.find( '.js-work-log-start' ).val();
var date_end = row.find( '.js-work-log-end' ).val();
if ( !task_work_id )
{
showPopupMessage( 'Brak identyfikatora wpisu.' );
return;
}
if ( !date_start || !date_end )
{
showPopupMessage( 'Uzupełnij datę start i koniec.' );
return;
}
button.attr( 'disabled', 'disabled' );
$.when(
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/change_task_work_date_start/',
data: {
task_work_id: task_work_id,
date_start: date_start
}
}),
$.ajax({
type: 'POST',
cache: false,
url: '/tasks/change_task_work_date_end/',
data: {
task_work_id: task_work_id,
date_end: date_end
}
})
).done( function() {
window.location.reload();
}).fail( function() {
button.removeAttr( 'disabled' );
showPopupMessage( 'Nie udało się zapisać zmian wpisu.' );
});
});
$( 'body' ).on( 'click', '.close-task', function(e){
e.preventDefault();

View File

@@ -35,9 +35,6 @@
<? if ( is_array( $this -> categories ) and count( $this -> categories ) ):?>
<? foreach ( $this -> categories as $category ):?>
<? $category_name = isset( $category['name'] ) ? (string)$category['name'] : '';?>
<? $category_text = isset( $category['text'] ) ? strip_tags( (string)$category['text'] ) : '';?>
<? $category_preview = trim( $category_text );?>
<? if ( strlen( $category_preview ) > 170 ) $category_preview = substr( $category_preview, 0, 170 ) . '...';?>
<article class="wiki-card" data-name="<?= htmlspecialchars( strtolower( $category_name ) );?>">
<div class="wiki-card-top">
<label class="wiki-select-one-label">
@@ -45,11 +42,6 @@
</label>
<a href="/wiki/category_preview/id=<?= (int)$category['id'];?>" class="wiki-card-title"><?= htmlspecialchars( $category_name );?></a>
</div>
<? if ( $category_preview !== '' ):?>
<div class="wiki-card-preview"><?= htmlspecialchars( $category_preview );?></div>
<? else:?>
<div class="wiki-card-preview wiki-card-preview-empty">Brak podgl&#261;du tre&#347;ci.</div>
<? endif;?>
<div class="wiki-card-bottom">
<div class="users">
<? $category_users = \factory\Wiki::category_users( (int)$category['id'] );?>
@@ -77,18 +69,10 @@
<? if ( is_array( $this -> categories ) and count( $this -> categories ) ):?>
<? foreach ( $this -> categories as $category ):?>
<? $category_name = isset( $category['name'] ) ? (string)$category['name'] : '';?>
<? $category_text = isset( $category['text'] ) ? strip_tags( (string)$category['text'] ) : '';?>
<? $category_preview = trim( $category_text );?>
<? if ( strlen( $category_preview ) > 170 ) $category_preview = substr( $category_preview, 0, 170 ) . '...';?>
<article class="wiki-card" data-name="<?= htmlspecialchars( strtolower( $category_name ) );?>">
<div class="wiki-card-top">
<a href="/wiki/category_preview/id=<?= (int)$category['id'];?>" class="wiki-card-title"><?= htmlspecialchars( $category_name );?></a>
</div>
<? if ( $category_preview !== '' ):?>
<div class="wiki-card-preview"><?= htmlspecialchars( $category_preview );?></div>
<? else:?>
<div class="wiki-card-preview wiki-card-preview-empty">Brak podgl&#261;du tre&#347;ci.</div>
<? endif;?>
</article>
<? endforeach;?>
<? else:?>
@@ -154,21 +138,22 @@
.wiki-main .wiki-categories-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 14px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
.wiki-main .wiki-card {
display: flex;
flex-direction: column;
gap: 10px;
gap: 12px;
border: 1px solid var(--wiki-border);
border-radius: 10px;
padding: 12px;
padding: 14px;
background: #fff;
transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
min-width: 0;
overflow: hidden;
min-height: 96px;
}
.wiki-main .wiki-card:hover {
@@ -197,20 +182,6 @@
text-decoration: underline;
}
.wiki-main .wiki-card-preview {
color: var(--wiki-text);
font-size: 13px;
line-height: 1.45;
min-height: 38px;
overflow-wrap: anywhere;
word-break: break-word;
}
.wiki-main .wiki-card-preview-empty {
color: var(--wiki-muted);
font-style: italic;
}
.wiki-main .wiki-card-bottom {
display: flex;
align-items: center;
@@ -271,6 +242,12 @@
max-width: none;
}
}
@media (min-width: 1400px) {
.wiki-main .wiki-categories-grid {
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
}
}
</style>
<script type="text/javascript">