diff --git a/app/controllers/TicketController.php b/app/controllers/TicketController.php
index 4c1e17d..a858055 100644
--- a/app/controllers/TicketController.php
+++ b/app/controllers/TicketController.php
@@ -99,6 +99,9 @@ class TicketController extends BaseController implements CRUD {
$this->f3->reroute('/tickets');
}
+ //
+ $f3->set('js', 'markdown_preview.js');
+
// dropdowns
$priorities = (new TicketPriority($this->getDB()))->findAll();
$statuses = (new TicketStatus($this->getDB()))->findAll();
diff --git a/app/extensions/BulmaFormHelper.php b/app/extensions/BulmaFormHelper.php
index f3da274..184bf43 100644
--- a/app/extensions/BulmaFormHelper.php
+++ b/app/extensions/BulmaFormHelper.php
@@ -6,6 +6,9 @@ class BulmaFormHelper extends \Prefab {
const H_FIELD_TEXTAREA = 2;
const H_FIELD_SELECT = 3;
const H_FIELD_SELECT_NEW = 4;
+
+ const FIELD_INPUT = 10;
+ const FIELD_TEXTAREA = 11;
static public function render($node) {
@@ -13,18 +16,19 @@ class BulmaFormHelper extends \Prefab {
$type = strtoupper($attr['type']) ?? null;
// all *
- $label = $attr['label'];
- $name = $attr['name'];
- $value = isset($attr['value']) ? $attr['value'] : '';
+ $label = $attr['label'] ?? '';
+ $name = $attr['name'] ?? '';
+ $value = $attr['value'] ?? '';
+ $class = $attr['class'] ?? '';
// select
- $options = isset($attr['options']) ? $attr['options'] : '';
- $selected = isset($attr['selected']) ? $attr['selected'] : '';
- //
+ $options = $attr['options'] ?? [];
+ $selected = $attr['selected'] ?? [];
+ // textarea
+ $rows = $attr['rows'] ?? '';
$label = \Template::instance()->build($label);
$name = \Template::instance()->build($name);
$value = \Template::instance()->build($value);
- $options_array = \Template::instance()->token($options);
if(defined("BulmaFormHelper::$type")){
@@ -43,6 +47,12 @@ class BulmaFormHelper extends \Prefab {
case BulmaFormHelper::H_FIELD_SELECT_NEW:
return BulmaFormHelper::build_h_field_select_new($attr);
break;
+ case BulmaFormHelper::FIELD_INPUT:
+ return BulmaFormHelper::build_field_input($label, $name, $value, $class);
+ break;
+ case BulmaFormHelper::FIELD_TEXTAREA:
+ return BulmaFormHelper::build_field_textarea($label, $name, $value, $class, $rows);
+ break;
default:
return '
Error: Bulma CSS Form TYPE ('.$type.') not defined.
';
break;
@@ -54,6 +64,73 @@ class BulmaFormHelper extends \Prefab {
}
+ static function build_field_input($label, $name, $value, $class, $rows=10){
+
+ $string_label = $label !== '' ? sprintf('', $label) : '';
+ $string = '
+
+ ';
+ return sprintf($string, $string_label, $name, $value, $class, $rows);
+ }
+
+ static function build_field_textarea($label, $name, $value, $class, $rows=10)
+ {
+ $string_label = $label !== '' ? sprintf('', $label) : '';
+ $string = '
+
+ ';
+ return sprintf($string, $string_label, $name, $value, $class,$rows);
+ }
+
+ static function build_h_field_textarea($label, $name, $value){
+ $string = '
+
+ ';
+ return $string;
+ }
+
+ static function build_h_field_input($label, $name, $value){
+ $string = '
+
+ ';
+ return $string;
+ }
+
static function build_h_field_select_new($attr)
{
$f3 = \Base::instance();
@@ -88,25 +165,7 @@ class BulmaFormHelper extends \Prefab {
return $html;
}
- static function build_h_field_input($label, $name, $value){
- $string = '
-
- ';
- return $string;
- }
+
static function build_h_field_select($label, $name, $options, $selected){
$opts = json_decode(str_replace("'", '"', $options));
@@ -139,25 +198,6 @@ class BulmaFormHelper extends \Prefab {
return $string;
}
- static function build_h_field_textarea($label, $name, $value){
- $string = '
-
- ';
- return $string;
- }
-
}
\Template::instance()->extend('bulma', 'BulmaFormHelper::render');
\ No newline at end of file
diff --git a/public/js/markdown_preview.js b/public/js/markdown_preview.js
new file mode 100644
index 0000000..72eec1f
--- /dev/null
+++ b/public/js/markdown_preview.js
@@ -0,0 +1,73 @@
+class TabSwitcherController {
+ constructor({ tabSelector, contentPrefix, textareaSelector, previewUrl }) {
+ this.tabSelector = tabSelector;
+ this.contentPrefix = contentPrefix;
+ this.textareaSelector = textareaSelector;
+ this.previewUrl = previewUrl;
+
+ this.tabParent = document.querySelector(tabSelector);
+ this.tabLinks = this.tabParent.querySelectorAll('a');
+
+ this.init();
+ }
+
+ init() {
+ this.tabLinks.forEach(link => {
+ link.addEventListener('click', (e) => this.handleTabClick(e, link));
+ });
+ }
+
+ async handleTabClick(e, link) {
+ e.preventDefault();
+ const selectedTab = link.getAttribute('data-tab');
+
+ // Update active tab
+ this.tabParent.querySelectorAll('li').forEach(li => li.classList.remove('is-active'));
+ link.parentElement.classList.add('is-active');
+
+ // Show active content
+ document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');
+ const activeContent = document.getElementById(`${this.contentPrefix}-${selectedTab}`);
+ if (activeContent) activeContent.style.display = '';
+
+ if (selectedTab === 'preview') {
+ await this.loadPreview();
+ }
+ }
+
+ async loadPreview() {
+ const previewTarget = document.getElementById('preview-output');
+ if (!previewTarget) return;
+
+ previewTarget.innerHTML = `
+
+ `;
+
+ await new Promise(resolve => setTimeout(resolve, 500));
+
+ const textarea = document.querySelector(this.textareaSelector);
+ const markdown = textarea ? textarea.value : '';
+
+ const res = await fetch(this.previewUrl, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: `content=${encodeURIComponent(markdown)}`
+ });
+
+ const html = await res.text();
+ previewTarget.innerHTML = html;
+ }
+ }
+
+ // Usage
+ document.addEventListener('DOMContentLoaded', () => {
+ new TabSwitcherController({
+ tabSelector: '.tabs',
+ contentPrefix: 'tab',
+ textareaSelector: '#description',
+ previewUrl: '/parsedown/preview'
+ });
+ });
+
\ No newline at end of file
diff --git a/ui/views/attachment/index.html b/ui/views/attachment/index.html
index de6588f..18fa0a0 100644
--- a/ui/views/attachment/index.html
+++ b/ui/views/attachment/index.html
@@ -1,6 +1,6 @@
-
Attachments
+
Attachments