feat: require compliance confirmation for paid features

Gate payment, redemption, subscription, and invitation reward flows behind an audited compliance acknowledgement.
This commit is contained in:
CaIon
2026-05-13 22:18:46 +08:00
parent aa56667b8f
commit 0526a22643
57 changed files with 1806 additions and 216 deletions
@@ -35,7 +35,7 @@ interface DataTableRowActionsProps {
export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { t } = useTranslation()
const { setOpen, setCurrentRow } = useSubscriptions()
const { setOpen, setCurrentRow, complianceConfirmed } = useSubscriptions()
return (
<DropdownMenu>
@@ -46,6 +46,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
<DropdownMenuItem
disabled={!complianceConfirmed}
onClick={() => {
setCurrentRow(row.original)
setOpen('update')
@@ -55,6 +56,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{t('Edit')}
</DropdownMenuItem>
<DropdownMenuItem
disabled={!complianceConfirmed}
onClick={() => {
setCurrentRow(row.original)
setOpen('toggle-status')
@@ -23,10 +23,14 @@ import { useSubscriptions } from './subscriptions-provider'
export function SubscriptionsPrimaryButtons() {
const { t } = useTranslation()
const { setOpen } = useSubscriptions()
const { setOpen, complianceConfirmed } = useSubscriptions()
return (
<div className='flex gap-2'>
<Button size='sm' onClick={() => setOpen('create')}>
<Button
size='sm'
onClick={() => setOpen('create')}
disabled={!complianceConfirmed}
>
<Plus className='h-4 w-4' />
{t('Create Plan')}
</Button>
@@ -18,8 +18,14 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState } from 'react'
import useDialogState from '@/hooks/use-dialog'
import {
getOptionValue,
useSystemOptions,
} from '@/features/system-settings/hooks/use-system-options'
import { type PlanRecord, type SubscriptionsDialogType } from '../types'
const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1'
type SubscriptionsContextType = {
open: SubscriptionsDialogType | null
setOpen: (str: SubscriptionsDialogType | null) => void
@@ -27,6 +33,7 @@ type SubscriptionsContextType = {
setCurrentRow: React.Dispatch<React.SetStateAction<PlanRecord | null>>
refreshTrigger: number
triggerRefresh: () => void
complianceConfirmed: boolean
}
const SubscriptionsContext =
@@ -40,6 +47,15 @@ export function SubscriptionsProvider({
const [open, setOpen] = useDialogState<SubscriptionsDialogType>(null)
const [currentRow, setCurrentRow] = useState<PlanRecord | null>(null)
const [refreshTrigger, setRefreshTrigger] = useState(0)
const { data } = useSystemOptions()
const complianceOptions = getOptionValue(data?.data, {
'payment_setting.compliance_confirmed': false,
'payment_setting.compliance_terms_version': '',
})
const complianceConfirmed =
complianceOptions['payment_setting.compliance_confirmed'] &&
complianceOptions['payment_setting.compliance_terms_version'] ===
CURRENT_COMPLIANCE_TERMS_VERSION
const triggerRefresh = () => setRefreshTrigger((prev) => prev + 1)
@@ -52,6 +68,7 @@ export function SubscriptionsProvider({
setCurrentRow,
refreshTrigger,
triggerRefresh,
complianceConfirmed,
}}
>
{children}