From 8d0dfba60f8fb948e25a2bc3be052523f3e7aa8a Mon Sep 17 00:00:00 2001 From: adonis Date: Mon, 7 Sep 2026 19:51:12 +0330 Subject: [PATCH] fix(plugins): move plugin store toggle onto each khanegi tenant Remove the global header switch and let SuperAdmin enable the store per project from the plugins list and tenant page. Co-authored-by: Cursor --- src/layouts/AppLayout.vue | 36 ------------------- src/pages/PluginPanelPage.vue | 65 +++++++++++++++++++++++++++++++++- src/pages/PluginTenantPage.vue | 58 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 37 deletions(-) diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue index ef40781..2484b3e 100644 --- a/src/layouts/AppLayout.vue +++ b/src/layouts/AppLayout.vue @@ -9,9 +9,7 @@ import api from '@/api/http' const auth = useAuthStore() const router = useRouter() const adminSmsEnabled = ref(true) -const pluginStoreEnabled = ref(false) const smsToggleLoading = ref(false) -const storeToggleLoading = ref(false) const canSettingsWrite = computed(() => auth.can('settings:write')) function goHome() { @@ -28,7 +26,6 @@ async function loadSmsToggle() { try { const { data } = await api.get('/settings/admin-sms') adminSmsEnabled.value = data.adminSmsEnabled !== false - pluginStoreEnabled.value = data.pluginStoreEnabled === true } catch { // keep default on; non-blocking for layout } @@ -53,25 +50,6 @@ async function onSmsToggle(checked: boolean) { } } -async function onStoreToggle(checked: boolean) { - if (!canSettingsWrite.value) return - storeToggleLoading.value = true - const previous = pluginStoreEnabled.value - pluginStoreEnabled.value = checked - try { - const { data } = await api.patch('/settings/plugin-store', { enabled: checked }) - pluginStoreEnabled.value = data.pluginStoreEnabled === true - message.success( - pluginStoreEnabled.value ? 'فروشگاه افزونه روشن شد' : 'فروشگاه افزونه خاموش شد', - ) - } catch (e: any) { - pluginStoreEnabled.value = previous - message.error(e?.response?.data?.message || 'تغییر فروشگاه افزونه ناموفق') - } finally { - storeToggleLoading.value = false - } -} - onMounted(loadSmsToggle) @@ -101,20 +79,6 @@ onMounted(loadSmsToggle) @change="onSmsToggle" /> -
- فروشگاه افزونه - -
diff --git a/src/pages/PluginPanelPage.vue b/src/pages/PluginPanelPage.vue index 647706a..17051ca 100644 --- a/src/pages/PluginPanelPage.vue +++ b/src/pages/PluginPanelPage.vue @@ -4,6 +4,7 @@ import { useRouter } from 'vue-router' import { message } from 'ant-design-vue' import { ArrowRightOutlined, AppstoreAddOutlined, SearchOutlined } from '@ant-design/icons-vue' import api from '@/api/http' +import { useAuthStore } from '@/stores/auth' type ExpiryLevel = 'healthy' | 'warning' | 'critical' | 'unknown' | 'none' @@ -14,6 +15,7 @@ type TenantRow = { domain: string slug?: string pluginCount: number + pluginStoreEnabled?: boolean summary: { expiryLevel: ExpiryLevel expiryLabel: string @@ -32,6 +34,9 @@ type RequestRow = { } const router = useRouter() +const auth = useAuthStore() +const canWrite = computed(() => auth.can('plugins:write')) +const storeBusyId = ref(null) const loadingList = ref(false) const tenants = ref([]) const q = ref('') @@ -72,6 +77,27 @@ function openTenant(id: string) { router.push({ name: 'plugin-tenant', params: { tenantId: id } }) } +async function onStoreToggle(tenant: TenantRow, checked: boolean) { + if (!canWrite.value) return + storeBusyId.value = tenant.id + const previous = tenant.pluginStoreEnabled === true + tenant.pluginStoreEnabled = checked + try { + const { data } = await api.patch(`/plugins/tenants/${tenant.id}/store-enabled`, { + enabled: checked, + }) + tenant.pluginStoreEnabled = data.pluginStoreEnabled === true + message.success( + tenant.pluginStoreEnabled ? 'فروشگاه افزونه روشن شد' : 'فروشگاه افزونه خاموش شد', + ) + } catch (e: any) { + tenant.pluginStoreEnabled = previous + message.error(e?.response?.data?.message || 'تغییر فروشگاه افزونه ناموفق') + } finally { + storeBusyId.value = null + } +} + async function loadTenants() { loadingList.value = true try { @@ -306,6 +332,21 @@ onMounted(() => { :class="toneClass(t.summary?.expiryLevel)" @click="openTenant(t.id)" > +
+ فروشگاه افزونه + +
{{ t.nameFa }} {{ t.summary?.expiryLabel }} {{ t.pluginCount }} افزونه @@ -409,7 +450,7 @@ onMounted(() => { .tenant-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(min(100%, 160px), 1fr)); + grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr)); gap: 10px; } @@ -426,6 +467,28 @@ onMounted(() => { color: #17324d; } +.sms-toggle { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 6px 10px; + border-radius: 999px; + background: #e8f8f1; + border: 1px solid #b7ebd4; + margin-bottom: 6px; +} +.sms-toggle.off { + background: #fff1f0; + border-color: #ffccc7; +} +.sms-toggle-label { + font-size: 0.75rem; + font-weight: 700; + color: #334155; + white-space: nowrap; +} + .tenant-card strong { font-size: 0.95rem; } .tenant-card span, .tenant-card small { opacity: 0.92; font-size: 0.82rem; } diff --git a/src/pages/PluginTenantPage.vue b/src/pages/PluginTenantPage.vue index 6550c33..89b0466 100644 --- a/src/pages/PluginTenantPage.vue +++ b/src/pages/PluginTenantPage.vue @@ -43,6 +43,8 @@ const loading = ref(false) const saving = ref(false) const renewing = ref(false) const creating = ref(false) +const storeToggleLoading = ref(false) +const pluginStoreEnabled = computed(() => tenant.value?.pluginStoreEnabled === true) const selectedId = ref(null) const showCreate = ref(false) @@ -52,6 +54,7 @@ const tenant = ref<{ slug: string nameFa: string domain: string + pluginStoreEnabled?: boolean } | null>(null) const plugins = ref([]) const planOptions = ref<{ months: number; label: string }[]>([]) @@ -280,6 +283,27 @@ function confirmDelete() { }) } +async function onStoreToggle(checked: boolean) { + if (!canWrite.value || !tenantId.value || !isKhanegi.value) return + storeToggleLoading.value = true + const previous = tenant.value?.pluginStoreEnabled === true + if (tenant.value) tenant.value.pluginStoreEnabled = checked + try { + const { data } = await api.patch(`/plugins/tenants/${tenantId.value}/store-enabled`, { + enabled: checked, + }) + if (tenant.value) tenant.value.pluginStoreEnabled = data.pluginStoreEnabled === true + message.success( + pluginStoreEnabled.value ? 'فروشگاه افزونه روشن شد' : 'فروشگاه افزونه خاموش شد', + ) + } catch (e: any) { + if (tenant.value) tenant.value.pluginStoreEnabled = previous + message.error(e?.response?.data?.message || 'تغییر فروشگاه افزونه ناموفق') + } finally { + storeToggleLoading.value = false + } +} + watch(() => route.params.tenantId, loadDetail) onMounted(loadDetail) @@ -296,6 +320,20 @@ onMounted(loadDetail) {{ tenant?.product === 'hamkar' ? 'همکار' : 'خانگی' }} · {{ tenant?.domain }}

+
+ فروشگاه افزونه + +
افزونه جدید @@ -648,4 +686,24 @@ onMounted(loadDetail) color: #64748b; font-size: 0.85rem; } + +.sms-toggle { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border-radius: 999px; + background: #e8f8f1; + border: 1px solid #b7ebd4; +} +.sms-toggle.off { + background: #fff1f0; + border-color: #ffccc7; +} +.sms-toggle-label { + font-size: 0.8rem; + font-weight: 700; + color: #334155; + white-space: nowrap; +}