feat: Add wiki integration to task management
- Implemented a multi-select dropdown for associating tasks with wiki entries in the task edit form. - Enhanced task popup to display related wiki entries with visibility controls based on user permissions. - Updated the wiki main view to support bulk actions for categories, including deletion and search functionality. - Created a new database migration for establishing many-to-many relationships between tasks and wiki entries. - Improved styling for wiki components to enhance user experience. - Added a new AGENTS.md file to outline communication and change management protocols.
This commit is contained in:
@@ -1,71 +1,303 @@
|
||||
<div class="row block-header">
|
||||
<? $is_admin = ( (int)$this -> user['id'] === 1 or (int)$this -> user['id'] === 3 );?>
|
||||
<div class="row block-header wiki-header">
|
||||
<div class="col-12">
|
||||
<h2>Wiki</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<? if ( $this -> user['id'] == 1 or $this -> user['id'] == 3 ):?>
|
||||
<div class="menu-buttons">
|
||||
<a href="/wiki/category_edit/" class="btn btn-success btn-sm" title="Dodaj kategorię">
|
||||
<i class="fa fa-plus"></i>Dodaj kategorię
|
||||
</a>
|
||||
|
||||
<div class="box wiki-main">
|
||||
<div class="wiki-toolbar">
|
||||
<div class="wiki-toolbar-left">
|
||||
<input type="text" class="form-control" id="wiki-search-name" placeholder="Szukaj wpisu po nazwie...">
|
||||
</div>
|
||||
<? endif;?>
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" class="form-control" id="search-name">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="wiki-categories">
|
||||
<? foreach ( $this -> categories as $category ):?>
|
||||
<div class="category">
|
||||
<div class="title">
|
||||
<a href="/wiki/category_preview/id=<?= $category['id'];?>"><?= $category['name'];?></a>
|
||||
</div>
|
||||
<? if ( $this -> user['id'] == 1 or $this -> user['id'] == 3 ):?>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="users">
|
||||
<? $category_users = \factory\Wiki::category_users( $category['id'] );?>
|
||||
<? if ( is_array( $category_users ) ): foreach ( $category_users as $user_tmp ):?>
|
||||
<?
|
||||
$user = \factory\Users::user_details( $user_tmp );
|
||||
echo '<div class="user" style="background: ' . $user['color'] . '" title="' . $user['name'] . ' ' . $user['surname'] . '">' . $user['name'][0] . $user['surname'][0] . '</div>';
|
||||
?>
|
||||
<? endforeach; endif;?>
|
||||
<div class="wiki-toolbar-right">
|
||||
<? if ( $is_admin ):?>
|
||||
<a href="/wiki/category_edit/" class="btn btn-success btn-sm" title="Dodaj kategorię">
|
||||
<i class="fa fa-plus"></i> Dodaj wpis
|
||||
</a>
|
||||
<? endif;?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<? if ( $is_admin ):?>
|
||||
<form method="POST" action="/wiki/categories_delete_bulk/" id="wiki-bulk-delete-form">
|
||||
<div class="wiki-bulk-actions">
|
||||
<label class="wiki-select-all-label">
|
||||
<input type="checkbox" class="g-checkbox" id="wiki-select-all">
|
||||
Zaznacz wszystko
|
||||
</label>
|
||||
<span class="wiki-selected-count">Zaznaczono: <strong id="wiki-selected-count-value">0</strong></span>
|
||||
<button type="button" class="btn btn-danger btn-sm" id="wiki-bulk-delete-btn" disabled>
|
||||
<i class="fa fa-trash"></i> Usuń zaznaczone
|
||||
</button>
|
||||
</div>
|
||||
<div class="wiki-categories-grid">
|
||||
<? 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">
|
||||
<input type="checkbox" class="g-checkbox wiki-select-one" name="ids[]" value="<?= (int)$category['id'];?>">
|
||||
</label>
|
||||
<a href="/wiki/category_preview/id=<?= (int)$category['id'];?>" class="wiki-card-title"><?= htmlspecialchars( $category_name );?></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="actions">
|
||||
<a href="/wiki/category_edit/id=<?= $category['id'];?>">edytuj</a>
|
||||
<a href="/wiki/category_delete/id=<?= $category['id'];?>" class="category-delete">usuń</a>
|
||||
<? if ( $category_preview !== '' ):?>
|
||||
<div class="wiki-card-preview"><?= htmlspecialchars( $category_preview );?></div>
|
||||
<? else:?>
|
||||
<div class="wiki-card-preview wiki-card-preview-empty">Brak podglądu treści.</div>
|
||||
<? endif;?>
|
||||
<div class="wiki-card-bottom">
|
||||
<div class="users">
|
||||
<? $category_users = \factory\Wiki::category_users( (int)$category['id'] );?>
|
||||
<? if ( is_array( $category_users ) ): foreach ( $category_users as $user_tmp ):?>
|
||||
<?
|
||||
$user = \factory\Users::user_details( $user_tmp );
|
||||
echo '<div class="user" style="background:' . htmlspecialchars( $user['color'] ) . ';" title="' . htmlspecialchars( $user['name'] . ' ' . $user['surname'] ) . '">' . htmlspecialchars( $user['name'][0] . $user['surname'][0] ) . '</div>';
|
||||
?>
|
||||
<? endforeach; endif;?>
|
||||
</div>
|
||||
<div class="wiki-card-actions">
|
||||
<a href="/wiki/category_edit/id=<?= (int)$category['id'];?>">edytuj</a>
|
||||
<a href="/wiki/category_delete/id=<?= (int)$category['id'];?>" class="category-delete">usuń</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<? endforeach;?>
|
||||
<? else:?>
|
||||
<div class="wiki-empty-state">Brak wpisów Wiki.</div>
|
||||
<? endif;?>
|
||||
</div>
|
||||
<? endforeach;?>
|
||||
</div>
|
||||
</form>
|
||||
<? else:?>
|
||||
<div class="wiki-categories-grid">
|
||||
<? 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ądu treści.</div>
|
||||
<? endif;?>
|
||||
</article>
|
||||
<? endforeach;?>
|
||||
<? else:?>
|
||||
<div class="wiki-empty-state">Brak wpisów Wiki.</div>
|
||||
<? endif;?>
|
||||
</div>
|
||||
<? endif;?>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.wiki-main {
|
||||
--wiki-bg-soft: #f4f8ff;
|
||||
--wiki-border: #d8e2f6;
|
||||
--wiki-title: #274985;
|
||||
--wiki-text: #425466;
|
||||
--wiki-muted: #728197;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-toolbar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--wiki-border);
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, var(--wiki-bg-soft) 100%);
|
||||
}
|
||||
|
||||
.wiki-main .wiki-toolbar-left {
|
||||
flex: 1;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-bulk-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--wiki-border);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-select-all-label,
|
||||
.wiki-main .wiki-select-one-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: var(--wiki-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-selected-count {
|
||||
color: var(--wiki-muted);
|
||||
}
|
||||
|
||||
.wiki-main .wiki-categories-grid {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
border: 1px solid var(--wiki-border);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card:hover {
|
||||
border-color: #b8c9eb;
|
||||
box-shadow: 0 8px 20px rgba(31, 61, 114, .08);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card-title {
|
||||
color: var(--wiki-title);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
line-height: 1.3;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card-title:hover {
|
||||
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;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card-actions a {
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wiki-main .users {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wiki-main .users .user {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .22);
|
||||
border: 1px solid rgba(255, 255, 255, .45);
|
||||
}
|
||||
|
||||
.wiki-main .wiki-card-actions a:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-empty-state {
|
||||
grid-column: 1 / -1;
|
||||
border: 1px dashed var(--wiki-border);
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
color: var(--wiki-muted);
|
||||
text-align: center;
|
||||
padding: 24px 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.wiki-main .wiki-toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.wiki-main .wiki-toolbar-left {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
$( function()
|
||||
{
|
||||
$( 'body' ).on( 'click', '.category-delete', function(e) {
|
||||
e.preventDefault();
|
||||
var href = $( this ).attr( 'href' );
|
||||
function updateBulkState() {
|
||||
var selected = $( '.wiki-select-one:checked' ).length;
|
||||
$( '#wiki-selected-count-value' ).text( selected );
|
||||
$( '#wiki-bulk-delete-btn' ).prop( 'disabled', selected === 0 );
|
||||
|
||||
var all_count = $( '.wiki-select-one' ).length;
|
||||
var all_selected = all_count > 0 && selected === all_count;
|
||||
$( '#wiki-select-all' ).prop( 'checked', all_selected );
|
||||
}
|
||||
|
||||
function confirmRedirect( href, content_text ) {
|
||||
$.confirm({
|
||||
title: 'Potwierdź',
|
||||
title: 'Potwierdź',
|
||||
type: 'orange',
|
||||
columnClass: 'col-md-8 col-md-offset-2 col-12',
|
||||
closeIcon: true,
|
||||
closeIconClass: 'fa fa-close',
|
||||
content: 'Na pewno chcesz usunąć wybrany wpis?',
|
||||
content: content_text,
|
||||
theme: 'modern',
|
||||
buttons: {
|
||||
submit: {
|
||||
text: '<i class="fa fa-check"></i>Zatwierdź',
|
||||
text: '<i class="fa fa-check"></i>Zatwierdź',
|
||||
btnClass: 'btn-success',
|
||||
action: function () {
|
||||
document.location.href = href;
|
||||
@@ -74,36 +306,76 @@
|
||||
cancel: {
|
||||
text: '<i class="fa fa-close"></i>Anuluj',
|
||||
btnClass: 'btn-danger',
|
||||
keys: ['enter'],
|
||||
keys: [ 'enter' ],
|
||||
action: function() {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$( 'body' ).on( 'click', '.category-delete', function( e ) {
|
||||
e.preventDefault();
|
||||
confirmRedirect( $( this ).attr( 'href' ), 'Na pewno chcesz usunąć wybrany wpis?' );
|
||||
});
|
||||
|
||||
$( '#wiki-select-all' ).on( 'change', function() {
|
||||
var is_checked = $( this ).is( ':checked' );
|
||||
$( '.wiki-select-one' ).prop( 'checked', is_checked );
|
||||
updateBulkState();
|
||||
});
|
||||
|
||||
$( 'body' ).on( 'change', '.wiki-select-one', function() {
|
||||
updateBulkState();
|
||||
});
|
||||
|
||||
$( '#wiki-bulk-delete-btn' ).on( 'click', function() {
|
||||
var selected = $( '.wiki-select-one:checked' ).length;
|
||||
if ( selected <= 0 )
|
||||
{
|
||||
$.alert( 'Najpierw zaznacz wpisy do usunięcia.' );
|
||||
return;
|
||||
}
|
||||
|
||||
$.confirm({
|
||||
title: 'Potwierdź',
|
||||
type: 'orange',
|
||||
columnClass: 'col-md-8 col-md-offset-2 col-12',
|
||||
closeIcon: true,
|
||||
closeIconClass: 'fa fa-close',
|
||||
content: 'Na pewno chcesz usunąć zaznaczone wpisy (' + selected + ')?',
|
||||
theme: 'modern',
|
||||
buttons: {
|
||||
submit: {
|
||||
text: '<i class="fa fa-check"></i>Zatwierdź',
|
||||
btnClass: 'btn-success',
|
||||
action: function () {
|
||||
$( '#wiki-bulk-delete-form' ).trigger( 'submit' );
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '<i class="fa fa-close"></i>Anuluj',
|
||||
btnClass: 'btn-danger',
|
||||
keys: [ 'enter' ],
|
||||
action: function() {}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var timer = '';
|
||||
$( '#search-name' ).keyup( function()
|
||||
var timer = null;
|
||||
$( '#wiki-search-name' ).on( 'keyup', function()
|
||||
{
|
||||
var _this = $( this);
|
||||
var category = _this.val();
|
||||
var phrase = String( $( this ).val() || '' ).toLowerCase().trim();
|
||||
clearTimeout( timer );
|
||||
timer = setTimeout( function()
|
||||
{
|
||||
category = category.toLowerCase();
|
||||
|
||||
$( '.category' ).hide();
|
||||
$( ".category .title a" ).each( function ( index )
|
||||
{
|
||||
var text = $( this ).text();
|
||||
text = text.trim().toLowerCase(); console.log( text );
|
||||
|
||||
if ( text.indexOf( category ) >= 0 )
|
||||
$( this ).parents( '.category' ).show();
|
||||
timer = setTimeout( function() {
|
||||
$( '.wiki-card' ).each( function() {
|
||||
var card = $( this );
|
||||
var name = String( card.attr( 'data-name' ) || '' );
|
||||
card.toggle( phrase === '' || name.indexOf( phrase ) >= 0 );
|
||||
});
|
||||
|
||||
if ( category == '' )
|
||||
$( '.category' ).show();
|
||||
}, 500 );
|
||||
}, 180 );
|
||||
});
|
||||
|
||||
updateBulkState();
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user