feat(billing): start demos on the core plan and apply tariff levels
New signups get the core module set with the 7-day demo. Superadmin can apply core, team, organization, or dedicated and the owner's environments take that level's access. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
import type { BillingAccountDto, BillingPlanCode, OwnerSeatUsageDto, StorageUsageDto } from '@mento/shared';
|
||||
import type { BillingAccountDto, BillingPlanCode, OwnerSeatUsageDto, ProductPlanCode, StorageUsageDto } from '@mento/shared';
|
||||
import {
|
||||
BILLING_PLAN_CODES,
|
||||
BILLING_PLAN_LABELS,
|
||||
BILLING_USER_CAPS,
|
||||
PRODUCT_PLAN_CODES,
|
||||
PRODUCT_PLAN_LABELS,
|
||||
isPermanentBillingPlan,
|
||||
} from '@mento/shared';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
@@ -19,6 +21,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
apply: [planCode: BillingPlanCode];
|
||||
product: [planCode: ProductPlanCode];
|
||||
}>();
|
||||
|
||||
const usage = ref<StorageUsageDto | null>(null);
|
||||
@@ -178,6 +181,22 @@ onMounted(() => {
|
||||
{{ BILLING_PLAN_LABELS[code] }}
|
||||
</button>
|
||||
</div>
|
||||
<strong>سطح امکانات</strong>
|
||||
<p class="muted">
|
||||
انتخاب سطح، دسترسی محیطهای این مالک را طبق تعرفه تیک میزند. هسته و تیم و سازمان سقف کاربر و محیط را هم تنظیم میکنند. ثبتنام دمو از هسته شروع میشود.
|
||||
</p>
|
||||
<div class="plan-row">
|
||||
<button
|
||||
v-for="code in PRODUCT_PLAN_CODES"
|
||||
:key="code"
|
||||
class="btn btn-ghost"
|
||||
type="button"
|
||||
:disabled="busyId === `product-${userId}-${code}`"
|
||||
@click="emit('product', code)"
|
||||
>
|
||||
{{ PRODUCT_PLAN_LABELS[code] }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="billing" class="storage">
|
||||
<strong>تعداد کاربر</strong>
|
||||
<p v-if="seats" class="muted">
|
||||
|
||||
@@ -6,8 +6,9 @@ import type {
|
||||
BillingPlanCode,
|
||||
PlatformOrganizationDto,
|
||||
PlatformOrgMemberDto,
|
||||
ProductPlanCode,
|
||||
} from '@mento/shared';
|
||||
import { BILLING_PLAN_LABELS } from '@mento/shared';
|
||||
import { BILLING_PLAN_LABELS, PRODUCT_PLAN_LABELS } from '@mento/shared';
|
||||
import { platformApi } from '@/lib/platform-api';
|
||||
import AdmBillingPanel from './AdmBillingPanel.vue';
|
||||
|
||||
@@ -58,6 +59,21 @@ async function loadOwners() {
|
||||
}
|
||||
}
|
||||
|
||||
async function applyProduct(userId: string, planCode: ProductPlanCode) {
|
||||
busyId.value = `product-${userId}-${planCode}`;
|
||||
error.value = '';
|
||||
success.value = '';
|
||||
try {
|
||||
await platformApi.post(`/platform/organizations/${orgId.value}/product-plan`, { planCode });
|
||||
await loadOwners();
|
||||
success.value = `سطح «${PRODUCT_PLAN_LABELS[planCode]}» روی دسترسیها اعمال شد`;
|
||||
} catch (e) {
|
||||
error.value = apiError(e, 'اعمال سطح امکانات ناموفق بود');
|
||||
} finally {
|
||||
busyId.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
async function applyPlan(userId: string, planCode: BillingPlanCode) {
|
||||
busyId.value = `plan-${userId}-${planCode}`;
|
||||
error.value = '';
|
||||
@@ -100,11 +116,12 @@ onMounted(() => {
|
||||
<p v-else-if="orgId && !owners.length" class="muted">این محیط مالک فعالی ندارد.</p>
|
||||
<AdmBillingPanel
|
||||
v-for="owner in owners"
|
||||
:key="`${owner.userId}-${owner.billing?.planCode ?? ''}-${owner.billing?.expiresAt ?? ''}`"
|
||||
:key="`${owner.userId}-${owner.billing?.planCode ?? ''}-${owner.billing?.expiresAt ?? ''}-${owner.billing?.maxUsers ?? ''}-${owner.billing?.maxOrganizations ?? ''}`"
|
||||
:user-id="owner.userId"
|
||||
:billing="owner.billing"
|
||||
:busy-id="busyId"
|
||||
@apply="applyPlan(owner.userId, $event)"
|
||||
@product="applyProduct(owner.userId, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
CreatePlatformOrganizationRequest,
|
||||
OrgModuleKey,
|
||||
PlatformOrganizationDto,
|
||||
ProductPlanCode,
|
||||
UpdatePlatformOrganizationRequest,
|
||||
UserRole,
|
||||
} from '@mento/shared';
|
||||
@@ -12,6 +13,8 @@ import {
|
||||
BILLING_PLAN_LABELS,
|
||||
ORG_MODULE_GROUPS,
|
||||
ORG_MODULE_LABELS,
|
||||
PRODUCT_PLAN_CODES,
|
||||
PRODUCT_PLAN_LABELS,
|
||||
USER_ROLE_LABELS,
|
||||
USER_ROLES,
|
||||
isBusinessField,
|
||||
@@ -265,6 +268,25 @@ async function setMembershipRole(membershipId: string, role: UserRole) {
|
||||
}
|
||||
}
|
||||
|
||||
async function applyProductPlan(orgId: string, planCode: ProductPlanCode) {
|
||||
savingModules.value = true;
|
||||
error.value = '';
|
||||
success.value = '';
|
||||
try {
|
||||
const { data } = await platformApi.post<PlatformOrganizationDto>(
|
||||
`/platform/organizations/${orgId}/product-plan`,
|
||||
{ planCode },
|
||||
);
|
||||
moduleDraft.value = [...(data.enabledModules ?? [])];
|
||||
if (detail.value?.id === orgId) detail.value = data;
|
||||
success.value = `سطح «${PRODUCT_PLAN_LABELS[planCode]}» روی دسترسیها اعمال شد`;
|
||||
} catch (e) {
|
||||
error.value = apiError(e, 'اعمال سطح امکانات ناموفق بود');
|
||||
} finally {
|
||||
savingModules.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleModule(key: OrgModuleKey) {
|
||||
if (moduleDraft.value.includes(key)) {
|
||||
moduleDraft.value = moduleDraft.value.filter((k) => k !== key);
|
||||
@@ -434,6 +456,16 @@ onMounted(() => {
|
||||
<div class="modules">
|
||||
<div class="modules-head">
|
||||
<strong>ماژولهای فعال</strong>
|
||||
<button
|
||||
v-for="code in PRODUCT_PLAN_CODES"
|
||||
:key="code"
|
||||
class="btn btn-ghost"
|
||||
type="button"
|
||||
:disabled="savingModules"
|
||||
@click="applyProductPlan(org.id, code)"
|
||||
>
|
||||
{{ PRODUCT_PLAN_LABELS[code] }}
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user