feat: integrate Quill rich text editor in admin templates
- Add Quill editor to content-page-edit.html - Add Quill editor to vendor-product-edit.html - Add Quill snow theme CSS - Update background-tasks.html and platform-homepage.html templates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
{% call page_header_flex(title='Background Tasks', subtitle='Monitor running and completed background tasks') %}
|
{% call page_header_flex(title='Background Tasks', subtitle='Monitor running and completed background tasks') %}
|
||||||
<!-- Flower Dashboard Link (Celery Monitoring) -->
|
<!-- Flower Dashboard Link (Celery Monitoring) -->
|
||||||
<a href="{{ config.FLOWER_URL | default('http://localhost:5555', true) }}"
|
<a href="{{ flower_url }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple mr-2"
|
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple mr-2"
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 text-sm">
|
<td class="px-4 py-3 text-sm">
|
||||||
<template x-if="task.celery_task_id">
|
<template x-if="task.celery_task_id">
|
||||||
<a :href="'{{ config.FLOWER_URL | default('http://localhost:5555', true) }}/task/' + task.celery_task_id"
|
<a :href="'{{ flower_url }}/task/' + task.celery_task_id"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-purple-600 hover:text-purple-800 dark:text-purple-400 dark:hover:text-purple-300 underline"
|
class="text-purple-600 hover:text-purple-800 dark:text-purple-400 dark:hover:text-purple-300 underline"
|
||||||
|
|||||||
@@ -3,11 +3,20 @@
|
|||||||
{% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %}
|
{% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %}
|
||||||
{% from 'shared/macros/headers.html' import page_header_flex, back_button, action_button %}
|
{% from 'shared/macros/headers.html' import page_header_flex, back_button, action_button %}
|
||||||
{% from 'shared/macros/inputs.html' import number_stepper %}
|
{% from 'shared/macros/inputs.html' import number_stepper %}
|
||||||
|
{% from 'shared/macros/richtext.html' import quill_css, quill_js, quill_editor %}
|
||||||
|
|
||||||
{% block title %}{% if page_id %}Edit{% else %}Create{% endif %} Content Page{% endblock %}
|
{% block title %}{% if page_id %}Edit{% else %}Create{% endif %} Content Page{% endblock %}
|
||||||
|
|
||||||
{% block alpine_data %}contentPageEditor({{ page_id if page_id else 'null' }}){% endblock %}
|
{% block alpine_data %}contentPageEditor({{ page_id if page_id else 'null' }}){% endblock %}
|
||||||
|
|
||||||
|
{% block quill_css %}
|
||||||
|
{{ quill_css() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block quill_script %}
|
||||||
|
{{ quill_js() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{# Dynamic title/subtitle and save button text based on create vs edit mode #}
|
{# Dynamic title/subtitle and save button text based on create vs edit mode #}
|
||||||
<div class="flex items-center justify-between my-6">
|
<div class="flex items-center justify-between my-6">
|
||||||
@@ -133,17 +142,31 @@
|
|||||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
Content <span class="text-red-500">*</span>
|
Content <span class="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
|
||||||
x-model="form.content"
|
<!-- Rich Text Editor for HTML format -->
|
||||||
required
|
<div x-show="form.content_format === 'html'" x-cloak>
|
||||||
rows="12"
|
{{ quill_editor(
|
||||||
class="w-full px-3 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:border-purple-500 dark:bg-gray-700 font-mono text-sm"
|
id='content-editor',
|
||||||
placeholder="<h2>Your content here...</h2>"
|
model='form.content',
|
||||||
></textarea>
|
placeholder='Write your content here...',
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
min_height='300px',
|
||||||
<span x-show="form.content_format === 'html'">Enter HTML content. Basic HTML tags are supported.</span>
|
toolbar='full',
|
||||||
<span x-show="form.content_format === 'markdown'">Enter Markdown content. Will be converted to HTML.</span>
|
help_text='Use the toolbar to format your content. Supports headings, lists, links, images, and more.'
|
||||||
</p>
|
) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Plain textarea for Markdown format -->
|
||||||
|
<div x-show="form.content_format === 'markdown'" x-cloak>
|
||||||
|
<textarea
|
||||||
|
x-model="form.content"
|
||||||
|
rows="12"
|
||||||
|
class="w-full px-3 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:border-purple-500 dark:bg-gray-700 font-mono text-sm"
|
||||||
|
placeholder="# Your heading here Write your **markdown** content..."
|
||||||
|
></textarea>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
Enter Markdown content. Will be converted to HTML when displayed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,20 @@
|
|||||||
{% extends "admin/base.html" %}
|
{% extends "admin/base.html" %}
|
||||||
{% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %}
|
{% from 'shared/macros/alerts.html' import loading_state, error_state, alert_dynamic %}
|
||||||
{% from 'shared/macros/headers.html' import page_header_flex, action_button %}
|
{% from 'shared/macros/headers.html' import page_header_flex, action_button %}
|
||||||
|
{% from 'shared/macros/richtext.html' import quill_css, quill_js, quill_editor %}
|
||||||
|
|
||||||
{% block title %}Platform Homepage Manager{% endblock %}
|
{% block title %}Platform Homepage Manager{% endblock %}
|
||||||
|
|
||||||
{% block alpine_data %}platformHomepageManager(){% endblock %}
|
{% block alpine_data %}platformHomepageManager(){% endblock %}
|
||||||
|
|
||||||
|
{% block quill_css %}
|
||||||
|
{{ quill_css() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block quill_script %}
|
||||||
|
{{ quill_js() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{# Note: Subtitle has inline HTML link, so using page_header_flex with custom structure #}
|
{# Note: Subtitle has inline HTML link, so using page_header_flex with custom structure #}
|
||||||
<div class="flex items-center justify-between my-6">
|
<div class="flex items-center justify-between my-6">
|
||||||
@@ -128,18 +137,15 @@
|
|||||||
|
|
||||||
<!-- Content (HTML) -->
|
<!-- Content (HTML) -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
{{ quill_editor(
|
||||||
Content (HTML)
|
id='homepage-content-editor',
|
||||||
</label>
|
model='page.content',
|
||||||
<textarea
|
label='Content',
|
||||||
x-model="page.content"
|
placeholder='Write your platform description here...',
|
||||||
rows="6"
|
min_height='200px',
|
||||||
class="w-full px-3 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:border-purple-500 dark:bg-gray-700 font-mono text-sm"
|
toolbar='standard',
|
||||||
placeholder="<p>Your platform description here...</p>"
|
help_text='HTML content shown below the title. Use the toolbar to format text, add links, and images.'
|
||||||
></textarea>
|
) }}
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
||||||
HTML content shown below the title. Basic HTML tags are supported.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,20 @@
|
|||||||
{% from 'shared/macros/alerts.html' import loading_state, error_state %}
|
{% from 'shared/macros/alerts.html' import loading_state, error_state %}
|
||||||
{% from 'shared/macros/headers.html' import detail_page_header %}
|
{% from 'shared/macros/headers.html' import detail_page_header %}
|
||||||
{% from 'shared/macros/modals.html' import media_picker_modal %}
|
{% from 'shared/macros/modals.html' import media_picker_modal %}
|
||||||
|
{% from 'shared/macros/richtext.html' import quill_css, quill_js, quill_editor %}
|
||||||
|
|
||||||
{% block title %}Edit Vendor Product{% endblock %}
|
{% block title %}Edit Vendor Product{% endblock %}
|
||||||
|
|
||||||
{% block alpine_data %}adminVendorProductEdit(){% endblock %}
|
{% block alpine_data %}adminVendorProductEdit(){% endblock %}
|
||||||
|
|
||||||
|
{% block quill_css %}
|
||||||
|
{{ quill_css() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block quill_script %}
|
||||||
|
{{ quill_js() }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% call detail_page_header("'Edit: ' + (product?.vendor_translations?.en?.title || 'Product')", '/admin/vendor-products', subtitle_show='product') %}
|
{% call detail_page_header("'Edit: ' + (product?.vendor_translations?.en?.title || 'Product')", '/admin/vendor-products', subtitle_show='product') %}
|
||||||
<span x-text="product?.vendor_name || 'Unknown Vendor'"></span>
|
<span x-text="product?.vendor_name || 'Unknown Vendor'"></span>
|
||||||
@@ -41,35 +50,99 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Translation Fields -->
|
<!-- Translation Fields - English -->
|
||||||
<template x-for="lang in ['en', 'fr', 'de', 'lu']" :key="lang">
|
<div x-show="activeLanguage === 'en'" class="space-y-4">
|
||||||
<div x-show="activeLanguage === lang" class="space-y-4">
|
<div>
|
||||||
<div>
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
||||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
Title (EN) <span class="text-red-500">*</span>
|
||||||
Title (<span x-text="lang.toUpperCase()"></span>) <span class="text-red-500">*</span>
|
</label>
|
||||||
</label>
|
<input
|
||||||
<input
|
type="text"
|
||||||
type="text"
|
x-model="form.translations.en.title"
|
||||||
x-model="form.translations[lang].title"
|
required
|
||||||
:required="lang === 'en'"
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
||||||
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
placeholder="Product title"
|
||||||
placeholder="Product title"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
|
||||||
Description (<span x-text="lang.toUpperCase()"></span>) <span class="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
x-model="form.translations[lang].description"
|
|
||||||
rows="5"
|
|
||||||
:required="lang === 'en'"
|
|
||||||
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
|
||||||
placeholder="Product description"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
{{ quill_editor(
|
||||||
|
id='desc-editor-en',
|
||||||
|
model='form.translations.en.description',
|
||||||
|
label='Description (EN)',
|
||||||
|
required=true,
|
||||||
|
placeholder='Enter product description in English...',
|
||||||
|
min_height='150px',
|
||||||
|
toolbar='standard'
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Translation Fields - French -->
|
||||||
|
<div x-show="activeLanguage === 'fr'" class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
||||||
|
Title (FR)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
x-model="form.translations.fr.title"
|
||||||
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
||||||
|
placeholder="Product title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{ quill_editor(
|
||||||
|
id='desc-editor-fr',
|
||||||
|
model='form.translations.fr.description',
|
||||||
|
label='Description (FR)',
|
||||||
|
placeholder='Enter product description in French...',
|
||||||
|
min_height='150px',
|
||||||
|
toolbar='standard'
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Translation Fields - German -->
|
||||||
|
<div x-show="activeLanguage === 'de'" class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
||||||
|
Title (DE)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
x-model="form.translations.de.title"
|
||||||
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
||||||
|
placeholder="Product title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{ quill_editor(
|
||||||
|
id='desc-editor-de',
|
||||||
|
model='form.translations.de.description',
|
||||||
|
label='Description (DE)',
|
||||||
|
placeholder='Enter product description in German...',
|
||||||
|
min_height='150px',
|
||||||
|
toolbar='standard'
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Translation Fields - Luxembourgish -->
|
||||||
|
<div x-show="activeLanguage === 'lu'" class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
|
||||||
|
Title (LU)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
x-model="form.translations.lu.title"
|
||||||
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"
|
||||||
|
placeholder="Product title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{ quill_editor(
|
||||||
|
id='desc-editor-lu',
|
||||||
|
model='form.translations.lu.description',
|
||||||
|
label='Description (LU)',
|
||||||
|
placeholder='Enter product description in Luxembourgish...',
|
||||||
|
min_height='150px',
|
||||||
|
toolbar='standard'
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Product Identifiers -->
|
<!-- Product Identifiers -->
|
||||||
|
|||||||
10
static/shared/css/vendor/quill.snow.css
vendored
Normal file
10
static/shared/css/vendor/quill.snow.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user