AppContext.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. "use client";
  2. import React, { ReactNode, useCallback, useEffect, useState } from "react";
  3. import { del, get, post } from "../../lib/api";
  4. export interface Settings {
  5. [key: string]: any;
  6. }
  7. export interface Dataset {
  8. [path: string]: any;
  9. }
  10. export interface DatasetsConfig {
  11. [datasetName: string]: Dataset;
  12. }
  13. export interface QueueConfig {
  14. maxConcurrent?: number;
  15. maxRetries?: number;
  16. [key: string]: any;
  17. }
  18. export interface WatcherConfig {
  19. enabled?: boolean;
  20. [key: string]: any;
  21. }
  22. export interface AppContextType {
  23. // Data
  24. settings: Settings | null;
  25. datasetsConfig: DatasetsConfig | null;
  26. queueConfig: QueueConfig | null;
  27. watcherConfig: WatcherConfig | null;
  28. datasets: string[] | null;
  29. // Loading states
  30. isLoading: boolean;
  31. isInitialized: boolean;
  32. // Errors
  33. error: Error | null;
  34. // Mutation functions
  35. updateSetting: (key: string, value: any) => Promise<void>;
  36. deleteSetting: (key: string) => Promise<void>;
  37. updateDatasets: (datasets: DatasetsConfig) => Promise<void>;
  38. updateQueueConfig: (config: QueueConfig) => Promise<void>;
  39. updateWatcherConfig: (config: WatcherConfig) => Promise<void>;
  40. // Refresh functions
  41. refreshSettings: () => Promise<void>;
  42. refreshDatasets: () => Promise<void>;
  43. refreshAll: () => Promise<void>;
  44. }
  45. const AppContext = React.createContext<AppContextType | undefined>(undefined);
  46. export function AppProvider({ children }: { children: ReactNode }) {
  47. const [settings, setSettings] = useState<Settings | null>(null);
  48. const [datasetsConfig, setDatasetsConfig] = useState<DatasetsConfig | null>(
  49. null
  50. );
  51. const [queueConfig, setQueueConfig] = useState<QueueConfig | null>(null);
  52. const [watcherConfig, setWatcherConfig] = useState<WatcherConfig | null>(
  53. null
  54. );
  55. const [datasets, setDatasets] = useState<string[] | null>(null);
  56. const [isLoading, setIsLoading] = useState(true);
  57. const [isInitialized, setIsInitialized] = useState(false);
  58. const [error, setError] = useState<Error | null>(null);
  59. // Load all initial data
  60. const initializeData = useCallback(async () => {
  61. try {
  62. setIsLoading(true);
  63. setError(null);
  64. // Load settings
  65. const settingsData = await get("/config/settings");
  66. setSettings(settingsData || {});
  67. // Extract specific configs from settings
  68. if (settingsData) {
  69. const queue = settingsData.queue || {};
  70. const watcher = settingsData.watcher || {};
  71. const datasetsData = settingsData.datasets || {};
  72. setQueueConfig(queue);
  73. setWatcherConfig(watcher);
  74. setDatasetsConfig(datasetsData);
  75. }
  76. // Load datasets list
  77. const datasetsList = await get("/files/all-datasets");
  78. setDatasets(datasetsList || []);
  79. setIsInitialized(true);
  80. } catch (err) {
  81. const error = err instanceof Error ? err : new Error(String(err));
  82. setError(error);
  83. console.error("Failed to initialize app context:", error);
  84. } finally {
  85. setIsLoading(false);
  86. }
  87. }, []);
  88. // Initial load
  89. useEffect(() => {
  90. initializeData();
  91. }, [initializeData]);
  92. // Listen for WebSocket events
  93. useEffect(() => {
  94. const handleSettingsUpdate = () => {
  95. initializeData();
  96. };
  97. const handleTaskUpdate = () => {
  98. // Refetch datasets in case new ones were created
  99. initializeData();
  100. };
  101. const handleFileUpdate = () => {
  102. // Refetch datasets in case new ones were created
  103. initializeData();
  104. };
  105. window.addEventListener(
  106. "settingsUpdate",
  107. handleSettingsUpdate as EventListener
  108. );
  109. window.addEventListener("taskUpdate", handleTaskUpdate as EventListener);
  110. window.addEventListener("fileUpdate", handleFileUpdate as EventListener);
  111. return () => {
  112. window.removeEventListener(
  113. "settingsUpdate",
  114. handleSettingsUpdate as EventListener
  115. );
  116. window.removeEventListener(
  117. "taskUpdate",
  118. handleTaskUpdate as EventListener
  119. );
  120. window.removeEventListener(
  121. "fileUpdate",
  122. handleFileUpdate as EventListener
  123. );
  124. };
  125. }, [initializeData]);
  126. // Mutation functions
  127. const updateSetting = useCallback(
  128. async (key: string, value: any) => {
  129. try {
  130. await post("/config/settings", { [key]: value });
  131. setSettings((prev) =>
  132. prev ? { ...prev, [key]: value } : { [key]: value }
  133. );
  134. await initializeData();
  135. } catch (err) {
  136. const error = err instanceof Error ? err : new Error(String(err));
  137. setError(error);
  138. throw error;
  139. }
  140. },
  141. [initializeData]
  142. );
  143. const deleteSetting = useCallback(
  144. async (key: string) => {
  145. try {
  146. await del(`/config/settings/${key}`);
  147. setSettings((prev) => {
  148. if (!prev) return null;
  149. const updated = { ...prev };
  150. delete updated[key];
  151. return updated;
  152. });
  153. await initializeData();
  154. } catch (err) {
  155. const error = err instanceof Error ? err : new Error(String(err));
  156. setError(error);
  157. throw error;
  158. }
  159. },
  160. [initializeData]
  161. );
  162. const updateDatasets = useCallback(
  163. async (datasetsData: DatasetsConfig) => {
  164. try {
  165. setDatasetsConfig(datasetsData);
  166. await updateSetting("datasets", datasetsData);
  167. } catch (err) {
  168. const error = err instanceof Error ? err : new Error(String(err));
  169. setError(error);
  170. throw error;
  171. }
  172. },
  173. [updateSetting]
  174. );
  175. const updateQueueConfig = useCallback(
  176. async (config: QueueConfig) => {
  177. try {
  178. setQueueConfig(config);
  179. await updateSetting("queue", config);
  180. } catch (err) {
  181. const error = err instanceof Error ? err : new Error(String(err));
  182. setError(error);
  183. throw error;
  184. }
  185. },
  186. [updateSetting]
  187. );
  188. const updateWatcherConfig = useCallback(
  189. async (config: WatcherConfig) => {
  190. try {
  191. setWatcherConfig(config);
  192. await updateSetting("watcher", config);
  193. } catch (err) {
  194. const error = err instanceof Error ? err : new Error(String(err));
  195. setError(error);
  196. throw error;
  197. }
  198. },
  199. [updateSetting]
  200. );
  201. const refreshSettings = useCallback(async () => {
  202. try {
  203. const settingsData = await get("/config/settings");
  204. setSettings(settingsData || {});
  205. if (settingsData) {
  206. setQueueConfig(settingsData.queue || {});
  207. setWatcherConfig(settingsData.watcher || {});
  208. setDatasetsConfig(settingsData.datasets || {});
  209. }
  210. } catch (err) {
  211. const error = err instanceof Error ? err : new Error(String(err));
  212. setError(error);
  213. throw error;
  214. }
  215. }, []);
  216. const refreshDatasets = useCallback(async () => {
  217. try {
  218. const datasetsList = await get("/files/all-datasets");
  219. setDatasets(datasetsList || []);
  220. } catch (err) {
  221. const error = err instanceof Error ? err : new Error(String(err));
  222. setError(error);
  223. throw error;
  224. }
  225. }, []);
  226. const refreshAll = useCallback(async () => {
  227. await initializeData();
  228. }, [initializeData]);
  229. const value: AppContextType = {
  230. settings,
  231. datasetsConfig,
  232. queueConfig,
  233. watcherConfig,
  234. datasets,
  235. isLoading,
  236. isInitialized,
  237. error,
  238. updateSetting,
  239. deleteSetting,
  240. updateDatasets,
  241. updateQueueConfig,
  242. updateWatcherConfig,
  243. refreshSettings,
  244. refreshDatasets,
  245. refreshAll
  246. };
  247. return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
  248. }
  249. export function useAppContext() {
  250. const context = React.useContext(AppContext);
  251. if (context === undefined) {
  252. throw new Error("useAppContext must be used within an AppProvider");
  253. }
  254. return context;
  255. }