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>
86 lines
2.5 KiB
TypeScript
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;
|
|
};
|