useOrganizationRestrictions.ts94 lines · main
1import type { ReactNode } from 'react'
2
3import { useIsFeatureEnabled } from './useIsFeatureEnabled'
4import { RESTRICTION_MESSAGES } from '@/components/interfaces/Organization/restriction.constants'
5import { useOverdueInvoicesQuery } from '@/data/invoices/invoices-overdue-query'
6import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
7import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
8
9export type WarningBannerProps = {
10 variant: 'danger' | 'warning' | 'note'
11 title: string
12 description: ReactNode
13}
14
15/**
16 * Compute billing-related restriction banners for the currently selected organization.
17 *
18 * The hook examines billing feature availability, overdue invoices, organization billing data,
19 * and restriction status to produce an ordered list of warning/danger banner props.
20 *
21 * @returns An object containing:
22 * - `warnings`: an array of `WarningBannerProps` to display for the selected organization (may be empty).
23 * - `org`: the selected organization data (may be `undefined`).
24 */
25export function useOrganizationRestrictions() {
26 const { data: org } = useSelectedOrganizationQuery()
27
28 const { data: overdueInvoices } = useOverdueInvoicesQuery()
29 const { data: organizations } = useOrganizationsQuery()
30
31 const warnings: WarningBannerProps[] = []
32
33 const billingEnabled = useIsFeatureEnabled('billing:all')
34 if (!billingEnabled) {
35 return { warnings, org }
36 }
37
38 const overdueInvoicesFromOtherOrgs = overdueInvoices?.filter(
39 (invoice) => invoice.organization_id !== org?.id
40 )
41 const thisOrgHasOverdueInvoices = overdueInvoices?.filter(
42 (invoice) => invoice.organization_id === org?.id
43 )
44
45 if (thisOrgHasOverdueInvoices?.length) {
46 warnings.push({
47 variant: 'danger',
48 title: RESTRICTION_MESSAGES.OVERDUE_INVOICES.title,
49 description: RESTRICTION_MESSAGES.OVERDUE_INVOICES.description(org?.slug ?? 'default'),
50 })
51 }
52
53 if (overdueInvoicesFromOtherOrgs?.length) {
54 const otherOrgSlug = organizations?.find(
55 (o) => o.id === overdueInvoicesFromOtherOrgs[0].organization_id
56 )?.slug
57 warnings.push({
58 variant: 'danger',
59 title: RESTRICTION_MESSAGES.OVERDUE_INVOICES_FROM_OTHER_ORGS.title,
60 description: RESTRICTION_MESSAGES.OVERDUE_INVOICES_FROM_OTHER_ORGS.description(
61 otherOrgSlug ?? org?.slug ?? 'default'
62 ),
63 })
64 }
65
66 if (org?.restriction_status === 'grace_period') {
67 warnings.push({
68 variant: 'warning',
69 title: RESTRICTION_MESSAGES.GRACE_PERIOD.title,
70 description: RESTRICTION_MESSAGES.GRACE_PERIOD.description(
71 org?.restriction_data?.['grace_period_end'] ?? '',
72 org.slug
73 ),
74 })
75 }
76
77 if (org?.restriction_status === 'grace_period_over') {
78 warnings.push({
79 variant: 'warning',
80 title: RESTRICTION_MESSAGES.GRACE_PERIOD_OVER.title,
81 description: RESTRICTION_MESSAGES.GRACE_PERIOD_OVER.description(org.slug),
82 })
83 }
84
85 if (org?.restriction_status === 'restricted') {
86 warnings.push({
87 variant: 'danger',
88 title: RESTRICTION_MESSAGES.RESTRICTED.title,
89 description: RESTRICTION_MESSAGES.RESTRICTED.description(org.slug),
90 })
91 }
92
93 return { warnings, org }
94}