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 = ' +
+ %1$s +
+ +
+
+ '; + 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 = ' +
+ %1$s +
+ +
+
+ '; + 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

@@ -37,7 +37,6 @@
-

Upload attachment

diff --git a/ui/views/comments/view.html b/ui/views/comments/view.html index 63d7146..e628487 100644 --- a/ui/views/comments/view.html +++ b/ui/views/comments/view.html @@ -1,6 +1,6 @@
-

Comments

+

Comments

diff --git a/ui/views/ticket/edit.html b/ui/views/ticket/edit.html index 066a888..bf8faad 100644 --- a/ui/views/ticket/edit.html +++ b/ui/views/ticket/edit.html @@ -1,60 +1,131 @@ -

Edit Ticket Form

- + + - - - - - - - - - - - - -
-

Custom Fields

- -
- -
- -
- - -
-
- - -
-
-
+
+
+ +
+
+ +
+ Cancel
-
- -
-
-
- - -
-
- - -
+
+ +
+
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + + + + + + + + + + +
PropertyValue
{{@key}} {{@value}}
+
+ +
+

Linked Tickets

+ + +
+

Parent Tickets

+ +
+ + + +
+

Child Tickets

+
+
+ + +
+ +
+ +
+
+
+
+ +
+
+ + */ ?>
- -
- \ No newline at end of file + +
+ +
+ + + + + + + + + +
diff --git a/ui/views/ticket/edit.html.v1 b/ui/views/ticket/edit.html.v1 new file mode 100644 index 0000000..066a888 --- /dev/null +++ b/ui/views/ticket/edit.html.v1 @@ -0,0 +1,60 @@ +

Edit Ticket Form

+ +
+ + + + + + + + + + + + +
+

Custom Fields

+ +
+ +
+ +
+ + +
+
+ + +
+
+
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ \ No newline at end of file