@@ -93,7 +104,7 @@
@endphp
| {{ $payment->provider ?? '—' }} |
- {{ $payment->plan_code ?? '—' }} |
+ {{ $formatPlan($payment->plan_code) }} |
{{ $payment->currency ?? 'USD' }} {{ number_format((float) ($payment->amount ?? 0), 2) }} |
{{ $status }}
diff --git a/app/resources/views/dashboard/user/keywords.blade.php b/app/resources/views/dashboard/user/keywords.blade.php
index 749f361..5f2db5a 100644
--- a/app/resources/views/dashboard/user/keywords.blade.php
+++ b/app/resources/views/dashboard/user/keywords.blade.php
@@ -133,10 +133,10 @@
-
+
@@ -179,11 +181,44 @@
const emojiEl = document.getElementById('keyword-emoji');
const textEl = document.getElementById('keyword-text');
const langEl = document.getElementById('keyword-lang');
+ const langDisplayEl = document.getElementById('keyword-lang-display');
+ const langDataList = document.getElementById('keyword-language-options');
const searchEl = document.getElementById('keyword-search');
const importBtn = document.getElementById('import-btn');
const importPanel = document.getElementById('import-panel');
const importCancel = document.getElementById('import-cancel');
+ window.dewemojiPopulateLanguageOptions?.(langDataList);
+
+ const langDisplayToCode = new Map();
+ const langCodeToDisplay = new Map();
+ langDataList?.querySelectorAll('option').forEach((option) => {
+ const code = (option.dataset.code || '').trim();
+ const display = (option.value || '').trim();
+ if (!code || !display) return;
+ langDisplayToCode.set(display.toLowerCase(), code);
+ langCodeToDisplay.set(code.toLowerCase(), display);
+ });
+
+ const normalizeLang = (value) => {
+ const v = (value || '').trim();
+ if (!v) return 'und';
+ const byDisplay = langDisplayToCode.get(v.toLowerCase());
+ if (byDisplay) return byDisplay;
+
+ const bracket = v.match(/\(([A-Za-z]{2,3}(?:-[A-Za-z]{2})?)\)\s*$/);
+ if (bracket?.[1]) return bracket[1];
+
+ if (/^[A-Za-z]{2,3}(?:-[A-Za-z]{2})?$/.test(v)) return v;
+ return 'und';
+ };
+
+ const displayLang = (code) => {
+ const c = (code || '').trim().toLowerCase();
+ if (!c) return '';
+ return langCodeToDisplay.get(c) || code;
+ };
+
const openModal = (mode, data = {}) => {
if (mode === 'add' && limitReached) return;
modal.classList.remove('hidden');
@@ -193,7 +228,8 @@
methodEl.value = mode === 'edit' ? 'PUT' : 'POST';
emojiEl.value = data.emoji || '';
textEl.value = data.keyword || '';
- langEl.value = data.lang || '';
+ langEl.value = (data.lang || 'und').trim() || 'und';
+ langDisplayEl.value = displayLang(data.lang || 'und');
emojiEl.focus();
};
@@ -209,6 +245,14 @@
if (e.target === modal) closeModal();
});
+ langDisplayEl?.addEventListener('input', () => {
+ langEl.value = normalizeLang(langDisplayEl.value);
+ });
+
+ form?.addEventListener('submit', () => {
+ langEl.value = normalizeLang(langDisplayEl?.value || '');
+ });
+
document.querySelectorAll('.edit-btn').forEach((btn) => {
btn.addEventListener('click', () => {
openModal('edit', {
diff --git a/app/resources/views/partials/language-datalist.blade.php b/app/resources/views/partials/language-datalist.blade.php
new file mode 100644
index 0000000..4dfceb5
--- /dev/null
+++ b/app/resources/views/partials/language-datalist.blade.php
@@ -0,0 +1,17 @@
+@php
+ $listId = $id ?? 'language-options';
+ // Fallbacks only. Full list is generated in JS via Intl APIs.
+ $languages = [
+ ['code' => 'und', 'name' => 'Undetermined'],
+ ['code' => 'en', 'name' => 'English'],
+ ['code' => 'id', 'name' => 'Indonesian'],
+ ['code' => 'es', 'name' => 'Spanish'],
+ ['code' => 'fr', 'name' => 'French'],
+ ['code' => 'de', 'name' => 'German'],
+ ];
+@endphp
+
diff --git a/app/resources/views/site/emoji-detail.blade.php b/app/resources/views/site/emoji-detail.blade.php
index dec1c23..669f88e 100644
--- a/app/resources/views/site/emoji-detail.blade.php
+++ b/app/resources/views/site/emoji-detail.blade.php
@@ -258,25 +258,26 @@
-
+
- Add keyword
-
@@ -337,9 +338,33 @@ addRecent(@json($symbol));
const form = document.getElementById('user-keyword-form');
const keywordInput = document.getElementById('user-keyword-input');
const langInput = document.getElementById('user-keyword-lang');
+ const langDataList = document.getElementById('user-keyword-language-options');
const list = document.getElementById('user-keyword-list');
const csrf = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
+ window.dewemojiPopulateLanguageOptions?.(langDataList);
+
+ const langDisplayToCode = new Map();
+ langDataList?.querySelectorAll('option').forEach((option) => {
+ const code = (option.dataset.code || '').trim();
+ const display = (option.value || '').trim();
+ if (!code || !display) return;
+ langDisplayToCode.set(display.toLowerCase(), code);
+ });
+
+ const normalizeLang = (value) => {
+ const v = (value || '').trim();
+ if (!v) return 'und';
+ const byDisplay = langDisplayToCode.get(v.toLowerCase());
+ if (byDisplay) return byDisplay;
+
+ const bracket = v.match(/\(([A-Za-z]{2,3}(?:-[A-Za-z]{2})?)\)\s*$/);
+ if (bracket?.[1]) return bracket[1];
+
+ if (/^[A-Za-z]{2,3}(?:-[A-Za-z]{2})?$/.test(v)) return v;
+ return 'und';
+ };
+
const openModal = () => {
modal.classList.remove('hidden');
modal.classList.add('flex');
@@ -365,7 +390,7 @@ addRecent(@json($symbol));
const payload = {
emoji_slug: @json($slug),
keyword: keywordInput.value.trim(),
- lang: langInput.value.trim() || 'und',
+ lang: normalizeLang(langInput.value),
};
if (!payload.keyword) return;
const res = await fetch('{{ route('dashboard.keywords.store') }}', {
diff --git a/app/resources/views/site/layout.blade.php b/app/resources/views/site/layout.blade.php
index 9d68410..9cda8a9 100644
--- a/app/resources/views/site/layout.blade.php
+++ b/app/resources/views/site/layout.blade.php
@@ -604,6 +604,71 @@
});
})();
+
@stack('scripts')
|