pénzügyi modul továbbfejlesztése (csomagkezelés)
This commit is contained in:
@@ -704,6 +704,113 @@
|
||||
<p class="text-xs text-slate-500 mt-1">{{ $t('packages.feature_capabilities_hint') }}</p>
|
||||
<p v-if="featureCapabilitiesError" class="text-xs text-red-400 mt-1">{{ featureCapabilitiesError }}</p>
|
||||
</div>
|
||||
<!-- ═══ TAB 4: Auto-Renewal Rules ═══ -->
|
||||
<div v-if="activeTab === 'renewal'" class="space-y-6">
|
||||
<!-- Renewal Enabled Toggle -->
|
||||
<div class="bg-slate-700/30 rounded-xl p-5">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-slate-200">🔄 {{ $t('packages.renewal_title') || 'Auto-Renewal' }}</h4>
|
||||
<p class="text-xs text-slate-400 mt-1">{{ $t('packages.renewal_subtitle') || 'Configure automatic subscription renewal rules for this package.' }}</p>
|
||||
</div>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="form.renewal_enabled"
|
||||
type="checkbox"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<div class="w-11 h-6 bg-slate-600 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-indigo-500 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
|
||||
<span class="ms-3 text-sm text-slate-300">{{ form.renewal_enabled ? 'Enabled' : 'Disabled' }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Renewal Settings (shown only when enabled) -->
|
||||
<div v-if="form.renewal_enabled" class="space-y-5">
|
||||
<!-- Auto-Renew Default Toggle -->
|
||||
<div class="bg-slate-700/30 rounded-xl p-5">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-slate-200">{{ $t('packages.renewal_auto_default') || 'Auto-Renew by Default' }}</p>
|
||||
<p class="text-xs text-slate-400 mt-0.5">{{ $t('packages.renewal_auto_default_hint') || 'New subscriptions will have auto-renew enabled by default.' }}</p>
|
||||
</div>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="form.renewal_auto_renew_default"
|
||||
type="checkbox"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<div class="w-11 h-6 bg-slate-600 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-indigo-500 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
|
||||
<span class="ms-3 text-sm text-slate-300">{{ form.renewal_auto_renew_default ? 'Yes' : 'No' }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Renewal Period (grid row) -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">{{ $t('packages.renewal_period') || 'Renewal Period (days)' }}</label>
|
||||
<input
|
||||
v-model.number="form.renewal_period_days"
|
||||
type="number"
|
||||
min="1"
|
||||
max="365"
|
||||
:placeholder="$t('packages.renewal_period_placeholder') || 'Leave empty = use duration.days'"
|
||||
class="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
/>
|
||||
<p class="text-xs text-slate-500 mt-1">{{ $t('packages.renewal_period_hint') || 'If empty, the subscription duration.days is used.' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">{{ $t('packages.renewal_grace_period') || 'Grace Period (days)' }}</label>
|
||||
<input
|
||||
v-model.number="form.renewal_grace_period_days"
|
||||
type="number"
|
||||
min="0"
|
||||
max="90"
|
||||
class="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
/>
|
||||
<p class="text-xs text-slate-500 mt-1">{{ $t('packages.renewal_grace_period_hint') || 'Days after expiry before subscription suspension.' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Retry Settings (grid row) -->
|
||||
<div class="bg-slate-700/30 rounded-xl p-5">
|
||||
<h4 class="text-sm font-semibold text-slate-200 mb-4">{{ $t('packages.renewal_retry_title') || 'Retry on Failure' }}</h4>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-slate-300">{{ $t('packages.renewal_retry') || 'Retry on Failure' }}</p>
|
||||
<p class="text-xs text-slate-400 mt-0.5">{{ $t('packages.renewal_retry_hint') || 'Retry failed payment attempts automatically.' }}</p>
|
||||
</div>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="form.renewal_retry_on_failure"
|
||||
type="checkbox"
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<div class="w-11 h-6 bg-slate-600 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-indigo-500 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div>
|
||||
<span class="ms-3 text-sm text-slate-300">{{ form.renewal_retry_on_failure ? 'Yes' : 'No' }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">{{ $t('packages.renewal_max_retry') || 'Max Retry Count' }}</label>
|
||||
<input
|
||||
v-model.number="form.renewal_max_retry_count"
|
||||
type="number"
|
||||
min="0"
|
||||
max="20"
|
||||
class="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-lg text-white text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Legacy data notice (empty state when disabled) -->
|
||||
<div v-if="!form.renewal_enabled" class="bg-slate-700/20 rounded-xl p-5 text-center">
|
||||
<p class="text-sm text-slate-400">{{ $t('packages.renewal_disabled_hint') || 'Auto-renewal is disabled for this package. Toggle the switch above to enable it.' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -825,6 +932,14 @@ interface SubscriptionTier {
|
||||
is_public?: boolean
|
||||
available_until?: string | null
|
||||
} | null
|
||||
renewal?: {
|
||||
enabled?: boolean
|
||||
auto_renew_default?: boolean
|
||||
renewal_period_days?: number | null
|
||||
retry_on_failure?: boolean
|
||||
max_retry_count?: number
|
||||
grace_period_days?: number
|
||||
} | null
|
||||
}
|
||||
is_custom: boolean
|
||||
tier_level: number
|
||||
@@ -855,6 +970,13 @@ interface PackageForm {
|
||||
trial_days_on_signup: number
|
||||
feature_capabilities_json: string
|
||||
pricing_zones: Record<string, { monthly_price: number; yearly_price: number; credit_price: number; currency: string }>
|
||||
// ── P0 Phase E: Auto-renewal rules ──
|
||||
renewal_enabled: boolean
|
||||
renewal_auto_renew_default: boolean
|
||||
renewal_period_days: number | null
|
||||
renewal_retry_on_failure: boolean
|
||||
renewal_max_retry_count: number
|
||||
renewal_grace_period_days: number
|
||||
}
|
||||
|
||||
// ── Tab Configuration ──────────────────────────────────────────────
|
||||
@@ -863,6 +985,7 @@ const tabs = [
|
||||
{ key: 'basic', label: '📋 Basic Info' },
|
||||
{ key: 'pricing', label: '💰 Pricing' },
|
||||
{ key: 'features', label: '⚡ Features' },
|
||||
{ key: 'renewal', label: '🔄 Auto-Renewal' },
|
||||
]
|
||||
|
||||
const activeTab = ref('basic')
|
||||
@@ -956,7 +1079,14 @@ const defaultForm: PackageForm = {
|
||||
is_default_fallback: false,
|
||||
trial_days_on_signup: 0,
|
||||
feature_capabilities_json: '{\n "ai_analysis": false,\n "priority_support": false,\n "api_access": false\n}',
|
||||
pricing_zones: {}
|
||||
pricing_zones: {},
|
||||
// ── P0 Phase E: Auto-renewal defaults ──
|
||||
renewal_enabled: false,
|
||||
renewal_auto_renew_default: false,
|
||||
renewal_period_days: null,
|
||||
renewal_retry_on_failure: true,
|
||||
renewal_max_retry_count: 3,
|
||||
renewal_grace_period_days: 7,
|
||||
}
|
||||
|
||||
const form = reactive<PackageForm>({ ...defaultForm })
|
||||
@@ -1181,6 +1311,15 @@ function openEditModal(pkg: SubscriptionTier) {
|
||||
// Load feature capabilities as JSON string
|
||||
form.feature_capabilities_json = JSON.stringify(pkg.feature_capabilities || {}, null, 2)
|
||||
|
||||
// ── P0 Phase E: Load auto-renewal rules ──
|
||||
const renewalFromRules = rules.renewal || {}
|
||||
form.renewal_enabled = renewalFromRules.enabled ?? false
|
||||
form.renewal_auto_renew_default = renewalFromRules.auto_renew_default ?? false
|
||||
form.renewal_period_days = renewalFromRules.renewal_period_days ?? null
|
||||
form.renewal_retry_on_failure = renewalFromRules.retry_on_failure ?? true
|
||||
form.renewal_max_retry_count = renewalFromRules.max_retry_count ?? 3
|
||||
form.renewal_grace_period_days = renewalFromRules.grace_period_days ?? 7
|
||||
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
@@ -1301,6 +1440,15 @@ async function savePackage() {
|
||||
lifecycle: {
|
||||
is_public: true,
|
||||
},
|
||||
// ── P0 Phase E: Auto-renewal rules ──
|
||||
renewal: {
|
||||
enabled: form.renewal_enabled,
|
||||
auto_renew_default: form.renewal_auto_renew_default,
|
||||
renewal_period_days: form.renewal_period_days,
|
||||
retry_on_failure: form.renewal_retry_on_failure,
|
||||
max_retry_count: form.renewal_max_retry_count,
|
||||
grace_period_days: form.renewal_grace_period_days,
|
||||
},
|
||||
}
|
||||
|
||||
if (isCreating.value) {
|
||||
|
||||
Reference in New Issue
Block a user