feat: add license activation system with feature gates
- License key generator tool at ~/Desktop/LicenseGenerator - Backend validator route (GET /api/license/status, POST /api/license/activate) - Activation page in sidebar with status, key input, and free/premium feature list - useLicense hook for frontend license state - Feature gates: premium eligibility buttons (DDMA, DeltaIns, Tufts, United, CCA) disabled without license - Feature gates: premium claim buttons (CCA, Delta MA, United, Tufts) and all PreAuth buttons disabled without license - Free features always active: MassHealth eligibility/claim, Documents, Payments, Backups, Reports - README: license key generator usage instructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ import { DeltaInsEligibilityButton } from "@/components/insurance-status/deltain
|
||||
import { TuftsSCOEligibilityButton } from "@/components/insurance-status/tufts-sco-button-modal";
|
||||
import { UnitedSCOEligibilityButton } from "@/components/insurance-status/united-sco-button-modal";
|
||||
import { CCAEligibilityButton } from "@/components/insurance-status/cca-button-modal";
|
||||
import { useLicense } from "@/hooks/use-license";
|
||||
|
||||
/**
|
||||
* Waits for a Selenium job to complete by racing socket.io events against
|
||||
@@ -95,6 +96,7 @@ function waitForSeleniumJob(
|
||||
export default function InsuranceStatusPage() {
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const { isLicensed } = useLicense();
|
||||
const dispatch = useAppDispatch();
|
||||
const { status, message, show } = useAppSelector(
|
||||
(state) => state.seleniumTasks.eligibilityCheck,
|
||||
@@ -868,39 +870,43 @@ export default function InsuranceStatusPage() {
|
||||
|
||||
{/* Row 1 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<DdmaEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "ddma"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_ddma_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
<div title={!isLicensed ? "License required" : undefined} className={!isLicensed ? "opacity-40 pointer-events-none" : ""}>
|
||||
<DdmaEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "ddma"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_ddma_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DeltaInsEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "delta-ins"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_deltains_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
<div title={!isLicensed ? "License required" : undefined} className={!isLicensed ? "opacity-40 pointer-events-none" : ""}>
|
||||
<DeltaInsEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "delta-ins"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_deltains_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
@@ -914,56 +920,62 @@ export default function InsuranceStatusPage() {
|
||||
|
||||
{/* Row 2 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<TuftsSCOEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "tufts-sco"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_tuftssco_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
<div title={!isLicensed ? "License required" : undefined} className={!isLicensed ? "opacity-40 pointer-events-none" : ""}>
|
||||
<TuftsSCOEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "tufts-sco"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_tuftssco_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UnitedSCOEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "united-sco"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
<div title={!isLicensed ? "License required" : undefined} className={!isLicensed ? "opacity-40 pointer-events-none" : ""}>
|
||||
<UnitedSCOEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "united-sco"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CCAEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "cca"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_cca_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
<div title={!isLicensed ? "License required" : undefined} className={!isLicensed ? "opacity-40 pointer-events-none" : ""}>
|
||||
<CCAEligibilityButton
|
||||
memberId={memberId}
|
||||
dateOfBirth={dateOfBirth}
|
||||
firstName={firstName}
|
||||
lastName={lastName}
|
||||
isFormIncomplete={isFormIncomplete}
|
||||
autoTrigger={triggerTarget === "cca"}
|
||||
onAutoTriggered={() => setTriggerTarget(null)}
|
||||
onPdfReady={(pdfId, fallbackFilename) => {
|
||||
setPreviewPdfId(pdfId);
|
||||
setPreviewFallbackFilename(
|
||||
fallbackFilename ?? `eligibility_cca_${memberId}.pdf`,
|
||||
);
|
||||
setPreviewOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
|
||||
Reference in New Issue
Block a user