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 <cursoragent@cursor.com>
This commit is contained in:
@@ -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)
|
||||
</script>
|
||||
|
||||
@@ -101,20 +79,6 @@ onMounted(loadSmsToggle)
|
||||
@change="onSmsToggle"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="canSettingsWrite"
|
||||
class="sms-toggle"
|
||||
:class="{ off: !pluginStoreEnabled }"
|
||||
>
|
||||
<span class="sms-toggle-label">فروشگاه افزونه</span>
|
||||
<a-switch
|
||||
:checked="pluginStoreEnabled"
|
||||
:loading="storeToggleLoading"
|
||||
checked-children="روشن"
|
||||
un-checked-children="خاموش"
|
||||
@change="onStoreToggle"
|
||||
/>
|
||||
</div>
|
||||
<button type="button" class="icon-btn" title="داشبورد" @click="goHome">
|
||||
<HomeOutlined />
|
||||
</button>
|
||||
|
||||
@@ -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<string | null>(null)
|
||||
const loadingList = ref(false)
|
||||
const tenants = ref<TenantRow[]>([])
|
||||
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)"
|
||||
>
|
||||
<div
|
||||
v-if="canWrite"
|
||||
class="sms-toggle"
|
||||
:class="{ off: !t.pluginStoreEnabled }"
|
||||
@click.stop
|
||||
>
|
||||
<span class="sms-toggle-label">فروشگاه افزونه</span>
|
||||
<a-switch
|
||||
:checked="t.pluginStoreEnabled === true"
|
||||
:loading="storeBusyId === t.id"
|
||||
checked-children="روشن"
|
||||
un-checked-children="خاموش"
|
||||
@change="(v: boolean) => onStoreToggle(t, v)"
|
||||
/>
|
||||
</div>
|
||||
<strong>{{ t.nameFa }}</strong>
|
||||
<span>{{ t.summary?.expiryLabel }}</span>
|
||||
<small>{{ t.pluginCount }} افزونه</small>
|
||||
@@ -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; }
|
||||
|
||||
@@ -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<string | null>(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<PluginItem[]>([])
|
||||
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)
|
||||
</script>
|
||||
@@ -296,6 +320,20 @@ onMounted(loadDetail)
|
||||
{{ tenant?.product === 'hamkar' ? 'همکار' : 'خانگی' }} · {{ tenant?.domain }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-if="isKhanegi && canWrite"
|
||||
class="sms-toggle"
|
||||
:class="{ off: !pluginStoreEnabled }"
|
||||
>
|
||||
<span class="sms-toggle-label">فروشگاه افزونه</span>
|
||||
<a-switch
|
||||
:checked="pluginStoreEnabled"
|
||||
:loading="storeToggleLoading"
|
||||
checked-children="روشن"
|
||||
un-checked-children="خاموش"
|
||||
@change="onStoreToggle"
|
||||
/>
|
||||
</div>
|
||||
<a-button v-if="canWrite" type="primary" @click="showCreate = true">
|
||||
<PlusOutlined /> افزونه جدید
|
||||
</a-button>
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user