feat: Comment edit/delete, auto-link URLs, markdown tables, mobile fixes

- Add comment edit/delete functionality (owner or admin can modify)
- Add edit/delete buttons to comments in TicketView
- Create update_comment.php and delete_comment.php API endpoints
- Add updateComment() and deleteComment() methods to CommentModel
- Show "(edited)" indicator on modified comments
- Add migration script for updated_at column

- Auto-link URLs in plain text comments (non-markdown)
- Add markdown table support with proper HTML rendering
- Preserve code blocks during markdown parsing

- Fix mobile UI elements showing on desktop (add display:none defaults)
- Add mobile styles for CreateTicketView form elements
- Stack status-priority-row on mobile devices

- Update cache busters to v20260124e
- Update Claude.md and README.md documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 16:59:29 -05:00
parent 7ecb593c0f
commit 98db586bcf
14 changed files with 977 additions and 20 deletions

View File

@@ -8,9 +8,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create New Ticket</title>
<link rel="icon" type="image/png" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/images/favicon.png">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124d">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/ticket.css?v=20260124d">
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124d"></script>
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124e">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/ticket.css?v=20260124e">
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124e"></script>
<script>
// CSRF Token for AJAX requests
window.CSRF_TOKEN = '<?php

View File

@@ -9,11 +9,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ticket Dashboard</title>
<link rel="icon" type="image/png" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/images/favicon.png">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124dc">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124e">
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/ascii-banner.js"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/toast.js"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/markdown.js?v=20260124dc"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124dc"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/markdown.js?v=20260124e"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124e"></script>
<script>
// CSRF Token for AJAX requests
window.CSRF_TOKEN = '<?php

View File

@@ -46,12 +46,12 @@ function formatDetails($details, $actionType) {
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ticket #<?php echo $ticket['ticket_id']; ?></title>
<link rel="icon" type="image/png" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/images/favicon.png">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124d">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/ticket.css?v=20260124d">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css?v=20260124e">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/ticket.css?v=20260124e">
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/toast.js"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/markdown.js?v=20260124d"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124d"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/ticket.js?v=20260124d"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/markdown.js?v=20260124e"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js?v=20260124e"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/ticket.js?v=20260124e"></script>
<script>
// CSRF Token for AJAX requests
window.CSRF_TOKEN = '<?php
@@ -298,15 +298,34 @@ function formatDetails($details, $actionType) {
<h2>Comment History</h2>
<div class="comments-list">
<?php
$currentUserId = $GLOBALS['currentUser']['user_id'] ?? null;
$isAdmin = $GLOBALS['currentUser']['is_admin'] ?? false;
foreach ($comments as $comment) {
// Use display_name_formatted which falls back appropriately
$displayName = $comment['display_name_formatted'] ?? $comment['user_name'] ?? 'Unknown User';
echo "<div class='comment'>";
$commentId = $comment['comment_id'];
$isOwner = ($comment['user_id'] == $currentUserId);
$canModify = $isOwner || $isAdmin;
$markdownEnabled = $comment['markdown_enabled'] ? 1 : 0;
echo "<div class='comment' data-comment-id='{$commentId}' data-markdown-enabled='{$markdownEnabled}'>";
echo "<div class='comment-header'>";
echo "<span class='comment-user'>" . htmlspecialchars($displayName) . "</span>";
echo "<span class='comment-date'>" . date('M d, Y H:i', strtotime($comment['created_at'])) . "</span>";
$dateStr = date('M d, Y H:i', strtotime($comment['created_at']));
$editedIndicator = !empty($comment['updated_at']) ? ' <span class="comment-edited">(edited)</span>' : '';
echo "<span class='comment-date'>{$dateStr}{$editedIndicator}</span>";
// Edit/Delete buttons for owner or admin
if ($canModify) {
echo "<div class='comment-actions'>";
echo "<button type='button' class='comment-action-btn edit-btn' onclick='editComment({$commentId})' title='Edit comment'>✏️</button>";
echo "<button type='button' class='comment-action-btn delete-btn' onclick='deleteComment({$commentId})' title='Delete comment'>🗑️</button>";
echo "</div>";
}
echo "</div>";
echo "<div class='comment-text' " . ($comment['markdown_enabled'] ? "data-markdown" : "") . ">";
echo "<div class='comment-text' id='comment-text-{$commentId}' " . ($comment['markdown_enabled'] ? "data-markdown" : "") . ">";
if ($comment['markdown_enabled']) {
// Markdown will be rendered by JavaScript
echo htmlspecialchars($comment['comment_text']);
@@ -315,6 +334,8 @@ function formatDetails($details, $actionType) {
echo nl2br(htmlspecialchars($comment['comment_text']));
}
echo "</div>";
// Hidden raw text for editing
echo "<textarea class='comment-edit-raw' id='comment-raw-{$commentId}' style='display:none;'>" . htmlspecialchars($comment['comment_text']) . "</textarea>";
echo "</div>";
}
?>