This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit fa98128d1b1b5921b6f5314a06e808a727f5d812 Author: Marat Gubaidullin <[email protected]> AuthorDate: Fri Feb 27 16:23:34 2026 -0500 Front-end Stores for 4.18.0 --- .../src/karavan/stores/ContainerStatusesStore.ts | 48 ++++++++++++ .../src/main/webui/src/karavan/stores/LogStore.ts | 58 ++++++++++++++ .../main/webui/src/karavan/stores/SettingsStore.ts | 88 ++++++++++++++++++++++ .../main/webui/src/karavan/stores/TopologyStore.ts | 63 ++++++++++++++++ 4 files changed, 257 insertions(+) diff --git a/karavan-app/src/main/webui/src/karavan/stores/ContainerStatusesStore.ts b/karavan-app/src/main/webui/src/karavan/stores/ContainerStatusesStore.ts new file mode 100644 index 00000000..8a91f5bd --- /dev/null +++ b/karavan-app/src/main/webui/src/karavan/stores/ContainerStatusesStore.ts @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {KaravanApi} from "@api/KaravanApi"; +import {create} from "zustand"; +import {ContainerStatus} from "@models/ProjectModels"; +import isEqual from "lodash/isEqual"; + +type ContainerStatusesState = { + containers: ContainerStatus[]; + fetchContainers: () => Promise<void>; + findContainers: (projectId: string) => ContainerStatus[]; +} + +export const useContainerStatusesStore = create<ContainerStatusesState>((set, get) => ({ + containers: [], + fetchContainers: async (): Promise<void> => { + const currentContainers = get().containers; + await new Promise<ContainerStatus[]>((resolve, reject) => { + KaravanApi.getAllContainerStatuses((containers: ContainerStatus[]) => { + resolve(containers); + }); + }) + .then((containers) => { + if (!isEqual(currentContainers, containers)) { + set({containers: containers}); + } + }); + }, + findContainers: (projectId: string) => { + const state = get(); + return state.containers.filter(c => c.projectId === projectId); + }, +})) + diff --git a/karavan-app/src/main/webui/src/karavan/stores/LogStore.ts b/karavan-app/src/main/webui/src/karavan/stores/LogStore.ts new file mode 100644 index 00000000..65b7d030 --- /dev/null +++ b/karavan-app/src/main/webui/src/karavan/stores/LogStore.ts @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ProjectEventBus} from "@bus/ProjectEventBus"; +import {unstable_batchedUpdates} from "react-dom"; +import {createWithEqualityFn} from "zustand/traditional"; +import {shallow} from "zustand/shallow"; + +const MAX_LOG_LINES = 1000; + +interface LogState { + podName?: string, + data: string[]; + setData: (data: string[]) => void; +} + +export const useLogStore = createWithEqualityFn<LogState>((set) => ({ + podName: undefined, + data: [], + setData: (data: string[]) => { + set({data: data}) + } +}), shallow) + +const sub = ProjectEventBus.onLog()?.subscribe((result: ["add" | "set", string]) => { + if (result[0] === 'add') { + unstable_batchedUpdates(() => { + useLogStore.setState((state: LogState) => { + const newEntry = result[1]?.length !== 0 ? result[1] : "\n"; + + // Combine, then slice from the end (negative index) + const newData = [...state.data, newEntry]; + const trimmedData = newData.length > MAX_LOG_LINES + ? newData.slice(-MAX_LOG_LINES) + : newData; + + return { data: trimmedData }; + }) + }) + } else if (result[0] === 'set') { + unstable_batchedUpdates(() => { + useLogStore.setState({data: [result[1]]}); + }) + } +}); \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/karavan/stores/SettingsStore.ts b/karavan-app/src/main/webui/src/karavan/stores/SettingsStore.ts new file mode 100644 index 00000000..33af15fb --- /dev/null +++ b/karavan-app/src/main/webui/src/karavan/stores/SettingsStore.ts @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ProjectFile, ProjectType} from "@models/ProjectModels"; +import {create} from "zustand"; +import {KaravanApi} from "@api/KaravanApi"; +import isEqual from "lodash/isEqual"; +import concat from "lodash/concat"; + +export const SettingsMenus = ['templates', 'kamelets', 'configuration'] as const; +export type SettingsMenu = typeof SettingsMenus[number]; + +type SettingsState = { + currentMenu: SettingsMenu; + setCurrentMenu: (currentMenu: SettingsMenu) => void; + selectedProjectId?: string; + setSelectedProjectId: (selectedProjectId?: string) => void; + selectedFileName?: string; + setSelectedFilename: (selectedFileName?: string) => void; +} + +export const useSettingsStore = create<SettingsState>((set, get) => ({ + currentMenu: SettingsMenus[0], + setCurrentMenu: (currentMenu: SettingsMenu) => { + set({ currentMenu: currentMenu }); + }, + setSelectedProjectId: (selectedProjectId: string) => { + set({ selectedProjectId: selectedProjectId }); + }, + setSelectedFilename: (selectedFileName: string) => { + set({ selectedFileName: selectedFileName }); + }, +})) + +type TemplatesState = { + templateFiles: ProjectFile[]; + saveTemplateFile: (file: ProjectFile) => Promise<void>; + fetchTemplateFiles: () => Promise<void>; +} + +export const useTemplatesStore = create<TemplatesState>((set, get) => ({ + templateFiles: [], + fetchTemplateFiles: async (): Promise<void> => { + await new Promise<ProjectFile[]>((resolve) => { + KaravanApi.getFiles(ProjectType.templates, resolve); + }).then(templates => { + const currentTemplateFiles = get().templateFiles; + if (!isEqual(currentTemplateFiles, templates)) { + set({ templateFiles: templates }); + } + }) + }, + saveTemplateFile: async (file: ProjectFile) => { + const prevSettings = [...get().templateFiles]; + const newTemplateFiles = concat(prevSettings.filter(f => f.name !== file.name), file) + set({ templateFiles: newTemplateFiles }); + + await new Promise<{ result: boolean; file: ProjectFile | any }>((resolve, reject) => { + KaravanApi.saveProjectFile(file, (result, file) => { + if (result) { + resolve({ result, file }); + } else { + // Reject the promise if the API explicitly returns result: false (Application error) + reject(new Error("API returned failure result.")); + } + }); + }).catch(error => { + set({ templateFiles: prevSettings }); + console.error(error); + }); + } +})) + + + diff --git a/karavan-app/src/main/webui/src/karavan/stores/TopologyStore.ts b/karavan-app/src/main/webui/src/karavan/stores/TopologyStore.ts new file mode 100644 index 00000000..f7088137 --- /dev/null +++ b/karavan-app/src/main/webui/src/karavan/stores/TopologyStore.ts @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {createWithEqualityFn} from "zustand/traditional"; + +interface TopologyState { + fileName?: string + setFileName: (fileName?: string) => void + showGroups: boolean + setShowGroups: (showGroups: boolean) => void + showBeans: boolean + setShowBeans: (showBeans: boolean) => void + showLegend: boolean + setShowLegend: (showLegend: boolean) => void + showStats: boolean + setShowStats: (showStats: boolean) => void +} + +export const useTopologyStore = createWithEqualityFn<TopologyState>((set, get) => { + return { + setFileName: (fileName?: string) => { + set((state: TopologyState) => { + return {fileName: fileName}; + }); + }, + showGroups: false, + setShowGroups: (showGroups: boolean) => { + set({showGroups: showGroups}); + }, + showBeans: false, + setShowBeans: (showBeans: boolean) => { + set((state: TopologyState) => { + return {showBeans: showBeans}; + }); + }, + showLegend: false, + setShowLegend: (showLegend: boolean) => { + set((state: TopologyState) => { + return {showLegend: showLegend}; + }); + }, + showStats: false, + setShowStats: (showStats: boolean) => { + set((state: TopologyState) => { + return {showStats: showStats}; + }); + }, + } +});
