Files
service-finder/frontend_admin/.nuxt/dist/server/_nuxt/point-rules-C1ou37W0.js.map.json
2026-06-30 08:56:55 +00:00

1 line
18 KiB
JSON

{"file":"point-rules-C1ou37W0.js","mappings":"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","names":["_unref","_ssrRenderList","_ssrInterpolate","_ssrRenderClass","_ssrRenderAttr","_ssrIncludeBooleanAttr","_ssrLooseContain"],"sources":["../../../../pages/gamification/point-rules.vue"],"sourcesContent":["<template>\n <div>\n <!-- Page Header -->\n <div class=\"mb-8\">\n <h1 class=\"text-2xl font-bold text-white\">{{ t('gamification.point_rules.title') }}</h1>\n <p class=\"text-slate-400 mt-1\">{{ t('gamification.point_rules.subtitle') }}</p>\n </div>\n\n <!-- Loading State -->\n <div v-if=\"loading\" class=\"flex items-center justify-center py-20\">\n <div class=\"text-slate-400 flex items-center gap-3\">\n <svg class=\"w-5 h-5 animate-spin\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n <span>{{ t('gamification.point_rules.loading') }}</span>\n </div>\n </div>\n\n <!-- Error State -->\n <div v-else-if=\"error\" class=\"bg-rose-500/10 border border-rose-500/30 rounded-xl p-6 mb-8\">\n <p class=\"text-rose-400\">{{ t('gamification.point_rules.load_error') }}</p>\n <button @click=\"fetchRules\" class=\"mt-3 text-sm text-rose-300 hover:text-rose-200 underline\">{{ t('gamification.point_rules.retry') }}</button>\n </div>\n\n <template v-else>\n <!-- Create Button -->\n <div class=\"mb-6 flex justify-end\">\n <button @click=\"openCreateModal\" class=\"px-4 py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded-lg text-sm font-medium transition flex items-center gap-2\">\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n {{ t('gamification.point_rules.create_btn') }}\n </button>\n </div>\n\n <!-- Rules Table -->\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 overflow-hidden\">\n <table class=\"w-full\">\n <thead>\n <tr class=\"border-b border-slate-700\">\n <th class=\"text-left px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_id') }}</th>\n <th class=\"text-left px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_action_key') }}</th>\n <th class=\"text-left px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_points') }}</th>\n <th class=\"text-left px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_description') }}</th>\n <th class=\"text-left px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_status') }}</th>\n <th class=\"text-right px-6 py-4 text-xs font-medium text-slate-400 uppercase tracking-wider\">{{ t('gamification.point_rules.col_actions') }}</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-slate-700/50\">\n <tr v-for=\"rule in rules\" :key=\"rule.id\" class=\"hover:bg-slate-700/30 transition\">\n <td class=\"px-6 py-4 text-sm text-slate-400\">{{ rule.id }}</td>\n <td class=\"px-6 py-4\">\n <code class=\"text-sm font-mono text-amber-300 bg-amber-500/10 px-2 py-0.5 rounded\">{{ rule.action_key }}</code>\n </td>\n <td class=\"px-6 py-4\">\n <span class=\"text-sm font-semibold\" :class=\"rule.points >= 0 ? 'text-emerald-400' : 'text-rose-400'\">\n {{ rule.points >= 0 ? '+' : '' }}{{ rule.points }}\n </span>\n </td>\n <td class=\"px-6 py-4 text-sm text-slate-300 max-w-xs truncate\">{{ rule.description || '—' }}</td>\n <td class=\"px-6 py-4\">\n <span class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium\" :class=\"rule.is_active ? 'bg-emerald-500/10 text-emerald-400' : 'bg-slate-500/10 text-slate-400'\">\n {{ rule.is_active ? t('gamification.point_rules.active') : t('gamification.point_rules.inactive') }}\n </span>\n </td>\n <td class=\"px-6 py-4 text-right\">\n <div class=\"flex items-center justify-end gap-2\">\n <button @click=\"openEditModal(rule)\" class=\"p-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-slate-700 transition\" :title=\"t('gamification.point_rules.edit')\">\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button @click=\"confirmDelete(rule)\" class=\"p-1.5 rounded-lg text-slate-400 hover:text-rose-400 hover:bg-rose-500/10 transition\" :title=\"t('gamification.point_rules.delete_title')\">\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </div>\n </td>\n </tr>\n <tr v-if=\"rules.length === 0\">\n <td colspan=\"6\" class=\"px-6 py-12 text-center text-sm text-slate-500\">\n {{ t('gamification.point_rules.empty') }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </template>\n\n <!-- Create/Edit Modal -->\n <div v-if=\"showModal\" class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\" @click.self=\"closeModal\">\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 w-full max-w-lg mx-4 shadow-2xl\">\n <h2 class=\"text-lg font-semibold text-white mb-6\">{{ editingRule ? t('gamification.point_rules.edit_title') : t('gamification.point_rules.create_title') }}</h2>\n\n <form @submit.prevent=\"saveRule\" class=\"space-y-4\">\n <!-- Action Key -->\n <div>\n <label class=\"block text-sm font-medium text-slate-300 mb-1\">{{ t('gamification.point_rules.action_key_label') }}</label>\n <input\n v-model=\"form.action_key\"\n type=\"text\"\n required\n maxlength=\"100\"\n :placeholder=\"t('gamification.point_rules.action_key_placeholder')\"\n class=\"w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 text-sm\"\n />\n </div>\n\n <!-- Points -->\n <div>\n <label class=\"block text-sm font-medium text-slate-300 mb-1\">{{ t('gamification.point_rules.points_label') }}</label>\n <input\n v-model.number=\"form.points\"\n type=\"number\"\n required\n min=\"-10000\"\n max=\"10000\"\n placeholder=\"0\"\n class=\"w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 text-sm\"\n />\n </div>\n\n <!-- Description -->\n <div>\n <label class=\"block text-sm font-medium text-slate-300 mb-1\">{{ t('gamification.point_rules.description_label') }}</label>\n <textarea\n v-model=\"form.description\"\n rows=\"3\"\n :placeholder=\"t('gamification.point_rules.description_placeholder')\"\n class=\"w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 text-sm resize-none\"\n ></textarea>\n </div>\n\n <!-- Is Active -->\n <div class=\"flex items-center gap-3\">\n <input\n v-model=\"form.is_active\"\n type=\"checkbox\"\n id=\"is_active\"\n class=\"w-4 h-4 rounded bg-slate-700 border-slate-600 text-amber-500 focus:ring-amber-500/50\"\n />\n <label for=\"is_active\" class=\"text-sm text-slate-300\">{{ t('gamification.point_rules.active_label') }}</label>\n </div>\n\n <!-- Error Message -->\n <div v-if=\"formError\" class=\"bg-rose-500/10 border border-rose-500/30 rounded-lg p-3\">\n <p class=\"text-sm text-rose-400\">{{ formError }}</p>\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-end gap-3 pt-2\">\n <button type=\"button\" @click=\"closeModal\" class=\"px-4 py-2 text-sm text-slate-400 hover:text-white transition\">{{ t('gamification.point_rules.cancel') }}</button>\n <button type=\"submit\" :disabled=\"saving\" class=\"px-4 py-2 bg-amber-600 hover:bg-amber-500 disabled:bg-amber-600/50 text-white rounded-lg text-sm font-medium transition flex items-center gap-2\">\n <svg v-if=\"saving\" class=\"w-4 h-4 animate-spin\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n {{ saving ? t('gamification.point_rules.saving') : t('gamification.point_rules.save') }}\n </button>\n </div>\n </form>\n </div>\n </div>\n\n <!-- Delete Confirmation Modal -->\n <div v-if=\"showDeleteModal\" class=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\" @click.self=\"showDeleteModal = false\">\n <div class=\"bg-slate-800 rounded-xl border border-slate-700 p-6 w-full max-w-md mx-4 shadow-2xl\">\n <h2 class=\"text-lg font-semibold text-white mb-2\">{{ t('gamification.point_rules.delete_confirm_title') }}</h2>\n <p class=\"text-sm text-slate-400 mb-6\">\n {{ t('gamification.point_rules.delete_confirm_body', { action_key: deletingRule?.action_key }) }}\n </p>\n <div class=\"flex justify-end gap-3\">\n <button @click=\"showDeleteModal = false\" class=\"px-4 py-2 text-sm text-slate-400 hover:text-white transition\">{{ t('gamification.point_rules.cancel') }}</button>\n <button @click=\"deleteRule\" :disabled=\"saving\" class=\"px-4 py-2 bg-rose-600 hover:bg-rose-500 disabled:bg-rose-600/50 text-white rounded-lg text-sm font-medium transition\">\n {{ saving ? t('gamification.point_rules.deleting') : t('gamification.point_rules.delete') }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Success Toast -->\n <div v-if=\"toast\" class=\"fixed bottom-6 right-6 z-50 bg-emerald-600 text-white px-4 py-3 rounded-lg shadow-lg text-sm flex items-center gap-2 animate-slide-up\">\n <svg class=\"w-4 h-4 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n {{ toast }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\ndefinePageMeta({\n middleware: 'auth',\n})\n\nconst { t } = useI18n()\n\ninterface PointRule {\n id: number\n action_key: string\n points: number\n description: string | null\n is_active: boolean\n}\n\nconst loading = ref(true)\nconst error = ref(false)\nconst rules = ref<PointRule[]>([])\nconst showModal = ref(false)\nconst showDeleteModal = ref(false)\nconst editingRule = ref<PointRule | null>(null)\nconst deletingRule = ref<PointRule | null>(null)\nconst saving = ref(false)\nconst formError = ref('')\nconst toast = ref('')\n\nconst form = reactive({\n action_key: '',\n points: 0,\n description: '',\n is_active: true,\n})\n\nfunction getHeaders(): Record<string, string> {\n const tokenCookie = useCookie('access_token')\n const token = tokenCookie.value\n return token ? { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' }\n}\n\nfunction showToast(msg: string) {\n toast.value = msg\n setTimeout(() => { toast.value = '' }, 3000)\n}\n\nfunction resetForm() {\n form.action_key = ''\n form.points = 0\n form.description = ''\n form.is_active = true\n formError.value = ''\n}\n\nfunction openCreateModal() {\n editingRule.value = null\n resetForm()\n showModal.value = true\n}\n\nfunction openEditModal(rule: PointRule) {\n editingRule.value = rule\n form.action_key = rule.action_key\n form.points = rule.points\n form.description = rule.description || ''\n form.is_active = rule.is_active\n formError.value = ''\n showModal.value = true\n}\n\nfunction closeModal() {\n showModal.value = false\n editingRule.value = null\n resetForm()\n}\n\nfunction confirmDelete(rule: PointRule) {\n deletingRule.value = rule\n showDeleteModal.value = true\n}\n\nasync function fetchRules() {\n loading.value = true\n error.value = false\n try {\n const data = await $fetch<PointRule[]>('/api/v1/admin/gamification/point-rules', { headers: getHeaders() })\n rules.value = data\n } catch (e) {\n console.error('Failed to fetch point rules:', e)\n error.value = true\n } finally {\n loading.value = false\n }\n}\n\nasync function saveRule() {\n saving.value = true\n formError.value = ''\n try {\n const payload = {\n action_key: form.action_key,\n points: form.points,\n description: form.description || null,\n is_active: form.is_active,\n }\n\n if (editingRule.value) {\n // Update existing rule\n await $fetch(`/api/v1/admin/gamification/point-rules/${editingRule.value.id}`, {\n method: 'PUT',\n headers: getHeaders(),\n body: payload,\n })\n showToast(t('gamification.point_rules.updated'))\n } else {\n // Create new rule\n await $fetch('/api/v1/admin/gamification/point-rules', {\n method: 'POST',\n headers: getHeaders(),\n body: payload,\n })\n showToast(t('gamification.point_rules.created'))\n }\n closeModal()\n await fetchRules()\n } catch (e: any) {\n if (e?.response?.status === 409) {\n formError.value = t('gamification.point_rules.duplicate_error')\n } else {\n formError.value = t('gamification.point_rules.save_error')\n }\n console.error('Failed to save point rule:', e)\n } finally {\n saving.value = false\n }\n}\n\nasync function deleteRule() {\n if (!deletingRule.value) return\n saving.value = true\n try {\n await $fetch(`/api/v1/admin/gamification/point-rules/${deletingRule.value.id}`, {\n method: 'DELETE',\n headers: getHeaders(),\n })\n showToast(t('gamification.point_rules.deleted'))\n showDeleteModal.value = false\n deletingRule.value = null\n await fetchRules()\n } catch (e) {\n console.error('Failed to delete point rule:', e)\n } finally {\n saving.value = false\n }\n}\n\nonMounted(() => {\n fetchRules()\n})\n</script>\n\n<style scoped>\n.animate-slide-up {\n animation: slideUp 0.3s ease-out;\n}\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(1rem);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n</style>\n"],"version":3}