Ajuste em Massa - Paciente, Terapeuta, Clinica e Admin - Inicio agenda
This commit is contained in:
@@ -70,13 +70,16 @@ const enabledFeatureIdsByPlanId = computed(() => {
|
||||
|
||||
const currentPlanId = computed(() => subscription.value?.plan_id || null)
|
||||
|
||||
function planKeyById(id) {
|
||||
function planKeyById (id) {
|
||||
return planById.value.get(id)?.key || null
|
||||
}
|
||||
|
||||
const currentPlanKey = computed(() => planKeyById(currentPlanId.value))
|
||||
const currentPlanKey = computed(() => {
|
||||
// ✅ fallback: se não carregou plans ainda, usa o plan_key da subscription
|
||||
return planKeyById(currentPlanId.value) || subscription.value?.plan_key || null
|
||||
})
|
||||
|
||||
function friendlyFeatureLabel(featureKey) {
|
||||
function friendlyFeatureLabel (featureKey) {
|
||||
return featureLabels[featureKey] || featureKey
|
||||
}
|
||||
|
||||
@@ -92,7 +95,7 @@ const sortedPlans = computed(() => {
|
||||
return arr
|
||||
})
|
||||
|
||||
function planBenefits(planId) {
|
||||
function planBenefits (planId) {
|
||||
const set = enabledFeatureIdsByPlanId.value.get(planId) || new Set()
|
||||
const list = features.value.map((f) => ({
|
||||
ok: set.has(f.id),
|
||||
@@ -104,34 +107,82 @@ function planBenefits(planId) {
|
||||
return list
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
function goBack () {
|
||||
router.back()
|
||||
}
|
||||
|
||||
function goBilling() {
|
||||
function goBilling () {
|
||||
router.push('/admin/billing')
|
||||
}
|
||||
|
||||
function contactSupport() {
|
||||
function contactSupport () {
|
||||
router.push('/admin/billing')
|
||||
}
|
||||
|
||||
async function fetchAll() {
|
||||
// ✅ revalida a rota atual para o guard reavaliar features após troca de plano
|
||||
async function revalidateCurrentRoute () {
|
||||
// tenta respeitar um redirectTo (quando usuário veio por recurso bloqueado)
|
||||
const redirectTo = route.query.redirectTo ? String(route.query.redirectTo) : null
|
||||
|
||||
// se existe redirectTo, tente ir para ele (guard decide se entra ou volta ao upgrade)
|
||||
if (redirectTo) {
|
||||
try {
|
||||
await router.replace(redirectTo)
|
||||
return
|
||||
} catch (_) {
|
||||
// se falhar, cai no refresh da rota atual
|
||||
}
|
||||
}
|
||||
|
||||
// força o vue-router a reprocessar a rota (dispara beforeEach)
|
||||
try {
|
||||
await router.replace(router.currentRoute.value.fullPath)
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
async function fetchAll () {
|
||||
loading.value = true
|
||||
try {
|
||||
const tid = tenantId.value
|
||||
if (!tid) throw new Error('Tenant ativo não encontrado.')
|
||||
|
||||
const [pRes, fRes, pfRes, sRes] = await Promise.all([
|
||||
supabase.from('plans').select('*').order('key', { ascending: true }),
|
||||
supabase.from('plans').select('*').eq('is_active', true).order('key', { ascending: true }),
|
||||
supabase.from('features').select('*').order('key', { ascending: true }),
|
||||
supabase.from('plan_features').select('plan_id, feature_id'),
|
||||
supabase
|
||||
.from('subscriptions')
|
||||
.select('id, tenant_id, plan_id, plan_key, interval, status, created_at, updated_at')
|
||||
// ✅ pega mais campos úteis e faz join do plano (ajuda a exibir e debugar)
|
||||
.select(`
|
||||
id,
|
||||
tenant_id,
|
||||
user_id,
|
||||
plan_id,
|
||||
plan_key,
|
||||
"interval",
|
||||
status,
|
||||
provider,
|
||||
source,
|
||||
started_at,
|
||||
current_period_start,
|
||||
current_period_end,
|
||||
created_at,
|
||||
updated_at,
|
||||
plan:plan_id (
|
||||
id,
|
||||
key,
|
||||
name,
|
||||
description,
|
||||
price_cents,
|
||||
currency,
|
||||
billing_interval,
|
||||
is_active
|
||||
)
|
||||
`)
|
||||
.eq('tenant_id', tid)
|
||||
.eq('status', 'active')
|
||||
.order('updated_at', { ascending: false })
|
||||
// ✅ created_at é mais confiável que updated_at em assinaturas manuais
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(1)
|
||||
.maybeSingle()
|
||||
])
|
||||
@@ -142,13 +193,20 @@ async function fetchAll() {
|
||||
|
||||
// ✅ subscription pode ser null sem quebrar a página
|
||||
if (sRes.error) {
|
||||
console.warn('[Upgrade] sem subscription ativa (ok):', sRes.error)
|
||||
console.warn('[Upgrade] erro ao buscar subscription:', sRes.error)
|
||||
}
|
||||
|
||||
plans.value = pRes.data || []
|
||||
features.value = fRes.data || []
|
||||
planFeatures.value = pfRes.data || []
|
||||
subscription.value = sRes.data || null
|
||||
|
||||
// pode remover esses logs depois
|
||||
console.groupCollapsed('[Upgrade] fetchAll')
|
||||
console.log('tenantId:', tid)
|
||||
console.log('subscription:', subscription.value)
|
||||
console.log('currentPlanKey:', currentPlanKey.value)
|
||||
console.groupEnd()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.add({ severity: 'error', summary: 'Erro', detail: e.message || String(e), life: 5000 })
|
||||
@@ -157,7 +215,7 @@ async function fetchAll() {
|
||||
}
|
||||
}
|
||||
|
||||
async function changePlan(targetPlanId) {
|
||||
async function changePlan (targetPlanId) {
|
||||
if (!subscription.value?.id) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
@@ -187,17 +245,32 @@ async function changePlan(targetPlanId) {
|
||||
|
||||
// atualiza estado local
|
||||
subscription.value.plan_id = data?.plan_id || targetPlanId
|
||||
subscription.value.plan_key = data?.plan_key || planKeyById(subscription.value.plan_id) || subscription.value.plan_key
|
||||
|
||||
// ✅ recarrega entitlements (sem reload)
|
||||
// (importante pra refletir o plano imediatamente)
|
||||
entitlementsStore.clear?.()
|
||||
await entitlementsStore.fetch(tid, { force: true })
|
||||
|
||||
// seu store tem loadForTenant no guard; se existir aqui também, use primeiro
|
||||
if (typeof entitlementsStore.loadForTenant === 'function') {
|
||||
await entitlementsStore.loadForTenant(tid, { force: true })
|
||||
} else if (typeof entitlementsStore.fetch === 'function') {
|
||||
await entitlementsStore.fetch(tid, { force: true })
|
||||
}
|
||||
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Plano atualizado',
|
||||
detail: `Agora você está no plano ${planKeyById(subscription.value.plan_id) || ''}`.trim(),
|
||||
detail: `Agora você está no plano ${planKeyById(subscription.value.plan_id) || subscription.value.plan_key || ''}`.trim(),
|
||||
life: 3000
|
||||
})
|
||||
|
||||
// ✅ garante consistência (principalmente se RPC mexer em mais campos)
|
||||
await fetchAll()
|
||||
|
||||
// ✅ dispara o guard novamente: se o usuário perdeu acesso a uma rota PRO,
|
||||
// ele deve ser redirecionado automaticamente.
|
||||
await revalidateCurrentRoute()
|
||||
} catch (e) {
|
||||
toast.add({ severity: 'error', summary: 'Erro', detail: e.message || String(e), life: 5000 })
|
||||
} finally {
|
||||
@@ -205,7 +278,11 @@ async function changePlan(targetPlanId) {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchAll)
|
||||
onMounted(async () => {
|
||||
// ✅ garante que o tenant já foi carregado antes de buscar planos
|
||||
if (!tenantStore.loaded) await tenantStore.loadSessionAndTenant()
|
||||
await fetchAll()
|
||||
})
|
||||
|
||||
// se trocar tenant ativo, recarrega
|
||||
watch(
|
||||
@@ -393,4 +470,4 @@ watch(
|
||||
Observação: alguns recursos PRO podem depender de configuração inicial (ex.: SMS exige provedor).
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
Reference in New Issue
Block a user