Skip to content

Add Environments in the Redux State #1716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
use redux environments in environments UI
  • Loading branch information
iamfaran committed May 27, 2025
commit abd6c3625b2af5d520b9bf6eb81f53db88b0b233
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
// client/packages/lowcoder/src/pages/setting/environments/Environments.tsx
import React from "react";
import { Switch, Route, useRouteMatch } from "react-router-dom";
import { EnvironmentProvider } from "./context/EnvironmentContext";
import EnvironmentRoutes from "./routes/EnvironmentRoutes";
import EnvironmentsList from "./EnvironmentsList";

/**
* Top-level Environments component
* Provides the EnvironmentProvider at the top level
* No longer needs the EnvironmentProvider since we use Redux
*/
const Environments: React.FC = () => {
const { path } = useRouteMatch();

return (
<EnvironmentProvider>
<Switch>
{/* Environment list route */}
<Route exact path={path}>
<EnvironmentsList />
</Route>

{/* All routes that need a specific environment */}
<Route path={`${path}/:envId`}>
<EnvironmentRoutes />
</Route>
</Switch>
</EnvironmentProvider>
<Switch>
{/* Environment list route */}
<Route exact path={path}>
<EnvironmentsList />
</Route>

{/* All routes that need a specific environment */}
<Route path={`${path}/:envId`}>
<EnvironmentRoutes />
</Route>
</Switch>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { Typography, Alert, Input, Button, Space, Empty, Card, Spin, Row, Col, Tooltip, Badge } from "antd";
import { SearchOutlined, CloudServerOutlined, SyncOutlined, PlusOutlined} from "@ant-design/icons";
import { useHistory } from "react-router-dom";
import { useEnvironmentContext } from "./context/EnvironmentContext";
import { useSelector, useDispatch } from "react-redux";
import { selectEnvironments, selectEnvironmentsLoading, selectEnvironmentsError } from "redux/selectors/enterpriseSelectors";
import { fetchEnvironments } from "redux/reduxActions/enterpriseActions";
import { Environment } from "./types/environment.types";
import EnvironmentsTable from "./components/EnvironmentsTable";
import CreateEnvironmentModal from "./components/CreateEnvironmentModal";
Expand All @@ -17,23 +19,28 @@ const { Title, Text } = Typography;
* Displays a table of environments
*/
const EnvironmentsList: React.FC = () => {
// Use the shared context instead of a local hook
const {
environments,
isLoading,
error,
refreshEnvironments
} = useEnvironmentContext();
// Use Redux state instead of context
const dispatch = useDispatch();
const environments = useSelector(selectEnvironments);
const isLoading = useSelector(selectEnvironmentsLoading);
const error = useSelector(selectEnvironmentsError);

// State for search input
const [searchText, setSearchText] = useState("");
const [isRefreshing, setIsRefreshing] = useState(false);
const [isCreateModalVisible, setIsCreateModalVisible] = useState(false);
const [isCreating, setIsCreating] = useState(false);

// Hook for navigation
const history = useHistory();

// Load environments on component mount
useEffect(() => {
// Only fetch if environments are not already loaded
if (environments.length === 0 && !isLoading) {
dispatch(fetchEnvironments());
}
}, [dispatch, environments.length, isLoading]);

// Filter environments based on search text
const filteredEnvironments = environments.filter((env) => {
const searchLower = searchText.toLowerCase();
Expand Down Expand Up @@ -62,18 +69,16 @@ const EnvironmentsList: React.FC = () => {
};

// Handle refresh
const handleRefresh = async () => {
setIsRefreshing(true);
await refreshEnvironments();
setIsRefreshing(false);
const handleRefresh = () => {
dispatch(fetchEnvironments());
};

// Handle create environment
const handleCreateEnvironment = async (environmentData: Partial<Environment>) => {
setIsCreating(true);
try {
await createEnvironment(environmentData);
await refreshEnvironments(); // Refresh the list after creation
dispatch(fetchEnvironments()); // Refresh the list after creation
} catch (error) {
console.error("Failed to create environment:", error);
throw error; // Re-throw to let the modal handle the error display
Expand Down Expand Up @@ -153,7 +158,7 @@ const EnvironmentsList: React.FC = () => {
Create Environment
</Button>
<Button
icon={<SyncOutlined spin={isRefreshing} />}
icon={<SyncOutlined spin={isLoading} />}
onClick={handleRefresh}
loading={isLoading}
type="default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import React, { useState, useEffect } from 'react';
import { Modal, Form, Select, Checkbox, Button, Spin, Input, Tag, Space, Alert } from 'antd';
import { messageInstance } from 'lowcoder-design/src/components/GlobalInstances';
import { useSelector } from 'react-redux';
import { selectEnvironments, selectEnvironmentsLoading } from 'redux/selectors/enterpriseSelectors';
import { Environment } from '../types/environment.types';
import { DeployableItemConfig } from '../types/deployable-item.types';
import { useEnvironmentContext } from '../context/EnvironmentContext';
import { getEnvironmentTagColor, formatEnvironmentType } from '../utils/environmentUtils';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { showFirstCredentialOverwriteConfirm, showSecondCredentialOverwriteConfirm } from './credentialConfirmations';
Expand All @@ -27,7 +28,8 @@ function DeployItemModal({
onSuccess
}: DeployItemModalProps) {
const [form] = Form.useForm();
const { environments, isLoading } = useEnvironmentContext();
const environments = useSelector(selectEnvironments);
const isLoading = useSelector(selectEnvironmentsLoading);
const [deploying, setDeploying] = useState(false);
const [credentialConfirmationStep, setCredentialConfirmationStep] = useState(0); // 0: not started, 1: first confirmation, 2: confirmed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import React, {
} from "react";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import { useParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { fetchEnvironments } from "redux/reduxActions/enterpriseActions";
import { getEnvironmentById, updateEnvironment } from "../services/environments.service";
import { Environment } from "../types/environment.types";
import { useEnvironmentContext } from './EnvironmentContext';


interface SingleEnvironmentContextState {
// Environment data
environment: Environment | null;
Expand Down Expand Up @@ -53,8 +54,8 @@ import React, {
const { envId } = useParams<{ envId: string }>();
const environmentId = propEnvironmentId || envId;

// Access the environments context to refresh the list
const { refreshEnvironments } = useEnvironmentContext();
// Use Redux dispatch to refresh environments instead of context
const dispatch = useDispatch();

// State for environment data
const [environment, setEnvironment] = useState<Environment | null>(null);
Expand Down Expand Up @@ -103,18 +104,16 @@ import React, {
messageInstance.success("Environment updated successfully");

// Refresh both the single environment and environments list
await Promise.all([
fetchEnvironment(), // Refresh the current environment
refreshEnvironments() // Refresh the environments list
]);
await fetchEnvironment(); // Refresh the current environment
dispatch(fetchEnvironments()); // Refresh the environments list using Redux

return updatedEnv;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Failed to update environment";
messageInstance.error(errorMessage);
throw err;
}
}, [environment, environmentId, fetchEnvironment, refreshEnvironments]);
}, [environment, environmentId, fetchEnvironment, dispatch]);

// Load environment data when the component mounts or environmentId changes
useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions client/packages/lowcoder/src/redux/sagas/enterpriseSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { call, put, takeLatest } from 'redux-saga/effects';
import { ReduxAction, ReduxActionTypes } from "constants/reduxActionConstants";
import { setEnterpriseLicense, fetchEnvironmentsSuccess, fetchEnvironmentsFailure } from "redux/reduxActions/enterpriseActions";
import { BrandingSettingResponse, EnterpriseLicenseResponse, FetchBrandingSettingPayload, getBranding, getEnterpriseLicense } from "api/enterpriseApi";
import { getEnvironments } from "pages/setting/environments/services/environments.service";
import { getEnvironmentsWithLicenseStatus } from "pages/setting/environments/services/environments.service";
import { Environment } from "pages/setting/environments/types/environment.types";

import { AxiosResponse } from 'axios';
Expand All @@ -19,7 +19,7 @@ function* fetchEnterpriseLicenseSaga(): Generator<any, void, EnterpriseLicenseRe

function* fetchEnvironmentsSaga(): Generator<any, void, Environment[]> {
try {
const environments: Environment[] = yield call(getEnvironments);
const environments: Environment[] = yield call(getEnvironmentsWithLicenseStatus);
yield put(fetchEnvironmentsSuccess(environments));
} catch (error) {
console.error('Failed to fetch environments:', error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ export const getWorkspaceBrandingSetting = (state: AppState) => {
}
// Environment selectors
export const selectEnvironments = (state: AppState) =>
state.ui.enterprise?.environments ?? [];
state.ui.enterprise?.environments ?? [];

export const selectEnvironmentsLoading = (state: AppState) =>
state.ui.enterprise?.environmentsLoading ?? false;

export const selectEnvironmentsError = (state: AppState) =>
state.ui.enterprise?.environmentsError ?? null;