refactor(portal): extract shared partner types and data composables
Move partner domain types out of data/customers.ts into types/partner.ts so the fixture data exports can be removed later without breaking type imports. Add usePartnerTenants / usePartnerMrr composables wrapping the shared-key partner fetches.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
// identity (not the customer's) — the design spec is explicit about this for
|
||||
// trust. For the prototype we just hold the customer id.
|
||||
|
||||
import type { CustomerOrg } from '~/data/customers'
|
||||
import type { CustomerOrg } from '~/types/partner'
|
||||
|
||||
const activeCustomerId = ref<string | null>(null)
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Shared fetch + derivations for partner MRR from /api/partner/mrr
|
||||
// (platform-api /me/partner/mrr). Like usePartnerTenants, the dashboard,
|
||||
// customers, and billing pages all read this under the 'partner-mrr' key, so
|
||||
// one cached payload serves all of them. This composable also owns the
|
||||
// derivations that were duplicated per page: the per-tenant lookup Map and the
|
||||
// per-currency display totals (amounts are never FX-summed across currencies).
|
||||
//
|
||||
// const { mrr, mrrByTenant, totalsDisplay, refresh } = usePartnerMrr()
|
||||
//
|
||||
// Synchronous (non-async) so `useFetch` runs in the caller's setup context even
|
||||
// when called after another await. Call WITHOUT await.
|
||||
|
||||
import type { MrrBreakdownRow, MrrResponse } from '~/types/partner'
|
||||
|
||||
export function usePartnerMrr() {
|
||||
const { data: mrr, refresh, error, pending } = useFetch<MrrResponse>('/api/partner/mrr', {
|
||||
key: 'partner-mrr',
|
||||
default: () => ({ totals: [], breakdown: [] }),
|
||||
})
|
||||
|
||||
// tenantId → its subscription's MRR row, for O(1) per-row lookups in the
|
||||
// customers table without a second fetch.
|
||||
const mrrByTenant = computed(() => {
|
||||
const m = new Map<string, MrrBreakdownRow>()
|
||||
for (const row of mrr.value?.breakdown ?? []) m.set(row.tenantId, row)
|
||||
return m
|
||||
})
|
||||
|
||||
// Per-currency totals in major units (minor / 100), e.g. { DKK: 55_750 }.
|
||||
const totalsDisplay = computed(() =>
|
||||
(mrr.value?.totals ?? []).map((t) => ({
|
||||
currency: t.currency,
|
||||
majorAmount: Math.round(t.monthlyMinor / 100),
|
||||
})),
|
||||
)
|
||||
|
||||
// Compact one-line summary, e.g. "55.750 DKK + 1.200 EUR / mo".
|
||||
const totalsLine = computed(() => {
|
||||
const parts = totalsDisplay.value.map(
|
||||
(t) => `${t.majorAmount.toLocaleString('da-DK')} ${t.currency}`,
|
||||
)
|
||||
if (parts.length === 0) return '0 DKK / mo'
|
||||
return parts.join(' + ') + ' / mo'
|
||||
})
|
||||
|
||||
// True when any subscription is custom/Enterprise-priced (renders as
|
||||
// "custom" rather than a misleading 0 in the totals).
|
||||
const hasCustomPriced = computed(() => (mrr.value?.breakdown ?? []).some((b) => b.custom))
|
||||
|
||||
return { mrr, mrrByTenant, totalsDisplay, totalsLine, hasCustomPriced, refresh, error, pending }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Shared fetch of the signed-in partner's customer tenants from
|
||||
// /api/partner/tenants (platform-api /me/partner/tenants). Several pages need
|
||||
// this list — dashboard, customers, and (later) billing — and they all key the
|
||||
// fetch as 'partner-tenants', so Nuxt dedupes to a single round-trip and shared
|
||||
// payload. Centralizing it here removes the divergent inline PartnerTenantDoc
|
||||
// copies that drifted between pages.
|
||||
//
|
||||
// Synchronous (non-async) so `useFetch` is invoked directly in the caller's
|
||||
// setup context — safe to call after another `await` in setup, unlike an async
|
||||
// wrapper. Call WITHOUT await:
|
||||
// const { tenants, refresh } = usePartnerTenants()
|
||||
|
||||
import type { PartnerTenantDoc } from '~/types/partner'
|
||||
|
||||
export function usePartnerTenants() {
|
||||
const { data: tenants, refresh, error, pending } = useFetch<PartnerTenantDoc[]>(
|
||||
'/api/partner/tenants',
|
||||
{ key: 'partner-tenants', default: () => [] },
|
||||
)
|
||||
return { tenants, refresh, error, pending }
|
||||
}
|
||||
Reference in New Issue
Block a user