114 lines
3.7 KiB
TypeScript
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;
|