refactor: rename third-party JS libs folder from vendor to lib

Rename static/shared/js/vendor/ to static/shared/js/lib/ to avoid
confusion with the app's "vendor" (seller) dashboard code in
static/vendor/js/.

- Rename directory: vendor/ → lib/
- Update all template references to use new path
- Update CDN fallback documentation
- Fix .gitignore to use /lib/ (root only) instead of lib/ (everywhere)

Third-party libraries:
- alpine.min.js
- chart.umd.min.js
- flatpickr.min.js
- quill.js
- tom-select.complete.min.js

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 20:36:53 +01:00
parent 65e5c55266
commit 7a7b612519
19 changed files with 482 additions and 60 deletions

View File

@@ -72,6 +72,9 @@
<!-- Flatpickr CSS with CDN fallback (loaded on demand via block) -->
{% block flatpickr_css %}{% endblock %}
<!-- Quill CSS with CDN fallback (loaded on demand via block) -->
{% block quill_css %}{% endblock %}
{% block extra_head %}{% endblock %}
</head>
<body x-cloak>
@@ -117,7 +120,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);
@@ -138,7 +141,7 @@
console.warn('Alpine.js CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.defer = true;
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/alpine.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/alpine.min.js") }}';
document.head.appendChild(fallbackScript);
};
@@ -152,7 +155,10 @@
<!-- 10. OPTIONAL: Flatpickr with CDN fallback (loaded on demand via block) -->
{% block flatpickr_script %}{% endblock %}
<!-- 11. LAST: Page-specific scripts -->
<!-- 11. OPTIONAL: Quill with CDN fallback (loaded on demand via block) -->
{% block quill_script %}{% endblock %}
<!-- 12. LAST: Page-specific scripts -->
{% block extra_scripts %}{% endblock %}
</body>
</html>

View File

@@ -309,7 +309,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);

View File

@@ -117,8 +117,22 @@
<!-- 4. API Client -->
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
<!-- 5. Alpine.js v3 -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.0/dist/cdn.min.js"></script>
<!-- 5. Alpine.js v3 with CDN fallback -->
<script>
(function() {
var script = document.createElement('script');
script.defer = true;
script.src = 'https://cdn.jsdelivr.net/npm/alpinejs@3.14.0/dist/cdn.min.js';
script.onerror = function() {
console.warn('Alpine.js CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.defer = true;
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/alpine.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);
})();
</script>
<!-- 6. Login Logic -->
<script src="{{ url_for('static', path='admin/js/login.js') }}"></script>

View File

@@ -593,7 +593,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);

View File

@@ -466,7 +466,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);

View File

@@ -2,11 +2,20 @@
{% extends "admin/base.html" %}
{% from 'shared/macros/headers.html' import detail_page_header %}
{% from 'shared/macros/modals.html' import media_picker_modal %}
{% from 'shared/macros/richtext.html' import quill_css, quill_js, quill_editor %}
{% block title %}Create Vendor Product{% endblock %}
{% block alpine_data %}adminVendorProductCreate(){% endblock %}
{% block quill_css %}
{{ quill_css() }}
{% endblock %}
{% block quill_script %}
{{ quill_js() }}
{% endblock %}
{% block extra_head %}
<!-- Tom Select CSS with local fallback -->
<link
@@ -90,34 +99,98 @@
</nav>
</div>
<!-- Translation Fields -->
<template x-for="lang in ['en', 'fr', 'de', 'lu']" :key="lang">
<div x-show="activeLanguage === lang" class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
Title (<span x-text="lang.toUpperCase()"></span>) <span x-show="lang === 'en'" class="text-red-500">*</span>
</label>
<input
type="text"
x-model="form.translations[lang].title"
: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 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>)
</label>
<textarea
x-model="form.translations[lang].description"
rows="5"
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 (HTML supported)"
></textarea>
</div>
<!-- Translation Fields - English -->
<div x-show="activeLanguage === 'en'" class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">
Title (EN) <span class="text-red-500">*</span>
</label>
<input
type="text"
x-model="form.translations.en.title"
required
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>
</template>
{{ quill_editor(
id='create-desc-editor-en',
model='form.translations.en.description',
label='Description (EN)',
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='create-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='create-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='create-desc-editor-lu',
model='form.translations.lu.description',
label='Description (LU)',
placeholder='Enter product description in Luxembourgish...',
min_height='150px',
toolbar='standard'
) }}
</div>
</div>
<!-- Product Identifiers -->
@@ -428,7 +501,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);

View File

@@ -390,7 +390,7 @@
script.onerror = function() {
console.warn('Tom Select CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/tom-select.complete.min.js") }}';
fallbackScript.src = '{{ url_for("static", path="shared/js/lib/tom-select.complete.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);