WellNuo/services/BeneficiaryDetailController.ts
Sergei fe4ff1a932 Simplify DB schema (name/address single fields) + subscription flow
Database:
- Simplified beneficiary schema: single `name` field instead of first_name/last_name
- Single `address` field instead of 5 separate address columns
- Added migration 008_update_notification_settings.sql

Backend:
- Updated all beneficiaries routes for new schema
- Fixed admin routes for simplified fields
- Updated notification settings routes
- Improved stripe and webhook handlers

Frontend:
- Updated all forms to use single name/address fields
- Added new equipment-status and purchase screens
- Added BeneficiaryDetailController service
- Added subscription service
- Improved navigation and auth flow
- Various UI improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-08 10:35:15 -08:00

86 lines
2.5 KiB
TypeScript

import type { Beneficiary } from '@/types';
import { isSubscriptionActive, isSubscriptionExpiringSoon } from '@/services/subscription';
export type BeneficiarySetupState =
| 'loading'
| 'awaiting_equipment'
| 'no_devices'
| 'no_subscription'
| 'ready';
export const hasBeneficiaryDevices = (beneficiary: Beneficiary): boolean => {
return Boolean(
beneficiary.hasDevices ||
(beneficiary.devices && beneficiary.devices.length > 0) ||
beneficiary.device_id
);
};
export const hasActiveSubscription = (beneficiary: Beneficiary): boolean => {
return isSubscriptionActive(beneficiary.subscription);
};
export const getBeneficiarySetupState = (
beneficiary: Beneficiary | null,
isLoading: boolean
): BeneficiarySetupState => {
if (isLoading || !beneficiary) return 'loading';
const hasDevices = hasBeneficiaryDevices(beneficiary);
const equipmentStatus = beneficiary.equipmentStatus;
// Waterfall priority:
// 1. First check devices (highest priority)
if (!hasDevices) {
// Equipment is on the way - waiting for delivery
if (equipmentStatus && ['ordered', 'shipped', 'delivered'].includes(equipmentStatus)) {
return 'awaiting_equipment';
}
// No devices and no order - need to buy equipment
return 'no_devices';
}
// 2. Then check subscription (only if devices exist)
if (!isSubscriptionActive(beneficiary.subscription)) {
return 'no_subscription';
}
// 3. All good → ready
return 'ready';
};
export const shouldShowSubscriptionWarning = (beneficiary: Beneficiary | null): boolean => {
return beneficiary ? isSubscriptionExpiringSoon(beneficiary.subscription) : false;
};
export type BeneficiaryRouteKey = 'index' | 'subscription' | 'equipment' | 'share';
export const getDefaultBeneficiaryRouteKey = (
beneficiary: Beneficiary | null,
isLoading: boolean
): BeneficiaryRouteKey | null => {
const state = getBeneficiarySetupState(beneficiary, isLoading);
if (state === 'loading') return null;
if (state === 'no_subscription') return 'subscription';
return 'index';
};
export const getRedirectRouteKey = (
currentRouteKey: BeneficiaryRouteKey,
beneficiary: Beneficiary | null,
isLoading: boolean
): BeneficiaryRouteKey | null => {
const state = getBeneficiarySetupState(beneficiary, isLoading);
if (state === 'loading') return null;
if (currentRouteKey === 'index' && state === 'no_subscription') {
return 'subscription';
}
if (currentRouteKey === 'subscription' && state !== 'no_subscription') {
return 'index';
}
return null;
};