Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
20 changes: 17 additions & 3 deletions src/dashboard/Data/AppOverview/AppOverview.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import AppOverviewActions from './AppOverviewActions.react';
import ComplianceCard from './ComplianceCard.react';
import { Link } from 'react-router-dom';

import MCPIntegrationIDE from './MCPIntegrationIDE.js';
const LazyConnectAppModal = lazy(() => import('./ConnectAppModal.react'));
const LazyMCPSetupModal = lazy(() => import('./MCPSetupModal.js'));

Expand Down Expand Up @@ -64,6 +65,7 @@ class AppOverview extends DashboardView {
showCopiedTooltip: false,
showConnectAppModal: false,
showMCPSetupModal: false,
selectedMcpIde: null,

isLoadingWebhosting: true,
webhosting: undefined,
Expand All @@ -75,6 +77,7 @@ class AppOverview extends DashboardView {
this.loadCardInformation = this.loadCardInformation.bind(this);
this.pollSchemas = this.pollSchemas.bind(this);
this.handleLimitChange = this.handleLimitChange.bind(this);
this.handleMcpIdeClick = this.handleMcpIdeClick.bind(this);
}

componentWillMount() {
Expand Down Expand Up @@ -124,6 +127,13 @@ class AppOverview extends DashboardView {
});
}

handleMcpIdeClick(ide) {
this.setState({
showMCPSetupModal: true,
selectedMcpIde: ide
});
}

