ui-nyla/src/hooks/useStore.ts
2026-03-24 14:16:45 +01:00

114 lines
3.7 KiB
TypeScript

/**
* useStore Hook
*
* Manages feature store interactions: loading catalog, activating/deactivating features.
* After each mutation, refreshes featureStore and dispatches 'features-changed' event
* so navigation and other components update in real-time.
*/
import { useState, useCallback, useEffect } from 'react';
import {
fetchStoreFeatures,
activateStoreFeature,
deactivateStoreFeature,
fetchUserMandates,
fetchSubscriptionInfo,
type StoreFeature,
type UserMandate,
type SubscriptionInfo,
} from '../api/storeApi';
import { useFeatureStore } from '../stores/featureStore';
interface UseStoreReturn {
features: StoreFeature[];
mandates: UserMandate[];
subscriptionInfo: SubscriptionInfo | null;
loading: boolean;
actionLoading: string | null;
error: string | null;
loadStore: () => Promise<void>;
loadSubscriptionInfo: (mandateId?: string) => Promise<void>;
activate: (featureCode: string, mandateId?: string) => Promise<void>;
deactivate: (featureCode: string, mandateId: string, instanceId: string) => Promise<void>;
}
export function useStore(): UseStoreReturn {
const [features, setFeatures] = useState<StoreFeature[]>([]);
const [mandates, setMandates] = useState<UserMandate[]>([]);
const [subscriptionInfo, setSubscriptionInfo] = useState<SubscriptionInfo | null>(null);
const [loading, setLoading] = useState(true);
const [actionLoading, setActionLoading] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const featureStore = useFeatureStore();
const loadSubscriptionInfo = useCallback(async (mandateId?: string) => {
try {
const info = await fetchSubscriptionInfo(mandateId);
setSubscriptionInfo(info);
} catch {
// non-critical
}
}, []);
const loadStore = useCallback(async () => {
setLoading(true);
setError(null);
try {
const [data, userMandates] = await Promise.all([
fetchStoreFeatures(),
fetchUserMandates(),
]);
setFeatures(data);
setMandates(userMandates);
const firstMandateId = userMandates.length > 0 ? userMandates[0].id : undefined;
await loadSubscriptionInfo(firstMandateId);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Failed to load store';
setError(msg);
} finally {
setLoading(false);
}
}, [loadSubscriptionInfo]);
useEffect(() => {
loadStore();
}, [loadStore]);
const _refreshAfterAction = useCallback(async () => {
await featureStore.loadFeatures();
window.dispatchEvent(new CustomEvent('features-changed'));
await loadStore();
}, [featureStore, loadStore]);
const activate = useCallback(async (featureCode: string, mandateId?: string) => {
setActionLoading(featureCode);
setError(null);
try {
await activateStoreFeature(featureCode, mandateId);
await _refreshAfterAction();
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Activation failed';
setError(msg);
} finally {
setActionLoading(null);
}
}, [_refreshAfterAction]);
const deactivate = useCallback(async (featureCode: string, mandateId: string, instanceId: string) => {
setActionLoading(featureCode);
setError(null);
try {
await deactivateStoreFeature(featureCode, mandateId, instanceId);
await _refreshAfterAction();
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Deactivation failed';
setError(msg);
} finally {
setActionLoading(null);
}
}, [_refreshAfterAction]);
return { features, mandates, subscriptionInfo, loading, actionLoading, error, loadStore, loadSubscriptionInfo, activate, deactivate };
}
export default useStore;