loadAllPerformanceData(currentApp, limit) {
const minutes = parseInt(limit);

Expand Down Expand Up @@ -266,7 +276,6 @@ class AppOverview extends DashboardView {
<AppKeysComponent appKeys={this.state.appKeys} copyText={this.copyText} />
<hr />
<button className={styles.appContentBtn} onClick={() => this.setState({ showConnectAppModal: true })}>Connect App</button>
<button className={styles.appMCPBtn} onClick={() => this.setState({ showMCPSetupModal: true })}>MCP Setup</button>
</div>
<div className={styles.appInformationBox}>
<div className={styles.appInfoCardHeader}>App Information</div>
Expand Down Expand Up @@ -304,6 +313,7 @@ class AppOverview extends DashboardView {
</div>
</div>
</div>
<MCPIntegrationIDE handleSelectedIDE={this.handleMcpIdeClick} />

<ComplianceCard loading={this.state.isLoadingAppPlanData} planData={this.state.appPlanData} appId={this.context.applicationId} isSignedBAA={this.context.custom.isSignedBAA} />

Expand Down Expand Up @@ -380,8 +390,12 @@ class AppOverview extends DashboardView {

{this.state.showMCPSetupModal && (
<Suspense fallback={'Loading...'}>
<LazyMCPSetupModal closeModal={() => this.setState({ showMCPSetupModal: false })} context={this.context} />
</Suspense>
<LazyMCPSetupModal
closeModal={() => this.setState({ showMCPSetupModal: false, selectedMcpIde: null })}
context={this.context}
selectedIDE={this.state.selectedMcpIde}
/>
</Suspense>
)}
</div>
);
Expand Down
64 changes: 64 additions & 0 deletions src/dashboard/Data/AppOverview/AppOverview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,70 @@
}
}

.mcpSelectIDEContainer {
display: flex;
flex-direction: column;
margin: 1.5rem 0;
border: 1px solid #253348;
border-radius: 8px;
background: #10203A;
overflow: hidden;
padding: 1.5rem;
gap: 1rem;

& .mcpChoiceIDEList {
display: flex;
gap: 1.5rem;
}

& .mcpChoiceIDE {
display: flex;
align-items: center;
padding: 1rem;
border: 1px solid #34506F;
transition: all 0.3s ease;
border-radius: 0.5rem;
cursor: pointer;
width: 100%;
gap: 1rem;
& .mcpChoiceTitle {
display: flex;
flex-direction: column;
gap: 0.5rem;
span:first-child {
font-size: 1rem;
font-weight: 600;
line-height: 140%;
}
span:last-child {
font-size: 0.75rem;
font-weight: 400;
}
}
& .mcpConnectBtn {
outline: none;
border: none;
border-radius: 0.5rem;
border: 1px solid #34506F;
background: rgba(255, 255, 255, 0.00);
padding: 0.5rem;
font-size: 0.75rem;
font-weight: 500;
flex-shrink: 0;
align-self: flex-end;

&:hover {
background: rgba($regal-blue, 0.1);
}
}
}


& svg {
fill: $light-blue;
}
}

@keyframes rotating {
from {
-ms-transform: rotate(0deg);
Expand Down
39 changes: 39 additions & 0 deletions src/dashboard/Data/AppOverview/MCPIntegrationIDE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import Icon from 'components/Icon/Icon.react';
import styles from './AppOverview.scss';

const MCPIntegrationIDE = ({ handleSelectedIDE }) => {
return (
<div className={styles.mcpSelectIDEContainer}>
<span>IDE integrations</span>
<div className={styles.mcpChoiceIDEList}>
<div className={styles.mcpChoiceIDE}>
<Icon name="b4a-cursor-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>
<span>Cursor</span>
<span>AI-powered editor to code faster and smarter.</span>
</div>
<button className={styles.mcpConnectBtn} onClick={() => handleSelectedIDE('cursor')}>Connect</button>
</div>
<div className={styles.mcpChoiceIDE}>
<Icon name="b4a-vscode-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>
<span>VSCode</span>
<span>Open-source editor for modern web and cloud development.</span>
</div>
<button className={styles.mcpConnectBtn} onClick={() => handleSelectedIDE('vscode')}>Connect</button>
</div>
<div className={styles.mcpChoiceIDE}>
<Icon name="b4a-windsurf-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>
<span>Windsurf</span>
<span>AI-native IDE built to keep developers in flow.</span>
</div>
<button className={styles.mcpConnectBtn} onClick={() => handleSelectedIDE('windsurf')}>Connect</button>
</div>
</div>
</div>
)
}

export default MCPIntegrationIDE;
37 changes: 8 additions & 29 deletions src/dashboard/Data/AppOverview/MCPSetupModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,16 @@ const getIDEContent = (ide, automatic, mcpKey) => {
}
}

const MCPSetupModal = ({ closeModal, context }) => {
const [currentStep, setCurrentStep] = useState(0);
const [selectedIDE, setSelectedIDE] = useState(null);
const MCPSetupModal = ({ closeModal, context, selectedIDE }) => {
const [currentStep, setCurrentStep] = useState(1);
const [automatic, setAutomatic] = useState(true);
const [mcpKey, setMcpKey] = useState('YOUR_ACCOUNT_KEY');

const handleIDEClick = (ide) => {
setSelectedIDE(ide);
setCurrentStep(1);
};
useEffect(() => {
if (!selectedIDE) {
setCurrentStep(1);
}
}, [selectedIDE]);

useEffect(() => {
const getMcpKey = async () => {
Expand All @@ -332,28 +332,7 @@ const MCPSetupModal = ({ closeModal, context }) => {

let content = null;

if (currentStep === 0) {
content = (
<div className={styles.mcpModalStep1}>
<div className={styles.mcpModalTitle}>Select Your IDE</div>
<div className={styles.mcpModalDescription}>Pick the IDE you want to use with the back4app MCP</div>
<div className={styles.mcpChoiceList}>
<div className={styles.mcpChoice} onClick={() => handleIDEClick('cursor')}>
<Icon name="b4a-cursor-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>Cursor</div>
</div>
<div className={styles.mcpChoice} onClick={() => handleIDEClick('vscode')}>
<Icon name="b4a-vscode-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>VSCode</div>
</div>
<div className={styles.mcpChoice} onClick={() => handleIDEClick('windsurf')}>
<Icon name="b4a-windsurf-icon" width={60} height={60} />
<div className={styles.mcpChoiceTitle}>Windsurf</div>
</div>
</div>
</div>
);
} else if (currentStep === 1) {
if (currentStep === 1) {
content = (
<div className={styles.mcpModalStep2}>
<div className={styles.mcpModalStep2Header}>
Expand Down
Loading